Update Docs
This commit is contained in:
@@ -1,42 +1,55 @@
|
||||
<div class="w-full -mt-10"><section class="relative py-20 overflow-hidden border-b border-base-200/30 text-center flex flex-col items-center"><div class="relative z-10 max-w-5xl mx-auto px-6 flex flex-col items-center"><div class="flex justify-center mb-10"><img src="logo.svg" alt="SigPro Logo" class="w-48 h-48 md:w-64 md:h-64 object-contain drop-shadow-2xl"></div><h1 class="text-7xl md:text-9xl font-black tracking-tighter mb-4 bg-clip-text text-transparent bg-linear-to-r from-primary via-secondary to-accent !text-center w-full">SigPro</h1><div class="text-2xl md:text-3xl font-bold text-base-content/90 mb-4 !text-center w-full">Atomic Unified Reactive Engine</div><div class="text-xl text-base-content/60 max-w-3xl mx-auto mb-10 leading-relaxed italic text-balance font-light !text-center w-full">"The efficiency of direct DOM manipulation with the elegance of functional reactivity."</div><div class="flex flex-wrap justify-center gap-4 w-full"><a href="#/install" class="btn btn-primary btn-lg shadow-xl shadow-primary/20 group px-10 border-none">Get Started <span class="group-hover:translate-x-1 transition-transform inline-block">→</span></a><button onclick="window.open('https://github.com/natxocc/sigpro')" class="btn btn-outline btn-lg border-base-300 hover:bg-base-300 hover:text-base-content">GitHub</button></div></div><div class="absolute top-0 left-1/2 -translate-x-1/2 w-full h-full -z-0 opacity-10 pointer-events-none"><div class="absolute top-10 left-1/4 w-96 h-96 bg-primary filter blur-3xl rounded-full animate-pulse"></div><div class="absolute bottom-10 right-1/4 w-96 h-96 bg-accent filter blur-3xl rounded-full animate-pulse" style="animation-delay: 2.5s"></div></div></section></div>
|
||||
|
||||
<section class="max-w-6xl mx-auto px-6 py-16"><div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 items-stretch"><div class="card bg-base-200/30 border border-base-300 hover:border-primary/40 transition-all p-1"><div class="card-body p-6"><h3 class="card-title text-xl font-black text-primary italic">FUNCTIONAL</h3><p class="text-sm opacity-70">No strings. No templates. Pure JS function calls for instant DOM mounting.</p></div></div><div class="card bg-base-200/30 border border-base-300 hover:border-secondary/40 transition-all p-1"><div class="card-body p-6"><h3 class="card-title text-xl font-black text-secondary italic">ATOMIC</h3><p class="text-sm opacity-70">Fine-grained signals update exactly what changes. No V-DOM diffing overhead.</p></div></div><div class="card bg-base-200/30 border border-base-300 hover:border-accent/40 transition-all p-1"><div class="card-body p-6"><h3 class="card-title text-xl font-black text-accent italic">ULTRA-THIN</h3><p class="text-sm opacity-70">Sub-2KB runtime. Infinitely smaller bundle than React, Vue or even Svelte.</p></div></div><div class="card bg-base-200/30 border border-base-300 hover:border-base-content/20 transition-all p-1"><div class="card-body p-6"><h3 class="card-title text-xl font-black italic text-base-content">COMPILER-FREE</h3><p class="text-sm opacity-70">Standard Vanilla JS. What you write is what the browser executes. Period.</p></div></div></div></section></div>
|
||||
<section class="max-w-6xl mx-auto px-6 py-16"><div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 items-stretch"><div class="card bg-base-200/30 border border-base-300 hover:border-primary/40 transition-all p-1"><div class="card-body p-6"><h3 class="card-title text-xl font-black text-primary italic">FUNCTIONAL</h3><p class="text-sm opacity-70">No strings. No templates. Pure JS function calls for instant DOM mounting.</p></div></div><div class="card bg-base-200/30 border border-base-300 hover:border-secondary/40 transition-all p-1"><div class="card-body p-6"><h3 class="card-title text-xl font-black text-secondary italic">ATOMIC</h3><p class="text-sm opacity-70">Fine‑grained signals update exactly what changes. No V‑DOM diffing overhead.</p></div></div><div class="card bg-base-200/30 border border-base-300 hover:border-accent/40 transition-all p-1"><div class="card-body p-6"><h3 class="card-title text-xl font-black text-accent italic">ULTRA‑THIN</h3><p class="text-sm opacity-70">Sub‑2KB runtime. Infinitely smaller bundle than React, Vue or even Svelte.</p></div></div><div class="card bg-base-200/30 border border-base-300 hover:border-base-content/20 transition-all p-1"><div class="card-body p-6"><h3 class="card-title text-xl font-black italic text-base-content">COMPILER‑FREE</h3><p class="text-sm opacity-70">Standard Vanilla JS. What you write is what the browser executes. Period.</p></div></div></div></section>
|
||||
|
||||
## Functional DOM Construction
|
||||
<div class="max-w-6xl mx-auto px-6 py-8"><h2 class="text-4xl font-black mb-6">Functional DOM Construction</h2><p class="text-lg opacity-80 mb-6">SigPro replaces slow "Template Parsing" with <strong>High‑Efficiency Function Calls</strong>. While other frameworks force the browser to parse strings of HTML or execute complex JSX transformations, SigPro uses a direct functional approach.</p>
|
||||
|
||||
SigPro replaces the slow "Template Parsing" with **High-Efficiency Function Calls**.
|
||||
<table class="table w-full mb-12">
|
||||
<thead><tr><th>Feature</th><th>Standard HTML / JSX</th><th>SigPro Functional</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Syntax</td><td><code><div>Hello</div></code></td><td><code>div("Hello")</code> (or <code>h('div', "Hello")</code>)</td></tr>
|
||||
<tr><td>Processing</td><td>Parse → Diff → Patch</td><td>Direct API Call</td></tr>
|
||||
<tr><td>Overhead</td><td>High (V‑DOM / Parser)</td><td><strong>Zero</strong></td></tr>
|
||||
<tr><td>Reactivity</td><td>Component‑wide</td><td><strong>Atomic (Node‑level)</strong></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
While other frameworks force the browser to parse strings of HTML or execute complex JSX transformations, SigPro uses a direct functional approach:
|
||||
<h3 class="text-2xl font-bold mt-8 mb-4">Less Code, More Power</h3>
|
||||
<p class="mb-4">By sharing a minuscule runtime, your final application bundle is <strong>infinitely smaller</strong>.</p>
|
||||
<ul class="list-disc pl-6 space-y-2 mb-8">
|
||||
<li><strong>React/Vue:</strong> You ship a heavy orchestrator (~30‑90KB) just to say "Hello World".</li>
|
||||
<li><strong>Solid/Svelte:</strong> You still depend on a compilation step that generates extra boilerplate.</li>
|
||||
<li><strong>SigPro:</strong> You ship <strong>Pure Vanilla JS</strong>. The runtime is so small that it often costs less than a single high‑res icon.</li>
|
||||
</ul>
|
||||
|
||||
| Feature | Standard HTML / JSX | SigPro Functional |
|
||||
| :--- | :--- | :--- |
|
||||
| **Syntax** | `<div>Hello</div>` | `Div("Hello")` |
|
||||
| **Processing** | Parse → Diff → Patch | Direct API Call |
|
||||
| **Overhead** | High (V-DOM / Parser) | **Zero** |
|
||||
| **Reactivity** | Component-wide | **Atomic (Node-level)** |
|
||||
<h3 class="text-2xl font-bold mt-8 mb-4">Two Ways to Use SigPro (v1.2.19+)</h3>
|
||||
<p class="mb-2"><strong>🎯 Modern ESM (recommended):</strong> Import only the functions you need – zero global pollution.</p>
|
||||
<pre class="bg-base-300/30 p-4 rounded-lg mb-4"><code>import { $, div, button, mount } from 'sigpro';
|
||||
const count = $(0);
|
||||
mount(() => div([ button({ onclick: () => count(count()+1) }, count) ]), '#app');</code></pre>
|
||||
<p class="mb-2"><strong>🌍 Classic Global (IIFE):</strong> Load the script and everything is automatically on <code>window</code> – no imports needed.</p>
|
||||
<pre class="bg-base-300/30 p-4 rounded-lg mb-4"><code><script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.19/dist/sigpro.js"></script>
|
||||
<script>
|
||||
const count = $(0);
|
||||
mount(() => div([ button({ onclick: () => count(count()+1) }, count) ]), '#app');
|
||||
</script></code></pre>
|
||||
<p class="mb-2"><strong>🔧 ESM + Global injection:</strong> If you want the global helpers but still use <code>type="module"</code>, call <code>sigpro()</code>.</p>
|
||||
<pre class="bg-base-300/30 p-4 rounded-lg"><code><script type="module">
|
||||
import { sigpro } from 'https://cdn.jsdelivr.net/npm/sigpro@1.2.19/+esm';
|
||||
sigpro(); // now $, div, button, etc. are global
|
||||
</script></code></pre>
|
||||
|
||||
### Less Code, More Power
|
||||
By sharing a miniscule runtime, your final application bundle is **infinitely smaller**.
|
||||
<h3 class="text-2xl font-bold mt-10 mb-4">Precision Engineering</h3>
|
||||
<h4 class="text-xl font-semibold mt-6 mb-2">1. Functional Efficiency</h4>
|
||||
<p><code>div()</code>, <code>button()</code>, <code>span()</code>… These aren't just wrappers; they are pre‑optimized constructors. When you call <code>div({ class: 'btn' }, "Click")</code>, SigPro creates the element and attaches its reactive listeners in a single, surgical operation.</p>
|
||||
|
||||
* **React/Vue:** You ship a heavy orchestrator (~30-90KB) just to say "Hello World".
|
||||
* **Solid/Svelte:** You still depend on a compilation step that generates extra boilerplate.
|
||||
* **SigPro:** You ship **Pure Vanilla JS**. The runtime is so small that it often costs less than a single high-res icon.
|
||||
<h4 class="text-xl font-semibold mt-6 mb-2">2. The "No‑Bundle" Bundle</h4>
|
||||
<p>Because SigPro is so small, it is the only engine where <strong>the more code you write, the more the efficiency gap grows</strong>. While others grow linearly with components and framework overhead, SigPro stays flat, leveraging the native power of modern browser engines.</p>
|
||||
|
||||
---
|
||||
<h4 class="text-xl font-semibold mt-6 mb-2">3. Shared Runtime</h4>
|
||||
<p>All components share the same atomic engine. One signal can update a single character in a paragraph across 100 components without ever re‑evaluating the component functions themselves.</p>
|
||||
</div>
|
||||
|
||||
## Precision Engineering
|
||||
|
||||
### 1. Functional Efficiency
|
||||
`Div()`, `Button()`, `Span()`... These aren't just wrappers; they are pre-optimized constructors. When you call `Div({ class: 'btn' }, "Click")`, SigPro creates the element and attaches its reactive listeners in a single, surgical operation.
|
||||
|
||||
### 2. The "No-Bundle" Bundle
|
||||
Because SigPro is so small, it is the only engine where **the more code you write, the more the efficiency gap grows**. While others grow linearly with components and framework overhead, SigPro stays flat, leveraging the native power of modern browser engines.
|
||||
|
||||
### 3. Shared Runtime
|
||||
All components share the same atomic engine. One signal can update a single character in a paragraph across 100 components without ever re-evaluating the component functions themselves.
|
||||
|
||||
---
|
||||
|
||||
<div class="bg-base-200/50 rounded-3xl p-10 my-16 border border-base-300 shadow-inner"><div class="grid grid-cols-1 lg:grid-cols-3 gap-8 items-center"><div class="lg:col-span-2"><h2 class="text-4xl font-black mb-4 mt-0 tracking-tight italic text-primary">Kill the Bloat.</h2><p class="text-xl opacity-80 leading-relaxed">Stop shipping 100KB of "Framework" for 2KB of business logic. SigPro gives you the tools to build ultra-fast, modern apps with <strong>True Vanilla Performance</strong>.</p></div></div></div>
|
||||
<div class="bg-base-200/50 rounded-3xl p-10 my-16 border border-base-300 shadow-inner max-w-6xl mx-auto"><div class="grid grid-cols-1 lg:grid-cols-3 gap-8 items-center"><div class="lg:col-span-2"><h2 class="text-4xl font-black mb-4 mt-0 tracking-tight italic text-primary">Kill the Bloat.</h2><p class="text-xl opacity-80 leading-relaxed">Stop shipping 100KB of "Framework" for 2KB of business logic. SigPro gives you the tools to build ultra‑fast, modern apps with <strong>True Vanilla Performance</strong>.</p></div></div></div>
|
||||
|
||||
<div class="text-center py-10 opacity-30 font-mono text-xs tracking-widest uppercase">Precision Reactive Engine — NatxoCC</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ The `each` function is a high‑performance keyed list renderer. It maps a react
|
||||
each(
|
||||
source: Signal<any[]> | (() => any[]) | any[],
|
||||
itemFn: (item: any, index: number) => Node | (() => Node),
|
||||
keyFn?: (item: any, index: number) => string | number
|
||||
keyField?: string
|
||||
): HTMLElement
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ each(
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **`source`** | `Signal`, `() => any[]`, or `any[]` | Yes | The reactive array to iterate over. |
|
||||
| **`itemFn`** | `(item, index) => Node` | Yes | Returns a DOM node (or a function that returns a node) for each item. |
|
||||
| **`keyFn`** | `(item, index) => string/number` | No | Extracts a unique key. Default: `item?.id ?? index`. |
|
||||
| **`keyField`** | `string` | No | Name of the property to use as unique key (e.g., `"id"`). Default: `item?.id ?? index`. |
|
||||
|
||||
**Returns:** A `div` with `style="display: contents"` that contains the live list.
|
||||
|
||||
@@ -26,7 +26,7 @@ each(
|
||||
|
||||
### 1. Basic Keyed List (Recommended)
|
||||
|
||||
Always provide a unique `id` as the key. This allows SigPro to reuse DOM nodes when the list is reordered or filtered.
|
||||
Pass the name of the property that contains the unique identifier (e.g., `"id"`). This allows SigPro to reuse DOM nodes when the list is reordered or filtered.
|
||||
|
||||
```javascript
|
||||
const users = $([
|
||||
@@ -37,14 +37,14 @@ const users = $([
|
||||
ul({ class: "list" }, [
|
||||
each(users,
|
||||
(user) => li({ class: "p-2" }, user.name),
|
||||
(user) => user.id // stable unique key
|
||||
"id" // ← use property "id" as stable key
|
||||
)
|
||||
]);
|
||||
```
|
||||
|
||||
### 2. Automatic Key (Simple Lists)
|
||||
|
||||
If you omit the `keyFn`, `each` defaults to `item?.id ?? index`. For primitive arrays or objects without an `id`, the index is used.
|
||||
If you omit the `keyField`, `each` defaults to `item?.id ?? index`. For primitive arrays or objects without an `id`, the index is used.
|
||||
|
||||
```javascript
|
||||
const tags = $(["Tech", "JS", "Web"]);
|
||||
@@ -55,7 +55,20 @@ div({ class: "flex gap-1" }, [
|
||||
]);
|
||||
```
|
||||
|
||||
### 3. Dynamic Content Using Functions
|
||||
### 3. Using a Different Property Name
|
||||
|
||||
If your unique identifier is not called `id` (e.g., `_id`, `userId`, `slug`), just pass the property name as the third parameter:
|
||||
|
||||
```javascript
|
||||
const products = $([
|
||||
{ _id: 101, name: "Laptop" },
|
||||
{ _id: 102, name: "Mouse" }
|
||||
]);
|
||||
|
||||
each(products, (item) => li(item.name), "_id");
|
||||
```
|
||||
|
||||
### 4. Dynamic Content Using Functions
|
||||
|
||||
If your `itemFn` returns a **function**, that function is re‑executed every time the item’s data changes (but the node is reused).
|
||||
|
||||
@@ -69,11 +82,11 @@ each(todos,
|
||||
input({ type: "checkbox", checked: () => todo.done, onInput: e => todo.done = e.target.checked }),
|
||||
span(() => todo.done ? s(todo.text) : todo.text)
|
||||
]),
|
||||
(todo) => todo.id
|
||||
"id"
|
||||
);
|
||||
```
|
||||
|
||||
### 4. Source as a Plain Array or Function
|
||||
### 5. Source as a Plain Array or Function
|
||||
|
||||
`source` can be a plain array (non‑reactive) or a function that returns an array – it will still react to changes if signals are read inside the function.
|
||||
|
||||
@@ -86,7 +99,7 @@ const filteredTodos = () => {
|
||||
return all;
|
||||
};
|
||||
|
||||
each(filteredTodos, (todo) => li(todo.text), (todo) => todo.id);
|
||||
each(filteredTodos, (todo) => li(todo.text), "id");
|
||||
```
|
||||
|
||||
---
|
||||
@@ -95,7 +108,7 @@ each(filteredTodos, (todo) => li(todo.text), (todo) => todo.id);
|
||||
|
||||
When the `source` changes, `each`:
|
||||
|
||||
1. **Compares keys** between the old and new items using the `keyFn`.
|
||||
1. **Compares keys** between the old and new items using the specified `keyField` (or `item.id` / index).
|
||||
2. **Reuses existing DOM nodes** for keys that stay the same.
|
||||
3. **Moves nodes** if order changed (no recreation).
|
||||
4. **Creates new nodes** for new keys.
|
||||
@@ -107,8 +120,8 @@ When the `source` changes, `each`:
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- **Stable keys** – Use a real `id` (like a database primary key). Avoid `Math.random()` or array `index` for lists that can be reordered.
|
||||
- **State preservation** – If a list item contains an input or a local state, using a stable key ensures that state is preserved even when the list is filtered or sorted.
|
||||
- **Stable keys** – Use a property that never changes (like a database primary key). Avoid `Math.random()` or array `index` for lists that can be reordered.
|
||||
- **State preservation** – If a list item contains an input or local state, using a stable key ensures that state is preserved even when the list is filtered or sorted.
|
||||
- **Lazy item functions** – If an item is expensive to render, wrap it in a function: `() => ExpensiveComponent(item)`. The component is only created when the item actually appears in the DOM.
|
||||
|
||||
---
|
||||
@@ -151,7 +164,7 @@ const App = () =>
|
||||
span(`${item.name} – $${item.price}`),
|
||||
button({ onClick: () => removeItem(item.id) }, "X")
|
||||
]),
|
||||
(item) => item.id
|
||||
"id"
|
||||
)
|
||||
)
|
||||
]);
|
||||
|
||||
@@ -31,6 +31,8 @@ fx(
|
||||
|
||||
**Returns:** The same DOM node (or the child if it is not a `Node`), after applying the animation setup.
|
||||
|
||||
> **Availability:** `fx` is exported from the SigPro module. In **ESM** you must import it (`import { fx } from 'sigpro'`) or inject all globals via `sigpro()`. In the **IIFE** classic script, it is automatically available on `window`. The examples below assume the function is already in scope.
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
SigPro leverages the native power and efficiency of **signals** to create robust global stores with **zero complexity**. While other frameworks force you into heavy libraries and rigid boilerplate (Redux, Pinia, or Svelte stores), SigPro treats “the store” as a simple architectural choice: **defining a signal outside of a component.**
|
||||
|
||||
> **Availability:** `$` (and other core functions) are exported from the SigPro module. In **ESM** you must import them (`import { $ } from 'sigpro'`) or inject all globals via `sigpro()`. In the **IIFE** classic script, `$` is automatically available on `window`. The examples below assume `$` is already in scope (via import or global).
|
||||
|
||||
## Modular Organization (Zero Constraints)
|
||||
|
||||
You are not restricted to a single `store.js`. You can organize your state by **feature**, **domain**, or **page**. Since a SigPro store is just a standard JavaScript module exporting signals, you can name your files whatever you like (`auth.js`, `cart.js`, `settings.js`) to keep your logic clean.
|
||||
@@ -12,7 +14,7 @@ Creating a dedicated file allows you to export only what you need. This modulari
|
||||
|
||||
```javascript
|
||||
// auth.js
|
||||
import { $ } from 'sigpro'; // or just rely on global `$` after import
|
||||
import { $ } from 'sigpro';
|
||||
|
||||
// A simple global signal
|
||||
export const user = $({ name: "Guest", loggedIn: false });
|
||||
@@ -136,4 +138,4 @@ const TodoApp = () =>
|
||||
]);
|
||||
|
||||
mount(TodoApp, "#app");
|
||||
```
|
||||
```
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
The `h` function is the **core DOM builder** of SigPro. It creates DOM elements from a tag name, props, and children. While the global tag helpers (`div()`, `button()`, etc.) are built on top of `h`, you may need `h` directly for dynamic tag names or when you prefer an explicit function style.
|
||||
|
||||
> **Availability:** `h` and all tag helpers (`div`, `button`, etc.) are exported from the SigPro module. In **ESM** you must import them (`import { h, div, button } from 'sigpro'`) or inject all globals via `sigpro()`. In the **IIFE** classic script, `h` and all tag helpers are automatically available on `window`. The examples below assume the functions are already in scope.
|
||||
|
||||
## Function Signature
|
||||
|
||||
```typescript
|
||||
@@ -120,16 +122,16 @@ h('svg', { width: 100, height: 100 }, [
|
||||
|
||||
---
|
||||
|
||||
## `h` vs Global Tag Helpers
|
||||
## `h` vs Tag Helpers
|
||||
|
||||
| Feature | `h('div', ...)` | `div(...)` |
|
||||
| Feature | `h('div', ...)` | `div(...)` (tag helper) |
|
||||
| :--- | :--- | :--- |
|
||||
| **Dynamic tag names** | ✅ `h(tagName, ...)` | ❌ Must know tag name at write time |
|
||||
| **Explicit style** | More verbose | Cleaner, DSL‑like |
|
||||
| **Tree shaking** | Same | Same (helpers are generated once) |
|
||||
| **Availability** | Import or global | Import or global (same) |
|
||||
| **Performance** | Identical | Identical (helpers call `h` internally) |
|
||||
|
||||
> **Recommendation:** Use global tag helpers (`div()`, `button()`, etc.) for most cases – they are shorter and more readable. Use `h` directly only when the tag name is dynamic (e.g., `h(props.tag, ...)`).
|
||||
> **Recommendation:** Use tag helpers (`div()`, `button()`, etc.) for most cases – they are shorter and more readable. Use `h` directly only when the tag name is dynamic (e.g., `h(props.tag, ...)`). Remember that in ESM you need to import the helpers or call `sigpro()` to make them global.
|
||||
|
||||
---
|
||||
|
||||
@@ -155,5 +157,5 @@ mount(App, '#app');
|
||||
|
||||
- `h` is the low‑level DOM builder used internally by all tag helpers.
|
||||
- It supports reactive attributes, reactive children, two‑way binding, event listeners, and SVG.
|
||||
- Use `h` directly when you need a **dynamic tag name**; otherwise, prefer the convenient global helpers.
|
||||
- Components written with `h` are fully reactive and automatically cleaned up.
|
||||
- Use `h` directly when you need a **dynamic tag name**; otherwise, prefer the convenient tag helpers (import them or inject globally).
|
||||
- Components written with `h` are fully reactive and automatically cleaned up.
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
Aquí tienes el archivo `h.md` actualizado, que ahora incluye:
|
||||
|
||||
- La función `h` (hyperscript)
|
||||
- Los helpers globales de etiquetas (lowercase)
|
||||
- Una nueva sección sobre **JSX con SigPro** (configuración, ejemplos y alternativas como `htm`).
|
||||
|
||||
El documento está en inglés, como los originales, y listo para integrarse en tu documentación.
|
||||
|
||||
---
|
||||
|
||||
```markdown
|
||||
# Hyperscript & Tag Helpers
|
||||
|
||||
SigPro provides two complementary ways to create DOM elements:
|
||||
@@ -318,6 +307,3 @@ mount(App, '#app');
|
||||
| **`htm`** | Optional | `` html`<div>...</div>` `` | Buildless but HTML‑like syntax |
|
||||
|
||||
> **Tip:** All approaches are fully reactive, support two‑way binding, events, SVG, and automatic cleanup. Choose the one that fits your workflow.
|
||||
```
|
||||
|
||||
```
|
||||
@@ -17,6 +17,8 @@ mount(component: Function | Node, target: string | HTMLElement): RuntimeObject
|
||||
- `container`: The actual DOM element created by the renderer.
|
||||
- `destroy()`: A method to completely unmount and clean up the application.
|
||||
|
||||
> **Availability:** `mount` is exported from the SigPro module. In **ESM** you must import it (`import { mount } from 'sigpro'`) or inject all globals via `sigpro()`. In the **IIFE** classic script, it is automatically available on `window`. The examples below assume the function is already in scope.
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
@@ -145,4 +147,4 @@ setTimeout(() => runtime.destroy(), 10000);
|
||||
| Manual destruction | `const app = mount(App, '#app'); app.destroy();` |
|
||||
| Auto‑replace on same target | Just call `mount` again – SigPro handles cleanup. |
|
||||
|
||||
> **Note:** The function name is `mount` (lowercase). It is exported from SigPro and also available globally after importing the library. The target must exist in the DOM at the time of mounting.
|
||||
> **Note:** The target must exist in the DOM at the time of mounting.
|
||||
@@ -1,10 +1,12 @@
|
||||
# ⚡ SigPro 1.2.18 – Complete API Reference
|
||||
# ⚡ SigPro – Complete API Reference
|
||||
|
||||
SigPro is a **Real‑DOM first** reactive micro‑framework. No virtual DOM, no diffing overhead – it updates the DOM directly with surgical precision. Built‑in automatic cleanup prevents memory leaks, and the API is designed to be both tiny and powerful.
|
||||
|
||||
```javascript
|
||||
import { $, $$, watch, h, when, each, fx, router, req, mount, batch } from 'sigpro'
|
||||
// or use globally as window.$ etc.
|
||||
// Or, if you prefer the global style in an ESM environment:
|
||||
// import { sigpro } from 'sigpro'; sigpro(); // then $, h, div... become window globals
|
||||
// In a classic IIFE script (<script src="sigpro.js">), everything is available globally automatically.
|
||||
```
|
||||
|
||||
---
|
||||
@@ -106,15 +108,23 @@ h('div', {}, [
|
||||
|
||||
### Tag shortcuts
|
||||
|
||||
SigPro defines **all standard HTML5 tags** as PascalCase globals (when run in browser) and also exports them as named exports. Example:
|
||||
When using the **ESM module with named imports**, you can import the tag helpers individually:
|
||||
|
||||
```javascript
|
||||
Div({ class: 'container' }, [
|
||||
H1({}, 'Title'),
|
||||
Button({ onClick: () => alert('hi') }, 'Click me')
|
||||
import { div, h1, button } from 'sigpro'
|
||||
|
||||
div({ class: 'container' }, [
|
||||
h1({}, 'Title'),
|
||||
button({ onClick: () => alert('hi') }, 'Click me')
|
||||
])
|
||||
```
|
||||
|
||||
If you prefer the **global style** (all tags and core functions on `window`), either:
|
||||
- Use the classic IIFE script: `<script src="sigpro.js"></script>`
|
||||
- Or in ESM, call `sigpro()` after importing: `import { sigpro } from 'sigpro'; sigpro();`
|
||||
|
||||
After that, you can write `div()`, `span()`, etc. directly, without any import.
|
||||
|
||||
Available tags: `a`, `abbr`, `article`, `aside`, `audio`, `b`, `blockquote`, `br`, `button`, `canvas`, `caption`, `cite`, `code`, `col`, `colgroup`, `datalist`, `dd`, `del`, `details`, `dfn`, `dialog`, `div`, `dl`, `dt`, `em`, `embed`, `fieldset`, `figcaption`, `figure`, `footer`, `form`, `h1`…`h6`, `header`, `hr`, `i`, `iframe`, `img`, `input`, `ins`, `kbd`, `label`, `legend`, `li`, `main`, `mark`, `meter`, `nav`, `object`, `ol`, `optgroup`, `option`, `output`, `p`, `picture`, `pre`, `progress`, `section`, `select`, `slot`, `small`, `source`, `span`, `strong`, `sub`, `summary`, `sup`, `svg`, `table`, `tbody`, `td`, `template`, `textarea`, `tfoot`, `th`, `thead`, `time`, `tr`, `u`, `ul`, `video`.
|
||||
|
||||
---
|
||||
@@ -128,8 +138,8 @@ Reactive conditional rendering. `condition` can be a boolean, a signal, or any f
|
||||
```javascript
|
||||
when(
|
||||
() => user.loggedIn(),
|
||||
() => Div({}, 'Welcome back!'),
|
||||
() => Button({ onClick: () => login() }, 'Login')
|
||||
() => div({}, 'Welcome back!'),
|
||||
() => button({ onClick: () => login() }, 'Login')
|
||||
)
|
||||
```
|
||||
|
||||
@@ -152,19 +162,7 @@ When the array changes, elements are added, removed, or reordered with minimal D
|
||||
|
||||
---
|
||||
|
||||
## 💥 Effects & Lifecycle
|
||||
|
||||
### `onUnmount(fn)`
|
||||
|
||||
Inside a component (function called from `h`), registers a cleanup function that runs when that component is removed from the DOM.
|
||||
|
||||
```javascript
|
||||
const Timer = () => {
|
||||
const interval = setInterval(() => console.log('tick'), 1000)
|
||||
onUnmount(() => clearInterval(interval))
|
||||
return Div({}, 'Timer running')
|
||||
}
|
||||
```
|
||||
## 💥 Batch
|
||||
|
||||
### `batch(fn)`
|
||||
|
||||
@@ -178,16 +176,6 @@ batch(() => {
|
||||
})
|
||||
```
|
||||
|
||||
### `untrack(fn)`
|
||||
|
||||
Run a function without tracking any signal reads.
|
||||
|
||||
```javascript
|
||||
const logCount = () => {
|
||||
untrack(() => console.log('count is', count()))
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✨ Animations – `fx(options, child)`
|
||||
@@ -218,14 +206,14 @@ Hash‑based SPA router. Returns a DOM node that renders the current route.
|
||||
|
||||
```javascript
|
||||
const routes = [
|
||||
{ path: '/', component: () => Div({}, 'Home') },
|
||||
{ path: '/user/:id', component: (params) => Div({}, `User ${params.id}`) },
|
||||
{ path: '*', component: () => Div({}, '404') }
|
||||
{ path: '/', component: () => div({}, 'Home') },
|
||||
{ path: '/user/:id', component: (params) => div({}, `User ${params.id}`) },
|
||||
{ path: '*', component: () => div({}, '404') }
|
||||
]
|
||||
|
||||
const App = () => Div({}, [
|
||||
A({ href: '#/' }, 'Home'),
|
||||
A({ href: '#/user/42' }, 'User 42'),
|
||||
const App = () => div({}, [
|
||||
a({ href: '#/' }, 'Home'),
|
||||
a({ href: '#/user/42' }, 'User 42'),
|
||||
router(routes)
|
||||
])
|
||||
```
|
||||
@@ -312,11 +300,11 @@ import { $, watch, h, mount } from 'sigpro'
|
||||
const count = $(0, 'counter') // persists in localStorage
|
||||
|
||||
const App = () =>
|
||||
Div({ class: 'counter' }, [
|
||||
H1({}, () => `Count: ${count()}`),
|
||||
Button({ onClick: () => count(count() + 1) }, '+'),
|
||||
Button({ onClick: () => count(count() - 1) }, '-'),
|
||||
Button({ onClick: () => count(0) }, 'Reset')
|
||||
div({ class: 'counter' }, [
|
||||
h1({}, () => `Count: ${count()}`),
|
||||
button({ onClick: () => count(count() + 1) }, '+'),
|
||||
button({ onClick: () => count(count() - 1) }, '-'),
|
||||
button({ onClick: () => count(0) }, 'Reset')
|
||||
])
|
||||
|
||||
mount(App, '#app')
|
||||
@@ -332,22 +320,8 @@ You can rename everything in one line:
|
||||
import { $ as signal, watch as effect, h as element, mount as render } from 'sigpro'
|
||||
```
|
||||
|
||||
Or assign globally:
|
||||
Or assign globally (after calling `sigpro()` or using the classic script):
|
||||
|
||||
```javascript
|
||||
window.myReactive = $
|
||||
```
|
||||
|
||||
All functions are also exposed on the global `window` object when included via `<script>`.
|
||||
|
||||
---
|
||||
|
||||
## 📜 License & Version
|
||||
|
||||
Current version: **1.2.18**
|
||||
Released under MIT.
|
||||
Zero dependencies, ~3KB gzipped.
|
||||
|
||||
---
|
||||
|
||||
> **Need legacy IE support?** Not supported – requires modern JavaScript (Proxy, WeakMap, etc.).
|
||||
```
|
||||
@@ -26,6 +26,8 @@ req(config: {
|
||||
|
||||
**Returns:** A controller object with reactive signals and methods.
|
||||
|
||||
> **Availability:** `req` is exported from the SigPro module. In **ESM** you must import it (`import { req } from 'sigpro'`) or inject all globals via `sigpro()`. In the **IIFE** classic script, it is automatically available on `window`. The examples below assume the function is already in scope.
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
@@ -179,4 +181,4 @@ mount(App, '#app');
|
||||
| `abort()` | `() => void` | Cancels the current request. |
|
||||
| `loading` | `Signal<boolean>` | `true` while request is in flight. |
|
||||
| `error` | `Signal<string\|null>` | Contains an error message, or `null`. |
|
||||
| `data` | `Signal<any\|null>` | Contains the parsed response (JSON), or `null`. |
|
||||
| `data` | `Signal<any\|null>` | Contains the parsed response (JSON), or `null`. |
|
||||
@@ -17,6 +17,8 @@ router(routes: Route[]): HTMLElement
|
||||
|
||||
**Returns:** A `div` element (with class `"router-hook"`) that acts as the router outlet. The router automatically destroys the previous view and mounts the matched component when the hash changes.
|
||||
|
||||
> **Availability:** `router` and its helper methods (`router.to`, `router.back`, `router.path`, `router.params`) are exported from the SigPro module. In **ESM** you must import them (`import { router } from 'sigpro'`) or inject all globals via `sigpro()`. In the **IIFE** classic script, they are automatically available on `window`. The examples below assume the functions are already in scope.
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
@@ -27,7 +27,7 @@ Creates a writable signal. It returns a function that acts as both **getter** an
|
||||
|
||||
<div id="demo-signal-simple"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const count = $(0);
|
||||
const App = () => div({ class: "example" }, [
|
||||
@@ -44,7 +44,7 @@ Creates a writable signal that syncs with the browser's storage.
|
||||
|
||||
<div id="demo-signal-persist"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const theme = $("light", "theme-persist-demo");
|
||||
const App = () => div([
|
||||
@@ -62,7 +62,7 @@ Creates a read-only signal that updates automatically when any signal used insid
|
||||
|
||||
<div id="demo-signal-computed"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const price = $(100);
|
||||
const tax = $(0.21);
|
||||
@@ -86,7 +86,7 @@ When calling the setter, you can pass an **updater function** to access the curr
|
||||
|
||||
<div id="demo-signal-updater"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const list = $(["A", "B"]);
|
||||
const App = () => div([
|
||||
@@ -123,7 +123,7 @@ $$<T extends object>(obj: T): T
|
||||
|
||||
<div id="demo-dollar-simple"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const state = $$({ count: 0, name: "Juan" });
|
||||
watch(() => console.log(`Count is now ${state.count}`));
|
||||
@@ -141,7 +141,7 @@ $$<T extends object>(obj: T): T
|
||||
|
||||
<div id="demo-dollar-deep"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const user = $$({
|
||||
profile: {
|
||||
@@ -164,7 +164,7 @@ $$<T extends object>(obj: T): T
|
||||
|
||||
<div id="demo-dollar-array"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const todos = $$([
|
||||
{ id: 1, text: "Learn SigPro", done: false },
|
||||
@@ -186,7 +186,7 @@ $$<T extends object>(obj: T): T
|
||||
|
||||
<div id="demo-dollar-mixed"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const form = $$({
|
||||
fields: { email: "", password: "" },
|
||||
@@ -232,7 +232,7 @@ $$<T extends object>(obj: T): T
|
||||
|
||||
<div id="demo-use-dollar"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const count = $(0);
|
||||
const firstName = $("John");
|
||||
@@ -254,7 +254,7 @@ $$<T extends object>(obj: T): T
|
||||
|
||||
<div id="demo-use-dollar-dollar"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const form = $$({ email: "", password: "" });
|
||||
const settings = $$({ theme: "dark", notifications: true });
|
||||
@@ -275,7 +275,7 @@ $$<T extends object>(obj: T): T
|
||||
## Important Notes
|
||||
|
||||
### ✅ DO:
|
||||
```js
|
||||
```javascript
|
||||
// Access properties directly
|
||||
state.count = 10;
|
||||
state.user.name = "Ana";
|
||||
@@ -287,7 +287,7 @@ watch(() => state.user.name, () => {});
|
||||
```
|
||||
|
||||
### ❌ DON'T:
|
||||
```js
|
||||
```javascript
|
||||
// Destructuring breaks reactivity
|
||||
const { count, user } = state; // ❌ count and user are not reactive
|
||||
|
||||
@@ -326,7 +326,7 @@ Like all SigPro reactive primitives, `$$()` integrates with the cleanup system:
|
||||
|
||||
<div id="demo-complete"></div>
|
||||
|
||||
```js
|
||||
```javascript
|
||||
{
|
||||
const app = {
|
||||
theme: $("dark", "theme_complete"),
|
||||
@@ -372,7 +372,7 @@ Like all SigPro reactive primitives, `$$()` integrates with the cleanup system:
|
||||
|
||||
If you have code using nested signals:
|
||||
|
||||
```js
|
||||
```javascript
|
||||
// Before - Manual nesting
|
||||
const user = $({
|
||||
name: $(""),
|
||||
|
||||
109
docs/api/tags.md
109
docs/api/tags.md
@@ -1,23 +1,48 @@
|
||||
# Global Tag Helpers
|
||||
|
||||
In **SigPro**, you don't need to manually type `h('div', ...)` for every element. To keep your code declarative and readable, the engine automatically generates **helper functions** for all standard HTML5 tags upon initialization.
|
||||
In **SigPro**, you don't need to manually type `h('div', ...)` for every element. To keep your code declarative and readable, the engine provides **helper functions** for all standard HTML5 tags.
|
||||
|
||||
## 1. How it Works
|
||||
|
||||
SigPro iterates through a list 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**.
|
||||
SigPro iterates through a list of standard HTML tags and creates a wrapper function for each one.
|
||||
- **Under the hood:** `h('button', { onclick: ... }, 'Click')`
|
||||
- **SigPro Style:** `button({ onclick: ... }, 'Click')`
|
||||
|
||||
* **Under the hood:** `h('button', { onclick: ... }, 'Click')`
|
||||
* **SigPro Style:** `button({ onclick: ... }, 'Click')`
|
||||
> **Note:** All tag helpers are **lowercase** (e.g., `div`, `span`, `button`). This keeps the syntax close to raw HTML.
|
||||
|
||||
> **Note:** All tag helpers are **lowercase** (e.g. `div`, `span`, `button`). PascalCase versions (`Div`, `Span`, `Button`) are **not** created. This keeps the syntax close to raw HTML.
|
||||
These helpers can be used in two ways, depending on your environment:
|
||||
|
||||
### Mode A: Classic (IIFE) – Auto‑global
|
||||
When you load the **IIFE bundle** (`sigpro.js`) with a traditional `<script>` tag (no `type="module"`), all tag helpers are automatically injected into the `window` object.
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.19/dist/sigpro.js"></script>
|
||||
<script>
|
||||
// div, span, button, ... are already global
|
||||
const App = () => div({ class: "card" }, "Hello");
|
||||
</script>
|
||||
```
|
||||
|
||||
### Mode B: ESM (Modern) – Explicit or Imported
|
||||
When you import the **ES module** (via `import` or CDN with `type="module"`), nothing is added to `window` by default. You have two options:
|
||||
|
||||
1. **Manual global injection** – import `sigpro` and call it:
|
||||
```javascript
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // now div, span, button, etc. become global
|
||||
```
|
||||
2. **Named imports** (recommended) – import the helpers you need directly:
|
||||
```javascript
|
||||
import { div, span, button } from 'sigpro';
|
||||
// use them directly
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. The Complete Global Registry
|
||||
## 2. The Complete List of Tag Helpers
|
||||
|
||||
The following functions are injected into the global scope using **lowercase** names to match HTML tags:
|
||||
All helpers are **lowercase** and follow HTML5 tag names. You can use them globally (after injection) or import them individually.
|
||||
|
||||
| Category | Available Global Functions |
|
||||
| Category | Available functions |
|
||||
| :--- | :--- |
|
||||
| **Structure** | `div`, `span`, `p`, `section`, `nav`, `main`, `header`, `footer`, `article`, `aside` |
|
||||
| **Typography** | `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `ul`, `ol`, `li`, `dl`, `dt`, `dd`, `strong`, `em`, `code`, `pre`, `small`, `b`, `u`, `mark` |
|
||||
@@ -26,26 +51,24 @@ The following functions are injected into the global scope using **lowercase** n
|
||||
| **Tables** | `table`, `thead`, `tbody`, `tr`, `th`, `td`, `tfoot`, `caption` |
|
||||
| **Media** | `img`, `canvas`, `video`, `audio`, `svg`, `iframe`, `picture`, `source` |
|
||||
|
||||
Full list includes: `a`, `abbr`, `article`, `aside`, `audio`, `b`, `blockquote`, `br`, `button`, `canvas`, `caption`, `cite`, `code`, `col`, `colgroup`, `datalist`, `dd`, `del`, `details`, `dfn`, `dialog`, `div`, `dl`, `dt`, `em`, `embed`, `fieldset`, `figcaption`, `figure`, `footer`, `form`, `h1`…`h6`, `header`, `hr`, `i`, `iframe`, `img`, `input`, `ins`, `kbd`, `label`, `legend`, `li`, `main`, `mark`, `meter`, `nav`, `object`, `ol`, `optgroup`, `option`, `output`, `p`, `picture`, `pre`, `progress`, `section`, `select`, `slot`, `small`, `source`, `span`, `strong`, `sub`, `summary`, `sup`, `svg`, `table`, `tbody`, `td`, `template`, `textarea`, `tfoot`, `th`, `thead`, `time`, `tr`, `u`, `ul`, `video`.
|
||||
Full list includes all standard tags: `a`, `abbr`, `article`, `aside`, `audio`, `b`, `blockquote`, `br`, `button`, `canvas`, `caption`, `cite`, `code`, `col`, `colgroup`, `datalist`, `dd`, `del`, `details`, `dfn`, `dialog`, `div`, `dl`, `dt`, `em`, `embed`, `fieldset`, `figcaption`, `figure`, `footer`, `form`, `h1`…`h6`, `header`, `hr`, `i`, `iframe`, `img`, `input`, `ins`, `kbd`, `label`, `legend`, `li`, `main`, `mark`, `meter`, `nav`, `object`, `ol`, `optgroup`, `option`, `output`, `p`, `picture`, `pre`, `progress`, `section`, `select`, `slot`, `small`, `source`, `span`, `strong`, `sub`, `summary`, `sup`, `svg`, `table`, `tbody`, `td`, `template`, `textarea`, `tfoot`, `th`, `thead`, `time`, `tr`, `u`, `ul`, `video`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Usage Patterns
|
||||
|
||||
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.")
|
||||
p("The zero‑VDOM framework.")
|
||||
]);
|
||||
```
|
||||
|
||||
### B. Children Only
|
||||
|
||||
If you don't need attributes, you can pass the content directly as the first argument.
|
||||
If you don't need attributes, pass the content directly as the first argument.
|
||||
|
||||
```javascript
|
||||
section([
|
||||
@@ -74,7 +97,7 @@ div({
|
||||
|
||||
### Two‑Way Binding (Automatic)
|
||||
|
||||
SigPro automatically bridges the **signal** and the **input element** bidirectionally when you assign a signal to `value` or `checked`.
|
||||
Assign a **signal** directly to `value` or `checked` on form inputs – SigPro automatically bridges the signal and the input element bidirectionally.
|
||||
|
||||
```javascript
|
||||
const search = $("");
|
||||
@@ -86,11 +109,11 @@ 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 backward synchronization.
|
||||
> **Pro Tip:** To make an input **read‑only** but still reactive, wrap the signal in a function: `value: () => search()` – this prevents backward synchronization.
|
||||
|
||||
### Dynamic Children
|
||||
|
||||
You can pass a function as a child – it will be re‑executed whenever any signal inside changes, and the DOM will be patched surgically.
|
||||
You can pass a **function as a child** – it will be re‑executed whenever any signal inside changes, and the DOM will be patched surgically.
|
||||
|
||||
```javascript
|
||||
const count = $(0);
|
||||
@@ -103,9 +126,9 @@ div([
|
||||
|
||||
---
|
||||
|
||||
## 5. Custom Components with `h()`
|
||||
## 5. Custom Components with `h()` or Tag Helpers
|
||||
|
||||
While the global tag helpers cover all standard HTML tags, you can create reusable components using the `h` function directly (or by returning the result of tag helpers).
|
||||
While the tag helpers cover all standard HTML tags, you can create reusable components using them directly.
|
||||
|
||||
### Basic Component
|
||||
|
||||
@@ -151,7 +174,7 @@ const Timer = () => {
|
||||
|
||||
| Use case | Recommendation |
|
||||
| :--- | :--- |
|
||||
| Standard tags (`div`, `span`, `button`) | Use global helpers: `div()`, `span()`, `button()` |
|
||||
| Standard tags (`div`, `span`, `button`) | Use tag helpers: `div()`, `span()`, `button()` |
|
||||
| Dynamic tag names (unknown at write time) | Use `h(tagName, props, children)` |
|
||||
| Components returning a single node | Any function that returns a node (using helpers or `h`) |
|
||||
|
||||
@@ -162,13 +185,17 @@ const Timer = () => {
|
||||
## 7. Complete Example
|
||||
|
||||
```javascript
|
||||
// In a modern ESM environment (recommended)
|
||||
import { div, h1, input, p, button, mount, $ } from 'sigpro';
|
||||
|
||||
const nameSignal = $('');
|
||||
|
||||
const App = () =>
|
||||
div({ class: "app" }, [
|
||||
h1("Welcome"),
|
||||
input({
|
||||
placeholder: "Your name",
|
||||
value: nameSignal,
|
||||
onInput: (e) => nameSignal(e.target.value)
|
||||
value: nameSignal
|
||||
}),
|
||||
p(() => `Hello, ${nameSignal() || "stranger"}!`),
|
||||
button({ onClick: () => alert("Clicked") }, "Click me")
|
||||
@@ -177,27 +204,41 @@ const App = () =>
|
||||
mount(App, '#app');
|
||||
```
|
||||
|
||||
---
|
||||
Or using the classic script (auto‑global):
|
||||
|
||||
<div class="alert alert-info">
|
||||
<div>
|
||||
<h3>Important Notes</h3>
|
||||
<ul>
|
||||
<li><b>Naming:</b> All tag helpers are <b>lowercase</b>. There are no PascalCase helpers (<code>Div</code>, <code>Button</code>).</li>
|
||||
<li><b>Global availability:</b> After importing SigPro (via <code>import 'sigpro'</code> or CDN), all helpers are on <code>window</code>. You can use them anywhere without importing.</li>
|
||||
<li><b>Custom components:</b> Use PascalCase for your own component functions to visually distinguish them from built‑in tags (e.g., <code>UserCard</code>).</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.19/dist/sigpro.js"></script>
|
||||
<script>
|
||||
const nameSignal = $('');
|
||||
const App = () => div({ class: "app" }, [
|
||||
h1("Welcome"),
|
||||
input({ placeholder: "Your name", value: nameSignal }),
|
||||
p(() => `Hello, ${nameSignal() || "stranger"}!`),
|
||||
button({ onClick: () => alert("Clicked") }, "Click me")
|
||||
]);
|
||||
mount(App, '#app');
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Summary
|
||||
## 8. Important Notes
|
||||
|
||||
- **Naming:** All tag helpers are **lowercase** – no PascalCase helpers (`Div`, `Button`).
|
||||
- **Global availability:**
|
||||
- **IIFE script** → automatically on `window`.
|
||||
- **ESM module** → not global by default; use `import { div } from 'sigpro'` or call `sigpro()` to inject all globals.
|
||||
- **Custom components:** Use **PascalCase** for your own component functions (e.g., `UserCard`) to visually distinguish them from built‑in tags.
|
||||
|
||||
---
|
||||
|
||||
## 9. Summary
|
||||
|
||||
| Feature | Description |
|
||||
| :--- | :--- |
|
||||
| **Tag helpers** | Lowercase functions for every HTML element (e.g., `div()`, `button()`). |
|
||||
| **Availability** | Auto‑global in IIFE; in ESM use named imports or `sigpro()`. |
|
||||
| **Reactive attributes** | Pass a function to any attribute to keep it synced. |
|
||||
| **Two‑way binding** | Assign a signal directly to `value` or `checked` on form elements. |
|
||||
| **Dynamic children** | Pass a function as a child for live updating content. |
|
||||
| **Auto‑cleanup** | All effects, events, and children are disposed when the element is removed. |
|
||||
| **Auto‑cleanup** | All effects, events, and children are disposed when the element is removed. |
|
||||
|
||||
@@ -20,6 +20,8 @@ watch(deps: Signal[], callback: (values: any[]) => void): StopFunction
|
||||
|
||||
**Returns:** A `StopFunction` that, when called, destroys the watcher and releases memory.
|
||||
|
||||
> **Availability:** `watch` is exported from the SigPro module. In **ESM** you must import it (`import { watch } from 'sigpro'`) or inject all globals via `sigpro()`. In the **IIFE** classic script, it is automatically available on `window`. The examples below assume the function is already in scope.
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
@@ -95,7 +97,7 @@ This is achieved via `queueMicrotask`, ensuring optimal performance.
|
||||
|
||||
## Key Points
|
||||
|
||||
- **Function name:** `watch` (lowercase) – exported from SigPro and also available globally.
|
||||
- **Function name:** `watch` (lowercase) – exported from SigPro and also available globally (depending on environment).
|
||||
- **Auto mode:** `watch(fn)` – automatically tracks any signals read inside `fn`.
|
||||
- **Explicit mode:** `watch([sig1, sig2], (values) => {...})` – only re‑runs when listed signals change; callback receives an array of their new values.
|
||||
- **Stop function:** returned by both modes; call it to dispose the effect and its children.
|
||||
@@ -119,5 +121,4 @@ watch([count, step], ([newCount, newStep]) => {
|
||||
|
||||
count(5); // logs: auto + explicit
|
||||
step(2); // logs: explicit only (auto does not track step)
|
||||
```
|
||||
|
||||
```
|
||||
@@ -20,7 +20,7 @@ when(
|
||||
|
||||
**Returns:** A `div` with `style="display:contents"` that acts as an anchor for the dynamic content. This element is part of the DOM and will be replaced/updated automatically.
|
||||
|
||||
> **Note:** The function name is `when` (lowercase). It is exported from SigPro and also available globally after importing.
|
||||
> **Availability:** `when` is exported from the SigPro module. In **ESM** you must import it (`import { when } from 'sigpro'`) or inject all globals via `sigpro()`. In the **IIFE** classic script, it is automatically available on `window`. The examples below assume the function is already in scope.
|
||||
|
||||
---
|
||||
|
||||
@@ -129,4 +129,4 @@ const App = () =>
|
||||
]);
|
||||
|
||||
mount(App, '#app');
|
||||
```
|
||||
```
|
||||
@@ -67,40 +67,5 @@
|
||||
<script src="./sigpro.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||
<style>
|
||||
button, input {
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
}
|
||||
button {
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.375rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin: 0.25rem;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #2563eb;
|
||||
}
|
||||
input {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 0.375rem;
|
||||
margin: 0.25rem;
|
||||
background-color: white;
|
||||
}
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 2px rgba(59,130,246,0.2);
|
||||
}
|
||||
/* Opcional: para listas y párrafos dentro de demos */
|
||||
.example, div:has(> button) {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
119
docs/install.md
119
docs/install.md
@@ -1,4 +1,4 @@
|
||||
# Installation & Setup (SigPro 1.2.18)
|
||||
# Installation & Setup
|
||||
|
||||
SigPro is designed to be drop-in ready. Whether you are building a complex application with a bundler or a simple reactive widget in a single HTML file, SigPro scales with your needs.
|
||||
|
||||
@@ -42,14 +42,40 @@ bun add sigpro
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<input type="radio" name="install_method" class="tab border-base-300 whitespace-nowrap" aria-label="CDN (ESM)" />
|
||||
<div class="tab-content bg-base-100 border-base-300 rounded-box p-6">
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
// Import the core – it auto-installs itself globally
|
||||
import 'https://cdn.jsdelivr.net/npm/sigpro@1.2.18/+esm';
|
||||
// Now $, $$, watch, h, when, each, fx, router, req, mount, batch and all lowercase tag helpers (div, button, etc.) are available
|
||||
// Import the module – no automatic global injection
|
||||
import { sigpro, $, h, mount } from 'https://cdn.jsdelivr.net/npm/sigpro@1.2.19/+esm';
|
||||
|
||||
// Option A: manually inject all globals (like the classic script)
|
||||
sigpro(); // now $, h, div, watch, etc. are on window
|
||||
|
||||
// Option B: use named imports (no global pollution)
|
||||
const count = $(0);
|
||||
mount(() => h1(() => `Count: ${count()}`), '#app');
|
||||
</script>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<input type="radio" name="install_method" class="tab border-base-300 whitespace-nowrap" aria-label="CDN (IIFE)" />
|
||||
<div class="tab-content bg-base-100 border-base-300 rounded-box p-6">
|
||||
|
||||
```html
|
||||
<!-- Classic script: auto‑installs everything on window -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.19/dist/sigpro.js"></script>
|
||||
<script>
|
||||
// $, h, div, button, watch, ... are already global
|
||||
const count = $(0);
|
||||
const App = () => div({ class: "card" }, [
|
||||
h1(() => `Count: ${count()}`),
|
||||
button({ onclick: () => count(count() + 1) }, "Increment")
|
||||
]);
|
||||
mount(App, '#app');
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -63,31 +89,26 @@ bun add sigpro
|
||||
SigPro uses **lowercase** Tag Helpers (e.g., `div`, `button`) to keep the syntax close to raw HTML, while still being pure JavaScript functions.
|
||||
|
||||
<div class="tabs tabs-box w-full mt-8 mb-12 bg-base-200/50 p-2 rounded-xl border border-base-300">
|
||||
<input type="radio" name="quick_start_tabs" class="tab !rounded-lg" aria-label="Mainstream (Bundlers)" checked />
|
||||
<input type="radio" name="quick_start_tabs" class="tab !rounded-lg" aria-label="Bundlers (ESM)" checked />
|
||||
<div class="tab-content bg-base-100 border-base-300 rounded-lg p-6 mt-2">
|
||||
|
||||
```javascript
|
||||
// File: App.js
|
||||
import 'sigpro'; // auto-installs globals
|
||||
// App.js – Using named imports (recommended)
|
||||
import { $, h1, button, div, mount } from 'sigpro';
|
||||
|
||||
export const App = () => {
|
||||
const count = $(0);
|
||||
|
||||
// Tag helpers like div, h1, button are available globally (lowercase)
|
||||
return div({ class: "card p-4" }, [
|
||||
h1(() => `Count is: ${count()}`),
|
||||
button(
|
||||
{
|
||||
class: "btn btn-primary",
|
||||
onclick: () => count(count() + 1),
|
||||
},
|
||||
{ class: "btn btn-primary", onclick: () => count(count() + 1) },
|
||||
"Increment"
|
||||
),
|
||||
]);
|
||||
};
|
||||
|
||||
// File: main.js
|
||||
import 'sigpro';
|
||||
// main.js
|
||||
import { mount } from 'sigpro';
|
||||
import { App } from './App.js';
|
||||
|
||||
mount(App, '#app');
|
||||
@@ -104,23 +125,19 @@ mount(App, '#app');
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<script type="module">
|
||||
import 'https://cdn.jsdelivr.net/npm/sigpro@1.2.18/+esm';
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.19/dist/sigpro.js"></script>
|
||||
<script>
|
||||
// Everything is already global – no import needed
|
||||
const name = $('Developer');
|
||||
|
||||
// Lowercase tag helpers: section, h2, input
|
||||
const App = () =>
|
||||
section({ class: "container" }, [
|
||||
h2(() => `Welcome, ${name()}`),
|
||||
input({
|
||||
type: "text",
|
||||
class: "input input-bordered",
|
||||
value: name, // ✅ Two-way binding: signal as value + automatic input event
|
||||
placeholder: "Type your name...",
|
||||
}),
|
||||
]);
|
||||
|
||||
const App = () => section({ class: "container" }, [
|
||||
h2(() => `Welcome, ${name()}`),
|
||||
input({
|
||||
type: "text",
|
||||
class: "input input-bordered",
|
||||
value: name,
|
||||
placeholder: "Type your name...",
|
||||
}),
|
||||
]);
|
||||
mount(App, '#app');
|
||||
</script>
|
||||
</body>
|
||||
@@ -132,26 +149,42 @@ mount(App, '#app');
|
||||
|
||||
---
|
||||
|
||||
## 3. Global by Design
|
||||
## 3. Global by Design (Two Modes)
|
||||
|
||||
One of SigPro's core strengths is its **Global API**, which eliminates "Import Hell" while remaining ESM-compatible.
|
||||
SigPro gives you full control over global pollution.
|
||||
|
||||
- **The "Zero-Config" Import:** By simply adding `import 'sigpro'` (or importing from the CDN), the framework automatically "hydrates" the global `window` object.
|
||||
- **Core Functions:** You get immediate access to `$`, `$$`, `watch`, `h`, `when`, `each`, `fx`, `router`, `req`, `mount`, `batch` anywhere in your scripts.
|
||||
- **Auto-Installation:** This happens instantly upon import thanks to its built-in self‑installation, making it "Plug & Play" for both local projects and CDN usage.
|
||||
### Mode A: Classic (IIFE) – Auto‑injection
|
||||
When you load the **IIFE bundle** (`sigpro.js`) with a traditional `<script>` tag (no `type="module"`), the library automatically injects:
|
||||
- All core functions (`$`, `$$`, `watch`, `h`, `when`, `each`, `fx`, `router`, `req`, `mount`, `batch`) into `window`.
|
||||
- Lowercase tag helpers (`div`, `span`, `button`, etc.) also become global functions.
|
||||
|
||||
- **Lowercase Tag Helpers:** All standard HTML tags are pre-registered as global functions (`div`, `span`, `button`, `section`, `input`, `h1`, `h2`, etc.).
|
||||
- **Clean UI Syntax:** Write UI structures that look almost like HTML but are pure, reactive JavaScript: `div({ class: "card" }, [ h1("Title") ])`.
|
||||
✅ Zero configuration – just drop the script and start coding.
|
||||
|
||||
- **Tree Shaking Friendly:** For maximum optimization, you can still use named imports: `import { $, watch, mount } from 'sigpro'`. Modern bundlers (Vite, esbuild) will prune unused code.
|
||||
### Mode B: ESM (Modern) – Explicit Injection
|
||||
When you import the **ESM module** (from CDN or via `import`), **nothing** is added to `window` by default. You have two clean options:
|
||||
|
||||
- **Custom Components:** We recommend using **PascalCase** for your own components (e.g., `UserCard()`) to distinguish them from built-in lowercase tag helpers.
|
||||
1. **Named imports** (recommended for most apps):
|
||||
```javascript
|
||||
import { $, h, mount } from 'sigpro';
|
||||
```
|
||||
No global pollution, perfect for bundlers and large projects.
|
||||
|
||||
2. **Manual global injection** (similar to classic mode but controlled):
|
||||
```javascript
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // now $, h, div, button, ... are on window
|
||||
```
|
||||
Useful for quick prototyping or when you prefer the global style.
|
||||
|
||||
### Why two modes?
|
||||
- **Legacy / no‑build**: Use the IIFE script and get everything automatically.
|
||||
- **Modern ESM**: Keep your global namespace clean, leverage tree‑shaking, or inject only when you need it.
|
||||
|
||||
---
|
||||
|
||||
## 4. Why no build step?
|
||||
|
||||
Because SigPro uses **native ES Modules** and standard JavaScript functions to generate the DOM, you don't actually _need_ a compiler like Babel or a transformer for JSX.
|
||||
Because SigPro uses **native ES Modules** and standard JavaScript functions to generate the DOM, you don't actually *need* a compiler like Babel or a transformer for JSX.
|
||||
|
||||
- **Development:** Just save and refresh. Pure JS, no "transpilation" required.
|
||||
- **Performance:** Extremely lightweight. Use any modern bundler (Vite, esbuild) only when you are ready to minify and tree-shake for production.
|
||||
@@ -177,7 +210,7 @@ SigPro stands out by removing the "Build Step" tax and the "Virtual DOM" overhea
|
||||
- **Fine-Grained Reactivity**: State changes only trigger updates where the data is actually used, not on the entire component.
|
||||
- **Native Web Standards**: Everything is a standard JS function. No custom template syntax to learn.
|
||||
- **Zero Magic**: No hidden compilers. What you write is what runs in the browser.
|
||||
- **Global by Design**: Tag helpers and core functions are available globally to eliminate "Import Hell" and keep your code clean.
|
||||
- **Global by Design** (with control): Tag helpers and core functions can be globally available (IIFE) or imported on demand (ESM) – you choose.
|
||||
|
||||
---
|
||||
|
||||
@@ -185,4 +218,4 @@ SigPro stands out by removing the "Build Step" tax and the "Virtual DOM" overhea
|
||||
|
||||
SigPro isn't just another framework; it's a bridge to the native web. By using standard ES Modules and functional DOM generation, you get the benefits of a modern reactive library with the weight of a utility script.
|
||||
|
||||
**Because, in the end... why fight the web when we can embrace it?**
|
||||
**Because, in the end... why fight the web when we can embrace it?**
|
||||
Reference in New Issue
Block a user