node-red-contrib-uibuilder
Version:
Easily create data-driven web UI's for Node-RED. Single- & Multi-page. Multiple UI's. Work with existing web development workflows or mix and match with no-code/low-code features.
329 lines (278 loc) • 12.8 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="../uibuilder/images/node-blue.ico">
<title>UIBuilder Experimental Features Demo</title>
<link type="text/css" rel="stylesheet" href="./index.css" media="all">
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.demo-section {
margin: 20px 0;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
.auto-grid {
margin: 10px 0;
}
.grid-item {
background: #f0f0f0;
padding: 10px;
text-align: center;
border-radius: 3px;
}
.uib-experimental-dialog {
padding: 20px;
border: none;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.uib-dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.uib-dialog-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}
.uib-dialog-footer {
margin-top: 15px;
text-align: right;
}
.uib-dialog-footer button {
margin-left: 10px;
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
.uib-primary {
background: #007cba;
color: white;
border-color: #007cba;
}
</style>
</head><body>
<header>
<h1 class="with-subtitle">UIBuilder Experimental Features Demo</h1>
<div role="doc-subtitle">Using UIBUILDER-EXPERIMENTAL for Node-RED</div>
</header>
<main>
<article class="demo-section"><h2>Reactive Binding Demo</h2>
<p>Type in the input to see reactive binding in action:</p>
<input type="text" id="nameInput" placeholder="Enter your name">
<p>Hello, <span uib-bind="userName">World</span>!</p>
<p>
How can we make this code-free? Perhaps using `uib-bind` on the input as well?
</p>
<label>
<input type="checkbox" id="showGreeting"> Show greeting
</label>
<div uib-show="showGreeting" style="display: none;">
<p>Welcome to the experimental features demo!</p>
</div>
</article>
<article class="demo-section"><h2>Template Engine Demo</h2>
<p>Templates automatically update when variables change:</p>
<button onclick="updateTemplate()">Update Template Data</button>
<button onclick="updateUserName()">Change User Name</button>
<button onclick="updateNotifications()">Add Notification</button>
<button onclick="toggleAutoUpdate()">Toggle Auto-Update</button>
<template id="greeting-template">
<h3>{{title}}</h3>
<p>Hello {{userName}}, you have {{notifications}} notifications.</p>
<p>Today is {{date}}.</p>
<p>Current time: {{currentTime}}</p>
</template>
<div uib-template="#greeting-template" id="template-output">
<!-- Template will be rendered here -->
</div>
<div style="margin-top: 15px;">
<h4>Manual Template Processing (no auto-update):</h4>
<div id="manual-template-output">
<!-- Manual template will be rendered here -->
</div>
</div>
</article>
<article class="demo-section"><h2>Auto Layout Demo</h2>
<button onclick="applyGridLayout()">Apply Grid Layout</button>
<button onclick="applyFlexLayout()">Apply Flex Layout</button>
<div class="auto-grid">
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
<div class="grid-item">Item 3</div>
<div class="grid-item">Item 4</div>
<div class="grid-item">Item 5</div>
<div class="grid-item">Item 6</div>
</div>
</article>
<article class="demo-section"><h2>Enhanced Dialog Demo</h2>
<button onclick="showConfirmDialog()">Show Confirmation Dialog</button>
<button onclick="showCustomDialog()">Show Custom Dialog</button>
<p id="dialog-result">Dialog result will appear here</p>
</article>
</main>
<script type="module">
// Import the experimental module
import '../uibuilder/experimental.mjs'
console.log('Experimental features loaded:', uibExperimental.getExperimentalMeta())
// Set up reactive bindings
setupReactiveDemo(uibExperimental)
// Initialize template demo
initializeTemplateDemo(uibExperimental)
// Make functions available globally for button clicks
window.updateTemplate = () => updateTemplate(uibExperimental)
window.updateUserName = () => updateUserName(uibExperimental)
window.updateNotifications = () => updateNotifications(uibExperimental)
window.toggleAutoUpdate = () => toggleAutoUpdate(uibExperimental)
window.applyGridLayout = () => applyGridLayout(uibExperimental)
window.applyFlexLayout = () => applyFlexLayout(uibExperimental)
window.showConfirmDialog = () => showConfirmDialog(uibExperimental)
window.showCustomDialog = () => showCustomDialog(uibExperimental)
function applyGridLayout(uibExp) {
uibExp.applyAutoLayout('.auto-grid', {
type: 'grid',
columns: 'repeat(auto-fit, minmax(150px, 1fr))',
gap: '10px'
})
}
function applyFlexLayout(uibExp) {
uibExp.applyAutoLayout('.auto-grid', {
type: 'flex',
wrap: 'wrap',
gap: '10px',
justify: 'space-around'
})
}
async function showConfirmDialog(uibExp) {
const result = await uibExp.showExperimentalDialog({
title: 'Confirm Action',
template: '<p>Are you sure you want to proceed?</p>',
modal: true
})
document.getElementById('dialog-result').textContent = `Confirmation result: ${result}`
}
async function showCustomDialog(uibExp) {
const result = await uibExp.showExperimentalDialog({
title: 'Custom Dialog',
template: `
<div style="text-align: center;">
<p>🎉 This is a custom dialog with experimental features!</p>
<p>It supports HTML templating and custom styling.</p>
<input type="text" placeholder="Enter something..." style="margin: 10px; padding: 5px;">
</div>
`,
modal: true
})
document.getElementById('dialog-result').textContent = `Custom dialog result: ${result}`
}
function setupReactiveDemo(uibExp) {
// Set up reactive binding for name input
const nameInput = document.getElementById('nameInput')
nameInput.addEventListener('input', (e) => {
uibExp.set('userName', e.target.value)
})
// Set up reactive binding for checkbox
const showCheckbox = document.getElementById('showGreeting')
showCheckbox.addEventListener('change', (e) => {
uibExp.set('showGreeting', e.target.checked)
})
// Initialize values
uibExp.set('userName', 'World')
uibExp.set('showGreeting', false)
}
// Template demo variables
let autoUpdateEnabled = true
function initializeTemplateDemo(uibExp) {
// Set initial template data
const initialData = {
title: 'Welcome Dashboard',
userName: 'John Doe',
notifications: 3,
date: new Date().toLocaleDateString(),
currentTime: new Date().toLocaleTimeString()
}
// Apply templates with auto-update
uibExp.applyTemplates(initialData, true)
// Also render manual template (no auto-update)
const manualElement = document.getElementById('manual-template-output')
const template = document.getElementById('greeting-template').innerHTML
manualElement.innerHTML = uibExp.processTemplate(template, initialData)
// Update time every second to demonstrate auto-updates
setInterval(() => {
uibExp.set('currentTime', new Date().toLocaleTimeString())
}, 1000)
}
function updateTemplate(uibExp) {
// Update multiple template variables at once
const newData = {
title: `Dashboard - ${Math.random().toString(36).substr(2, 5)}`,
date: new Date().toLocaleDateString(),
notifications: Math.floor(Math.random() * 10)
}
// This will automatically trigger re-render of auto-update templates
Object.entries(newData).forEach(([key, value]) => {
uibExp.set(key, value)
})
// Manually update the non-auto-update template
const manualElement = document.getElementById('manual-template-output')
const template = document.getElementById('greeting-template').innerHTML
const currentData = {
title: uibExp.get('title'),
userName: uibExp.get('userName'),
notifications: uibExp.get('notifications'),
date: uibExp.get('date'),
currentTime: uibExp.get('currentTime')
}
manualElement.innerHTML = uibExp.processTemplate(template, currentData)
}
function updateUserName(uibExp) {
const names = ['John Doe', 'Jane Smith', 'Bob Johnson', 'Alice Williams', 'Charlie Brown']
const currentName = uibExp.get('userName') || 'John Doe'
const currentIndex = names.indexOf(currentName)
const newName = names[(currentIndex + 1) % names.length]
// This will automatically update all templates using {{userName}}
uibExp.set('userName', newName)
}
function updateNotifications(uibExp) {
const current = uibExp.get('notifications') || 0
// This will automatically update all templates using {{notifications}}
uibExp.set('notifications', current + 1)
}
function toggleAutoUpdate(uibExp) {
autoUpdateEnabled = !autoUpdateEnabled
const button = event.target
button.textContent = autoUpdateEnabled ? 'Disable Auto-Update' : 'Enable Auto-Update'
if (autoUpdateEnabled) {
// Re-enable auto-updates by re-applying templates
const currentData = {
title: uibExp.get('title') || 'Welcome Dashboard',
userName: uibExp.get('userName') || 'John Doe',
notifications: uibExp.get('notifications') || 3,
date: uibExp.get('date') || new Date().toLocaleDateString(),
currentTime: uibExp.get('currentTime') || new Date().toLocaleTimeString()
}
uibExp.applyTemplates(currentData, true)
} else {
// Disable auto-updates by unbinding templates
const templateElement = document.querySelector('[uib-template]')
if (templateElement) {
uibExp.unbindTemplate(templateElement)
}
}
}
</script>
</body></html>