Update Funcions

This commit is contained in:
2026-04-06 18:28:07 +02:00
parent 5bbd2adf82
commit bf3069d439
24 changed files with 793 additions and 293 deletions

View File

@@ -1,11 +1,11 @@
# Reactive Lists: `$for( )`
# Reactive Lists: `For( )`
The `$for` function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple `.map()`, `$for` is **keyed**, meaning it only updates, moves, or deletes the specific items that changed.
The `For` function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple `.map()`, `For` is **keyed**, meaning it only updates, moves, or deletes the specific items that changed.
## Function Signature
```typescript
$for(
For(
source: Signal<any[]> | Function | any[],
render: (item: any, index: number) => HTMLElement,
keyFn?: (item: any, index: number) => string | number
@@ -34,7 +34,7 @@ const users = $([
]);
Ul({ class: "list" }, [
$for(users,
For(users,
(user) => Li({ class: "p-2" }, user.name),
(user) => user.id // Stable and unique key
)
@@ -42,14 +42,14 @@ Ul({ class: "list" }, [
```
### 2. Simplified Usage (Automatic Key)
If you omit the third parameter, `$for` will automatically use the array index as the key. This is ideal for simple lists that don't change order frequently.
If you omit the third parameter, `For` will automatically use the array index as the key. This is ideal for simple lists that don't change order frequently.
```javascript
const tags = $(["Tech", "JS", "Web"]);
// No need to pass keyFn if the index is sufficient
Div({ class: "flex gap-1" }, [
$for(tags, (tag) => Badge(tag))
For(tags, (tag) => Badge(tag))
]);
```
@@ -57,7 +57,7 @@ Div({ class: "flex gap-1" }, [
## How it Works (The Reconciliation)
When the `source` signal changes, `$for` performs the following steps:
When the `source` signal changes, `For` performs the following steps:
1. **Key Diffing**: It compares the new keys with the previous ones stored in an internal `Map`.
2. **Node Reuse**: If a key already exists, the DOM node is **reused** and moved to its new position. No new elements are created.
@@ -68,13 +68,13 @@ When the `source` signal changes, `$for` performs the following steps:
## Performance Tips
* **Stable Keys**: Never use `Math.random()` as a key. This will force SigPro to destroy and recreate the entire list on every update, killing performance.
* **State Preservation**: If your list items have internal state (like an input with text), `$for` ensures that state is preserved even if the list is reordered, as long as the key (`id`) remains the same.
* **State Preservation**: If your list items have internal state (like an input with text), `For` ensures that state is preserved even if the list is reordered, as long as the key (`id`) remains the same.
---
## Summary Comparison
| Feature | Standard `Array.map` | SigPro `$for` |
| Feature | Standard `Array.map` | SigPro `For` |
| :--- | :--- | :--- |
| **Re-render** | Re-renders everything | Only updates changes |
| **DOM Nodes** | Re-created every time | **Reused via Keys** |

View File

@@ -1,11 +1,11 @@
# The DOM Factory: `$html( )`
# The DOM Factory: `Tag( )`
`$html` is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses `$watch` to maintain a live, high-performance link between your Signals and the Document Object Model.
`Tag` is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses `Watch` to maintain a live, high-performance link between your Signals and the Document Object Model.
## Function Signature
```typescript
$html(tagName: string, props?: Object, children?: any[] | any): HTMLElement
Tag(tagName: string, props?: Object, children?: any[] | any): HTMLElement
```
| Parameter | Type | Required | Description |
@@ -50,7 +50,7 @@ Button({
```
### 4. Reactive Attributes (One-Way)
If an attribute value is a **function** (like a Signal), `$html` creates an internal **`$watch`** to keep the DOM in sync with the state.
If an attribute value is a **function** (like a Signal), `Tag` creates an internal **`Watch`** to keep the DOM in sync with the state.
```javascript
Div({
@@ -72,7 +72,7 @@ Input({
> **Note:** To use a Signal as **read-only** in an input, wrap it in an anonymous function: `value: () => username()`.
### 6. Reactive Children
Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary using `$watch` for that specific part of the DOM.
Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary using `Watch` for that specific part of the DOM.
```javascript
Div({}, [
@@ -85,20 +85,20 @@ Div({}, [
---
## Memory Management (Internal)
Every element created with `$html` is "self-aware" regarding its reactive dependencies.
* **`._cleanups`**: A hidden `Set` attached to the element that stores all `stop()` functions from its internal `$watch` calls and event listeners.
* **Lifecycle**: When an element is removed by a Controller (`$if`, `$for`, or `$router`), SigPro performs a recursive **"sweep"** to execute these cleanups, ensuring **zero memory leaks**.
Every element created with `Tag` is "self-aware" regarding its reactive dependencies.
* **`._cleanups`**: A hidden `Set` attached to the element that stores all `stop()` functions from its internal `Watch` calls and event listeners.
* **Lifecycle**: When an element is removed by a Controller (`If`, `For`, or `Router`), SigPro performs a recursive **"sweep"** to execute these cleanups, ensuring **zero memory leaks**.
---
## Tag Constructors (The Shortcuts)
Instead of writing `$html("div", ...)` every time, SigPro provides PascalCase global functions for all standard HTML tags. These are direct mappings to the `$html` factory.
Instead of writing `Tag("div", ...)` every time, SigPro provides PascalCase global functions for all standard HTML tags. These are direct mappings to the `Tag` factory.
```javascript
// This:
Div({ class: "wrapper" }, [ Span("Hello") ])
// Is exactly equivalent to:
$html("div", { class: "wrapper" }, [ $html("span", {}, "Hello") ])
Tag("div", { class: "wrapper" }, [ Tag("span", {}, "Hello") ])
```

View File

@@ -1,11 +1,11 @@
# Reactive Branching: `$if( )`
# Reactive Branching: `If( )`
The `$if` function is a reactive control flow operator. It manages the conditional rendering of components with optional smooth transitions, ensuring that only the active branch exists in the DOM and in memory.
The `If` function is a reactive control flow operator. It manages the conditional rendering of components with optional smooth transitions, ensuring that only the active branch exists in the DOM and in memory.
## Function Signature
```typescript
$if(
If(
condition: Signal<boolean> | Function,
thenVal: Component | Node,
otherwiseVal?: Component | Node,
@@ -53,7 +53,7 @@ const fade = {
}
};
$if(show, Modal, null, fade);
If(show, Modal, null, fade);
```
---
@@ -68,7 +68,7 @@ const isVisible = $(false);
Div([
Button({ onclick: () => isVisible(!isVisible()) }, "Toggle Message"),
$if(isVisible,
If(isVisible,
P("Now you see me!"),
P("Now you don't...")
)
@@ -83,7 +83,7 @@ const showModal = $(false);
Div([
Button({ onclick: () => showModal(true) }, "Open Modal"),
$if(showModal,
If(showModal,
() => Modal({ onClose: () => showModal(false) }),
null,
fade // ← Smooth enter/exit animation
@@ -93,10 +93,10 @@ Div([
### 3. Lazy Component Loading
Unlike CSS `display: none`, `$if` is **lazy**. The inactive branch is never created, saving memory.
Unlike CSS `display: none`, `If` is **lazy**. The inactive branch is never created, saving memory.
```javascript
$if(() => user.isLogged(),
If(() => user.isLogged(),
() => Dashboard(), // Only executed if logged in
() => LoginGate() // Only executed if guest
)
@@ -105,15 +105,15 @@ $if(() => user.isLogged(),
### 4. Complex Conditions
```javascript
$if(() => count() > 10 && status() === 'ready',
If(() => count() > 10 && status() === 'ready',
Span("Threshold reached!")
)
```
### 5. $if.not Helper
### 5. If.not Helper
```javascript
$if.not(loading,
If.not(loading,
() => Content(), // Shows when loading is FALSE
() => Spinner() // Shows when loading is TRUE
)
@@ -123,10 +123,10 @@ $if.not(loading,
## Automatic Cleanup
One of the core strengths of `$if` is its integrated **Cleanup** logic. SigPro ensures that when a branch is swapped out, it is completely purged.
One of the core strengths of `If` is its integrated **Cleanup** logic. SigPro ensures that when a branch is swapped out, it is completely purged.
1. **Stop Watchers**: All `$watch` calls inside the inactive branch are permanently stopped.
2. **Unbind Events**: Event listeners attached via `$html` are removed.
1. **Stop Watchers**: All `Watch` calls inside the inactive branch are permanently stopped.
2. **Unbind Events**: Event listeners attached via `Tag` are removed.
3. **Recursive Sweep**: SigPro performs a deep "sweep" of the removed branch.
4. **Transition Respect**: When using transitions, destruction only happens AFTER the `out` animation completes.
@@ -142,7 +142,7 @@ One of the core strengths of `$if` is its integrated **Cleanup** logic. SigPro e
## Technical Comparison
| Feature | Standard CSS `hidden` | SigPro `$if` |
| Feature | Standard CSS `hidden` | SigPro `If` |
| :--- | :--- | :--- |
| **DOM Presence** | Always present | Only if active |
| **Reactivity** | Still processing | **Paused/Destroyed** |

View File

@@ -11,7 +11,7 @@ SigPro works seamlessly with JSX.
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "$html",
"jsxFactory": "Tag",
"jsxFragmentFactory": "Fragment"
}
}
@@ -25,7 +25,7 @@ import { defineConfig } from 'vite'
export default defineConfig({
esbuild: {
jsxFactory: '$html',
jsxFactory: 'Tag',
jsxFragmentFactory: 'Fragment'
}
})
@@ -38,7 +38,7 @@ export default defineConfig({
export default {
plugins: [
['@babel/plugin-transform-react-jsx', {
pragma: '$html',
pragma: 'Tag',
pragmaFrag: 'Fragment'
}]
]
@@ -49,7 +49,7 @@ export default {
```jsx
// App.jsx
import { $, $mount, Fragment } from 'sigpro';
import { $, Mount, Fragment } from 'sigpro';
const Button = ({ onClick, children }) => (
<button class="btn btn-primary" onclick={onClick}>
@@ -76,7 +76,7 @@ const App = () => {
);
};
$mount(App, '#app');
Mount(App, '#app');
```
## What Gets Compiled
@@ -90,8 +90,8 @@ Your JSX:
Compiles to:
```javascript
$html('div', { class: "container" },
$html(Button, {}, "Click")
Tag('div', { class: "container" },
Tag(Button, {}, "Click")
)
```
@@ -109,7 +109,7 @@ SigPro automatically injects `Div()`, `Button()`, `Span()`, and all other HTML t
<div id="app"></div>
<script>
const { $, $mount, Fragment } = SigPro;
const { $, Mount, Fragment } = SigPro;
const App = () => {
const count = $(0);
@@ -127,7 +127,7 @@ SigPro automatically injects `Div()`, `Button()`, `Span()`, and all other HTML t
]);
};
$mount(App, '#app');
Mount(App, '#app');
</script>
</body>
</html>
@@ -138,10 +138,10 @@ SigPro automatically injects `Div()`, `Button()`, `Span()`, and all other HTML t
For a JSX-like syntax without a build step, use `htm`:
```javascript
import { $, $mount } from 'https://unpkg.com/sigpro';
import { $, Mount } from 'https://unpkg.com/sigpro';
import htm from 'https://esm.sh/htm';
const html = htm.bind($html);
const html = htm.bind(Tag);
const App = () => {
const count = $(0);
@@ -156,7 +156,7 @@ const App = () => {
`;
};
$mount(App, '#app');
Mount(App, '#app');
```
## Summary

View File

@@ -1,11 +1,11 @@
# Application Mounter: `$mount( )`
# Application Mounter: `Mount( )`
The `$mount` function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle.
The `Mount` function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle.
## Function Signature
```typescript
$mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
Mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
```
| Parameter | Type | Default | Description |
@@ -27,7 +27,7 @@ import SigPro from 'sigpro';
import App from './App.js';
// Mounts your main App component
$mount(App, '#app-root');
Mount(App, '#app-root');
```
### 2. Reactive "Islands"
@@ -42,17 +42,17 @@ const Counter = () => {
};
// Mount only the counter into a specific sidebar div
$mount(Counter, '#sidebar-widget');
Mount(Counter, '#sidebar-widget');
```
---
## How it Works (Lifecycle & Cleanup)
When `$mount` is executed, it performs these critical steps to ensure a leak-free environment:
When `Mount` is executed, it performs these critical steps to ensure a leak-free environment:
1. **Duplicate Detection**: If you call `$mount` on a target that already has a SigPro instance, it automatically calls `.destroy()` on the previous instance. This prevents "Zombie Effects" from stacking in memory.
2. **Internal Scoping**: It executes the component function inside an internal **Reactive Owner**. This captures every `$watch` and event listener created during the render.
1. **Duplicate Detection**: If you call `Mount` on a target that already has a SigPro instance, it automatically calls `.destroy()` on the previous instance. This prevents "Zombie Effects" from stacking in memory.
2. **Internal Scoping**: It executes the component function inside an internal **Reactive Owner**. This captures every `Watch` and event listener created during the render.
3. **Target Injection**: It clears the target using `replaceChildren()` and appends the new component.
4. **Runtime Creation**: It returns a control object:
* `container`: The actual DOM element created.
@@ -62,10 +62,10 @@ When `$mount` is executed, it performs these critical steps to ensure a leak-fre
## Manual Unmounting
While SigPro handles most cleanups automatically (via `$if`, `$for`, and `$router`), you can manually destroy any mounted instance. This is vital for imperatively managed UI like **Toasts** or **Modals**.
While SigPro handles most cleanups automatically (via `If`, `For`, and `Router`), you can manually destroy any mounted instance. This is vital for imperatively managed UI like **Toasts** or **Modals**.
```javascript
const instance = $mount(MyToast, '#toast-container');
const instance = Mount(MyToast, '#toast-container');
// Later, to remove the toast and kill its reactivity:
instance.destroy();
@@ -77,9 +77,9 @@ instance.destroy();
| Goal | Code Pattern |
| :--- | :--- |
| **Mount to body** | `$mount(App)` |
| **Mount to CSS Selector** | `$mount(App, '#root')` |
| **Mount to DOM Node** | `$mount(App, myElement)` |
| **Clean & Re-mount** | Calling `$mount` again on the same target |
| **Total Cleanup** | `const app = $mount(App); app.destroy();` |
| **Mount to body** | `Mount(App)` |
| **Mount to CSS Selector** | `Mount(App, '#root')` |
| **Mount to DOM Node** | `Mount(App, myElement)` |
| **Clean & Re-mount** | Calling `Mount` again on the same target |
| **Total Cleanup** | `const app = Mount(App); app.destroy();` |

View File

@@ -5,17 +5,16 @@ SigPro is a high-performance micro-framework that updates the **Real DOM** surgi
<div class="text-center my-8">
<div class="flex justify-center gap-2 flex-wrap mb-4">
<span class="badge badge-primary badge-lg font-mono text-lg">$-$$</span>
<span class="badge badge-secondary badge-lg font-mono text-lg">$watch</span>
<span class="badge badge-accent badge-lg font-mono text-lg">$html</span>
<span class="badge badge-info badge-lg font-mono text-lg">$if</span>
<span class="badge badge-success badge-lg font-mono text-lg">$for</span>
<span class="badge badge-warning badge-lg font-mono text-lg">$router</span>
<span class="badge badge-error badge-lg font-mono text-lg">$mount</span>
<span class="badge badge-secondary badge-lg font-mono text-lg">Watch</span>
<span class="badge badge-accent badge-lg font-mono text-lg">Tag</span>
<span class="badge badge-info badge-lg font-mono text-lg">If</span>
<span class="badge badge-success badge-lg font-mono text-lg">For</span>
<span class="badge badge-warning badge-lg font-mono text-lg">Router</span>
<span class="badge badge-error badge-lg font-mono text-lg">Mount</span>
</div>
<h1 class="text-5xl font-black bg-gradient-to-r from-primary via-secondary to-accent bg-clip-text text-transparent">
⚡ All the power! ⚡
</h1>
<p class="text-xl opacity-70 mt-2">6 functions that will change the way you code</p>
</div>
## Core Functions
@@ -48,32 +47,32 @@ Explore the reactive building blocks of SigPro.
<td>Creates a <b>Deep Reactive Proxy</b>. Track nested property access automatically. No need for manual signals.</td>
</tr>
<tr>
<td><code class="text-secondary font-bold">$watch(fn)</code></td>
<td><code class="text-secondary font-bold">Watch(fn)</code></td>
<td class="font-mono text-xs opacity-70">(function) => stopFn</td>
<td><b>Auto Mode:</b> Tracks any signal touched inside. Returns a stop function.</td>
</tr>
<tr>
<td><code class="text-secondary font-bold">$watch(deps, fn)</code></td>
<td><code class="text-secondary font-bold">Watch(deps, fn)</code></td>
<td class="font-mono text-xs opacity-70">(Array, function) => stopFn</td>
<td><b>Explicit Mode:</b> Only runs when signals in <code>deps</code> change.</td>
</tr>
<tr>
<td><code class="text-accent font-bold">$if(cond, then, else?)</code></td>
<td><code class="text-accent font-bold">If(cond, then, else?)</code></td>
<td class="font-mono text-xs opacity-70">(Signal|bool, fn, fn?) => Node</td>
<td>Reactive conditional. Automatically destroys "else" branch memory.</td>
</tr>
<tr>
<td><code class="text-accent font-bold">$for(src, render, key)</code></td>
<td><code class="text-accent font-bold">For(src, render, key)</code></td>
<td class="font-mono text-xs opacity-70">(Signal, fn, fn) => Node</td>
<td><b>Keyed Loop:</b> Optimized list renderer. Uses the key function for surgical DOM moves.</td>
</tr>
<tr>
<td><code class="text-info font-bold">$router(routes)</code></td>
<td><code class="text-info font-bold">Router(routes)</code></td>
<td class="font-mono text-xs opacity-70">(Array) => Node</td>
<td><b>SPA Router:</b> Hash-based routing with dynamic params (<code>:id</code>) and auto-cleanup.</td>
</tr>
<tr>
<td><code class="font-bold">$mount(node, target)</code></td>
<td><code class="font-bold">Mount(node, target)</code></td>
<td class="font-mono text-xs opacity-70">(any, string|Node) => Runtime</td>
<td>Entry point. Cleans the target and mounts the app with full lifecycle management.</td>
</tr>
@@ -107,8 +106,8 @@ SigPro provides **PascalCase** wrappers for all standard HTML5 tags (e.g., `Div`
<div class="card bg-base-200 border border-base-300 shadow-sm">
<div class="card-body p-5">
<h3 class="text-xs font-black uppercase tracking-widest opacity-60">Dynamic Routing</h3>
<code class="text-info font-bold text-sm bg-base-300/50 p-2 rounded-lg">$router.to('/user/1')</code>
<p class="text-xs mt-3 leading-relaxed">Navigate programmatically. Access params via <code>$router.params().id</code>.</p>
<code class="text-info font-bold text-sm bg-base-300/50 p-2 rounded-lg">Router.to('/user/1')</code>
<p class="text-xs mt-3 leading-relaxed">Navigate programmatically. Access params via <code>Router.params().id</code>.</p>
</div>
</div>
@@ -131,14 +130,14 @@ SigPro provides **PascalCase** wrappers for all standard HTML5 tags (e.g., `Div`
---
## Custom API BYOS (Bring Your Own Syntax)
## Custom API (Bring Your Own Syntax)
SigPro's core functions are intentionally simple and can be easily renamed in **one line** to match your preferred coding style.
### One-Line Renaming
```javascript
import { $ as signal, $mount as render, $html as tag, $if as when, $for as each, $watch as effect } from 'sigpro';
import { $ as signal, Mount as render, Tag as tag, If as when, For as each, Watch as effect } from 'sigpro';
// Now use your custom names
const count = signal(0);
@@ -166,21 +165,21 @@ const useState = (initial) => {
};
const useEffect = (fn, deps) => {
deps ? SigPro.$watch(deps, fn) : SigPro.$watch(fn);
deps ? SigPro.Watch(deps, fn) : SigPro.Watch(fn);
};
// Usage
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => console.log(count()), [count]);
return SigPro.$html('button', { onClick: () => setCount(count() + 1) }, count);
return SigPro.Tag('button', { onClick: () => setCount(count() + 1) }, count);
};
```
### Create Vue-like API
```javascript
import { $ as ref, $watch as watch, $mount as mount } from 'sigpro';
import { $ as ref, Watch as watch, Mount as mount } from 'sigpro';
const computed = (fn) => ref(fn);
const createApp = (component) => ({ mount: (selector) => mount(component, selector) });
@@ -197,7 +196,7 @@ Create a central configuration file to reuse your custom naming across the entir
```javascript
// config/sigpro.config.js
import { $ as signal, $mount as render, $html as tag, $if as when, $for as each, $watch as effect } from 'sigpro';
import { $ as signal, Mount as render, Tag as tag, If as when, For as each, Watch as effect } from 'sigpro';
// Re-export everything with your custom names
export { signal, render, tag, when, each, effect };
@@ -229,7 +228,7 @@ render(App, '#app');
> **Why rename?** Team preferences, framework migration, or just personal taste. SigPro adapts to you, not the other way around.
> [!IMPORTANT]
> **Performance Hint:** For lists (`$for`), always provide a unique key function `(item) => item.id` to prevent unnecessary node creation and enable reordering.
> **Performance Hint:** For lists (`For`), always provide a unique key function `(item) => item.id` to prevent unnecessary node creation and enable reordering.
> [!TIP]
> **Pro Tip:** Use `$$()` for complex nested state objects instead of multiple `$()` signals. It's cleaner and automatically tracks deep properties.

View File

@@ -1,11 +1,11 @@
# Routing: `$router()` & Utilities
# Routing: `Router()` & Utilities
SigPro includes a built-in, lightweight **Hash Router** to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically.
## Router Signature
```typescript
$router(routes: Route[]): HTMLElement
Router(routes: Route[]): HTMLElement
```
### Route Object
@@ -19,13 +19,13 @@ $router(routes: Route[]): HTMLElement
## Usage Patterns
### 1. Defining Routes
The `$router` returns a `div` element with the class `.router-outlet`. When the hash changes, the router destroys the previous view and mounts the new one inside this container.
The `Router` returns a `div` element with the class `.router-outlet`. When the hash changes, the router destroys the previous view and mounts the new one inside this container.
```javascript
const App = () => Div({ class: "app-layout" }, [
Navbar(),
// The router outlet is placed here
$router([
Router([
{ path: "/", component: Home },
{ path: "/profile/:id", component: (params) => UserProfile(params.id) },
{ path: "*", component: () => H1("404 Not Found") }
@@ -34,7 +34,7 @@ const App = () => Div({ class: "app-layout" }, [
```
### 2. Dynamic Segments (`:id`)
The router automatically parses URL parameters (like `:id`) and passes them as an object to the component function. You can also access them globally via `$router.params()`.
The router automatically parses URL parameters (like `:id`) and passes them as an object to the component function. You can also access them globally via `Router.params()`.
```javascript
// If the URL is #/profile/42
@@ -49,23 +49,23 @@ const UserProfile = (params) => {
SigPro provides a set of programmatic methods to control the history and read the state.
### `$router.to(path)`
### `Router.to(path)`
Navigates to a specific path. It automatically formats the hash (e.g., `/home` becomes `#/home`).
```javascript
Button({ onclick: () => $router.to("/dashboard") }, "Go to Dashboard")
Button({ onclick: () => Router.to("/dashboard") }, "Go to Dashboard")
```
### `$router.back()`
### `Router.back()`
Goes back to the previous page in the browser history.
```javascript
Button({ onclick: () => $router.back() }, "Back")
Button({ onclick: () => Router.back() }, "Back")
```
### `$router.path()`
### `Router.path()`
Returns the current path (without the `#`).
```javascript
$watch(() => {
console.log("Current path is:", $router.path());
Watch(() => {
console.log("Current path is:", Router.path());
});
```
@@ -74,7 +74,7 @@ $watch(() => {
## Technical Behavior
* **Automatic Cleanup**: Every time you navigate, the router calls `.destroy()` on the previous view. This ensures that all **signals, effects, and event listeners** from the old page are purged from memory.
* **Reactive Params**: `$router.params` is a signal (`$`). You can react to parameter changes without re-mounting the entire router outlet.
* **Reactive Params**: `Router.params` is a signal (`$`). You can react to parameter changes without re-mounting the entire router outlet.
* **Hash-Based**: By using `window.location.hash`, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects.
---

View File

@@ -94,7 +94,7 @@ $$<T extends object>(obj: T): T
```javascript
const state = $$({ count: 0, name: "Juan" });
$watch(() => state.count, () => {
Watch(() => state.count, () => {
console.log(`Count is now ${state.count}`);
});
@@ -118,7 +118,7 @@ const user = $$({
});
// This works! Tracks deep property access
$watch(() => user.profile.address.city, () => {
Watch(() => user.profile.address.city, () => {
console.log("City changed");
});
@@ -135,7 +135,7 @@ const todos = $$([
{ id: 2, text: "Build an app", done: false }
]);
$watch(() => todos.length, () => {
Watch(() => todos.length, () => {
console.log(`You have ${todos.length} todos`);
});
@@ -164,7 +164,7 @@ const canSubmit = $(() =>
form.fields.password.length > 6
);
$watch(canSubmit, (valid) => {
Watch(canSubmit, (valid) => {
form.isValid(valid); // Update signal inside reactive object
});
```
@@ -224,8 +224,8 @@ state.user.name = "Ana";
todos.push(newItem);
// Track in effects
$watch(() => state.count, () => {});
$watch(() => state.user.name, () => {});
Watch(() => state.count, () => {});
Watch(() => state.user.name, () => {});
```
### ❌ DON'T:
@@ -248,7 +248,7 @@ Like all SigPro reactive primitives, `$$()` integrates with the cleanup system:
- Effects tracking reactive properties are automatically disposed
- No manual cleanup needed
- Works with `$watch`, `$if`, and `$for`
- Works with `Watch`, `If`, and `For`
---
@@ -302,7 +302,7 @@ const app = {
// UI component
const UserProfile = () => {
return Div({}, [
$if(() => app.isLoggedIn(),
If(() => app.isLoggedIn(),
() => Div({}, [
H2(`Welcome ${app.user.name}`),
P(`Email: ${app.user.email}`),

View File

@@ -1,12 +1,12 @@
# Global Tag Helpers
In **SigPro**, you don't need to manually type `$html('div', ...)` for every element. To keep your code declarative and readable, the engine automatically generates **Global Helper Functions** for all standard HTML5 tags upon initialization.
In **SigPro**, you don't need to manually type `Tag('div', ...)` for every element. To keep your code declarative and readable, the engine automatically generates **Global Helper Functions** for all standard HTML5 tags upon initialization.
## 1. How it Works
SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the `window` object. This creates a specialized **DSL** (Domain Specific Language) that looks like a template engine but is **100% standard JavaScript**.
* **Under the hood:** `$html('button', { onclick: ... }, 'Click')`
* **Under the hood:** `Tag('button', { onclick: ... }, 'Click')`
* **SigPro Style:** `Button({ onclick: ... }, 'Click')`
---
@@ -51,10 +51,10 @@ Section([
## 4. Reactive Power
These helpers are natively wired into SigPro's **`$watch`** engine.
These helpers are natively wired into SigPro's **`Watch`** engine.
### Reactive Attributes (One-Way)
Simply pass a Signal (function) to any attribute. SigPro creates an internal `$watch` to keep the DOM in sync.
Simply pass a Signal (function) to any attribute. SigPro creates an internal `Watch` to keep the DOM in sync.
```javascript
const theme = $("light");
@@ -80,12 +80,12 @@ Input({
> **Pro Tip:** If you want an input to be **read-only** but still reactive, wrap the signal in an anonymous function: `value: () => search()`. This prevents the "backwards" synchronization.
### Dynamic Flow & Cleanup
Combine tags with Core controllers. SigPro automatically cleans up the `$watch` instances and event listeners when nodes are removed from the DOM.
Combine tags with Core controllers. SigPro automatically cleans up the `Watch` instances and event listeners when nodes are removed from the DOM.
```javascript
const items = $(["Apple", "Banana", "Cherry"]);
Ul({ class: "list-disc" }, [
$for(items, (item) => Li(item), (item) => item)
For(items, (item) => Li(item), (item) => item)
]);
```
@@ -128,15 +128,15 @@ const UserStatus = (name, online) => (
---
## 6. Custom Tags with `$html`
## 6. Custom Tags with `Tag`
Create reusable components using `$html`. All reactivity auto-cleans itself.
Create reusable components using `Tag`. All reactivity auto-cleans itself.
### Basic Example
```javascript
const UserCard = (props, children) =>
$html('div', { class: 'card p-4', 'data-id': props.id }, children);
Tag('div', { class: 'card p-4', 'data-id': props.id }, children);
UserCard({ id: 123 }, [H3("John Doe"), P("john@example.com")]);
```
@@ -146,7 +146,7 @@ UserCard({ id: 123 }, [H3("John Doe"), P("john@example.com")]);
```javascript
const Counter = (initial = 0) => {
const count = $(initial);
return $html('div', { class: 'flex gap-2' }, [
return Tag('div', { class: 'flex gap-2' }, [
Button({ onclick: () => count(count() - 1) }, '-'),
Span(() => count()),
Button({ onclick: () => count(count() + 1) }, '+')
@@ -161,7 +161,7 @@ Only for external resources (intervals, sockets, third-party libs):
```javascript
const Timer = () => {
const time = $(new Date().toLocaleTimeString());
const el = $html('span', {}, () => time());
const el = Tag('span', {}, () => time());
const interval = setInterval(() => time(new Date().toLocaleTimeString()), 1000);
el._cleanups.add(() => clearInterval(interval)); // Manual cleanup
@@ -170,15 +170,15 @@ const Timer = () => {
};
```
### `$html` vs Tag Helpers
### `Tag` vs Tag Helpers
| Use | Recommendation |
|:---|:---|
| Standard tags (`div`, `span`) | `Div()`, `Span()` helpers |
| Reusable components | Function returning `$html` |
| Dynamic tag names | `$html(tagName, props, children)` |
| Reusable components | Function returning `Tag` |
| Dynamic tag names | `Tag(tagName, props, children)` |
> **Auto-cleanup**: `$html` automatically destroys watchers, events, and nested components. Only add to `_cleanups` for external resources.
> **Auto-cleanup**: `Tag` automatically destroys watchers, events, and nested components. Only add to `_cleanups` for external resources.
---

View File

@@ -1,15 +1,15 @@
# Reactivity Control: `$watch( )`
# Reactivity Control: `Watch( )`
The `$watch` function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. `$watch` is **polymorphic**: it can track dependencies automatically or follow an explicit list.
The `Watch` function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. `Watch` is **polymorphic**: it can track dependencies automatically or follow an explicit list.
## Function Signature
```typescript
// Automatic Mode (Magic Tracking)
$watch(callback: Function): StopFunction
Watch(callback: Function): StopFunction
// Explicit Mode (Isolated Dependencies)
$watch(deps: Signal[], callback: Function): StopFunction
Watch(deps: Signal[], callback: Function): StopFunction
```
| Parameter | Type | Required | Description |
@@ -29,7 +29,7 @@ Any signal you "touch" inside the callback becomes a dependency. SigPro tracks t
```javascript
const count = $(0);
$watch(() => {
Watch(() => {
// Re-runs every time 'count' changes
console.log(`Count is: ${count()}`);
});
@@ -42,7 +42,7 @@ This mode **isolates** execution. The callback only triggers when the signals in
const sPath = $("/home");
const user = $("Admin");
$watch([sPath], () => {
Watch([sPath], () => {
// Only triggers when 'sPath' changes.
// Changes to 'user' will NOT trigger this, preventing accidental re-renders.
console.log(`Navigating to ${sPath()} as ${user()}`);
@@ -53,11 +53,11 @@ $watch([sPath], () => {
If your logic creates timers, event listeners, or other reactive effects, SigPro tracks them as "children" of the current watch. When the watcher re-runs or stops, it kills everything inside automatically.
```javascript
$watch(() => {
Watch(() => {
const timer = setInterval(() => console.log("Tick"), 1000);
// Register a manual cleanup if needed
// Or simply rely on SigPro to kill nested $watch() calls
// Or simply rely on SigPro to kill nested Watch() calls
return () => clearInterval(timer);
});
```
@@ -68,7 +68,7 @@ $watch(() => {
Call the returned function to manually kill the watcher. This is essential for manual DOM injections (like Toasts) or long-lived background processes.
```javascript
const stop = $watch(() => console.log(count()));
const stop = Watch(() => console.log(count()));
// Later...
stop(); // The link between the signal and this code is physically severed.
@@ -83,7 +83,7 @@ SigPro batches updates. If you update multiple signals in the same execution blo
const a = $(0);
const b = $(0);
$watch(() => console.log(a(), b()));
Watch(() => console.log(a(), b()));
// This triggers only ONE re-run.
a(1);