Update Readme.md

This commit is contained in:
Natxo
2026-03-15 12:27:01 +01:00
committed by GitHub
parent a67d2e3241
commit 450f90af48

132
Readme.md
View File

@@ -120,7 +120,7 @@ count(5);
count(prev => prev + 1); // Use function for previous value count(prev => prev + 1); // Use function for previous value
// Read with dependency tracking (inside effect) // Read with dependency tracking (inside effect)
$e(() => { $effect(() => {
console.log(count()); // Will be registered as dependency console.log(count()); // Will be registered as dependency
}); });
``` ```
@@ -128,7 +128,7 @@ $e(() => {
#### Computed Signal #### Computed Signal
```typescript ```typescript
import { $, $e } from 'sigpro'; import { $, $effect } from 'sigpro';
const firstName = $('John'); const firstName = $('John');
const lastName = $('Doe'); const lastName = $('Doe');
@@ -176,16 +176,16 @@ type Signal<T> = {
--- ---
## 💾 `$$(key, initialValue, [storage])` - Persistent Signal ## 💾 `$storage(key, initialValue, [storage])` - Persistent Signal
Signal that automatically syncs with localStorage or sessionStorage. Signal that automatically syncs with localStorage or sessionStorage.
### Basic Persistent State ### Basic Persistent State
```js ```js
import { $$ } from 'sigpro'; import { $storage } from 'sigpro';
// Automatically saves to localStorage // Automatically saves to localStorage
const theme = $$('theme', 'light'); const theme = $storage('theme', 'light');
const user = $$('user', null); const user = $storage('user', null);
theme('dark'); // Saved to localStorage theme('dark'); // Saved to localStorage
// Page refresh... theme() returns 'dark' // Page refresh... theme() returns 'dark'
@@ -194,15 +194,15 @@ theme('dark'); // Saved to localStorage
### Session Storage ### Session Storage
```js ```js
// Use sessionStorage instead // Use sessionStorage instead
const tempData = $$('temp', {}, sessionStorage); const tempData = $storage('temp', {}, sessionStorage);
``` ```
### Real-World Example ### Real-World Example
```js ```js
import { $$, html } from 'sigpro'; import { $storage, html } from 'sigpro';
// User preferences persist across sessions // User preferences persist across sessions
const settings = $$('app-settings', { const settings = $storage('app-settings', {
darkMode: false, darkMode: false,
fontSize: 16, fontSize: 16,
language: 'en' language: 'en'
@@ -228,9 +228,9 @@ const view = html`
### Shopping Cart Example ### Shopping Cart Example
```js ```js
import { $$, html } from 'sigpro'; import { $storage, html } from 'sigpro';
const cart = $$('shopping-cart', []); const cart = $storage('shopping-cart', []);
const addToCart = (item) => { const addToCart = (item) => {
cart([...cart(), item]); cart([...cart(), item]);
@@ -241,7 +241,7 @@ const CartView = html`
<h3>Cart (${() => cart().length} items)</h3> <h3>Cart (${() => cart().length} items)</h3>
<ul> <ul>
${() => cart().map(item => html` ${() => cart().map(item => html`
<li>${item.name} - $${item.price}</li> <li>${item.name} - $storage{item.price}</li>
`)} `)}
</ul> </ul>
<button @click=${() => cart([])}>Clear Cart</button> <button @click=${() => cart([])}>Clear Cart</button>
@@ -252,7 +252,7 @@ const CartView = html`
### Auto-Cleanup ### Auto-Cleanup
```js ```js
// Remove from storage when value is null/undefined // Remove from storage when value is null/undefined
$$('temp', null); // Removes 'temp' from storage $storage('temp', null); // Removes 'temp' from storage
``` ```
**Parameters:** **Parameters:**
@@ -264,20 +264,20 @@ $$('temp', null); // Removes 'temp' from storage
--- ---
### `$e(effect)` - Effects ### `$effectffect(effect)` - Effects
Executes a function and automatically re-runs it when its dependencies change. Executes a function and automatically re-runs it when its dependencies change.
#### Basic Effect #### Basic Effect
```typescript ```typescript
import { $, $e } from 'sigpro'; import { $, $effectffect } from 'sigpro';
const count = $(0); const count = $(0);
const name = $('World'); const name = $('World');
// Effect runs immediately and on dependency changes // Effect runs immediately and on dependency changes
$e(() => { $effectffect(() => {
console.log(`Count is: ${count()}`); // Only depends on count console.log(`Count is: ${count()}`); // Only depends on count
}); });
// Log: "Count is: 0" // Log: "Count is: 0"
@@ -291,11 +291,11 @@ name('Universe'); // No log (name is not a dependency)
#### Effect with Cleanup #### Effect with Cleanup
```typescript ```typescript
import { $, $e } from 'sigpro'; import { $, $effectffect } from 'sigpro';
const userId = $(1); const userId = $(1);
$e(() => { $effectffect(() => {
const id = userId(); const id = userId();
let isSubscribed = true; let isSubscribed = true;
@@ -319,17 +319,17 @@ userId(2); // Previous subscription cleaned up, new one created
#### Nested Effects #### Nested Effects
```typescript ```typescript
import { $, $e } from 'sigpro'; import { $, $effectffect } from 'sigpro';
const show = $(true); const show = $(true);
const count = $(0); const count = $(0);
$e(() => { $effectffect(() => {
if (!show()) return; if (!show()) return;
// This effect is nested inside the conditional // This effect is nested inside the conditional
// It will only be active when show() is true // It will only be active when show() is true
$e(() => { $effectffect(() => {
console.log('Count changed:', count()); console.log('Count changed:', count());
}); });
}); });
@@ -342,12 +342,12 @@ show(true); // Inner effect recreated, logs "Count changed: 1"
#### Manual Effect Control #### Manual Effect Control
```typescript ```typescript
import { $, $e } from 'sigpro'; import { $, $effectffect } from 'sigpro';
const count = $(0); const count = $(0);
// Stop effect manually // Stop effect manually
const stop = $e(() => { const stop = $effectffect(() => {
console.log('Effect running:', count()); console.log('Effect running:', count());
}); });
@@ -363,28 +363,28 @@ count(2); // No log
--- ---
## 📡 `$f(data, url, [loading])` - Fetch ## 📡 `$fetch(data, url, [loading])` - Fetch
Simple fetch wrapper with automatic JSON handling and optional loading signal. Perfect for API calls. Simple fetch wrapper with automatic JSON handling and optional loading signal. Perfect for API calls.
### Basic Fetch ### Basic Fetch
```js ```js
import { $, $f } from 'sigpro'; import { $, $fetch } from 'sigpro';
const userData = $(null); const userData = $(null);
// Simple POST request // Simple POST request
const result = await $f('/api/users', { name: 'John' }); const result = await $fetch('/api/users', { name: 'John' });
``` ```
### Fetch with Loading State ### Fetch with Loading State
```js ```js
import { $, $f } from 'sigpro'; import { $, $fetch } from 'sigpro';
const loading = $(false); const loading = $(false);
const userData = $(null); const userData = $(null);
async function loadUser(id) { async function loadUser(id) {
const data = await $f(`/api/users/${id}`, null, loading); const data = await $fetch(`/api/users/${id}`, null, loading);
if (data) userData(data); if (data) userData(data);
} }
@@ -401,7 +401,7 @@ html`
### Error Handling ### Error Handling
```js ```js
const data = await $f('/api/users', { id: 123 }); const data = await $fetch('/api/users', { id: 123 });
if (!data) { if (!data) {
// Handle error silently (returns null on failure) // Handle error silently (returns null on failure)
console.error('Request failed'); console.error('Request failed');
@@ -669,7 +669,7 @@ html`<div class=${className}></div>`
html`<a href="${href}" class="link ${className}">Link</a>` html`<a href="${href}" class="link ${className}">Link</a>`
// Reactive attributes update when signal changes // Reactive attributes update when signal changes
$e(() => { $effectffect(() => {
// The attribute updates automatically // The attribute updates automatically
console.log('Class changed:', className()); console.log('Class changed:', className());
}); });
@@ -813,16 +813,16 @@ const Page = () => html`
--- ---
### `$c(tagName, setupFunction, observedAttributes)` - Web Components ### `$component(tagName, setupFunction, observedAttributes)` - Web Components
Creates Custom Elements with reactive properties. Uses Light DOM (no Shadow DOM) and a slot system based on node filtering. Creates Custom Elements with reactive properties. Uses Light DOM (no Shadow DOM) and a slot system based on node filtering.
#### Basic Component #### Basic Component
```javascript ```javascript
import { $, $c, html } from 'sigpro'; import { $, $component, html } from 'sigpro';
$c('my-counter', (props, context) => { $component('my-counter', (props, context) => {
// props contains signals for each observed attribute // props contains signals for each observed attribute
// context: { slot, emit, host, onUnmount } // context: { slot, emit, host, onUnmount }
@@ -859,9 +859,9 @@ Usage:
#### Component with Named Slots #### Component with Named Slots
```javascript ```javascript
import { $, $c, html } from 'sigpro'; import { $, $component, html } from 'sigpro';
$c('my-card', (props, { slot }) => { $component('my-card', (props, { slot }) => {
return html` return html`
<div class="card"> <div class="card">
<div class="header"> <div class="header">
@@ -897,9 +897,9 @@ Usage:
#### Component with Props and Events #### Component with Props and Events
```javascript ```javascript
import { $, $c, html } from 'sigpro'; import { $, $component, html } from 'sigpro';
$c('todo-item', (props, { emit, host }) => { $component('todo-item', (props, { emit, host }) => {
const handleToggle = () => { const handleToggle = () => {
props.completed(c => !c); props.completed(c => !c);
emit('toggle', { id: props.id(), completed: props.completed() }); emit('toggle', { id: props.id(), completed: props.completed() });
@@ -939,13 +939,13 @@ Usage:
#### Component with Cleanup #### Component with Cleanup
```javascript ```javascript
import { $, $c, html, $e } from 'sigpro'; import { $, $component, html, $effect } from 'sigpro';
$c('timer-widget', (props, { onUnmount }) => { $component('timer-widget', (props, { onUnmount }) => {
const seconds = $(0); const seconds = $(0);
// Effect with automatic cleanup // Effect with automatic cleanup
$e(() => { $effect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
seconds(s => s + 1); seconds(s => s + 1);
}, 1000); }, 1000);
@@ -971,9 +971,9 @@ $c('timer-widget', (props, { onUnmount }) => {
#### Complete Context API #### Complete Context API
```javascript ```javascript
import { $, $c, html } from 'sigpro'; import { $, $component, html } from 'sigpro';
$c('context-demo', (props, context) => { $component('context-demo', (props, context) => {
// Context properties: // Context properties:
// - slot(name) - Gets child nodes with matching slot attribute // - slot(name) - Gets child nodes with matching slot attribute
// - emit(name, detail) - Dispatches custom event // - emit(name, detail) - Dispatches custom event
@@ -1015,9 +1015,9 @@ $c('context-demo', (props, context) => {
#### Practical Example: Todo App Component #### Practical Example: Todo App Component
```javascript ```javascript
import { $, $c, html } from 'sigpro'; import { $, $component, html } from 'sigpro';
$c('todo-app', () => { $component('todo-app', () => {
const todos = $([]); const todos = $([]);
const newTodo = $(''); const newTodo = $('');
const filter = $('all'); const filter = $('all');
@@ -1104,7 +1104,7 @@ $c('todo-app', () => {
}, []); }, []);
``` ```
#### Key Points About `$c`: #### Key Points About `$component`:
1. **Light DOM only** - No Shadow DOM, children are accessible and styleable from outside 1. **Light DOM only** - No Shadow DOM, children are accessible and styleable from outside
2. **Slot system** - `slot()` function filters child nodes by `slot` attribute 2. **Slot system** - `slot()` function filters child nodes by `slot` attribute
@@ -1115,16 +1115,16 @@ $c('todo-app', () => {
--- ---
### `$r(routes)` - Router ### `$router(routes)` - Router
Hash-based router for SPAs with reactive integration. Hash-based router for SPAs with reactive integration.
#### Basic Routing #### Basic Routing
```typescript ```typescript
import { $r, html } from 'sigpro'; import { $router, html } from 'sigpro';
const router = $r([ const router = $router([
{ {
path: '/', path: '/',
component: () => html` component: () => html`
@@ -1147,9 +1147,9 @@ document.body.appendChild(router);
#### Route Parameters #### Route Parameters
```typescript ```typescript
import { $r, html } from 'sigpro'; import { $router, html } from 'sigpro';
const router = $r([ const router = $router([
{ {
path: '/user/:id', path: '/user/:id',
component: (params) => html` component: (params) => html`
@@ -1176,9 +1176,9 @@ const router = $r([
#### Nested Routes #### Nested Routes
```typescript ```typescript
import { $r, html, $ } from 'sigpro'; import { $router, html, $ } from 'sigpro';
const router = $r([ const router = $router([
{ {
path: '/', path: '/',
component: () => html` component: () => html`
@@ -1192,7 +1192,7 @@ const router = $r([
path: '/dashboard', path: '/dashboard',
component: () => { component: () => {
// Nested router // Nested router
const subRouter = $r([ const subRouter = $router([
{ {
path: '/', path: '/',
component: () => html`<h2>Dashboard Home</h2>` component: () => html`<h2>Dashboard Home</h2>`
@@ -1225,19 +1225,19 @@ const router = $r([
#### Route Guards #### Route Guards
```typescript ```typescript
import { $r, html, $ } from 'sigpro'; import { $router, html, $ } from 'sigpro';
const isAuthenticated = $(false); const isAuthenticated = $(false);
const requireAuth = (component) => (params) => { const requireAuth = (component) => (params) => {
if (!isAuthenticated()) { if (!isAuthenticated()) {
$r.go('/login'); $router.go('/login');
return null; return null;
} }
return component(params); return component(params);
}; };
const router = $r([ const router = $router([
{ {
path: '/', path: '/',
component: () => html`<h1>Public Home</h1>` component: () => html`<h1>Public Home</h1>`
@@ -1261,25 +1261,25 @@ const router = $r([
#### Navigation #### Navigation
```typescript ```typescript
import { $r } from 'sigpro'; import { $router } from 'sigpro';
// Navigate to path // Navigate to path
$r.go('/user/42'); $router.go('/user/42');
// Navigate with replace // Navigate with replace
$r.go('/dashboard', { replace: true }); $router.go('/dashboard', { replace: true });
// Go back // Go back
$r.back(); $router.back();
// Go forward // Go forward
$r.forward(); $router.forward();
// Get current path // Get current path
const currentPath = $r.getCurrentPath(); const currentPath = $router.getCurrentPath();
// Listen to navigation // Listen to navigation
$r.listen((path, oldPath) => { $router.listen((path, oldPath) => {
console.log(`Navigated from ${oldPath} to ${path}`); console.log(`Navigated from ${oldPath} to ${path}`);
}); });
``` ```
@@ -1287,9 +1287,9 @@ $r.listen((path, oldPath) => {
#### Route Transitions #### Route Transitions
```typescript ```typescript
import { $r, html, $e } from 'sigpro'; import { $router, html, $effect } from 'sigpro';
const router = $r([ const router = $router([
{ {
path: '/', path: '/',
component: () => html`<div class="page home">Home</div>` component: () => html`<div class="page home">Home</div>`
@@ -1301,7 +1301,7 @@ const router = $r([
]); ]);
// Add transitions // Add transitions
$e(() => { $effect(() => {
const currentPath = router.getCurrentPath(); const currentPath = router.getCurrentPath();
const pages = document.querySelectorAll('.page'); const pages = document.querySelectorAll('.page');