diff --git a/Readme.md b/Readme.md index 53a9c12..25fadff 100644 --- a/Readme.md +++ b/Readme.md @@ -1318,469 +1318,6 @@ $e(() => { }); ``` ---- - -## 🎮 Complete Examples - -### Real-time Todo Application - -```typescript -import { $, $e, html, $c } from 'sigpro'; - -// Styles -const styles = html` - -`; - -$c('todo-app', () => { - // State - const todos = $(() => { - const saved = localStorage.getItem('todos'); - return saved ? JSON.parse(saved) : []; - }); - - const newTodo = $(''); - const filter = $('all'); // 'all', 'active', 'completed' - const editingId = $(null); - const editText = $(''); - - // Save to localStorage on changes - $e(() => { - localStorage.setItem('todos', JSON.stringify(todos())); - }); - - // Filtered todos - const filteredTodos = $(() => { - const currentFilter = filter(); - const allTodos = todos(); - - switch (currentFilter) { - case 'active': - return allTodos.filter(t => !t.completed); - case 'completed': - return allTodos.filter(t => t.completed); - default: - return allTodos; - } - }); - - // Stats - const stats = $(() => { - const all = todos(); - return { - total: all.length, - completed: all.filter(t => t.completed).length, - active: all.filter(t => !t.completed).length - }; - }); - - // Actions - const addTodo = () => { - const text = newTodo().trim(); - if (!text) return; - - todos([ - ...todos(), - { - id: Date.now(), - text, - completed: false, - createdAt: new Date().toISOString() - } - ]); - newTodo(''); - }; - - const toggleTodo = (id) => { - todos(todos().map(todo => - todo.id === id - ? { ...todo, completed: !todo.completed } - : todo - )); - }; - - const deleteTodo = (id) => { - todos(todos().filter(todo => todo.id !== id)); - if (editingId() === id) { - editingId(null); - } - }; - - const startEdit = (todo) => { - editingId(todo.id); - editText(todo.text); - }; - - const saveEdit = (id) => { - const text = editText().trim(); - if (!text) { - deleteTodo(id); - } else { - todos(todos().map(todo => - todo.id === id ? { ...todo, text } : todo - )); - } - editingId(null); - }; - - const clearCompleted = () => { - todos(todos().filter(t => !t.completed)); - }; - - return html` - ${styles} -
-

📝 Todo App

- - -
- e.key === 'Enter' && addTodo()} - /> - -
- - -
- - - -
- - -
- ${() => filteredTodos().map(todo => html` -
- ${editingId() === todo.id ? html` - { - if (e.key === 'Enter') saveEdit(todo.id); - if (e.key === 'Escape') editingId(null); - }} - @blur=${() => saveEdit(todo.id)} - autofocus - /> - ` : html` - toggleTodo(todo.id)} - /> - startEdit(todo)}> - ${todo.text} - - - `} -
- `)} -
- - -
- ${() => { - const s = stats(); - return html` - Total: ${s.total} | - Active: ${s.active} | - Completed: ${s.completed} - ${s.completed > 0 ? html` - - ` : ''} - `; - }} -
-
- `; -}, []); -``` - -### Data Dashboard with Real-time Updates - -```typescript -import { $, $e, html, $c } from 'sigpro'; - -// Simulated WebSocket connection -class DataStream { - constructor() { - this.listeners = new Set(); - this.interval = setInterval(() => { - const data = { - timestamp: Date.now(), - value: Math.random() * 100, - category: ['A', 'B', 'C'][Math.floor(Math.random() * 3)] - }; - this.listeners.forEach(fn => fn(data)); - }, 1000); - } - - subscribe(listener) { - this.listeners.add(listener); - return () => this.listeners.delete(listener); - } - - destroy() { - clearInterval(this.interval); - } -} - -$c('data-dashboard', () => { - const stream = new DataStream(); - const dataPoints = $([]); - const selectedCategory = $('all'); - const timeWindow = $(60); // seconds - - // Subscribe to data stream - $e(() => { - const unsubscribe = stream.subscribe((newData) => { - dataPoints(prev => { - const updated = [...prev, newData]; - const maxAge = timeWindow() * 1000; - const cutoff = Date.now() - maxAge; - return updated.filter(d => d.timestamp > cutoff); - }); - }); - - return unsubscribe; - }); - - // Filtered data - const filteredData = $(() => { - const data = dataPoints(); - const category = selectedCategory(); - - if (category === 'all') return data; - return data.filter(d => d.category === category); - }); - - // Statistics - const statistics = $(() => { - const data = filteredData(); - if (data.length === 0) return null; - - const values = data.map(d => d.value); - return { - count: data.length, - avg: values.reduce((a, b) => a + b, 0) / values.length, - min: Math.min(...values), - max: Math.max(...values), - last: values[values.length - 1] - }; - }); - - // Cleanup on unmount - onUnmount(() => { - stream.destroy(); - }); - - return html` -
-

📊 Real-time Dashboard

- - -
- - - - Time window: ${timeWindow}s -
- - - ${() => { - const stats = statistics(); - if (!stats) return html`

Waiting for data...

`; - - return html` -
-
Points: ${stats.count}
-
Average: ${stats.avg.toFixed(2)}
-
Min: ${stats.min.toFixed(2)}
-
Max: ${stats.max.toFixed(2)}
-
Last: ${stats.last.toFixed(2)}
-
- `; - }} - - -
- ${() => filteredData().map(point => html` -
- `)} -
-
- `; -}, []); -``` - -## 🔧 Advanced Patterns - -### Custom Hooks - -```typescript -import { $, $e } from 'sigpro'; - -// useLocalStorage hook -function useLocalStorage(key, initialValue) { - const stored = localStorage.getItem(key); - const signal = $(stored ? JSON.parse(stored) : initialValue); - - $e(() => { - localStorage.setItem(key, JSON.stringify(signal())); - }); - - return signal; -} - -// useDebounce hook -function useDebounce(signal, delay) { - const debounced = $(signal()); - let timeout; - - $e(() => { - const value = signal(); - clearTimeout(timeout); - timeout = setTimeout(() => { - debounced(value); - }, delay); - }); - - return debounced; -} - -// useFetch hook -function useFetch(url) { - const data = $(null); - const error = $(null); - const loading = $(true); - - const fetchData = async () => { - loading(true); - error(null); - try { - const response = await fetch(url()); - const json = await response.json(); - data(json); - } catch (e) { - error(e); - } finally { -