Update print statement from 'Hello' to 'Goodbye'

This commit is contained in:
Natxo
2026-03-14 01:15:25 +01:00
committed by GitHub
parent 6987041dc0
commit 867e8d8964

463
Readme.md
View File

@@ -1318,469 +1318,6 @@ $e(() => {
}); });
``` ```
---
## 🎮 Complete Examples
### Real-time Todo Application
```typescript
import { $, $e, html, $c } from 'sigpro';
// Styles
const styles = html`
<style>
.todo-app {
max-width: 500px;
margin: 2rem auto;
font-family: system-ui, sans-serif;
}
.todo-input {
display: flex;
gap: 0.5rem;
margin-bottom: 2rem;
}
.todo-input input {
flex: 1;
padding: 0.5rem;
border: 2px solid #e0e0e0;
border-radius: 4px;
}
.todo-input button {
padding: 0.5rem 1rem;
background: #0070f3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.todo-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
border-bottom: 1px solid #e0e0e0;
}
.todo-item.completed span {
text-decoration: line-through;
color: #999;
}
.todo-item button {
margin-left: auto;
padding: 0.25rem 0.5rem;
background: #ff4444;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.filters {
display: flex;
gap: 0.5rem;
margin: 1rem 0;
}
.filters button {
padding: 0.25rem 0.5rem;
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
.filters button.active {
background: #0070f3;
color: white;
border-color: #0070f3;
}
.stats {
margin-top: 1rem;
padding: 0.5rem;
background: #f5f5f5;
border-radius: 4px;
text-align: center;
}
</style>
`;
$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}
<div class="todo-app">
<h1>📝 Todo App</h1>
<!-- Add Todo -->
<div class="todo-input">
<input
type="text"
:value=${newTodo}
placeholder="What needs to be done?"
@keydown=${(e) => e.key === 'Enter' && addTodo()}
/>
<button @click=${addTodo}>Add</button>
</div>
<!-- Filters -->
<div class="filters">
<button
class=${() => filter() === 'all' ? 'active' : ''}
@click=${() => filter('all')}
>
All
</button>
<button
class=${() => filter() === 'active' ? 'active' : ''}
@click=${() => filter('active')}
>
Active
</button>
<button
class=${() => filter() === 'completed' ? 'active' : ''}
@click=${() => filter('completed')}
>
Completed
</button>
</div>
<!-- Todo List -->
<div class="todo-list">
${() => filteredTodos().map(todo => html`
<div class="todo-item ${todo.completed ? 'completed' : ''}" key=${todo.id}>
${editingId() === todo.id ? html`
<input
type="text"
:value=${editText}
@keydown=${(e) => {
if (e.key === 'Enter') saveEdit(todo.id);
if (e.key === 'Escape') editingId(null);
}}
@blur=${() => saveEdit(todo.id)}
autofocus
/>
` : html`
<input
type="checkbox"
?checked=${todo.completed}
@change=${() => toggleTodo(todo.id)}
/>
<span @dblclick=${() => startEdit(todo)}>
${todo.text}
</span>
<button @click=${() => deleteTodo(todo.id)}>✕</button>
`}
</div>
`)}
</div>
<!-- Stats -->
<div class="stats">
${() => {
const s = stats();
return html`
<span>Total: ${s.total}</span> |
<span>Active: ${s.active}</span> |
<span>Completed: ${s.completed}</span>
${s.completed > 0 ? html`
<button @click=${clearCompleted}>Clear completed</button>
` : ''}
`;
}}
</div>
</div>
`;
}, []);
```
### 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`
<div class="dashboard">
<h2>📊 Real-time Dashboard</h2>
<!-- Controls -->
<div class="controls">
<select :value=${selectedCategory}>
<option value="all">All Categories</option>
<option value="A">Category A</option>
<option value="B">Category B</option>
<option value="C">Category C</option>
</select>
<input
type="range"
min="10"
max="300"
step="10"
:value=${timeWindow}
/>
<span>Time window: ${timeWindow}s</span>
</div>
<!-- Statistics -->
${() => {
const stats = statistics();
if (!stats) return html`<p>Waiting for data...</p>`;
return html`
<div class="stats">
<div>Points: ${stats.count}</div>
<div>Average: ${stats.avg.toFixed(2)}</div>
<div>Min: ${stats.min.toFixed(2)}</div>
<div>Max: ${stats.max.toFixed(2)}</div>
<div>Last: ${stats.last.toFixed(2)}</div>
</div>
`;
}}
<!-- Chart (simplified) -->
<div class="chart">
${() => filteredData().map(point => html`
<div
class="bar"
style="
height: ${point.value}px;
background: ${point.category === 'A' ? '#ff4444' :
point.category === 'B' ? '#44ff44' : '#4444ff'};
"
title="${new Date(point.timestamp).toLocaleTimeString()}: ${point.value.toFixed(2)}"
></div>
`)}
</div>
</div>
`;
}, []);
```
## 🔧 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 {