New 1.1.3

This commit is contained in:
2026-03-26 00:39:30 +01:00
parent 5d799608a3
commit 58f6876261
121 changed files with 3038 additions and 4707 deletions

View File

@@ -40,7 +40,10 @@ export default defineConfig({
text: 'API Reference',
items: [
{ text: 'Quick Start', link: '/api/quick' },
{ text: '$', link: '/api/$' },
{ text: '$', link: '/api/signal' },
{ text: '$.effect', link: '/api/effect' },
{ text: '$.ignore', link: '/api/ignore' },
{ text: '$.view', link: '/api/view' },
{ text: '$.html', link: '/api/html' },
{ text: '$.router', link: '/api/router' },
{ text: '$.mount', link: '/api/mount' },
@@ -48,18 +51,11 @@ export default defineConfig({
]
},
{
text: 'Plugins',
text: 'UI Components',
items: [
{ text: 'Quick Start', link: '/plugins/quick' },
{ text: 'Custom', link: '/plugins/custom' },
{ text: 'Quick Start', link: '/ui/quick' }
]
},
{
text: 'Examples',
items: [
{ text: 'Demo Core', link: '/examples' }
]
}
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/natxocc/sigpro' }

View File

@@ -1,142 +0,0 @@
# The Reactive Core: `$( )`
The `$` function is a **Unified Reactive Constructor**. It detects the type of input you provide and returns the appropriate reactive primitive.
---
## 1. Signals (Atomic State)
A **Signal** is the simplest form of reactivity. It holds a single value (string, number, boolean, null).
### **Option A: Standard Signal (RAM)**
Ideal for volatile state that shouldn't persist after a page refresh.
```javascript
const $count = $(0);
// Usage:
$count(); // Getter: returns 0
$count(10); // Setter: updates to 10
$count(c => c + 1); // Functional update: updates to 11
```
### **Option B: Persistent Signal (Disk)**
By adding a `key`, SigPro links the signal to `localStorage`.
```javascript
// Syntax: $(initialValue, "storage-key")
const $theme = $("light", "app-theme");
// It restores the value from disk automatically on load.
// When you update it, it saves to disk instantly:
$theme("dark"); // localStorage.getItem("app-theme") is now "dark"
```
---
## 2. Stores (Reactive Objects)
A **Store** is a proxy that wraps an **Object**. SigPro makes every property reactive recursively. You access and set properties as if they were individual signals.
### **Option A: Standard Store (RAM)**
```javascript
const user = $({
name: "Alice",
profile: { bio: "Developer" }
});
// Getter: Call the property as a function
console.log(user.name()); // "Alice"
// Setter: Pass the value to the property function
user.name("Bob");
// Nested updates work exactly the same:
user.profile.bio("Architect");
```
### **Option B: Persistent Store (Disk)**
The most powerful way to save complex state. The **entire object tree** is serialized to JSON and kept in sync with the disk.
```javascript
const settings = $({
volume: 50,
notifications: true
}, "user-settings");
// Any change in the object triggers a disk sync:
settings.volume(100); // The whole JSON is updated in localStorage
```
---
## 3. Stores (Reactive Arrays)
When you pass an **Array**, SigPro tracks changes to the list. You can use standard methods or access indexes as reactive getters.
```javascript
const $list = $(["Item 1", "Item 2"]);
// Get by index
console.log($list[0]()); // "Item 1"
// Update by index
$list[0]("Updated Item");
// Note: For adding/removing items, use standard array methods
// which SigPro makes reactive (push, pop, splice, etc.)
```
---
## 4. Computed (Derived Logic)
A **Computed Signal** is a read-only value that depends on other signals. It is defined by passing a **function that returns a value**.
```javascript
const $price = $(100);
const $tax = $(0.21);
// This function HAS a return statement
const $total = $(() => {
return $price() * (1 + $tax());
});
// Usage (Read-only):
console.log($total()); // 121
$price(200);
console.log($total()); // 242 (Auto-updated)
```
---
## 5. Effects (Reactive Actions)
An **Effect** is used for side-effects. It is defined by passing a **function that does NOT return a value**. It runs once immediately and then re-runs whenever its dependencies change.
```javascript
const $name = $("Alice");
// This function has NO return statement (Side-effect)
$(() => {
console.log("The name changed to:", $name());
document.title = `Profile: ${$name()}`;
});
$name("Bob"); // Triggers the console.log and updates document.title
```
---
## 6. Summary: Input Mapping
| If you pass... | SigPro creates a... | Access Method |
| :--- | :--- | :--- |
| **A Value** | **Signal** | `$var()` / `$var(val)` |
| **An Object** | **Store** | `obj.prop()` / `obj.prop(val)` |
| **An Array** | **Array Store** | `arr[i]()` / `arr.push()` |
| **Function (returns)** | **Computed** | `$comp()` (Read-only) |
| **Function (no return)** | **Effect** | Automatically executed |
---
## 💡 Naming Convention: The `$` Prefix
To keep your code clean, always prefix your reactive variables with `$`. This tells you at a glance that you need to call it as a function to get its value.
```javascript
const name = "Static"; // Just a string
const $name = $("Alice"); // A Reactive Signal
```

90
src/docs/api/effect.md Normal file
View File

@@ -0,0 +1,90 @@
# ⚡ Side Effects: `$.effect( )`
The `$.effect` function allows you to run a piece of code whenever the signals it depends on are updated. It automatically tracks any signal called within its body.
## 🛠 Function Signature
```typescript
$.effect(callback: Function): StopFunction
```
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **`callback`** | `Function` | Yes | The code to run. It will execute immediately and then re-run on dependency changes. |
**Returns:** A `StopFunction` that, when called, cancels the effect and prevents further executions.
---
## 📖 Usage Patterns
### 1. Basic Tracking
Any signal you "touch" inside the effect becomes a dependency.
```javascript
const count = $(0);
$.effect(() => {
// This runs every time 'count' changes
console.log(`The count is now: ${count()}`);
});
count(1); // Console: "The count is now: 1"
```
### 2. Manual Cleanup
If your effect creates something that needs to be destroyed (like a timer or a global event listener), you can return a cleanup function.
```javascript
$.effect(() => {
const timer = setInterval(() => console.log("Tick"), 1000);
// SigPro will run this BEFORE the next effect execution
// or when the effect is stopped.
return () => clearInterval(timer);
});
```
### 3. Nesting & Automatic Cleanup
If you create a signal or another effect inside an effect, SigPro tracks them as "children". When the parent effect re-runs or stops, all children are automatically cleaned up to prevent memory leaks.
```javascript
$.effect(() => {
if (isLoggedIn()) {
// This sub-effect is only active while 'isLoggedIn' is true
$.effect(() => {
console.log("Fetching user data...");
});
}
});
```
---
## 🛑 Stopping an Effect
You can stop an effect manually by calling the function it returns. This is useful for one-time operations or complex logic.
```javascript
const stop = $.effect(() => {
console.log(count());
});
// Later...
stop(); // The effect is destroyed and will never run again.
```
---
## 💡 Pro Tip: Batching
SigPro uses a **Microtask Queue** to handle updates. If you update multiple signals at once, the effect will only run **once** at the end of the current task.
```javascript
const a = $(0);
const b = $(0);
$.effect(() => console.log(a(), b()));
// This triggers only ONE re-run, not two.
a(1);
b(2);
```

View File

@@ -1,103 +1,93 @@
# Rendering Engine: `$.html`
# 🏗️ The DOM Factory: `$.html( )`
The `$.html` function is the architect of your UI. It creates standard HTML elements and wires them directly to your signals without the need for a Virtual DOM.
`$.html` is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It is the foundation for all Tag Constructors in SigPro.
## 1. Syntax: `$.html(tag, [props], [content])`
## 🛠 Function Signature
```typescript
$.html(tagName: string, props?: Object, children?: any[] | any): HTMLElement
```
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **tag** | `string` | **Yes** | Any valid HTML5 tag (e.g., `'div'`, `'button'`, `'input'`). |
| **props** | `Object` | No | Attributes, event listeners, and reactive bindings. |
| **content** | `any` | No | Text, Nodes, Arrays, or Reactive Functions. |
### Example:
```javascript
const myButton = $.html('button', { class: 'btn-primary' }, 'Click me');
```
| **`tagName`** | `string` | Yes | Valid HTML tag name (e.g., `"div"`, `"button"`). |
| **`props`** | `Object` | No | HTML attributes, event listeners, and reactive bindings. |
| **`children`** | `any` | No | Nested elements, text strings, or reactive functions. |
---
## 2. Global Tag Helpers
## 📖 Key Features
To avoid repetitive `$.html` calls, SigPro automatically exposes common tags to the global `window` object. This allows for a clean, declarative syntax.
### 1. Attribute Handling
SigPro intelligently decides how to apply each property:
* **Standard Props**: Applied via `setAttribute` or direct property assignment.
* **Boolean Props**: Uses `toggleAttribute` (e.g., `checked`, `disabled`, `hidden`).
* **Class Names**: Supports `class` or `className` interchangeably.
### 2. Event Listeners & Modifiers
Events are defined by the `on` prefix. SigPro supports **Dot Notation** for common event operations:
```javascript
// Instead of $.html('div', ...), just use:
div({ id: 'wrapper' }, [
h1("Welcome"),
p("This is SigPro.")
$.html("button", {
// e.preventDefault() is called automatically
"onsubmit.prevent": (e) => save(e),
// e.stopPropagation() is called automatically
"onclick.stop": () => console.log("No bubbling"),
// { once: true } listener option
"onclick.once": () => console.log("Runs only once")
}, "Click Me");
```
### 3. Reactive Attributes
If an attribute value is a **function** (like a Signal), `$.html` creates an internal `$.effect` to keep the DOM in sync with the state.
```javascript
$.html("div", {
// Updates the class whenever 'theme()' changes
class: () => theme() === "dark" ? "bg-black" : "bg-white"
});
```
### 4. Reactive Children
Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary for that specific part of the DOM.
```javascript
$.html("div", {}, [
H1("Static Title"),
// Only this text node re-renders when 'count' changes
() => `Current count: ${count()}`
]);
```
---
## 3. Handling Properties & Attributes
## 🔄 Two-Way Binding Operator (`$`)
SigPro distinguishes between static attributes and reactive bindings using the **`$` prefix**.
### Static vs. Reactive Attributes
* **Static:** Applied once during creation.
* **Reactive (`$`):** Automatically updates the DOM when the signal changes.
| Property | Syntax | Result |
| :--- | :--- | :--- |
| **Attribute** | `{ id: 'main' }` | `id="main"` |
| **Event** | `{ onclick: fn }` | Adds an event listener. |
| **Reactive Attr** | `{ $class: $theme }` | Updates `class` whenever `$theme()` changes. |
| **Boolean Attr** | `{ $disabled: $isBusy }` | Toggles the `disabled` attribute automatically. |
---
## 4. Two-Way Data Binding
For form inputs, SigPro provides a powerful shortcut using `$value` or `$checked`. It automatically handles the event listening and the value synchronization.
When a property starts with `$`, `$.html` enables bidirectional synchronization. This is primarily used for form inputs.
```javascript
const $text = $("Type here...");
input({
type: 'text',
$value: $text // Syncs input -> signal and signal -> input
});
p(["You typed: ", $text]);
```
---
## 5. Reactive Content (Dynamic Children)
The `content` argument is incredibly flexible. If you pass a **function**, SigPro treats it as a reactive "portal" that re-renders only that specific part of the DOM.
### Text & Nodes
```javascript
const $count = $(0);
// Text node updates surgically
div(["Count: ", $count]);
// Conditional rendering with a function
div(() => {
return $count() > 10
? h1("High Score!")
: p("Keep going...");
$.html("input", {
type: "text",
$value: username // Syncs input value <-> signal
});
```
### The "Guillotine" (Performance Tip)
When a reactive function in the content returns a **new Node**, SigPro uses `replaceWith()` to swap the old node for the new one. This ensures that:
1. The update is nearly instantaneous.
2. The old node is correctly garbage-collected.
## 🧹 Automatic Cleanup
Every element created with `$.html` gets a hidden `._cleanups` property (a `Set`).
* When SigPro removes an element via `$.view` or `$.router`, it automatically executes all functions stored in this Set (stopping effects, removing listeners, etc.).
---
## 6. Summary: Content Types
## 💡 Tag Constructors (The Shortcuts)
| Input | Behavior |
| :--- | :--- |
| **String / Number** | Appended as a TextNode. |
| **HTMLElement** | Appended directly to the parent. |
| **Array** | Each item is processed and appended in order. |
| **Function `() => ...`** | Creates a **live reactive zone** that updates automatically. |
Instead of writing `$.html("div", ...)` every time, SigPro provides PascalCase global functions:
```javascript
// This:
Div({ class: "wrapper" }, [ Span("Hello") ])
// Is exactly equivalent to:
$.html("div", { class: "wrapper" }, [ $.html("span", {}, "Hello") ])
```

75
src/docs/api/ignore.md Normal file
View File

@@ -0,0 +1,75 @@
# 🛑 Untracking: `$.ignore( )`
The `$.ignore` function allows you to read a signal's value inside an effect or a computed signal **without** creating a dependency.
## 🛠 Function Signature
```typescript
$.ignore(callback: Function): any
```
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **`callback`** | `Function` | Yes | A function where signals can be read "silently". |
**Returns:** Whatever the callback function returns.
---
## 📖 Usage Patterns
### 1. Preventing Dependency Tracking
Normally, reading a signal inside `$.effect` makes the effect re-run when that signal changes. `$.ignore` breaks this link.
```javascript
const count = $(0);
const logLabel = $("System Log");
$.effect(() => {
// This effect tracks 'count'...
const currentCount = count();
// ...but NOT 'logLabel'.
// Changing 'logLabel' will NOT re-run this effect.
const label = $.ignore(() => logLabel());
console.log(`${label}: ${currentCount}`);
});
count(1); // Console: "System Log: 1" (Triggers re-run)
logLabel("UI"); // Nothing happens in console (Ignored)
```
### 2. Reading State in Event Handlers
Inside complex UI logic, you might want to take a "snapshot" of a signal without triggering a reactive chain.
```javascript
const handleClick = () => {
// Accessing state without letting the caller know we touched it
const data = $.ignore(() => mySignal());
process(data);
};
```
### 3. Avoiding Infinite Loops
If you need to **write** to a signal based on its own value inside an effect (and you aren't using the functional updater), `$.ignore` prevents the effect from triggering itself.
```javascript
$.effect(() => {
const value = someSignal();
if (value > 100) {
// We update the signal, but we ignore the read to avoid a loop
$.ignore(() => someSignal(0));
}
});
```
---
## 💡 Why use it?
* **Performance:** Prevents expensive effects from running when non-essential data changes.
* **Logic Control:** Allows "sampling" a signal at a specific point in time.
* **Safety:** Essential for complex state orchestrations where circular dependencies might occur.

View File

@@ -1,107 +1,112 @@
# Application Mounter: `$.mount`
# 🔌 Application Mounter: `$.mount( )`
The `$.mount` function is the entry point of your reactive world. It takes a **SigPro component** (or a plain DOM node) and injects it into the real document, bridging the gap between your logic and the browser.
The `$.mount` function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document.
## 1. Syntax: `$.mount(node, [target])`
## 1. Function Signature
```typescript
$.mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
```
| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| **node** | `HTMLElement` or `Function` | **Required** | The component or element to render. |
| **target** | `string` or `HTMLElement` | `document.body` | Where to mount the app (CSS selector or Element). |
| **`node`** | `Function` or `Node` | **Required** | The component function or DOM element to render. |
| **`target`** | `string` or `Node` | `document.body` | CSS selector or DOM element where the app will live. |
**Returns:** A `Runtime` object containing the `container` and a `destroy()` method.
---
## 2. Usage Scenarios
## 2. Common Usage Scenarios
### A. The "Clean Slate" (Main Entry)
In a modern app, you usually want to control the entire page. By default, `$.mount` clears the target's existing HTML before mounting your application.
### A. The "Clean Slate" (SPA Entry)
In a modern Single Page Application, you typically want SigPro to manage the entire view. By default, if no target is provided, it mounts to `document.body`.
```javascript
// src/main.js
import { $ } from 'sigpro';
import { $ } from './sigpro.js';
import App from './App.js';
// SigPro: No .then() needed, global tags are ready immediately
// Mounts your main App component directly to the body
$.mount(App);
```
### B. Targeting a Specific Container
If you have an existing HTML structure and want **SigPro** to manage only a specific section (like a `#root` div), pass a CSS selector or a reference.
If your HTML has a predefined structure, you can tell SigPro exactly where to render by passing a CSS selector or a direct reference.
```html
<div id="sidebar"></div>
<div id="app-root"></div>
<main id="app-root"></main>
```
```javascript
// Mount to a specific ID
// Mount using a CSS selector
$.mount(MyComponent, '#app-root');
// Or using a direct DOM reference
const sidebar = document.getElementById('sidebar');
// Mount using a direct DOM reference
const sidebar = document.querySelector('#sidebar');
$.mount(SidebarComponent, sidebar);
```
---
## 3. Creating "Reactive Islands"
One of SigPro's strengths is its ability to work alongside "Old School" static HTML. You can inject a reactive widget into any part of a legacy page.
### C. Creating "Reactive Islands"
SigPro is excellent for "sprinkling" reactivity onto legacy or static pages. You can inject small reactive widgets into any part of an existing HTML layout.
```javascript
// A small reactive widget
const CounterWidget = () => {
const $c = $(0);
return button({ onclick: () => $c(v => v + 1) }, [
"Clicks: ", $c
const count = $(0);
return Button({ onclick: () => count(c => c + 1) }, [
"Clicks: ", count
]);
};
// Mount it into an existing div in your static HTML
// Mount it into a specific div in your static HTML
$.mount(CounterWidget, '#counter-container');
```
---
## 4. How it Works (Lifecycle)
When `$.mount` is called, it performs three critical steps:
## 3. How it Works (Lifecycle)
1. **Resolution:** If you passed a **Function**, it executes it once to generate the initial DOM node.
2. **Clearance:** It sets `target.innerHTML = ''`. This prevents "zombie" HTML or static placeholders from interfering with your app.
3. **Injection:** It appends the resulting node to the target.
When `$.mount` is executed, it performs these critical steps:
1. **Resolution & Wrapping**: If you pass a **Function**, SigPro wraps it in a `$.view()`. This starts tracking all internal signals and effects.
2. **Target Clearance**: It uses `target.replaceChildren()`. This efficiently wipes any existing HTML or "zombie" nodes inside the target before mounting.
3. **Injection**: The component's container is appended to the target.
4. **Memory Management**: It stores the `Runtime` instance associated with that DOM element. If you call `$.mount` again on the same target, SigPro automatically **destroys the previous app** to prevent memory leaks.
---
## 5. Global vs. Local Scope
## 4. Global vs. Local Scope
### Global (The "Framework" Way)
In a standard Vite project, you initialize SigPro in your entry file. This makes `$` and the tag helpers (`div`, `button`, etc.) available globally for a clean, declarative developer experience.
### The "Framework" Way (Global)
By importing your core in your entry file, SigPro automatically initializes global Tag Constructors (`Div`, `Span`, `H1`, etc.). This allows for a clean, declarative DX across your entire project.
```javascript
// src/main.js
import { $ } from 'sigpro';
// main.js
import './sigpro.js';
// Any component in any file can now use:
$.mount(() => h1("Global App"));
// Now any file can simply do:
$.mount(() => H1("Global SigPro App"));
```
### Local (The "Library" Way)
If you prefer to avoid polluting the `window` object, you can import and use SigPro locally within specific modules.
### The "Library" Way (Local)
If you prefer to avoid global variables, you can use the low-level `$.html` factory to create elements locally.
```javascript
// widget.js
import { $ } from 'sigpro';
import { $ } from './sigpro.js';
const myNode = $.html('div', 'Local Widget');
const myNode = $.html('div', { class: 'widget' }, 'Local Instance');
$.mount(myNode, '#widget-target');
```
---
## 6. Summary Cheat Sheet
## 5. Summary Cheat Sheet
| Goal | Code |
| Goal | Code Pattern |
| :--- | :--- |
| **Mount to body** | `$.mount(App)` |
| **Mount to ID** | `$.mount(App, '#id')` |
| **Mount to ID** | `$.mount(App, '#root')` |
| **Mount to Element** | `$.mount(App, myElement)` |
| **Direct Function** | `$.mount(() => div("Hi"), '#widget')` |
| **Mount raw Node** | `$.mount(Div("Hello"), '#id')` |
| **Unmount/Destroy** | `const app = $.mount(App); app.destroy();` |

View File

@@ -1,176 +1,38 @@
# Quick API Reference
# Quick API Reference
SigPro is a minimal yet powerful engine. Here is a complete overview of its capabilities.
SigPro is a high-performance micro-framework that updates the **Real DOM** surgically. No Virtual DOM, no unnecessary re-renders.
## 1. Core API Summary
## 🟢 Core Functions
| Function | Description | Example |
| Function | Signature | Description |
| :--- | :--- | :--- |
| **`$(val, key?)`** | Creates a Signal, Computed, or Store (with optional persistence). | `const $n = $(0)` |
| **`$.html()`** | The base engine to create reactive HTMLElements. | `$.html('div', {}, 'Hi')` |
| **`Tags`** | Global helpers (div, span, button, etc.) built on top of `$.html`. | `div("Hello SigPro")` |
| **`$.mount()`** | Mounts a component into a target element (clears target first). | `$.mount(App, '#app')` |
| **`$.router()`** | Hash-based router with dynamic params and lazy loading. | `$.router(routes)` |
| **`$.plugin()`** | Extends SigPro or loads external scripts/plugins. | `$.plugin(MyPlugin)` |
| `$(val, key?)` | `(any, string?) => Signal` | Creates a **Signal**. If `key` is provided, it persists in `localStorage`. |
| `$(fn)` | `(function) => Computed` | Creates a **Computed Signal** that auto-updates when its dependencies change. |
| `$.effect(fn)` | `(function) => stopFn` | Runs a side-effect that tracks signals. Returns a function to manually stop it. |
| `$.html(tag, props, children)` | `(string, object, any) => HTMLElement` | The low-level DOM factory powering all tag constructors. |
| `$.router(routes)` | `(Array) => HTMLElement` | Initializes the hash-based router for SPAs. |
| `$.go(path)` | `(string) => void` | Programmatic navigation (e.g., `$.go('/home')`). |
| `$.mount(comp, target)` | `(any, string\|Node) => Runtime` | Mounts the application into the specified DOM element. |
| `$.ignore(fn)` | `(function) => any` | Executes code without tracking any signals inside it. |
---
## 2. The Power of `$` (Reactivity)
## 🏗️ Element Constructors (Tags)
The `$` function adapts to whatever you pass to it:
SigPro provides PascalCase wrappers for all standard HTML5 tags (e.g., `Div`, `Span`, `Button`).
### **Signals & Persistent State**
Reactive values in RAM or synced with `localStorage`.
### Syntax Pattern
```javascript
const $count = $(0); // Simple Signal
const $theme = $('dark', 'app-theme'); // Persistent Signal (Disk)
$count(10); // Update value
console.log($count()); // Get value: 10
Tag({ attributes }, [children])
```
### **Computed Signals**
Read-only signals that update automatically when their dependencies change.
```javascript
const $double = $(() => $count() * 2);
```
### Attribute & Content Handling
### **Reactive Stores (Objects + Disk)**
Transforms an object into a reactive tree. If a `key` is provided, the **entire structure** persists.
```javascript
// Store in RAM + Disk (Auto-syncs nested properties)
const state = $({
user: { name: 'Natxo' },
settings: { dark: true }
}, 'my-app-state');
| Pattern | Code Example | Behavior |
| :--- | :--- | :--- |
| **Static** | `class: "text-red"` | Standard HTML attribute string. |
| **Reactive** | `disabled: isLoading` | Updates automatically when `isLoading()` changes. |
| **Two-way** | `$value: username` | Syncs input with signal **both ways** (Binding Operator). |
| **Text** | `P({}, () => count())` | Updates text node whenever `count` changes. |
| **Boolean** | `hidden: isHidden` | Toggles the attribute based on signal truthiness. |
// Accessing properties (they become signals)
state.user.name(); // Get: 'Natxo'
state.user.name('Guest'); // Set & Sync to Disk: 'my-app-state_user_name'
```
---
### **3. UI Creation: Constructor vs. Direct Tags**
SigPro provides the `$.html` engine for defining any element and global "Sugar Tags" for rapid development.
::: code-group
```javascript [Engine Constructor ($.html)]
// 1. DEFINE: Create a custom piece of UI
// This returns a real DOM element ready to be used.
const MyHero = $.html('section', { class: 'hero' }, [
h1("Internal Title")
]);
// 2. USE: Nest it inside other elements like a standard tag
const Page = () => div([
MyHero, // We just drop the variable here
p("This paragraph is outside the Hero section.")
]);
$.mount(Page, '#app');
```
```javascript [Global Helpers (Tags)]
// Use pre-defined global tags to compose layouts instantly.
// No need to define them, just call them.
const Page = () => div({ id: 'main' }, [
section({ class: 'hero' }, [
h1("Direct Global Tag"),
p("Building UI without boilerplate.")
]),
button({ onclick: () => alert('Hi!') }, "Click Me")
]);
$.mount(Page, '#app');
```
:::
---
### **Technical Breakdown**
* **`$.html(tag, props, children)`**: This is the core factory. Use it when you need to create an element dynamically or when working with **Custom Elements / Web Components**.
* **`Global Tags (div, p, etc.)`**: These are shortcut functions that SigPro injects into the `window` object. They internally call `$.html` for you, making your component code much cleaner and easier to read.
---
### **Key Difference**
* **`$.html`**: Acts as a **constructor**. Use it when you want to "bake" a specific structure (like a Section that *always* contains an H1) into a single variable.
* **`Global Tags`**: Act as **scaffolding**. Use them to wrap different contents dynamically as you build your views.
### **Global Tags (Standard Syntax)**
SigPro declares standard tags in the global scope so you don't have to import them.
```javascript
const Card = (title, $val) => div({ class: 'card' }, [
h2(title),
p("Reactive content below:"),
input({
type: 'number',
$value: $val, // Automatic Two-way binding
$style: () => $val() > 10 ? 'color: red' : 'color: green'
})
]);
```
---
## 4. Mounting: `$.mount`
The entry point of your application. It links your JavaScript logic to a specific DOM element.
```html
<div id="app"></div>
```
```javascript
// In your main.js
const App = () => main([
h1("Welcome to SigPro"),
p("Everything here is reactive.")
]);
// Usage: $.mount(component, selectorOrElement)
$.mount(App, '#app');
```
---
## 5. Navigation: `$.router`
A robust hash-based router (`#/path`) that handles view switching automatically.
```javascript
const routes = [
{ path: '/', component: Home },
{ path: '/user/:id', component: (params) => h1(`User ID: ${params.id}`) },
{ path: '/admin', component: () => import('./Admin.js') }, // Native Lazy Loading
{ path: '*', component: () => p("404 - Not Found") }
];
// Initialize and mount the router
$.mount($.router(routes), '#app');
// Programmatic navigation
$.router.go('/user/42');
```
---
## 6. Plugins: `$.plugin`
Extend the engine or load external dependencies.
```javascript
// 1. Function-based plugin
$.plugin(($) => {
$.myHelper = () => console.log("Plugin active!");
});
// 2. Load external scripts
await $.plugin('https://cdn.example.com/library.js');
```

View File

@@ -1,106 +1,87 @@
# Routing Engine: `$.router`
# 🚦 Routing: `$.router( )` & `$.go( )`
The `$.router` is SigPro's high-performance, hash-based navigation system. It connects the browser's URL directly to your reactive signals, enabling seamless page transitions without full reloads.
SigPro includes a built-in, lightweight **Hash Router** to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically.
## 1. Core Features
## 🛠 Router Signature
* **Hash-based:** Works everywhere without special server configuration (using `#/path`).
* **Lazy Loading:** Pages are only downloaded when the user visits the route, keeping the initial bundle under 2KB.
* **Reactive:** The view updates automatically and surgically when the hash changes.
* **Dynamic Routes:** Built-in support for parameters like `/user/:id`.
```typescript
$.router(routes: Route[]): HTMLElement
```
### Route Object
| Property | Type | Description |
| :--- | :--- | :--- |
| **`path`** | `string` | The URL fragment (e.g., `"/home"`, `"/user/:id"`, or `"*"`). |
| **`component`** | `Function` | A function that returns a Tag or a `$.view`. |
---
## 2. Syntax: `$.router(routes)`
## 📖 Usage Patterns
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **routes** | `Array<Object>` | **Yes** | An array of route definitions `{ path, component }`. |
---
## 3. Setting Up Routes
In your `App.js` (or a dedicated routes file), define your navigation map and inject it into your layout.
### 1. Defining Routes
The router returns a `div` element with the class `.router-outlet`. When the hash changes, the router destroys the previous view and mounts the new one inside this container.
```javascript
const routes = [
{ path: '/', component: () => h1("Home Page") },
{
path: '/admin',
// Lazy Loading: This file is only fetched when needed
component: () => import('./pages/Admin.js')
},
{ path: '/user/:id', component: (params) => h2(`User ID: ${params.id}`) },
{ path: '*', component: () => div("404 - Page Not Found") }
];
export default () => div([
header([
h1("SigPro App"),
nav([
button({ onclick: () => $.router.go('/') }, "Home"),
button({ onclick: () => $.router.go('/admin') }, "Admin")
])
]),
// The router returns a reactive div that swaps content
main($.router(routes))
const App = () => Div({ class: "app-layout" }, [
Navbar(),
// The router outlet is placed here
$.router([
{ path: "/", component: Home },
{ path: "/profile/:id", component: UserProfile },
{ path: "*", component: NotFound }
])
]);
```
---
## 4. Navigation (`$.router.go`)
To move between pages programmatically (e.g., inside an `onclick` event or after a successful fetch), use the `$.router.go` helper.
### 2. Dynamic Segments (`:id`)
When a path contains a colon (e.g., `:id`), the router parses that segment and passes it as an object to the component function.
```javascript
button({
onclick: () => $.router.go('/admin')
}, "Go to Admin")
```
---
## 5. How it Works (Under the Hood)
The router tracks the `window.location.hash` and uses a reactive signal to trigger a re-render of the specific area where `$.router(routes)` is placed.
1. **Match:** It filters your route array to find the best fit, handling dynamic segments (`:id`) and fallbacks (`*`).
2. **Resolve:** * If it's a standard function, it executes it immediately.
* If it's a **Promise** (via `import()`), it renders a temporary `Loading...` state and swaps the content once the module arrives.
3. **Inject:** It replaces the previous DOM node with the new page content surgically using `replaceWith()`.
---
## 6. Integration with UI Components
Since the router is reactive, you can easily create "active" states in your navigation menus by checking the current hash.
```javascript
// Example of a reactive navigation link
const NavLink = (path, label) => {
const $active = $(() => window.location.hash === `#${path}`);
return button({
$class: () => $active() ? 'nav-active' : 'nav-link',
onclick: () => $.router.go(path)
}, label);
// If the URL is #/profile/42
const UserProfile = (params) => {
return H1(`User ID is: ${params.id}`); // Displays "User ID is: 42"
};
nav([
NavLink('/', 'Home'),
NavLink('/settings', 'Settings')
]);
```
---
## 7. Summary: Route Component Types
## 🏎 Programmatic Navigation: `$.go( )`
| Component Type | Behavior |
| :--- | :--- |
| **HTMLElement** | Rendered immediately. |
| **Function `(params) => ...`** | Executed with URL parameters and rendered. |
| **Promise / `import()`** | Triggers **Lazy Loading** with a loading state. |
| **String / Number** | Rendered as simple text inside a span. |
To navigate between pages without using an `<a>` tag, use `$.go`. This function updates the browser's hash, which in turn triggers the router to swap components.
### Signature
```typescript
$.go(path: string): void
```
### Examples
```javascript
// Navigate to a static path
Button({ onclick: () => $.go("/") }, "Home")
// Navigate to a dynamic path
Button({
onclick: () => $.go(`/profile/${user.id}`)
}, "View Profile")
```
---
## ⚡ Technical Behavior
* **Automatic Cleanup**: Every time you navigate, the router calls `.destroy()` on the previous `$.view`. This ensures that all **signals, effects, and event listeners** from the old page are purged from memory.
* **Hash-Based**: By using `window.location.hash`, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects.
* **Initial Load**: On the first execution, `$.router` automatically reads the current hash or defaults to `/` if empty.
---
## 🎨 Styling the Outlet
Since the router returns a standard DOM element, you can style the transition or the container easily:
```css
.router-outlet {
flex: 1;
padding: 2rem;
animation: fadeIn 0.2s ease-in;
}
```

68
src/docs/api/signal.md Normal file
View File

@@ -0,0 +1,68 @@
# 💎 The Signal Function: `$( )`
The `$( )` function is the core constructor of SigPro. It defines how data is stored, computed, and persisted.
## 🛠 Function Signature
```typescript
$(initialValue: any, key?: string): Signal
$(computation: Function): ComputedSignal
```
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **`initialValue`** | `any` | Yes* | The starting value of your signal. |
| **`computation`** | `Function` | Yes* | A function that returns a value based on other signals. |
| **`key`** | `string` | No | A unique name to persist the signal in `localStorage`. |
*\*Either an initial value or a computation function must be provided.*
---
## 📖 Usage Patterns
### 1. Simple State
**`$(value)`**
Creates a writable signal. It returns a function that acts as both **getter** and **setter**.
```javascript
const count = $(0);
count(); // Read (0)
count(10); // Write (10)
```
### 2. Persistent State
**`$(value, key)`**
Creates a writable signal that syncs with the browser's storage.
```javascript
const theme = $("light", "app-theme");
theme("dark"); // Automatically calls localStorage.setItem("app-theme", '"dark"')
```
*Note: On page load, SigPro will prioritize the value found in `localStorage` over the `initialValue`.*
### 3. Computed State (Derived)
**`$(function)`**
Creates a read-only signal that updates automatically when any signal used inside it changes.
```javascript
const price = $(100);
const tax = $(0.21);
// This tracks both 'price' and 'tax' automatically
const total = $(() => price() * (1 + tax()));
```
---
## 🔄 Updating with Logic
When calling the setter, you can pass an **updater function** to access the current value safely.
```javascript
const list = $(["A", "B"]);
// Adds "C" using the previous state
list(prev => [...prev, "C"]);
```

View File

@@ -1,128 +1,136 @@
# Global Tag Helpers
# 🎨 Global Tag Helpers
In **SigPro**, you don't need to write `$.html('div', ...)` every time. To keep your code clean and readable, the engine automatically generates global helper functions for all standard HTML tags upon initialization.
In **SigPro**, you don't need to manually type `$.html('div', ...)` for every element. To keep your code declarative and readable, the engine automatically generates **Global Helper Functions** for all standard HTML5 tags upon initialization.
## 1. How it Works
SigPro iterates through an internal manifest of standard HTML tags and attaches a wrapper function for each one directly to the `window` object. This creates a native "DSL" (Domain Specific Language) that looks like a template engine but is **100% standard JavaScript**.
SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the `window` object. This creates a specialized **DSL** (Domain Specific Language) that looks like a template engine but is **100% standard JavaScript**.
* **Under the hood:** `$.html('button', { onclick: ... }, 'Click')`
* **SigPro Style:** `button({ onclick: ... }, 'Click')`
* **SigPro Style:** `Button({ onclick: ... }, 'Click')`
---
## 2. The Complete Global Registry
The following tags are injected into the global scope and are ready to use as soon as SigPro loads:
The following functions are injected into the global scope (using **PascalCase** to prevent naming collisions with common JS variables) and are ready to use:
| Category | Available Global 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`, `i`, `b`, `u`, `mark` |
| **Interactive** | `button`, `a`, `label`, `br`, `hr`, `details`, `summary` |
| **Forms** | `form`, `input`, `select`, `option`, `textarea`, `fieldset`, `legend` |
| **Tables** | `table`, `thead`, `tbody`, `tr`, `th`, `td`, `tfoot`, `caption` |
| **Media & Graphics** | `img`, `canvas`, `video`, `audio`, `svg`, `path`, `iframe` |
| **Structure** | `Div`, `Span`, `P`, `Section`, `Nav`, `Main`, `Header`, `Footer`, `Article`, `Aside` |
| **Typography** | `H1` to `H6`, `Ul`, `Ol`, `Li`, `Dl`, `Dt`, `Dd`, `Strong`, `Em`, `Code`, `Pre`, `Small`, `B`, `U`, `Mark` |
| **Interactive** | `Button`, `A`, `Label`, `Br`, `Hr`, `Details`, `Summary`, `Dialog` |
| **Forms** | `Form`, `Input`, `Select`, `Option`, `Textarea`, `Fieldset`, `Legend` |
| **Tables** | `Table`, `Thead`, `Tbody`, `Tr`, `Th`, `Td`, `Tfoot`, `Caption` |
| **Media** | `Img`, `Canvas`, `Video`, `Audio`, `Svg`, `Iframe`, `Picture`, `Source` |
> "In SigPro, tags are not 'magic' strings handled by a compiler. They are **functional imitations** of HTML elements. Every time you call `div()`, you are executing a standard JavaScript function that returns a real DOM element. This gives you the speed of a specialized DSL with the transparency of pure JS."
::: danger WARNING: GLOBAL NAMING COLLISIONS
Since **SigPro** injects these helpers directly into the `window` object, they are regular JavaScript functions. This means **they can be overwritten**.
If you declare a variable, constant, or function with the same name as an HTML tag (e.g., `const div = ...` or `function p()`), you will **nullify or shadow** the built-in SigPro helper for that tag in your current scope.
**Best Practice:** To avoid conflicts, always use **PascalCase** for your custom components (e.g., `UserCard`, `AppHeader`) to distinguish them from the **lowercase** global HTML helpers.
:::
> **The SigPro Philosophy:** Tags are not "magic strings" handled by a compiler. They are **functional constructors**. Every time you call `Div()`, you execute a pure JS function that returns a real, reactive DOM element.
---
## 3. Usage Patterns (Argument Flexibility)
## 3. Usage Patterns (Smart Arguments)
The tag functions are "smart". They detect whether you are passing attributes, content, or both.
SigPro tag helpers are flexible. They automatically detect if you are passing attributes, children, or both.
### A. Attributes + Content
The standard way to build complex nodes.
### A. Attributes + Children
The standard way to build structured UI.
```javascript
div({ class: 'container', id: 'main-wrapper' }, [
h1("Welcome"),
p("This is SigPro.")
Div({ class: 'container', id: 'main' }, [
H1("Welcome to SigPro"),
P("The zero-VDOM framework.")
]);
```
### B. Content Only (The "Skipper")
If you don't need attributes, you can pass the content (string, array, or function) as the **first and only** argument.
### B. Children Only (The "Skipper")
If you don't need attributes, you can skip the object and pass the content (string, array, or function) directly as the first argument.
```javascript
section([
h2("No Attributes Needed"),
button("Click Me")
Section([
H2("Clean Syntax"),
Button("I have no props!")
]);
```
### C. Primitive Content
For simple tags, you can just pass a string or a number.
For simple tags, just pass a string or a number.
```javascript
h1("Hello World");
span(42);
H1("Hello World");
Span(42);
```
---
## 4. Reactive Attributes & Content
## 4. Reactive Power Examples
These helpers fully support SigPro's reactivity. Attributes starting with `$` are automatically tracked.
These helpers are natively wired into SigPro's reactivity. No manual `useEffect` or `watch` calls are needed.
### Reactive Attributes
Simply pass a Signal (function) to any attribute. SigPro handles the rest.
```javascript
const $count = $(0);
const theme = $("light");
div({ class: 'counter-app' }, [
h2(["Current Count: ", $count]), // Auto-unwrapping text content
button({
onclick: () => $count(c => c + 1),
$style: () => $count() > 5 ? "color: red" : "color: green" // Reactive style
}, "Increment")
Div({
// Updates 'class' automatically when theme() changes
class: () => `app-box ${theme()}`
}, "Themeable Box");
```
### The Binding Operator (`$`)
Use the `$` prefix for **Two-Way Binding** on inputs.
```javascript
const search = $("");
Input({
type: "text",
placeholder: "Search...",
$value: search // UI updates Signal AND Signal updates UI
});
```
### Dynamic Lists
Combine tags with `ui.For` for high-performance list rendering.
```javascript
const items = $(["Apple", "Banana", "Cherry"]);
Ul({ class: "list-disc" }, [
ui.For(items, (item) => Li(item), (item) => item)
]);
```
---
::: danger
## ⚠️ Important: Naming Conventions
## 5. Technical Implementation
As seen in the SigPro core, the engine registers these tags dynamically. This means **zero imports** are needed for UI creation in your component files.
```javascript
// Internal SigPro loop
tags.forEach(t => window[t] = (p, c) => $.html(t, p, c));
```
Because they are real functions, you get full IDE autocompletion and valid JS syntax highlighting without needing special plugins like JSX.
Since SigPro injects these helpers into the global `window` object, follow these rules to avoid bugs:
1. **Avoid Shadowing**: Don't name your local variables like the tags (e.g., `const Div = ...`). This will "hide" the SigPro helper.
2. **Custom Components**: Always use **PascalCase**, **UPPERCASE**, or **Underscore** prefixes for your own component functions (e.g., `UserCard`, `INPUT`, or `_Input`) to distinguish them from the built-in Tag Helpers and avoid naming collisions.
:::
---
## 6. Comparison: Logic to UI
## 6. Logic to UI Comparison
Here is how a dynamic **Task Item** component translates from SigPro logic to the final DOM structure.
Here is how a dynamic **User Status** component translates from SigPro logic to the final DOM structure.
::: code-group
```javascript [SigPro Component]
const Task = (title, $done) => (
li({ class: 'task-item' }, [
input({
type: 'checkbox',
$checked: $done // Two-way reactive binding
```javascript
// SigPro Component
const UserStatus = (name, $online) => (
Div({ class: 'flex items-center gap-2' }, [
Span({
// Boolean toggle for 'hidden' attribute
hidden: () => !$online(),
class: 'w-3 h-3 bg-green-500 rounded-full'
}),
span({
$style: () => $done() ? "text-decoration: line-through" : ""
}, title)
P({
// Reactive text content
class: () => $online() ? "text-bold" : "text-gray-400"
}, name)
])
);
```
```html [Rendered HTML]
<li class="task-item">
<input type="checkbox" checked>
<style="text-decoration: line-through">Buy milk</span>
</li>
```
:::
| State (`$online`) | Rendered HTML |
| :--- | :--- |
| **`true`** | `<div class="flex..."><span class="w-3..."></span><p class="text-bold">John</p></div>` |
| **`false`** | `<div class="flex..."><span hidden class="w-3..."></span><p class="text-gray-400">John</p></div>` |

78
src/docs/api/view.md Normal file
View File

@@ -0,0 +1,78 @@
# 🖼️ Component Lifecycle: `$.view( )`
The `$.view` function is a specialized wrapper used to manage the lifecycle of a UI component. It tracks all signals, effects, and DOM elements created within it, providing a single point of destruction to prevent memory leaks.
## 🛠 Function Signature
```typescript
$.view(renderFn: Function): RuntimeObject
```
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **`renderFn`** | `Function` | Yes | A function that returns a DOM Node or an array of Nodes. |
**Returns:** A `Runtime` object containing:
* `container`: A `div` (with `display: contents`) holding the rendered content.
* `destroy()`: A function that unmounts the view and cleans up all internal effects/listeners.
---
## 📖 Usage Patterns
### 1. Basic Component Wrapper
When you wrap logic in `$.view`, SigPro creates a "boundary".
```javascript
const myView = $.view(() => {
const count = $(0);
// This effect is "owned" by this view
$.effect(() => console.log(count()));
return Div([
H1("Internal View"),
Button({ onclick: () => count(c => c + 1) }, "Add")
]);
});
// To show it:
document.body.appendChild(myView.container);
// To kill it (removes from DOM and stops all internal effects):
myView.destroy();
```
### 2. Manual Cleanups with `onCleanup`
The `renderFn` receives a helper object that allows you to register custom cleanup logic (like closing a WebSocket or a third-party library instance).
```javascript
const ChartComponent = () => $.view(({ onCleanup }) => {
const socket = new WebSocket("ws://...");
onCleanup(() => {
socket.close();
console.log("Cleanup: WebSocket closed");
});
return Div({ class: "chart-container" });
});
```
---
## 🏗️ Internal Architecture
When `$.view` is called, it performs the following:
1. **Isolation**: It creates a new `Set` of cleanups.
2. **Execution**: It runs your function and captures any `$.effect` or child `$.view` created during execution.
3. **Container**: It wraps the result in a `display: contents` element (which doesn't affect your CSS layout).
4. **Disposal**: When `destroy()` is called, it recursively calls all cleanup functions and removes the container from the DOM.
---
## 💡 Why use `$.view`?
* **Automatic Cleanup**: You don't have to manually stop every effect when a user navigates away.
* **Router Integration**: The SigPro Router (`$.router`) uses `$.view` internally to swap pages and clean up the previous one automatically.
* **Performance**: It ensures that background processes (like intervals or observers) stop as soon as the element is no longer visible.

View File

@@ -10,7 +10,7 @@ Choose the method that best fits your workflow:
```bash [npm]
npm install sigpro
````
```
```bash [pnpm]
pnpm add sigpro
@@ -26,7 +26,12 @@ bun add sigpro
```html [CDN (ESM)]
<script type="module">
// Import the core and UI components
import { $ } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/+esm';
import { UI } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/ui/+esm';
// Initialize UI components globally
UI($);
</script>
```
@@ -34,9 +39,9 @@ bun add sigpro
-----
## 2\. Quick Start Examples
## 2. Quick Start Examples
Depending on your installation method, here is how you can get SigPro running in seconds.
SigPro uses **PascalCase** for Tag Helpers (e.g., `Div`, `Button`) to provide a clean, component-like syntax without needing JSX.
::: code-group
@@ -45,13 +50,15 @@ Depending on your installation method, here is how you can get SigPro running in
import { $ } from 'sigpro';
export const App = () => {
// $ is global, but we import it for better IDE intellisense
const $count = $(0);
// Tags like div, h1, button are available globally
return div({ class: 'card' }, [
h1(["Count is: ", $count]),
button({ onclick: () => $count(c => c + 1) }, "Increment")
// Tag Helpers like Div, H1, Button are available globally
return Div({ class: 'card p-4' }, [
H1(["Count is: ", $count]),
Button({
class: 'btn btn-primary',
onclick: () => $count(c => c + 1)
}, "Increment")
]);
};
@@ -69,16 +76,16 @@ $.mount(App, '#app');
<div id="app"></div>
<script type="module">
// Import directly from CDN
import { $ } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/+esm';
const $name = $("Developer");
// No need to import div, section, h2, input... they are global!
const App = () => section([
h2(["Welcome, ", $name]),
input({
// No need to import Div, Section, H2, Input... they are global!
const App = () => Section({ class: 'container' }, [
H2(["Welcome, ", $name]),
Input({
type: 'text',
class: 'input input-bordered',
$value: $name, // Automatic two-way binding
placeholder: 'Type your name...'
})
@@ -94,17 +101,46 @@ $.mount(App, '#app');
-----
## 3\. Global by Design
## 3. Global by Design
One of SigPro's core strengths is its **Global API**.
One of SigPro's core strengths is its **Global API**, which eliminates "Import Hell".
* **The `$` Function:** Once loaded, it attaches itself to `window.$`. While you can use `import` for better IDE support and type checking, it is accessible everywhere.
* **HTML Tags:** Common tags (`div`, `span`, `button`, etc.) are automatically registered in the global scope. This eliminates "Import Hell" and keeps your components clean and readable.
* **The `$` Function:** Once loaded, it attaches itself to `window.$`. It handles state, effects, and DOM mounting.
* **Tag Helpers (PascalCase):** Common HTML tags (`Div`, `Span`, `Button`, `Input`, etc.) are automatically registered in the global scope.
* **Custom Components:** We recommend using **PascalCase** (e.g., `UserCard`) or prefixes like `_Input` to keep your code organized and distinguish your logic from standard tags.
## 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 loader 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. No complex HMR issues.
* **Production:** Use any bundler (Vite, esbuild, Rollup) to tree-shake and minify your final code for maximum performance.
* **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.
## 5. Why SigPro? (The Competitive Edge)
SigPro stands out by removing the "Build Step" tax and the "Virtual DOM" overhead. It is the closest you can get to writing raw HTML/JS while maintaining modern reactivity.
| Feature | **SigPro** | **SolidJS** | **Svelte** | **React** | **Vue** |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **Bundle Size** | **~2KB** | ~7KB | ~4KB | ~40KB+ | ~30KB |
| **DOM Strategy** | **Direct DOM** | Direct DOM | Compiled DOM | Virtual DOM | Virtual DOM |
| **Reactivity** | **Fine-grained** | Fine-grained | Compiled | Re-renders | Proxies |
| **Build Step** | **Optional** | Required | Required | Required | Optional |
| **Learning Curve**| **Minimal** | Medium | Low | High | Medium |
| **Initialization**| **Ultra-Fast** | Very Fast | Fast | Slow | Medium |
---
## 6. Key Advantages
* **Extreme Performance**: No Virtual DOM reconciliation. SigPro updates the specific node or attribute instantly when a Signal changes.
* **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 the `$` function are available globally to eliminate "Import Hell" and keep your code clean.
## 7. Summary
SigPro isn't just another framework; it's a bridge to the native web. By using standard ES Modules and functional DOM generation, you gain the benefits of a modern library with the weight of a utility script.
**Because, in the end... why fight the web when we can embrace it?**

View File

@@ -1,97 +0,0 @@
# Creating Custom Plugins
There are two main ways to expose a plugin's functionality: **Static/Manual Imports** (cleaner for large projects) or **Global/Automatic Window Injection** (easier for quick scripts and global helpers).
## 1. The Anatomy of a Plugin
A plugin is a standard JavaScript function. By convention, if a plugin adds a global helper or component, it should be prefixed with an underscore (`_`).
```javascript
// plugins/my-utils.js
export const MyUtils = ($) => {
// 1. Attach to the SigPro instance
$.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
// 2. Attach to the Window (Global access)
window._hello = (name) => div(`Hello, ${$.capitalize(name)}!`);
// 3. You can also return values if needed
return { version: '1.0.0' };
};
```
---
## 2. Integration Strategies
### Option A: Manual Import (Recommended)
This approach keeps your global namespace clean. You import the logic only where you need it, but the plugin still initializes the core `$` extensions.
```javascript
// main.js
import { $ } from 'sigpro';
import { MyUtils } from './plugins/my-utils.js';
$.plugin(MyUtils);
// App.js
export default () => {
const name = "sigpro";
// $.capitalize was added by the plugin
return h1($.capitalize(name));
};
```
### Option B: Automatic Window Injection
If your plugin defines global tags (like `_button` or `_hello`), you should attach them to the `window` object inside the plugin function. This makes them available everywhere without imports.
```javascript
// plugins/theme.js
export const Theme = ($) => {
const $dark = $(false);
window._themeToggle = () => button({
onclick: () => $dark(v => !v),
class: () => $dark() ? 'bg-black text-white' : 'bg-white text-black'
}, "Toggle Mode");
};
// main.js
$.plugin(Theme).then(() => {
// _themeToggle is now a global function
$.mount(App);
});
```
---
## 3. Asynchronous Plugins
If your plugin needs to load external data or scripts before the app starts, make it `async`. SigPro will wait for it.
```javascript
export const ConfigLoader = async ($) => {
const res = await fetch('/config.json');
const config = await res.json();
$.config = config; // Attach loaded config to SigPro
};
// Usage
$.plugin(ConfigLoader).then(() => {
console.log("Config loaded:", $.config);
$.mount(App);
});
```
---
## 4. Best Practices for Plugin Authors
| Rule | Description |
| :--- | :--- |
| **Prefixing** | Use `_` for UI components (`_modal`) and `$.` for logic (`$.fetch`). |
| **Idempotency** | Ensure calling `$.plugin(MyPlugin)` twice doesn't break the app. |
| **Encapsulation** | Use the `$` instance passed as an argument rather than importing it again inside the plugin. |
| **Reactivity** | Always use `$(...)` for internal state so the app stays reactive. |

View File

@@ -1,94 +0,0 @@
# Extending SigPro: `$.plugin`
The plugin system is the engine's modular backbone. It allows you to inject new functionality directly into the `$` object, register custom global tags, or load external libraries seamlessly.
## 1. How Plugins Work
A plugin in **SigPro** is a function that receives the core instance. When you call `$.plugin(MyPlugin)`, the engine hands over the `$` object so the plugin can attach new methods or extend the reactive system.
### Functional Plugin Example
```javascript
// A plugin that adds a simple watcher to any signal
const Logger = ($) => {
$.watch = (target, label = "Log") => {
$(() => console.log(`[${label}]:`, target()));
};
};
// Activation
$.plugin(Logger);
const $count = $(0);
$.watch($count, "Counter"); // Now available globally via $
```
---
## 2. Initialization Patterns (SigPro)
Thanks to the **Synchronous Tag Engine**, you no longer need complex `import()` nesting. Global tags like `div()`, `span()`, and `button()` are ready the moment you import the Core.
### The "Natural" Start (Recommended)
This is the standard way to build apps. It's clean, readable, and supports standard ESM imports.
```javascript
// main.js
import { $ } from 'sigpro';
import { Fetch } from 'sigpro/plugins';
import App from './App.js'; // Static import works perfectly!
// 1. Register plugins
$.plugin(Fetch);
// 2. Mount your app directly
$.mount(App, '#app');
```
---
## 3. Resource Plugins (External Scripts)
You can pass a **URL** or an **Array of URLs**. SigPro will inject them as `<script>` tags and return a **Promise** that resolves when the scripts are fully loaded. This is perfect for integrating heavy third-party libraries only when needed.
```javascript
// Loading external libraries as plugins
$.plugin([
'https://cdn.jsdelivr.net/npm/chart.js',
'https://cdn.example.com/custom-ui-lib.js'
]).then(() => {
console.log("External resources are ready to use!");
$.mount(DashboardApp);
});
```
---
## 4. Polymorphic Loading Reference
The `$.plugin` method is smart; it adapts its behavior based on the input type:
| Input Type | Action | Behavior |
| :--- | :--- | :--- |
| **Function** | Executes `fn($)` | **Synchronous**: Immediate availability. |
| **String (URL)** | Injects `<script src="...">` | **Asynchronous**: Returns a Promise. |
| **Array** | Processes each item in the list | Returns a Promise if any item is a URL. |
---
## 💡 Pro Tip: When to use `.then()`?
In **SigPro**, you only need `.then()` in two specific cases:
1. **External Assets:** When loading a plugin via a URL (CDN).
2. **Strict Dependency:** If your `App.js` requires a variable that is strictly defined inside an asynchronous external script (like `window.Chart`).
For everything else (UI components, Router, Local State), just call `$.plugin()` and continue with your code. It's that simple.
---
### Summary Cheat Sheet
| Goal | Code |
| :--- | :--- |
| **Local Plugin** | `$.plugin(myPlugin)` |
| **Multiple Plugins** | `$.plugin([UI, Router])` |
| **External Library** | `$.plugin('https://...').then(...)` |
| **Hybrid Load** | `$.plugin([UI, 'https://...']).then(...)` |

View File

@@ -1,114 +0,0 @@
# Button Component
The `_button` component creates reactive buttons with built-in support for loading states, icons, badges, and disabled states.
## Basic Usage
<div id="basic-button-demo"></div>
```javascript
_button({ onclick: () => alert('Clicked!') }, 'Click Me')
```
## Loading State
The `$loading` signal automatically shows a spinner and disables the button.
<div id="loading-button-demo"></div>
```javascript
const $loading = $(false)
_button({
$loading: $loading,
onclick: async () => {
$loading(true)
await saveData()
$loading(false)
}
}, 'Save')
```
## Icons
Add icons to buttons using the `icon` prop.
<div id="icon-button-demo"></div>
```javascript
_button({ icon: '⭐' }, 'Favorite')
_button({ icon: '💾' }, 'Save')
_button({ icon: '🗑️', class: 'btn-error' }, 'Delete')
```
## Badges
Add badges to buttons for notifications or status indicators.
<div id="badge-button-demo"></div>
```javascript
_button({ badge: '3' }, 'Notifications')
_button({ badge: 'New', badgeClass: 'badge-secondary' }, 'Update Available')
```
## Button Variants
Use daisyUI classes to style your buttons.
<div id="variant-button-demo"></div>
```javascript
_button({ class: 'btn-primary' }, 'Primary')
_button({ class: 'btn-secondary' }, 'Secondary')
_button({ class: 'btn-outline' }, 'Outline')
_button({ class: 'btn-sm' }, 'Small')
```
## Counter Example
<div id="counter-demo"></div>
```javascript
const $count = $(0)
_button({
onclick: () => $count($count() + 1),
icon: '🔢'
}, () => `Count: ${$count()}`)
```
## Async Action Example
<div id="async-demo"></div>
```javascript
const $saving = $(false)
const $success = $(false)
_button({
$loading: $saving,
icon: '💾',
onclick: async () => {
$saving(true)
await saveToDatabase()
$saving(false)
$success(true)
setTimeout(() => $success(false), 2000)
}
}, 'Save')
```
## API Reference
| Prop | Type | Description |
|------|------|-------------|
| `$loading` | `Signal<boolean>` | Shows spinner and disables button |
| `$disabled` | `Signal<boolean>` | Disables the button |
| `icon` | `string \| Node` | Icon to display before text |
| `badge` | `string` | Badge text to display |
| `badgeClass` | `string` | Additional CSS classes for badge |
| `class` | `string \| function` | Additional CSS classes |
| `onclick` | `function` | Click event handler |
| `type` | `string` | Button type ('button', 'submit', etc.) |

View File

@@ -1,112 +0,0 @@
# Form Components
SigPro UI provides a complete set of reactive form components including select dropdowns, checkboxes, radio buttons, and range sliders.
## Select Dropdown (`_select`)
Creates a reactive dropdown select with options.
<div id="select-demo"></div>
```javascript
const $role = $('user')
_select({
label: 'User Role',
options: [
{ value: 'admin', label: 'Administrator' },
{ value: 'user', label: 'Standard User' },
{ value: 'guest', label: 'Guest' }
],
$value: $role
})
```
## Checkbox (`_checkbox`)
Reactive checkbox with label.
<div id="checkbox-demo"></div>
```javascript
const $agreed = $(false)
_checkbox({
label: 'I agree to the terms and conditions',
$value: $agreed
})
```
## Radio Button (`_radio`)
Radio buttons for selecting one option from a group.
<div id="radio-demo"></div>
```javascript
const $payment = $('credit')
_radio({ name: 'payment', label: 'Credit Card', value: 'credit', $value: $payment })
_radio({ name: 'payment', label: 'PayPal', value: 'paypal', $value: $payment })
_radio({ name: 'payment', label: 'Crypto', value: 'crypto', $value: $payment })
```
## Range Slider (`_range`)
Reactive range slider for numeric values.
<div id="range-demo"></div>
```javascript
const $volume = $(50)
_range({
label: 'Volume',
min: 0,
max: 100,
step: 1,
$value: $volume
})
```
## Complete Form Example
<div id="complete-form-demo"></div>
## API Reference
### `_select`
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Field label |
| `options` | `Array<{value: any, label: string}>` | Select options |
| `$value` | `Signal<any>` | Selected value signal |
### `_checkbox`
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Checkbox label |
| `$value` | `Signal<boolean>` | Checked state signal |
### `_radio`
| Prop | Type | Description |
|------|------|-------------|
| `name` | `string` | Radio group name |
| `label` | `string` | Radio option label |
| `value` | `any` | Value for this option |
| `$value` | `Signal<any>` | Group selected value signal |
### `_range`
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Slider label |
| `min` | `number` | Minimum value |
| `max` | `number` | Maximum value |
| `step` | `number` | Step increment |
| `$value` | `Signal<number>` | Current value signal |

View File

@@ -1,137 +0,0 @@
# Input Component
The `_input` component creates reactive form inputs with built-in support for labels, tooltips, error messages, and two-way binding.
## Basic Usage
<div id="basic-input-demo"></div>
```javascript
const $name = $('')
_input({
label: 'Name',
placeholder: 'Enter your name',
$value: $name
})
```
## With Tooltip
The `tip` prop adds an info badge with a tooltip.
<div id="tooltip-input-demo"></div>
```javascript
_input({
label: 'Username',
tip: 'Choose a unique username (min. 3 characters)',
placeholder: 'johndoe123',
$value: $username
})
```
## With Error Handling
The `$error` signal displays an error message and styles the input accordingly.
<div id="error-input-demo"></div>
```javascript
const $email = $('')
const $error = $(null)
const validate = (value) => {
if (value && !value.includes('@')) {
$error('Please enter a valid email address')
} else {
$error(null)
}
}
_input({
label: 'Email',
type: 'email',
placeholder: 'user@example.com',
$value: $email,
$error: $error,
oninput: (e) => validate(e.target.value)
})
```
## Input Types
The component supports all standard HTML input types.
<div id="types-input-demo"></div>
```javascript
_input({ label: 'Text', placeholder: 'Text input', $value: $text })
_input({ label: 'Password', type: 'password', placeholder: '••••••••', $value: $password })
_input({ label: 'Number', type: 'number', placeholder: '0', $value: $number })
```
## Two-Way Binding
The `$value` prop creates two-way binding between the input and the signal.
<div id="binding-input-demo"></div>
```javascript
const $message = $('Hello World')
_input({
label: 'Message',
$value: $message
})
// The input updates when signal changes, and vice versa
_button({ onclick: () => $message('Reset!') }, 'Reset Signal')
```
## API Reference
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Field label text |
| `tip` | `string` | Tooltip text shown on hover |
| `$value` | `Signal<any>` | Two-way bound value signal |
| `$error` | `Signal<string\|null>` | Error message signal |
| `type` | `string` | Input type (text, email, password, number, etc.) |
| `placeholder` | `string` | Placeholder text |
| `class` | `string \| function` | Additional CSS classes |
| `oninput` | `function` | Input event handler |
| `onchange` | `function` | Change event handler |
| `disabled` | `boolean` | Disabled state |
## Examples
### Registration Form Field
<div id="register-demo"></div>
```javascript
const $username = $('')
const $usernameError = $(null)
const $email = $('')
const $emailError = $(null)
_input({
label: 'Username',
placeholder: 'johndoe',
$value: $username,
$error: $usernameError,
oninput: (e) => validateUsername(e.target.value)
})
_input({
label: 'Email',
type: 'email',
placeholder: 'john@example.com',
$value: $email,
$error: $emailError,
oninput: (e) => validateEmail(e.target.value)
})
```

View File

@@ -1,53 +0,0 @@
# Installation
## Prerequisites
- Node.js 18 or higher
- A project with SigPro already installed
## Step 1: Install Dependencies
```bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
```
## Step 2: Configure Tailwind CSS v4
Create a CSS file (e.g., `src/app.css`):
```css
@import "tailwindcss";
@plugin "daisyui";
```
## Step 3: Import CSS in Your Entry Point
```javascript
// main.js
import './app.css';
import { $ } from 'sigpro';
import { UI } from 'sigpro/plugins';
$.plugin(UI).then(() => {
console.log('✅ UI Components ready');
import('./App.js').then(app => $.mount(app.default));
});
```
## Step 4: Verify Installation
<div id="test-install"></div>
## Troubleshooting
### Styles not applying?
- Make sure `app.css` is imported before any other code
- Check that Tailwind is properly configured in your build tool
### Components not found?
- Ensure `$.plugin(UI)` has completed before using components
- Check browser console for any loading errors
### Reactive updates not working?
- Make sure you're passing signals, not primitive values
- Use `$value` prop for two-way binding

View File

@@ -1,33 +0,0 @@
# UI Components
The **SigPro UI** plugin is a high-level component library built on top of the reactive core. It leverages **Tailwind CSS v4** for utility styling and **daisyUI v5** for semantic, themeable components.
## Features
- 🚀 **Fully Reactive**: Every component automatically updates with signals
- 🎨 **Themeable**: Supports all daisyUI themes out of the box
- 📱 **Responsive**: Designed to work on all devices
- 🔧 **Zero Dependencies**: Pure SigPro with no framework overhead
## Quick Demo
<div id="quick-demo"></div>
## What's Included
The UI plugin provides a comprehensive set of reactive components:
| Category | Components |
|----------|------------|
| **Actions** | `_button` |
| **Forms** | `_input`, `_select`, `_checkbox`, `_radio`, `_range` |
| **Layout** | `_fieldset`, `_accordion`, `_drawer` |
| **Navigation** | `_navbar`, `_menu`, `_tabs` |
| **Overlays** | `_modal`, `_dropdown` |
| **Feedback** | `_badge`, `_tooltip` |
## Next Steps
- [Installation Guide](/ui/installation) - Set up Tailwind and daisyUI
- [Button Component](/ui/button) - Explore the button component
- [Form Components](/ui/form) - Build reactive forms with validation

View File

@@ -1,117 +0,0 @@
# Layout Components
Layout components for structuring your application with containers, sections, and collapsible panels.
## Fieldset (`_fieldset`)
Groups related form fields with a legend.
<div id="fieldset-demo"></div>
```javascript
_fieldset({ legend: 'Personal Information' }, [
_input({ label: 'Full Name', $value: $name }),
_input({ label: 'Email Address', type: 'email', $value: $email }),
_select({ label: 'Role', options: [...], $value: $role })
])
```
## Accordion (`_accordion`)
Collapsible content panels. Can be used as standalone or grouped.
### Single Accordion
<div id="single-accordion-demo"></div>
```javascript
_accordion({ title: 'What is SigPro UI?' }, [
$.html('p', {}, 'SigPro UI is a reactive component library...')
])
```
### Grouped Accordions (Radio Behavior)
When multiple accordions share the same `name`, only one can be open at a time.
<div id="grouped-accordion-demo"></div>
```javascript
// Grouped accordions - only one open at a time
_accordion({ title: 'Getting Started', name: 'faq' }, content1)
_accordion({ title: 'Installation', name: 'faq' }, content2)
_accordion({ title: 'Customization', name: 'faq' }, content3)
```
### Accordion with Open State
Control the initial open state with the `open` prop.
<div id="open-accordion-demo"></div>
```javascript
_accordion({ title: 'Open by Default', open: true }, [
$.html('p', {}, 'This accordion starts open.')
])
```
## Complete Layout Example
<div id="complete-layout-demo"></div>
## API Reference
### `_fieldset`
| Prop | Type | Description |
|------|------|-------------|
| `legend` | `string` | Fieldset title/legend text |
| `class` | `string \| function` | Additional CSS classes |
### `_accordion`
| Prop | Type | Description |
|------|------|-------------|
| `title` | `string` | Accordion header text |
| `name` | `string` | Group name for radio behavior (optional) |
| `open` | `boolean` | Initially open state (default: false) |
## Styling Tips
### Custom Fieldset Styling
```javascript
_fieldset({
legend: 'Custom Styled',
class: 'bg-primary/10 border-primary'
}, [
// content
])
```
### Custom Accordion Styling
```javascript
_accordion({
title: 'Styled Accordion',
class: 'bg-base-200'
}, [
// content
])
```
### Nested Layouts
Layout components can be nested to create complex structures:
```javascript
_fieldset({ legend: 'Main Section' }, [
_accordion({ title: 'Subsection 1' }, [
_input({ label: 'Field 1', $value: $field1 })
]),
_accordion({ title: 'Subsection 2' }, [
_input({ label: 'Field 2', $value: $field2 })
])
])
```

View File

@@ -1,90 +0,0 @@
# Modal & Drawer Components
Overlay components for dialogs, side panels, and popups with reactive control.
## Modal (`_modal`)
A dialog component that appears on top of the page. The modal is completely removed from the DOM when closed, optimizing performance.
### Basic Modal
<div id="basic-modal-demo"></div>
```javascript
const $open = $(false)
_button({ onclick: () => $open(true) }, 'Open Modal')
_modal({ $open: $open, title: 'Welcome' }, [
$.html('p', {}, 'This is a simple modal dialog.'),
_button({ onclick: () => $open(false) }, 'Close')
])
```
### Modal with Actions
<div id="action-modal-demo"></div>
```javascript
const $open = $(false)
const $result = $(null)
_modal({ $open: $open, title: 'Confirm Delete' }, [
$.html('p', {}, 'Are you sure you want to delete this item?'),
_button({ class: 'btn-error', onclick: () => {
$result('Item deleted')
$open(false)
} }, 'Delete')
])
```
### Modal with Form
<div id="form-modal-demo"></div>
## Drawer (`_drawer`)
A sidebar panel that slides in from the side.
### Basic Drawer
<div id="basic-drawer-demo"></div>
```javascript
const $open = $(false)
_drawer({
id: 'my-drawer',
$open: $open,
content: $.html('div', {}, 'Main content'),
side: $.html('div', { class: 'p-4' }, [
$.html('h3', {}, 'Menu'),
$.html('ul', { class: 'menu' }, [
$.html('li', {}, [$.html('a', { onclick: () => $open(false) }, 'Close')])
])
])
})
```
### Drawer with Navigation Menu
<div id="nav-drawer-demo"></div>
## API Reference
### `_modal`
| Prop | Type | Description |
|------|------|-------------|
| `$open` | `Signal<boolean>` | Controls modal visibility |
| `title` | `string` | Modal title text |
### `_drawer`
| Prop | Type | Description |
|------|------|-------------|
| `id` | `string` | Unique identifier for the drawer |
| `$open` | `Signal<boolean>` | Controls drawer visibility |
| `content` | `HTMLElement` | Main content area |
| `side` | `HTMLElement` | Sidebar content |

View File

@@ -1,124 +0,0 @@
# Navigation Components
Navigation components for building menus, navbars, and tabs with reactive active states.
## Navbar (`_navbar`)
A responsive navigation bar with built-in styling.
<div id="navbar-demo"></div>
```javascript
const $active = $('Home')
_navbar({ class: 'shadow-md' }, [
div({ class: 'flex-1' }, [
a({ class: 'text-xl font-bold' }, 'MyApp')
]),
div({ class: 'flex-none gap-2' }, [
_button({
class: () => $active() === 'Home' ? 'btn-primary btn-sm' : 'btn-ghost btn-sm',
onclick: () => $active('Home')
}, 'Home'),
_button({
class: () => $active() === 'About' ? 'btn-primary btn-sm' : 'btn-ghost btn-sm',
onclick: () => $active('About')
}, 'About')
])
])
```
## Menu (`_menu`)
Vertical navigation menu with active state highlighting.
<div id="menu-demo"></div>
```javascript
const $selected = $('dashboard')
_menu({ items: [
{
label: 'Dashboard',
icon: '📊',
active: () => $selected() === 'dashboard',
onclick: () => $selected('dashboard')
},
{
label: 'Analytics',
icon: '📈',
active: () => $selected() === 'analytics',
onclick: () => $selected('analytics')
}
]})
```
## Tabs (`_tabs`)
Horizontal tabs with lifted styling and active state.
<div id="tabs-demo"></div>
```javascript
const $activeTab = $('profile')
_tabs({ items: [
{
label: 'Profile',
active: () => $activeTab() === 'profile',
onclick: () => $activeTab('profile')
},
{
label: 'Settings',
active: () => $activeTab() === 'settings',
onclick: () => $activeTab('settings')
}
]})
```
## Dropdown (`_dropdown`)
A dropdown menu that appears on click.
<div id="dropdown-demo"></div>
```javascript
const $selected = $(null)
_dropdown({ label: 'Options' }, [
li([a({ onclick: () => $selected('Edit') }, '✏️ Edit')]),
li([a({ onclick: () => $selected('Duplicate') }, '📋 Duplicate')]),
li([a({ onclick: () => $selected('Delete') }, '🗑️ Delete')])
])
```
## Complete Navigation Example
<div id="complete-nav-demo"></div>
## API Reference
### `_navbar`
| Prop | Type | Description |
|------|------|-------------|
| `class` | `string \| function` | Additional CSS classes |
### `_menu`
| Prop | Type | Description |
|------|------|-------------|
| `items` | `Array<{label: string, icon?: any, active?: boolean\|function, onclick: function}>` | Menu items |
### `_tabs`
| Prop | Type | Description |
|------|------|-------------|
| `items` | `Array<{label: string, active: boolean\|function, onclick: function}>` | Tab items |
### `_dropdown`
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Dropdown trigger text |
| `class` | `string \| function` | Additional CSS classes |

118
src/docs/ui/quick.md Normal file
View File

@@ -0,0 +1,118 @@
# 🧩 UI Components `(WIP)`
> **Status: Work In Progress.** > These are high-level, complex visual components designed to speed up development. They often replace native HTML elements with "superpowered" versions that handle their own internal logic, reactivity, and accessibility.
## 1. What are UI Components?
Unlike **Tag Helpers** (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions.
* **Stateful**: They manage complex internal states (like date ranges, search filtering, or API lifecycles).
* **Reactive**: Attributes prefixed with `$` are automatically tracked.
* **Self-Cleaning**: They automatically use `_cleanups` to destroy observers or event listeners when removed from the DOM.
* **Themed**: Fully compatible with Tailwind CSS and DaisyUI themes.
---
## 2. The UI Registry (Available Now)
| Category | Components |
| :--- | :--- |
| **Logic & Flow** | `If`, `For`, `Json` |
| **Forms & Inputs** | `Button`, `Input`, `Select`, `Autocomplete`, `Datepicker`, `Colorpicker`, `CheckBox`, `Radio`, `Range`, `Rating`, `Swap` |
| **Feedback** | `Alert`, `Toast`, `Modal`, `Loading`, `Badge`, `Tooltip`, `Indicator` |
| **Navigation** | `Navbar`, `Menu`, `Drawer`, `Tabs`, `Accordion`, `Dropdown` |
| **Data & Layout** | `Request`, `Response`, `Grid` (AG-Grid), `List`, `Stack`, `Timeline`, `Stat`, `Fieldset`, `Fab` |
---
## 3. Examples with "Superpowers"
### A. The Declarative API Flow (`Request` & `Response`)
Instead of manually managing `loading` and `error` flags, use these two together to handle data fetching elegantly.
```javascript
// 1. Define the request (it tracks dependencies automatically)
const userProfile = Request(
() => `https://api.example.com/user/${userId()}`
);
// 2. Render the UI based on the request state
Div({ class: "p-4" }, [
Response(userProfile, (data) =>
Div([
H1(data.name),
P(data.email)
])
)
]);
```
### B. Smart Inputs & Autocomplete
Native inputs are boring. SigPro UI inputs handle labels, icons, password toggles, and validation states out of the box.
```javascript
const searchQuery = $("");
Autocomplete({
label: "Find a Country",
placeholder: "Start typing...",
options: ["Spain", "France", "Germany", "Italy", "Portugal"],
$value: searchQuery,
onSelect: (val) => console.log("Selected:", val)
});
```
### C. The Reactive Datepicker
Handles single dates or ranges with a clean, reactive interface.
```javascript
const myDate = $(""); // or { start: "", end: "" } for range
Datepicker({
label: "Select Expiry Date",
$value: myDate,
range: false // Set to true for range selection
});
```
### D. Imperative Toasts & Modals
Sometimes you just need to trigger a message without cluttering your template.
```javascript
// Show a notification from anywhere in your logic
Toast("Settings saved successfully!", "alert-success", 3000);
// Control a modal with a simple signal
const isModalOpen = $(false);
Modal({
$open: isModalOpen,
title: "Delete Account",
buttons: [
Button({ class: "btn-error", onclick: doDelete }, "Confirm")
]
}, "This action cannot be undone.");
```
---
## 4. Internationalization (i18n)
The UI library comes with a built-in locale system. It currently supports `es` and `en`.
```javascript
// Set the global UI language
SetLocale("en");
// Access translated strings in your own components
const t = tt("confirm"); // Returns a signal that tracks the current locale
```
---
## 5. Best Practices
* **Use `$` for Reactivity**: If a property starts with `$`, it expects a Signal (e.g., `$value: mySignal`).
* **Key your Lists**: When using `For`, always provide a `keyFn` to ensure high-performance DOM reconciliation.
* **Cleanups**: If you build custom components that use `setInterval` or `observers`, add them to the element's `_cleanups` Set.