Update Funcions
This commit is contained in:
@@ -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** |
|
||||
|
||||
@@ -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") ])
|
||||
```
|
||||
|
||||
@@ -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** |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();` |
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
@@ -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}`),
|
||||
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user