Change Docs to Docsify
This commit is contained in:
File diff suppressed because one or more lines are too long
83
docs/api/for.md
Normal file
83
docs/api/for.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# 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.
|
||||
|
||||
## Function Signature
|
||||
|
||||
```typescript
|
||||
$for(
|
||||
source: Signal<any[]> | Function | any[],
|
||||
render: (item: any, index: number) => HTMLElement,
|
||||
keyFn: (item: any, index: number) => string | number
|
||||
): HTMLElement
|
||||
```
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **`source`** | `Signal` | Yes | The reactive array to iterate over. |
|
||||
| **`render`** | `Function` | Yes | A function that returns a component or Node for each item. |
|
||||
| **`keyFn`** | `Function` | Yes | A function to extract a **unique ID** for each item (crucial for performance). |
|
||||
|
||||
**Returns:** A `div` element with `display: contents` containing the live list.
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### 1. Basic Keyed List
|
||||
Always use a unique property (like an `id`) as a key to ensure SigPro doesn't recreate nodes unnecessarily.
|
||||
|
||||
```javascript
|
||||
const users = $([
|
||||
{ id: 1, name: "Alice" },
|
||||
{ id: 2, name: "Bob" }
|
||||
]);
|
||||
|
||||
Ul({ class: "list" }, [
|
||||
$for(users,
|
||||
(user) => Li({ class: "p-2" }, user.name),
|
||||
(user) => user.id
|
||||
)
|
||||
]);
|
||||
```
|
||||
|
||||
### 2. Handling Primitive Arrays
|
||||
If your array contains simple strings or numbers, you can use the value itself or the index as a key (though the index is less efficient for reordering).
|
||||
|
||||
```javascript
|
||||
const tags = $(["Tech", "JS", "Web"]);
|
||||
|
||||
Div({ class: "flex gap-1" }, [
|
||||
$for(tags, (tag) => Badge(tag), (tag) => tag)
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How it Works (The Reconciliation)
|
||||
|
||||
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.
|
||||
3. **Cleanup**: If a key disappears from the list, SigPro calls `.destroy()` on that specific item's instance. This stops all its internal watchers and removes its DOM nodes.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
* **Component Encapsulation**: If each item in your list has its own complex internal state, `$for` ensures that state is preserved even if the list is reordered, as long as the key remains the same.
|
||||
|
||||
---
|
||||
|
||||
## Summary Comparison
|
||||
|
||||
| Feature | Standard `Array.map` | SigPro `$for` |
|
||||
| :--- | :--- | :--- |
|
||||
| **Re-render** | Re-renders everything | Only updates changes |
|
||||
| **DOM Nodes** | Re-created every time | **Reused via Keys** |
|
||||
| **Memory** | Potential leaks | **Automatic Cleanup** |
|
||||
| **State** | Lost on re-render | **Preserved per item** |
|
||||
File diff suppressed because one or more lines are too long
91
docs/api/html.md
Normal file
91
docs/api/html.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# The DOM Factory: `$html( )`
|
||||
|
||||
`$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.
|
||||
|
||||
## Function Signature
|
||||
|
||||
```typescript
|
||||
$html(tagName: string, props?: Object, children?: any[] | any): HTMLElement
|
||||
```
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **`tagName`** | `string` | Yes | Valid HTML tag name (e.g., `"div"`, `"button"`). |
|
||||
| **`props`** | `Object` | No | HTML attributes, event listeners, and reactive bindings. |
|
||||
| **`children`** | `any` | No | Nested elements, text strings, or reactive functions. |
|
||||
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
### 1. Attribute Handling
|
||||
SigPro intelligently decides how to apply each property:
|
||||
* **Standard Props**: Applied via `setAttribute` or direct property assignment.
|
||||
* **Class Names**: Supports `class` or `className` interchangeably.
|
||||
* **Boolean Props**: Automatic handling for `checked`, `disabled`, `hidden`, etc.
|
||||
|
||||
### 2. Event Listeners
|
||||
Events are defined by the `on` prefix. SigPro automatically registers the listener and ensures it is cleaned up when the element is destroyed.
|
||||
|
||||
```javascript
|
||||
Button({
|
||||
onclick: (e) => console.log("Clicked!", e),
|
||||
}, "Click Me");
|
||||
```
|
||||
|
||||
### 3. 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.
|
||||
|
||||
```javascript
|
||||
Div({
|
||||
// Updates the class whenever 'theme()' changes
|
||||
class: () => theme() === "dark" ? "bg-black" : "bg-white"
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Smart Two-Way Binding (Automatic)
|
||||
SigPro automatically enables **bidirectional synchronization** when it detects a **Signal** assigned to a form-capable attribute (`value` or `checked`) on an input element (`input`, `textarea`, `select`).
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
// Syncs input value <-> signal automatically
|
||||
Input({
|
||||
type: "text",
|
||||
value: username // No special symbols needed!
|
||||
})
|
||||
```
|
||||
|
||||
> **Note:** To use a Signal as **read-only** in an input, wrap it in an anonymous function: `value: () => username()`.
|
||||
|
||||
### 5. 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.
|
||||
|
||||
```javascript
|
||||
Div({}, [
|
||||
H1("Static Title"),
|
||||
// Only this text node re-renders when 'count' changes
|
||||
() => `Current count: ${count()}`
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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**.
|
||||
|
||||
---
|
||||
|
||||
## Tag Constructors (The Shortcuts)
|
||||
|
||||
Instead of writing `$html("div", ...)` every time, SigPro provides PascalCase global functions for all standard HTML tags:
|
||||
|
||||
```javascript
|
||||
// This:
|
||||
Div({ class: "wrapper" }, [ Span("Hello") ])
|
||||
|
||||
// Is exactly equivalent to:
|
||||
$html("div", { class: "wrapper" }, [ $html("span", {}, "Hello") ])
|
||||
```
|
||||
File diff suppressed because one or more lines are too long
88
docs/api/if.md
Normal file
88
docs/api/if.md
Normal file
@@ -0,0 +1,88 @@
|
||||
|
||||
# Reactive Branching: `$if( )`
|
||||
|
||||
The `$if` function is a reactive control flow operator. It manages the conditional rendering of components, ensuring that only the active branch exists in the DOM and in memory.
|
||||
|
||||
## Function Signature
|
||||
|
||||
```typescript
|
||||
$if(
|
||||
condition: Signal<boolean> | Function,
|
||||
thenVal: Component | Node,
|
||||
otherwiseVal?: Component | Node
|
||||
): HTMLElement
|
||||
```
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **`condition`** | `Signal` | Yes | A reactive source that determines which branch to render. |
|
||||
| **`thenVal`** | `any` | Yes | The content to show when the condition is **truthy**. |
|
||||
| **`otherwiseVal`** | `any` | No | The content to show when the condition is **falsy** (defaults to null). |
|
||||
|
||||
**Returns:** A `div` element with `display: contents` that acts as a reactive portal for the branches.
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### 1. Simple Toggle
|
||||
The most common use case is showing or hiding a single element based on a state.
|
||||
|
||||
```javascript
|
||||
const isVisible = $(false);
|
||||
|
||||
Div([
|
||||
Button({ onclick: () => isVisible(!isVisible()) }, "Toggle Message"),
|
||||
|
||||
$if(isVisible,
|
||||
P("Now you see me!"),
|
||||
P("Now you don't...")
|
||||
)
|
||||
]);
|
||||
```
|
||||
|
||||
### 2. Lazy Component Loading
|
||||
Unlike using a hidden class (CSS `display: none`), `$if` is **lazy**. The branch that isn't active **is never created**. This saves memory and initial processing time.
|
||||
|
||||
```javascript
|
||||
$if(() => user.isLogged(),
|
||||
() => Dashboard(), // Only executed if logged in
|
||||
() => LoginGate() // Only executed if guest
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
1. **Stop Watchers**: All `$.watch` calls inside the inactive branch are permanently stopped.
|
||||
2. **Unbind Events**: Event listeners attached via `$.html` are removed.
|
||||
3. **Recursive Sweep**: SigPro performs a deep "sweep" of the removed branch to ensure no nested reactive effects remain active.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
* **Function Wrappers**: If your branches are heavy (e.g., they contain complex components), wrap them in a function `() => MyComponent()`. This prevents the component from being initialized until the condition actually meets its requirement.
|
||||
* **Logical Expressions**: You can pass a complex computed function as the condition:
|
||||
```javascript
|
||||
$if(() => count() > 10 && status() === 'ready',
|
||||
Span("Threshold reached!")
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Technical Comparison
|
||||
|
||||
| Feature | Standard CSS `hidden` | SigPro `$if` |
|
||||
| :--- | :--- | :--- |
|
||||
| **DOM Presence** | Always present | Only if active |
|
||||
| **Reactivity** | Still processing in background | **Paused/Destroyed** |
|
||||
| **Memory usage** | Higher | **Optimized** |
|
||||
| **Cleanup** | Manual | **Automatic** |
|
||||
|
||||
File diff suppressed because one or more lines are too long
85
docs/api/mount.md
Normal file
85
docs/api/mount.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# 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.
|
||||
|
||||
## Function Signature
|
||||
|
||||
```typescript
|
||||
$mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
|
||||
```
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **`node`** | `Function` or `Node` | **Required** | The component function or DOM element to render. |
|
||||
| **`target`** | `string` or `Node` | `document.body` | CSS selector or DOM element where the app will live. |
|
||||
|
||||
**Returns:** A `Runtime` object containing the `container` and a `destroy()` method to wipe all reactivity and DOM nodes.
|
||||
|
||||
---
|
||||
|
||||
## Common Usage Scenarios
|
||||
|
||||
### 1. The SPA Entry Point
|
||||
In a Single Page Application, you typically mount your main component to the body or a root div. SigPro manages the entire view from that point.
|
||||
|
||||
```javascript
|
||||
import { $ } from './sigpro.js';
|
||||
import App from './App.js';
|
||||
|
||||
// Mounts your main App component
|
||||
$mount(App, '#app-root');
|
||||
```
|
||||
|
||||
### 2. Reactive "Islands"
|
||||
SigPro is perfect for adding reactivity to static pages. You can mount small widgets into specific parts of an existing HTML layout.
|
||||
|
||||
```javascript
|
||||
const Counter = () => {
|
||||
const count = $(0);
|
||||
return Button({ onclick: () => count(c => c + 1) }, [
|
||||
"Clicks: ", count
|
||||
]);
|
||||
};
|
||||
|
||||
// Mount only the counter into a specific sidebar div
|
||||
$mount(Counter, '#sidebar-widget');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How it Works (Lifecycle & Cleanup)
|
||||
|
||||
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.
|
||||
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.
|
||||
* `destroy()`: The "kill switch" that runs all cleanups, stops all watchers, and removes the element from the DOM.
|
||||
|
||||
---
|
||||
|
||||
## 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**.
|
||||
|
||||
```javascript
|
||||
const instance = $mount(MyToast, '#toast-container');
|
||||
|
||||
// Later, to remove the toast and kill its reactivity:
|
||||
instance.destroy();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary Cheat Sheet
|
||||
|
||||
| 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();` |
|
||||
|
||||
File diff suppressed because one or more lines are too long
130
docs/api/quick.md
Normal file
130
docs/api/quick.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# ⚡ Quick API Reference
|
||||
|
||||
SigPro is a high-performance micro-framework that updates the **Real DOM** surgically. No Virtual DOM, no unnecessary re-renders, and built-in **Cleanup** (memory cleanup).
|
||||
|
||||
## Core Functions
|
||||
|
||||
Explore the reactive building blocks of SigPro.
|
||||
|
||||
<div class="overflow-x-auto my-8 border border-base-300 rounded-xl shadow-sm">
|
||||
<table class="table table-zebra w-full">
|
||||
<thead class="bg-base-200 text-base-content">
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Signature</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code class="text-primary font-bold">$(val, key?)</code></td>
|
||||
<td class="font-mono text-xs opacity-70">(any, string?) => Signal</td>
|
||||
<td>Creates a <b>Signal</b>. If <code>key</code> is provided, it persists in <code>localStorage</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code class="text-primary font-bold">$(fn)</code></td>
|
||||
<td class="font-mono text-xs opacity-70">(function) => Computed</td>
|
||||
<td>Creates a <b>Computed Signal</b> that auto-updates when dependencies change.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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 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 class="font-mono text-xs opacity-70">(Signal, fn, fn?) => Node</td>
|
||||
<td>Reactive conditional. Automatically destroys "else" branch memory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code class="text-accent font-bold">$for(list, itemFn)</code></td>
|
||||
<td class="font-mono text-xs opacity-70">(Signal, fn) => Node</td>
|
||||
<td>Optimized list renderer. Manages individual item lifecycles.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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. Creates a root instance with <code>.destroy()</code> capabilities.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Element Constructors (Tags)
|
||||
|
||||
SigPro provides **PascalCase** wrappers for all standard HTML5 tags (e.g., `Div`, `Span`, `Button`).
|
||||
|
||||
### Syntax Pattern
|
||||
|
||||
<div class="mockup-code bg-base-300 text-base-content my-6">
|
||||
<pre data-prefix=">"><code>Tag({ attributes }, [children])</code></pre>
|
||||
</div>
|
||||
|
||||
### Attribute & Content Handling
|
||||
|
||||
Learn how to bind data to your elements effectively.
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 my-10">
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm hover:border-primary/30 transition-colors">
|
||||
<div class="card-body p-5">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<h3 class="text-xs font-black uppercase tracking-widest opacity-60">Static</h3>
|
||||
<span class="badge badge-outline badge-xs opacity-50">HTML5</span>
|
||||
</div>
|
||||
<code class="text-primary font-bold text-sm bg-base-300/50 p-2 rounded-lg">class: "text-red"</code>
|
||||
<p class="text-xs mt-3 leading-relaxed">Standard HTML attribute passed as a plain string. No reactivity overhead.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm hover:border-secondary/30 transition-colors">
|
||||
<div class="card-body p-5">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<h3 class="text-xs font-black uppercase tracking-widest opacity-60">Reactive</h3>
|
||||
<span class="badge badge-secondary badge-xs">SIGNAL</span>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<code class="text-xs opacity-60 italic">const isLoading = $(false);</code>
|
||||
<code class="text-secondary font-bold text-sm bg-base-300/50 p-2 rounded-lg">disabled: isLoading</code>
|
||||
</div>
|
||||
<p class="text-xs mt-3 leading-relaxed">Updates automatically via internal <code>$watch</code>. Only the attribute changes in the DOM.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm hover:border-primary/30 transition-colors">
|
||||
<div class="card-body p-5">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<h3 class="text-xs font-black uppercase tracking-widest opacity-60">Two-way Binding</h3>
|
||||
<span class="badge badge-primary badge-xs">MAGIC</span>
|
||||
</div>
|
||||
<code class="text-xs opacity-60 italic">const username = $('Me');</code>
|
||||
<code class="text-primary font-bold text-sm bg-base-300/50 p-2 rounded-lg">value: username</code>
|
||||
<p class="text-xs mt-3 leading-relaxed font-medium">
|
||||
<b>Automatic Sync:</b> Works out-of-the-box for <code>Input</code>, <code>Textarea</code>, and <code>Checkbox</code>.
|
||||
It syncs the element <span class="text-primary">↔</span> signal both ways without manual event listeners.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm hover:border-accent/30 transition-colors">
|
||||
<div class="card-body p-5">
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<h3 class="text-xs font-black uppercase tracking-widest opacity-60">Surgical Text</h3>
|
||||
<span class="badge badge-accent badge-xs">FAST</span>
|
||||
</div>
|
||||
<code class="text-accent font-bold text-sm bg-base-300/50 p-2 rounded-lg">P({}, () => count())</code>
|
||||
<p class="text-xs mt-3 leading-relaxed">Updates the specific text node surgically without ever re-rendering the parent <code>P</code> tag.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
> [!TIP]
|
||||
> **Performance Hint:** Always use functions `() => signal()` for dynamic children to ensure SigPro only updates the specific node and not the whole container.
|
||||
File diff suppressed because one or more lines are too long
96
docs/api/router.md
Normal file
96
docs/api/router.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# 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
|
||||
```
|
||||
|
||||
### Route Object
|
||||
| Property | Type | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| **`path`** | `string` | The URL fragment (e.g., `"/"`, `"/user/:id"`, or `"*"`). |
|
||||
| **`component`** | `Function` | A function that returns a Node, a String, or a reactive View. |
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
```javascript
|
||||
const App = () => Div({ class: "app-layout" }, [
|
||||
Navbar(),
|
||||
// The router outlet is placed here
|
||||
$router([
|
||||
{ path: "/", component: Home },
|
||||
{ path: "/profile/:id", component: (params) => UserProfile(params.id) },
|
||||
{ path: "*", component: () => H1("404 Not Found") }
|
||||
])
|
||||
]);
|
||||
```
|
||||
|
||||
### 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()`.
|
||||
|
||||
```javascript
|
||||
// If the URL is #/profile/42
|
||||
const UserProfile = (params) => {
|
||||
return H1(`User ID is: ${params.id}`); // Displays "User ID is: 42"
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Navigation Utilities
|
||||
|
||||
SigPro provides a set of programmatic methods to control the history and read the state.
|
||||
|
||||
### `$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")
|
||||
```
|
||||
|
||||
### `$router.back()`
|
||||
Goes back to the previous page in the browser history.
|
||||
```javascript
|
||||
Button({ onclick: () => $router.back() }, "Back")
|
||||
```
|
||||
|
||||
### `$router.path()`
|
||||
Returns the current path (without the `#`).
|
||||
```javascript
|
||||
$watch(() => {
|
||||
console.log("Current path is:", $router.path());
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
* **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.
|
||||
|
||||
---
|
||||
|
||||
## Styling the Outlet
|
||||
The router returns a standard `div` with the `.router-outlet` class. You can easily style it or add transitions:
|
||||
|
||||
```css
|
||||
.router-outlet {
|
||||
display: block;
|
||||
min-height: 100vh;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
```
|
||||
File diff suppressed because one or more lines are too long
68
docs/api/signal.md
Normal file
68
docs/api/signal.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# The Signal Function: `$( )`
|
||||
|
||||
The `$( )` function is the core constructor of SigPro. It defines how data is stored, computed, and persisted.
|
||||
|
||||
## Function Signature
|
||||
|
||||
```typescript
|
||||
$(initialValue: any, key?: string): Signal
|
||||
$(computation: Function): ComputedSignal
|
||||
```
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **`initialValue`** | `any` | Yes* | The starting value of your signal. |
|
||||
| **`computation`** | `Function` | Yes* | A function that returns a value based on other signals. |
|
||||
| **`key`** | `string` | No | A unique name to persist the signal in `localStorage`. |
|
||||
|
||||
*\*Either an initial value or a computation function must be provided.*
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### 1. Simple State
|
||||
**`$(value)`**
|
||||
Creates a writable signal. It returns a function that acts as both **getter** and **setter**.
|
||||
|
||||
```javascript
|
||||
const count = $(0);
|
||||
|
||||
count(); // Read (0)
|
||||
count(10); // Write (10)
|
||||
```
|
||||
|
||||
### 2. Persistent State
|
||||
**`$(value, key)`**
|
||||
Creates a writable signal that syncs with the browser's storage.
|
||||
|
||||
```javascript
|
||||
const theme = $("light", "app-theme");
|
||||
|
||||
theme("dark"); // Automatically calls localStorage.setItem("app-theme", '"dark"')
|
||||
```
|
||||
*Note: On page load, SigPro will prioritize the value found in `localStorage` over the `initialValue`.*
|
||||
|
||||
### 3. Computed State (Derived)
|
||||
**`$(function)`**
|
||||
Creates a read-only signal that updates automatically when any signal used inside it changes.
|
||||
|
||||
```javascript
|
||||
const price = $(100);
|
||||
const tax = $(0.21);
|
||||
|
||||
// This tracks both 'price' and 'tax' automatically
|
||||
const total = $(() => price() * (1 + tax()));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updating with Logic
|
||||
When calling the setter, you can pass an **updater function** to access the current value safely.
|
||||
|
||||
```javascript
|
||||
const list = $(["A", "B"]);
|
||||
|
||||
// Adds "C" using the previous state
|
||||
list(prev => [...prev, "C"]);
|
||||
```
|
||||
File diff suppressed because one or more lines are too long
133
docs/api/tags.md
Normal file
133
docs/api/tags.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# 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.
|
||||
|
||||
## 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')`
|
||||
* **SigPro Style:** `Button({ onclick: ... }, 'Click')`
|
||||
|
||||
---
|
||||
|
||||
## 2. The Complete Global Registry
|
||||
|
||||
The following functions are injected into the global scope using **PascalCase** to prevent naming collisions with common JS variables:
|
||||
|
||||
| Category | Available Global Functions |
|
||||
| :--- | :--- |
|
||||
| **Structure** | `Div`, `Span`, `P`, `Section`, `Nav`, `Main`, `Header`, `Footer`, `Article`, `Aside` |
|
||||
| **Typography** | `H1` to `H6`, `Ul`, `Ol`, `Li`, `Dl`, `Dt`, `Dd`, `Strong`, `Em`, `Code`, `Pre`, `Small`, `B`, `U`, `Mark` |
|
||||
| **Interactive** | `Button`, `A`, `Label`, `Br`, `Hr`, `Details`, `Summary`, `Dialog` |
|
||||
| **Forms** | `Form`, `Input`, `Select`, `Option`, `Textarea`, `Fieldset`, `Legend` |
|
||||
| **Tables** | `Table`, `Thead`, `Tbody`, `Tr`, `Th`, `Td`, `Tfoot`, `Caption` |
|
||||
| **Media** | `Img`, `Canvas`, `Video`, `Audio`, `Svg`, `Iframe`, `Picture`, `Source` |
|
||||
|
||||
---
|
||||
|
||||
## 3. Usage Patterns (Smart Arguments)
|
||||
|
||||
SigPro tag helpers are flexible. They automatically detect if you are passing attributes, children, or both.
|
||||
|
||||
### A. Attributes + Children
|
||||
```javascript
|
||||
Div({ class: 'container', id: 'main' }, [
|
||||
H1("Welcome to SigPro"),
|
||||
P("The zero-VDOM framework.")
|
||||
]);
|
||||
```
|
||||
|
||||
### B. Children Only (The "Skipper")
|
||||
If you don't need attributes, you can pass the content directly as the first argument.
|
||||
```javascript
|
||||
Section([
|
||||
H2("Clean Syntax"),
|
||||
Button("I have no props!")
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Reactive Power
|
||||
|
||||
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.
|
||||
```javascript
|
||||
const theme = $("light");
|
||||
|
||||
Div({
|
||||
class: () => `app-box ${theme()}`
|
||||
}, "Themeable Box");
|
||||
```
|
||||
|
||||
### Smart Two-Way Binding (Automatic)
|
||||
SigPro automatically bridges the **Signal** and the **Input** element bi-directionally when you assign a Signal to `value` or `checked`. No special operators are required.
|
||||
|
||||
```javascript
|
||||
const search = $("");
|
||||
|
||||
// UI updates Signal AND Signal updates UI automatically
|
||||
Input({
|
||||
type: "text",
|
||||
placeholder: "Search...",
|
||||
value: search
|
||||
});
|
||||
```
|
||||
|
||||
> **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.
|
||||
```javascript
|
||||
const items = $(["Apple", "Banana", "Cherry"]);
|
||||
|
||||
Ul({ class: "list-disc" }, [
|
||||
$for(items, (item) => Li(item), (item) => item)
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<div class="alert alert-error shadow-lg my-8 border-l-8 border-error bg-error/10 text-error-content">
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
|
||||
<div>
|
||||
<h3 class="font-bold text-lg">Important: Naming Conventions</h3>
|
||||
<div class="text-sm opacity-90">
|
||||
<ol class="list-decimal ml-4 mt-2 space-y-2">
|
||||
<li><b>Avoid Shadowing:</b> Don't name your local variables like the tags (e.g., <code>const Div = ...</code>). This will "hide" the global SigPro helper.</li>
|
||||
<li><b>Custom Components:</b> Always use <b>PascalCase</b> for your own component functions (e.g., <code>UserCard</code>, <code>NavMenu</code>) to distinguish them from built-in Tag Helpers.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 5. Logic to UI Comparison
|
||||
|
||||
Here is how a dynamic **User Status** component translates from SigPro logic to the final DOM structure.
|
||||
|
||||
```javascript
|
||||
const UserStatus = (name, online) => (
|
||||
Div({ class: 'flex items-center gap-2' }, [
|
||||
Span({
|
||||
hidden: () => !online(),
|
||||
class: 'w-3 h-3 bg-green-500 rounded-full'
|
||||
}),
|
||||
P({
|
||||
class: () => online() ? "text-bold" : "text-gray-400"
|
||||
}, name)
|
||||
])
|
||||
);
|
||||
```
|
||||
|
||||
| State (`online`) | Rendered HTML | Memory Management |
|
||||
| :--- | :--- | :--- |
|
||||
| **`true`** | `<div class="flex..."><span class="w-3..."></span><p class="text-bold">John</p></div>` | Watcher active |
|
||||
| **`false`** | `<div class="flex..."><span hidden class="w-3..."></span><p class="text-gray-400">John</p></div>` | Attribute synced |
|
||||
|
||||
File diff suppressed because one or more lines are too long
91
docs/api/watch.md
Normal file
91
docs/api/watch.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# 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.
|
||||
|
||||
## Function Signature
|
||||
|
||||
```typescript
|
||||
// Automatic Mode (Magic Tracking)
|
||||
$watch(callback: Function): StopFunction
|
||||
|
||||
// Explicit Mode (Isolated Dependencies)
|
||||
$watch(deps: Signal[], callback: Function): StopFunction
|
||||
```
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **`target / deps`** | `Function` | `Array` | Yes | Either the code to run (Auto) or an array of signals to watch (Explicit). |
|
||||
| **`callback`** | `Function` | Only in Explicit | The code that will run when the `deps` change. |
|
||||
|
||||
**Returns:** A `StopFunction` that, when called, destroys the watcher and releases memory.
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### 1. Automatic Mode (Default)
|
||||
Any signal you "touch" inside the callback becomes a dependency. SigPro tracks them behind the scenes.
|
||||
|
||||
```javascript
|
||||
const count = $(0);
|
||||
|
||||
$watch(() => {
|
||||
// Re-runs every time 'count' changes
|
||||
console.log(`Count is: ${count()}`);
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Explicit Mode (Advanced Cleanup)
|
||||
This mode **isolates** execution. The callback only triggers when the signals in the array change. Any other signal accessed *inside* the callback will NOT trigger a re-run. This is the "gold standard" for Routers and heavy components.
|
||||
|
||||
```javascript
|
||||
const sPath = $("/home");
|
||||
const user = $("Admin");
|
||||
|
||||
$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()}`);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Automatic Cleanup
|
||||
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(() => {
|
||||
const timer = setInterval(() => console.log("Tick"), 1000);
|
||||
|
||||
// Register a manual cleanup if needed
|
||||
// Or simply rely on SigPro to kill nested $watch() calls
|
||||
return () => clearInterval(timer);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Stopping a Watcher
|
||||
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()));
|
||||
|
||||
// Later...
|
||||
stop(); // The link between the signal and this code is physically severed.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pro Tip: The Microtask Queue
|
||||
SigPro batches updates. If you update multiple signals in the same execution block, the watcher will only fire **once** at the end of the task.
|
||||
|
||||
```javascript
|
||||
const a = $(0);
|
||||
const b = $(0);
|
||||
|
||||
$watch(() => console.log(a(), b()));
|
||||
|
||||
// This triggers only ONE re-run.
|
||||
a(1);
|
||||
b(2);
|
||||
```
|
||||
Reference in New Issue
Block a user