Compare commits

...

2 Commits

Author SHA1 Message Date
af5bd1a537 Tree shaking ESM autoimport IIFE
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 3s
2026-04-26 15:38:26 +02:00
a792e72b63 Update Docs 2026-04-26 15:38:10 +02:00
18 changed files with 306 additions and 265 deletions

View File

@@ -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> <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">Finegrained signals update exactly what changes. No VDOM 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">ULTRATHIN</h3><p class="text-sm opacity-70">Sub2KB 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">COMPILERFREE</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>HighEfficiency 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>&lt;div&gt;Hello&lt;/div&gt;</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 (VDOM / Parser)</td><td><strong>Zero</strong></td></tr>
<tr><td>Reactivity</td><td>Componentwide</td><td><strong>Atomic (Nodelevel)</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 (~3090KB) 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 highres icon.</li>
</ul>
| Feature | Standard HTML / JSX | SigPro Functional | <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>
| **Syntax** | `<div>Hello</div>` | `Div("Hello")` | <pre class="bg-base-300/30 p-4 rounded-lg mb-4"><code>import { $, div, button, mount } from 'sigpro';
| **Processing** | Parse → Diff → Patch | Direct API Call | const count = $(0);
| **Overhead** | High (V-DOM / Parser) | **Zero** | mount(() => div([ button({ onclick: () => count(count()+1) }, count) ]), '#app');</code></pre>
| **Reactivity** | Component-wide | **Atomic (Node-level)** | <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>&lt;script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.19/dist/sigpro.js"&gt;&lt;/script&gt;
&lt;script&gt;
const count = $(0);
mount(() => div([ button({ onclick: () => count(count()+1) }, count) ]), '#app');
&lt;/script&gt;</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>&lt;script type="module"&gt;
import { sigpro } from 'https://cdn.jsdelivr.net/npm/sigpro@1.2.19/+esm';
sigpro(); // now $, div, button, etc. are global
&lt;/script&gt;</code></pre>
### Less Code, More Power <h3 class="text-2xl font-bold mt-10 mb-4">Precision Engineering</h3>
By sharing a miniscule runtime, your final application bundle is **infinitely smaller**. <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 preoptimized 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". <h4 class="text-xl font-semibold mt-6 mb-2">2. The "NoBundle" Bundle</h4>
* **Solid/Svelte:** You still depend on a compilation step that generates extra boilerplate. <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>
* **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">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 reevaluating the component functions themselves.</p>
</div>
## Precision Engineering <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 ultrafast, modern apps with <strong>True Vanilla Performance</strong>.</p></div></div></div>
### 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="text-center py-10 opacity-30 font-mono text-xs tracking-widest uppercase">Precision Reactive Engine — NatxoCC</div> <div class="text-center py-10 opacity-30 font-mono text-xs tracking-widest uppercase">Precision Reactive Engine — NatxoCC</div>

View File

@@ -8,7 +8,7 @@ The `each` function is a highperformance keyed list renderer. It maps a react
each( each(
source: Signal<any[]> | (() => any[]) | any[], source: Signal<any[]> | (() => any[]) | any[],
itemFn: (item: any, index: number) => Node | (() => Node), itemFn: (item: any, index: number) => Node | (() => Node),
keyFn?: (item: any, index: number) => string | number keyField?: string
): HTMLElement ): HTMLElement
``` ```
@@ -16,7 +16,7 @@ each(
| :--- | :--- | :--- | :--- | | :--- | :--- | :--- | :--- |
| **`source`** | `Signal`, `() => any[]`, or `any[]` | Yes | The reactive array to iterate over. | | **`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. | | **`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. **Returns:** A `div` with `style="display: contents"` that contains the live list.
@@ -26,7 +26,7 @@ each(
### 1. Basic Keyed List (Recommended) ### 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 ```javascript
const users = $([ const users = $([
@@ -37,14 +37,14 @@ const users = $([
ul({ class: "list" }, [ ul({ class: "list" }, [
each(users, each(users,
(user) => li({ class: "p-2" }, user.name), (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) ### 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 ```javascript
const tags = $(["Tech", "JS", "Web"]); 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 reexecuted every time the items data changes (but the node is reused). If your `itemFn` returns a **function**, that function is reexecuted every time the items 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 }), input({ type: "checkbox", checked: () => todo.done, onInput: e => todo.done = e.target.checked }),
span(() => todo.done ? s(todo.text) : todo.text) 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 (nonreactive) or a function that returns an array it will still react to changes if signals are read inside the function. `source` can be a plain array (nonreactive) 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; 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`: 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. 2. **Reuses existing DOM nodes** for keys that stay the same.
3. **Moves nodes** if order changed (no recreation). 3. **Moves nodes** if order changed (no recreation).
4. **Creates new nodes** for new keys. 4. **Creates new nodes** for new keys.
@@ -107,8 +120,8 @@ When the `source` changes, `each`:
## Performance Tips ## 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. - **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 a local state, using a stable key ensures that state is preserved even when the list is filtered or sorted. - **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. - **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}`), span(`${item.name} $${item.price}`),
button({ onClick: () => removeItem(item.id) }, "X") button({ onClick: () => removeItem(item.id) }, "X")
]), ]),
(item) => item.id "id"
) )
) )
]); ]);

View File

@@ -31,6 +31,8 @@ fx(
**Returns:** The same DOM node (or the child if it is not a `Node`), after applying the animation setup. **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 ## Usage Patterns

View File

@@ -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.** 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) ## 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. 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 ```javascript
// auth.js // auth.js
import { $ } from 'sigpro'; // or just rely on global `$` after import import { $ } from 'sigpro';
// A simple global signal // A simple global signal
export const user = $({ name: "Guest", loggedIn: false }); export const user = $({ name: "Guest", loggedIn: false });

View File

@@ -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. 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 ## Function Signature
```typescript ```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 | | **Dynamic tag names** | ✅ `h(tagName, ...)` | ❌ Must know tag name at write time |
| **Explicit style** | More verbose | Cleaner, DSLlike | | **Explicit style** | More verbose | Cleaner, DSLlike |
| **Tree shaking** | Same | Same (helpers are generated once) | | **Availability** | Import or global | Import or global (same) |
| **Performance** | Identical | Identical (helpers call `h` internally) | | **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 lowlevel DOM builder used internally by all tag helpers. - `h` is the lowlevel DOM builder used internally by all tag helpers.
- It supports reactive attributes, reactive children, twoway binding, event listeners, and SVG. - It supports reactive attributes, reactive children, twoway binding, event listeners, and SVG.
- Use `h` directly when you need a **dynamic tag name**; otherwise, prefer the convenient global helpers. - 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. - Components written with `h` are fully reactive and automatically cleaned up.

View File

@@ -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 # Hyperscript & Tag Helpers
SigPro provides two complementary ways to create DOM elements: SigPro provides two complementary ways to create DOM elements:
@@ -318,6 +307,3 @@ mount(App, '#app');
| **`htm`** | Optional | `` html`<div>...</div>` `` | Buildless but HTMLlike syntax | | **`htm`** | Optional | `` html`<div>...</div>` `` | Buildless but HTMLlike syntax |
> **Tip:** All approaches are fully reactive, support twoway binding, events, SVG, and automatic cleanup. Choose the one that fits your workflow. > **Tip:** All approaches are fully reactive, support twoway binding, events, SVG, and automatic cleanup. Choose the one that fits your workflow.
```
```

View File

@@ -17,6 +17,8 @@ mount(component: Function | Node, target: string | HTMLElement): RuntimeObject
- `container`: The actual DOM element created by the renderer. - `container`: The actual DOM element created by the renderer.
- `destroy()`: A method to completely unmount and clean up the application. - `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 ## Usage Patterns
@@ -145,4 +147,4 @@ setTimeout(() => runtime.destroy(), 10000);
| Manual destruction | `const app = mount(App, '#app'); app.destroy();` | | Manual destruction | `const app = mount(App, '#app'); app.destroy();` |
| Autoreplace on same target | Just call `mount` again SigPro handles cleanup. | | Autoreplace 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.

View File

@@ -1,10 +1,12 @@
# ⚡ SigPro 1.2.18 Complete API Reference # ⚡ SigPro Complete API Reference
SigPro is a **RealDOM first** reactive microframework. No virtual DOM, no diffing overhead it updates the DOM directly with surgical precision. Builtin automatic cleanup prevents memory leaks, and the API is designed to be both tiny and powerful. SigPro is a **RealDOM first** reactive microframework. No virtual DOM, no diffing overhead it updates the DOM directly with surgical precision. Builtin automatic cleanup prevents memory leaks, and the API is designed to be both tiny and powerful.
```javascript ```javascript
import { $, $$, watch, h, when, each, fx, router, req, mount, batch } from 'sigpro' 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 ### 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 ```javascript
Div({ class: 'container' }, [ import { div, h1, button } from 'sigpro'
H1({}, 'Title'),
Button({ onClick: () => alert('hi') }, 'Click me') 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`. 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 ```javascript
when( when(
() => user.loggedIn(), () => user.loggedIn(),
() => Div({}, 'Welcome back!'), () => div({}, 'Welcome back!'),
() => Button({ onClick: () => login() }, 'Login') () => button({ onClick: () => login() }, 'Login')
) )
``` ```
@@ -152,19 +162,7 @@ When the array changes, elements are added, removed, or reordered with minimal D
--- ---
## 💥 Effects & Lifecycle ## 💥 Batch
### `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(fn)` ### `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)` ## ✨ Animations `fx(options, child)`
@@ -218,14 +206,14 @@ Hashbased SPA router. Returns a DOM node that renders the current route.
```javascript ```javascript
const routes = [ const routes = [
{ path: '/', component: () => Div({}, 'Home') }, { path: '/', component: () => div({}, 'Home') },
{ path: '/user/:id', component: (params) => Div({}, `User ${params.id}`) }, { path: '/user/:id', component: (params) => div({}, `User ${params.id}`) },
{ path: '*', component: () => Div({}, '404') } { path: '*', component: () => div({}, '404') }
] ]
const App = () => Div({}, [ const App = () => div({}, [
A({ href: '#/' }, 'Home'), a({ href: '#/' }, 'Home'),
A({ href: '#/user/42' }, 'User 42'), a({ href: '#/user/42' }, 'User 42'),
router(routes) router(routes)
]) ])
``` ```
@@ -312,11 +300,11 @@ import { $, watch, h, mount } from 'sigpro'
const count = $(0, 'counter') // persists in localStorage const count = $(0, 'counter') // persists in localStorage
const App = () => const App = () =>
Div({ class: 'counter' }, [ div({ class: 'counter' }, [
H1({}, () => `Count: ${count()}`), h1({}, () => `Count: ${count()}`),
Button({ onClick: () => count(count() + 1) }, '+'), button({ onClick: () => count(count() + 1) }, '+'),
Button({ onClick: () => count(count() - 1) }, '-'), button({ onClick: () => count(count() - 1) }, '-'),
Button({ onClick: () => count(0) }, 'Reset') button({ onClick: () => count(0) }, 'Reset')
]) ])
mount(App, '#app') 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' 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 ```javascript
window.myReactive = $ 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.).

View File

@@ -26,6 +26,8 @@ req(config: {
**Returns:** A controller object with reactive signals and methods. **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 ## Usage Patterns

View File

@@ -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. **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 ## Usage Patterns

View File

@@ -27,7 +27,7 @@ Creates a writable signal. It returns a function that acts as both **getter** an
<div id="demo-signal-simple"></div> <div id="demo-signal-simple"></div>
```js ```javascript
{ {
const count = $(0); const count = $(0);
const App = () => div({ class: "example" }, [ 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> <div id="demo-signal-persist"></div>
```js ```javascript
{ {
const theme = $("light", "theme-persist-demo"); const theme = $("light", "theme-persist-demo");
const App = () => div([ 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> <div id="demo-signal-computed"></div>
```js ```javascript
{ {
const price = $(100); const price = $(100);
const tax = $(0.21); 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> <div id="demo-signal-updater"></div>
```js ```javascript
{ {
const list = $(["A", "B"]); const list = $(["A", "B"]);
const App = () => div([ const App = () => div([
@@ -123,7 +123,7 @@ $$<T extends object>(obj: T): T
<div id="demo-dollar-simple"></div> <div id="demo-dollar-simple"></div>
```js ```javascript
{ {
const state = $$({ count: 0, name: "Juan" }); const state = $$({ count: 0, name: "Juan" });
watch(() => console.log(`Count is now ${state.count}`)); watch(() => console.log(`Count is now ${state.count}`));
@@ -141,7 +141,7 @@ $$<T extends object>(obj: T): T
<div id="demo-dollar-deep"></div> <div id="demo-dollar-deep"></div>
```js ```javascript
{ {
const user = $$({ const user = $$({
profile: { profile: {
@@ -164,7 +164,7 @@ $$<T extends object>(obj: T): T
<div id="demo-dollar-array"></div> <div id="demo-dollar-array"></div>
```js ```javascript
{ {
const todos = $$([ const todos = $$([
{ id: 1, text: "Learn SigPro", done: false }, { id: 1, text: "Learn SigPro", done: false },
@@ -186,7 +186,7 @@ $$<T extends object>(obj: T): T
<div id="demo-dollar-mixed"></div> <div id="demo-dollar-mixed"></div>
```js ```javascript
{ {
const form = $$({ const form = $$({
fields: { email: "", password: "" }, fields: { email: "", password: "" },
@@ -232,7 +232,7 @@ $$<T extends object>(obj: T): T
<div id="demo-use-dollar"></div> <div id="demo-use-dollar"></div>
```js ```javascript
{ {
const count = $(0); const count = $(0);
const firstName = $("John"); const firstName = $("John");
@@ -254,7 +254,7 @@ $$<T extends object>(obj: T): T
<div id="demo-use-dollar-dollar"></div> <div id="demo-use-dollar-dollar"></div>
```js ```javascript
{ {
const form = $$({ email: "", password: "" }); const form = $$({ email: "", password: "" });
const settings = $$({ theme: "dark", notifications: true }); const settings = $$({ theme: "dark", notifications: true });
@@ -275,7 +275,7 @@ $$<T extends object>(obj: T): T
## Important Notes ## Important Notes
### ✅ DO: ### ✅ DO:
```js ```javascript
// Access properties directly // Access properties directly
state.count = 10; state.count = 10;
state.user.name = "Ana"; state.user.name = "Ana";
@@ -287,7 +287,7 @@ watch(() => state.user.name, () => {});
``` ```
### ❌ DON'T: ### ❌ DON'T:
```js ```javascript
// Destructuring breaks reactivity // Destructuring breaks reactivity
const { count, user } = state; // ❌ count and user are not reactive 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> <div id="demo-complete"></div>
```js ```javascript
{ {
const app = { const app = {
theme: $("dark", "theme_complete"), 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: If you have code using nested signals:
```js ```javascript
// Before - Manual nesting // Before - Manual nesting
const user = $({ const user = $({
name: $(""), name: $(""),

View File

@@ -1,23 +1,48 @@
# Global Tag Helpers # 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 ## 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')` > **Note:** All tag helpers are **lowercase** (e.g., `div`, `span`, `button`). This keeps the syntax close to raw HTML.
* **SigPro Style:** `button({ onclick: ... }, 'Click')`
> **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) Autoglobal
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` | | **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` | | **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` | | **Tables** | `table`, `thead`, `tbody`, `tr`, `th`, `td`, `tfoot`, `caption` |
| **Media** | `img`, `canvas`, `video`, `audio`, `svg`, `iframe`, `picture`, `source` | | **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 ## 3. Usage Patterns
SigPro tag helpers are flexible. They automatically detect if you are passing attributes, children, or both.
### A. Attributes + Children ### A. Attributes + Children
```javascript ```javascript
div({ class: 'container', id: 'main' }, [ div({ class: 'container', id: 'main' }, [
h1("Welcome to SigPro"), h1("Welcome to SigPro"),
p("The zero-VDOM framework.") p("The zeroVDOM framework.")
]); ]);
``` ```
### B. Children Only ### 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 ```javascript
section([ section([
@@ -74,7 +97,7 @@ div({
### TwoWay Binding (Automatic) ### TwoWay 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 ```javascript
const search = $(""); const search = $("");
@@ -86,11 +109,11 @@ input({
}); });
``` ```
> **Pro Tip:** If you want an input to be **readonly** but still reactive, wrap the signal in an anonymous function: `value: () => search()`. This prevents backward synchronization. > **Pro Tip:** To make an input **readonly** but still reactive, wrap the signal in a function: `value: () => search()` this prevents backward synchronization.
### Dynamic Children ### Dynamic Children
You can pass a function as a child it will be reexecuted whenever any signal inside changes, and the DOM will be patched surgically. You can pass a **function as a child** it will be reexecuted whenever any signal inside changes, and the DOM will be patched surgically.
```javascript ```javascript
const count = $(0); 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 ### Basic Component
@@ -151,7 +174,7 @@ const Timer = () => {
| Use case | Recommendation | | 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)` | | 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`) | | Components returning a single node | Any function that returns a node (using helpers or `h`) |
@@ -162,13 +185,17 @@ const Timer = () => {
## 7. Complete Example ## 7. Complete Example
```javascript ```javascript
// In a modern ESM environment (recommended)
import { div, h1, input, p, button, mount, $ } from 'sigpro';
const nameSignal = $('');
const App = () => const App = () =>
div({ class: "app" }, [ div({ class: "app" }, [
h1("Welcome"), h1("Welcome"),
input({ input({
placeholder: "Your name", placeholder: "Your name",
value: nameSignal, value: nameSignal
onInput: (e) => nameSignal(e.target.value)
}), }),
p(() => `Hello, ${nameSignal() || "stranger"}!`), p(() => `Hello, ${nameSignal() || "stranger"}!`),
button({ onClick: () => alert("Clicked") }, "Click me") button({ onClick: () => alert("Clicked") }, "Click me")
@@ -177,26 +204,40 @@ const App = () =>
mount(App, '#app'); mount(App, '#app');
``` ```
--- Or using the classic script (autoglobal):
<div class="alert alert-info"> ```html
<div> <script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.19/dist/sigpro.js"></script>
<h3>Important Notes</h3> <script>
<ul> const nameSignal = $('');
<li><b>Naming:</b> All tag helpers are <b>lowercase</b>. There are no PascalCase helpers (<code>Div</code>, <code>Button</code>).</li> const App = () => div({ class: "app" }, [
<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> h1("Welcome"),
<li><b>Custom components:</b> Use PascalCase for your own component functions to visually distinguish them from builtin tags (e.g., <code>UserCard</code>).</li> input({ placeholder: "Your name", value: nameSignal }),
</ul> p(() => `Hello, ${nameSignal() || "stranger"}!`),
</div> button({ onClick: () => alert("Clicked") }, "Click me")
</div> ]);
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 builtin tags.
---
## 9. Summary
| Feature | Description | | Feature | Description |
| :--- | :--- | | :--- | :--- |
| **Tag helpers** | Lowercase functions for every HTML element (e.g., `div()`, `button()`). | | **Tag helpers** | Lowercase functions for every HTML element (e.g., `div()`, `button()`). |
| **Availability** | Autoglobal in IIFE; in ESM use named imports or `sigpro()`. |
| **Reactive attributes** | Pass a function to any attribute to keep it synced. | | **Reactive attributes** | Pass a function to any attribute to keep it synced. |
| **Twoway binding** | Assign a signal directly to `value` or `checked` on form elements. | | **Twoway binding** | Assign a signal directly to `value` or `checked` on form elements. |
| **Dynamic children** | Pass a function as a child for live updating content. | | **Dynamic children** | Pass a function as a child for live updating content. |

View File

@@ -20,6 +20,8 @@ watch(deps: Signal[], callback: (values: any[]) => void): StopFunction
**Returns:** A `StopFunction` that, when called, destroys the watcher and releases memory. **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 ## Usage Patterns
@@ -95,7 +97,7 @@ This is achieved via `queueMicrotask`, ensuring optimal performance.
## Key Points ## 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`. - **Auto mode:** `watch(fn)` automatically tracks any signals read inside `fn`.
- **Explicit mode:** `watch([sig1, sig2], (values) => {...})` only reruns when listed signals change; callback receives an array of their new values. - **Explicit mode:** `watch([sig1, sig2], (values) => {...})` only reruns 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. - **Stop function:** returned by both modes; call it to dispose the effect and its children.
@@ -120,4 +122,3 @@ watch([count, step], ([newCount, newStep]) => {
count(5); // logs: auto + explicit count(5); // logs: auto + explicit
step(2); // logs: explicit only (auto does not track step) step(2); // logs: explicit only (auto does not track step)
``` ```

View File

@@ -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. **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.
--- ---

View File

@@ -67,40 +67,5 @@
<script src="./sigpro.js"></script> <script src="./sigpro.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.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> <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> </body>
</html> </html>

View File

@@ -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. 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> </div>
<input type="radio" name="install_method" class="tab border-base-300 whitespace-nowrap" aria-label="CDN (ESM)" /> <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"> <div class="tab-content bg-base-100 border-base-300 rounded-box p-6">
```html ```html
<script type="module"> <script type="module">
// Import the core it auto-installs itself globally // Import the module no automatic global injection
import 'https://cdn.jsdelivr.net/npm/sigpro@1.2.18/+esm'; import { sigpro, $, h, mount } from 'https://cdn.jsdelivr.net/npm/sigpro@1.2.19/+esm';
// Now $, $$, watch, h, when, each, fx, router, req, mount, batch and all lowercase tag helpers (div, button, etc.) are available
// 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: autoinstalls 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> </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. 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"> <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"> <div class="tab-content bg-base-100 border-base-300 rounded-lg p-6 mt-2">
```javascript ```javascript
// File: App.js // App.js Using named imports (recommended)
import 'sigpro'; // auto-installs globals import { $, h1, button, div, mount } from 'sigpro';
export const App = () => { export const App = () => {
const count = $(0); const count = $(0);
// Tag helpers like div, h1, button are available globally (lowercase)
return div({ class: "card p-4" }, [ return div({ class: "card p-4" }, [
h1(() => `Count is: ${count()}`), h1(() => `Count is: ${count()}`),
button( button(
{ { class: "btn btn-primary", onclick: () => count(count() + 1) },
class: "btn btn-primary",
onclick: () => count(count() + 1),
},
"Increment" "Increment"
), ),
]); ]);
}; };
// File: main.js // main.js
import 'sigpro'; import { mount } from 'sigpro';
import { App } from './App.js'; import { App } from './App.js';
mount(App, '#app'); mount(App, '#app');
@@ -104,23 +125,19 @@ mount(App, '#app');
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module"> <script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.19/dist/sigpro.js"></script>
import 'https://cdn.jsdelivr.net/npm/sigpro@1.2.18/+esm'; <script>
// Everything is already global no import needed
const name = $('Developer'); const name = $('Developer');
const App = () => section({ class: "container" }, [
// Lowercase tag helpers: section, h2, input h2(() => `Welcome, ${name()}`),
const App = () => input({
section({ class: "container" }, [ type: "text",
h2(() => `Welcome, ${name()}`), class: "input input-bordered",
input({ value: name,
type: "text", placeholder: "Type your name...",
class: "input input-bordered", }),
value: name, // ✅ Two-way binding: signal as value + automatic input event ]);
placeholder: "Type your name...",
}),
]);
mount(App, '#app'); mount(App, '#app');
</script> </script>
</body> </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. ### Mode A: Classic (IIFE) Autoinjection
- **Core Functions:** You get immediate access to `$`, `$$`, `watch`, `h`, `when`, `each`, `fx`, `router`, `req`, `mount`, `batch` anywhere in your scripts. When you load the **IIFE bundle** (`sigpro.js`) with a traditional `<script>` tag (no `type="module"`), the library automatically injects:
- **Auto-Installation:** This happens instantly upon import thanks to its built-in selfinstallation, making it "Plug & Play" for both local projects and CDN usage. - 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.). ✅ Zero configuration just drop the script and start coding.
- **Clean UI Syntax:** Write UI structures that look almost like HTML but are pure, reactive JavaScript: `div({ class: "card" }, [ h1("Title") ])`.
- **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 / nobuild**: Use the IIFE script and get everything automatically.
- **Modern ESM**: Keep your global namespace clean, leverage treeshaking, or inject only when you need it.
--- ---
## 4. Why no build step? ## 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. - **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. - **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. - **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. - **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. - **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.
--- ---

View File

@@ -1,6 +1,6 @@
{ {
"name": "sigpro", "name": "sigpro",
"version": "1.2.19", "version": "1.2.20",
"type": "module", "type": "module",
"license": "MIT", "license": "MIT",
"main": "./dist/sigpro.esm.min.js", "main": "./dist/sigpro.esm.min.js",

View File

@@ -577,14 +577,17 @@ const mount = (comp, target) => {
return inst return inst
} }
const SigPro = Object.freeze({ $, $$, watch, h, when, each, fx, router, req, mount, batch }) const sigproFn = Object.freeze({ $, $$, watch, h, when, each, fx, router, req, mount, batch })
if (typeof window !== "undefined") { const sigpro = () => {
Object.assign(window, SigPro) if (typeof window !== "undefined") {
"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 h2 h3 h4 h5 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" Object.assign(window, sigproFn)
.split(" ").forEach(tag => { "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 h2 h3 h4 h5 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"
window[tag] = (props, children) => h(tag, props, children) .split(" ").forEach(tag => { window[tag] = (props, children) => h(tag, props, children) })
}) console.log("SigPro DX installed.")
}
} }
export { $, $$, watch, h, when, each, fx, router, req, mount, batch } if (typeof import.meta === 'undefined' && typeof window !== 'undefined') sigpro()
export { sigpro, $, $$, watch, h, when, each, fx, router, req, mount, batch }