State Management
IndexedDB signals, cross-tab sync, undo/redo history, and form state machines.
IndexedDB Signals
Signal backed by IndexedDB — survives page refresh, handles large data.
No data stored
<div id="demo-idb"> <button id="btn-idb-set">Store Value</button> <button id="btn-idb-get">Read Value</button> <pre id="output-idb">No data stored</pre> </div>
import { enhance } from 'weblisk'; import { idbSignal } from 'weblisk/state/idb.js'; enhance('#demo-idb', (el, { $ }) => { const [val, setVal, { ready }] = idbSignal('demo-idb-value', { message: 'none', ts: 0 }); ready.then(() => { $('#output-idb').textContent = `Current: ${JSON.stringify(val(), null, 2)}`; }); $('#btn-idb-set').addEventListener('click', () => { const data = { message: `Stored at ${new Date().toLocaleTimeString()}`, ts: Date.now() }; setVal(data); }); $('#btn-idb-get').addEventListener('click', () => { $('#output-idb').textContent = `Current: ${JSON.stringify(val(), null, 2)}`; }); });
Cross-Tab Sync
Open this page in another tab — changes sync instantly via BroadcastChannel.
Counter: 0
import { effect, enhance } from 'weblisk'; import { synced } from 'weblisk/state/sync.js'; enhance('#demo-sync', (el, { $ }) => { const [count, setCount] = synced('demo-counter', 0); effect(() => { $('#output-sync').textContent = `Counter: ${count()}\n(Open another tab to see sync!)`; }); $('#btn-sync').addEventListener('click', () => setCount(c => c + 1) ); });
Undo / Redo
Edit the text, then undo/redo with the buttons or keyboard shortcuts.
History: 1 entry
<input type="text" id="undo-input" placeholder="Type something..." /> <button id="btn-undo">↩ Undo</button> <button id="btn-redo">↪ Redo</button> <pre id="output-undo"></pre>
import { effect, enhance } from 'weblisk'; import { undoable } from 'weblisk/state/history.js'; enhance('#demo-undo', (el, { $ }) => { const [text, setText, { undo, redo, canUndo, canRedo }] = undoable(''); let debounce; $('#undo-input').addEventListener('input', () => { clearTimeout(debounce); debounce = setTimeout( () => setText($('#undo-input').value), 300 ); }); effect(() => { const val = text(); if ($('#undo-input').value !== val) $('#undo-input').value = val; $('#output-undo').textContent = `Value: "${val}"\nCan undo: ${canUndo()} | Can redo: ${canRedo()}`; }); $('#btn-undo').addEventListener('click', undo); $('#btn-redo').addEventListener('click', redo); });
Form State Machine
Declarative form with built-in validation, submit state, and error tracking.
Fill out the form above
import { effect, enhance } from 'weblisk'; import { formState } from 'weblisk/state/form.js'; enhance('#demo-form', (el, { $ }) => { const fs = formState({ fields: { email: { required: true, pattern: /^[^\s@]+@[^\s@]+$/, message: 'Invalid email' }, age: { required: true, validate: v => { const n = parseInt(v); if (isNaN(n) || n < 1 || n > 120) return 'Must be 1-120'; return null; } } }, onSubmit: async (values) => { await new Promise(r => setTimeout(r, 500)); console.log('Submitted:', values); } }); fs.bind($('#demo-form-el')); effect(() => { const errs = fs.errors(); const vals = fs.values(); let text = `Values: ${JSON.stringify(vals)}\n`; text += `Valid: ${fs.valid()} | Dirty: ${fs.dirty()}\n`; if (Object.keys(errs).length) text += `Errors: ${JSON.stringify(errs)}`; if (fs.submitted()) text += '\nForm submitted!'; if (fs.submitting()) text = 'Submitting...'; $('#output-form').textContent = text; }); });