Update Docs
This commit is contained in:
@@ -7,8 +7,7 @@ export default defineConfig({
|
||||
description: "Minimalist Reactive Library",
|
||||
outDir: '../../docs',
|
||||
base: isDev ? '/absproxy/5174/sigpro/' : '/sigpro/',
|
||||
|
||||
// AÑADIDO: Head para estilos
|
||||
|
||||
head: [
|
||||
['link', { rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/npm/daisyui@5/dist/full.css' }]
|
||||
],
|
||||
@@ -26,18 +25,15 @@ export default defineConfig({
|
||||
logo: '/logo.svg',
|
||||
nav: [
|
||||
{ text: 'Home', link: '/' },
|
||||
{ text: 'Guide', link: '/guide/getting-started' },
|
||||
{ text: 'Install', link: '/install' },
|
||||
{ text: 'Api', link: '/api/quick' },
|
||||
// AÑADIDO: UI en nav
|
||||
{ text: 'UI', link: '/ui/introduction' },
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
text: 'Introduction',
|
||||
items: [
|
||||
{ text: 'What is SigPro?', link: '/' },
|
||||
{ text: 'Why', link: '/guide/why' },
|
||||
{ text: 'Guide', link: '/guide/getting-started' },
|
||||
{ text: 'Installation', link: '/install' },
|
||||
{ text: 'Vite Plugin', link: '/vite/plugin' },
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -55,28 +51,13 @@ export default defineConfig({
|
||||
text: 'Plugins',
|
||||
items: [
|
||||
{ text: 'Quick Start', link: '/plugins/quick' },
|
||||
{ text: '@core UI Plugin', link: '/plugins/core.ui' },
|
||||
{ text: '@core Debug', link: '/plugins/core.debug' },
|
||||
{ text: 'Custom', link: '/plugins/custom' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Vite Router Plugin',
|
||||
text: 'Examples',
|
||||
items: [
|
||||
{ text: 'Vite Plugin', link: '/vite/plugin' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'UI Components',
|
||||
items: [
|
||||
{ text: 'Introduction', link: '/ui/introduction' },
|
||||
{ text: 'Installation', link: '/ui/installation' },
|
||||
{ text: 'Button', link: '/ui/button' },
|
||||
{ text: 'Input', link: '/ui/input' },
|
||||
{ text: 'Form Components', link: '/ui/form' },
|
||||
{ text: 'Modal & Drawer', link: '/ui/modal' },
|
||||
{ text: 'Navigation', link: '/ui/navigation' },
|
||||
{ text: 'Layout', link: '/ui/layout' },
|
||||
{ text: 'Demo Core', link: '/examples' }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,102 +1,142 @@
|
||||
# The Reactive Core: `$( )`
|
||||
|
||||
The `$` function is the heart of **SigPro**. It is a **Unified Reactive Constructor** that handles state, derivations, and automatic persistence through a single, consistent interface.
|
||||
|
||||
## 1. The Constructor: `$( input, [key] )`
|
||||
|
||||
Depending on the arguments you pass, SigPro creates different reactive primitives:
|
||||
|
||||
| Argument | Type | Required | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **input** | `Value` / `Function` | **Yes** | Initial state or reactive logic. |
|
||||
| **key** | `string` | No | If provided, the signal **persists** in `localStorage`. |
|
||||
The `$` function is a **Unified Reactive Constructor**. It detects the type of input you provide and returns the appropriate reactive primitive.
|
||||
|
||||
---
|
||||
|
||||
## 2. Signal (State & Persistence)
|
||||
## 1. Signals (Atomic State)
|
||||
A **Signal** is the simplest form of reactivity. It holds a single value (string, number, boolean, null).
|
||||
|
||||
A **Signal** is a reactive "box" for data. SigPro now supports **Native Persistence**: if you provide a second argument (the `key`), the signal will automatically sync with `localStorage`.
|
||||
|
||||
* **Standard:** `const $count = $(0);`
|
||||
* **Persistent:** `const $theme = $("light", "app-theme");` (Restores value on page reload).
|
||||
|
||||
### Example:
|
||||
### **Option A: Standard Signal (RAM)**
|
||||
Ideal for volatile state that shouldn't persist after a page refresh.
|
||||
```javascript
|
||||
const $user = $("Guest", "session-user"); // Automatically saved/loaded
|
||||
const $count = $(0);
|
||||
|
||||
// Read (Getter)
|
||||
console.log($user());
|
||||
// Usage:
|
||||
$count(); // Getter: returns 0
|
||||
$count(10); // Setter: updates to 10
|
||||
$count(c => c + 1); // Functional update: updates to 11
|
||||
```
|
||||
|
||||
// Update (Setter + Auto-save to Disk)
|
||||
$user("Alice");
|
||||
### **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");
|
||||
|
||||
// Functional Update
|
||||
$user(prev => prev.toUpperCase());
|
||||
// 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"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Computed (Derived State)
|
||||
## 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.
|
||||
|
||||
When you pass a **function** that **returns a value**, SigPro creates a **Computed Signal**. It tracks dependencies and recalculates only when necessary.
|
||||
### **Option A: Standard Store (RAM)**
|
||||
```javascript
|
||||
const user = $({
|
||||
name: "Alice",
|
||||
profile: { bio: "Developer" }
|
||||
});
|
||||
|
||||
* **Syntax:** `const $derived = $(() => logic);`
|
||||
// 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**.
|
||||
|
||||
### Example:
|
||||
```javascript
|
||||
const $price = $(100);
|
||||
const $qty = $(2);
|
||||
const $tax = $(0.21);
|
||||
|
||||
// Auto-tracks $price and $qty
|
||||
const $total = $(() => $price() * $qty());
|
||||
|
||||
$qty(3); // $total updates to 300 automatically
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Effects (Reactive Actions)
|
||||
|
||||
An **Effect** is a function that **does not return a value**. It performs an action (side effect) whenever the signals it "touches" change.
|
||||
|
||||
* **When to use:** Logging, manual DOM tweaks, or syncing with external APIs.
|
||||
* **Syntax:** `$(() => { action });`
|
||||
|
||||
### Example:
|
||||
```javascript
|
||||
const $status = $("online");
|
||||
|
||||
// Runs every time $status changes
|
||||
$(() => {
|
||||
console.log("System status is now:", $status());
|
||||
// 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. Summary Table: Usage Guide
|
||||
|
||||
| Primitive | Logic Type | Persistence? | Typical Use Case |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Signal** | Mutable State | **Yes** (Optional) | `$(0, 'counter')` |
|
||||
| **Computed** | Derived / Read-only | No | `$(() => $a() + $b())` |
|
||||
| **Effect** | Imperative Action | No | `$(() => alert($msg()))` |
|
||||
|
||||
---
|
||||
|
||||
## 💡 Pro Tip: The Power of Native Persistence
|
||||
|
||||
In SigPro, you don't need external plugins for basic storage. By using the `key` parameter in a Signal, you gain:
|
||||
1. **Zero Boilerplate:** No more `JSON.parse(localStorage.getItem(...))`.
|
||||
2. **Instant Hydration:** The value is restored **before** the UI renders, preventing "flicker".
|
||||
3. **Atomic Safety:** Data is saved to disk exactly when the signal changes, ensuring your app state is always safe.
|
||||
|
||||
---
|
||||
|
||||
### Naming Convention
|
||||
We use the **`$` prefix** (e.g., `$count`) for reactive functions to distinguish them from static variables at a glance:
|
||||
## 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
|
||||
let count = 0; // Static
|
||||
const $count = $(0); // Reactive Signal
|
||||
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
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Application Mounter: `$.router.mount` (Core)
|
||||
# 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.
|
||||
|
||||
|
||||
@@ -1,99 +1,176 @@
|
||||
# Quick API Reference ⚡
|
||||
# Quick API Reference
|
||||
|
||||
This is a high-level summary of the **SigPro** core API. For detailed guides and edge cases, please refer to the specific documentation for each module.
|
||||
SigPro is a minimal yet powerful engine. Here is a complete overview of its capabilities.
|
||||
|
||||
## 1. Core Reactivity: `$( )`
|
||||
## 1. Core API Summary
|
||||
|
||||
The `$` function is a polymorphic constructor. It creates **Signals** (state) or **Computed Effects** (logic) based on the input type.
|
||||
| Function | Description | Example |
|
||||
| :--- | :--- | :--- |
|
||||
| **`$(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)` |
|
||||
|
||||
| Usage | Input Type | Returns | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Signal** | `any` | `Function` | A getter/setter for reactive state. |
|
||||
| **Computed** | `Function` | `Function` | A read-only signal that auto-updates when its dependencies change. |
|
||||
---
|
||||
|
||||
**Example:**
|
||||
## 2. The Power of `$` (Reactivity)
|
||||
|
||||
The `$` function adapts to whatever you pass to it:
|
||||
|
||||
### **Signals & Persistent State**
|
||||
Reactive values in RAM or synced with `localStorage`.
|
||||
```javascript
|
||||
const $count = $(0); // Signal
|
||||
const $double = $(() => $count() * 2); // Computed
|
||||
const $count = $(0); // Simple Signal
|
||||
const $theme = $('dark', 'app-theme'); // Persistent Signal (Disk)
|
||||
|
||||
$count(10); // Update value
|
||||
console.log($count()); // Get value: 10
|
||||
```
|
||||
|
||||
### **Computed Signals**
|
||||
Read-only signals that update automatically when their dependencies change.
|
||||
```javascript
|
||||
const $double = $(() => $count() * 2);
|
||||
```
|
||||
|
||||
### **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');
|
||||
|
||||
// Accessing properties (they become signals)
|
||||
state.user.name(); // Get: 'Natxo'
|
||||
state.user.name('Guest'); // Set & Sync to Disk: 'my-app-state_user_name'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Rendering Engine: `$.html`
|
||||
### **3. UI Creation: Constructor vs. Direct Tags**
|
||||
|
||||
SigPro uses a hyperscript-style engine to create live DOM nodes.
|
||||
SigPro provides the `$.html` engine for defining any element and global "Sugar Tags" for rapid development.
|
||||
|
||||
| Argument | Type | Required | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **tag** | `string` | Yes | Standard HTML tag (e.g., 'div', 'button'). |
|
||||
| **props** | `Object` | No | Attributes (`id`), Events (`onclick`), or Reactive Props (`$value`). |
|
||||
| **content** | `any` | No | String, Node, Array, or Reactive Function. |
|
||||
::: code-group
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
$.html('button', { onclick: () => alert('Hi!') }, 'Click Me');
|
||||
```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');
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. Global Helpers (Tag Proxies)
|
||||
### **Technical Breakdown**
|
||||
|
||||
To keep your code clean, SigPro automatically exposes common HTML tags to the global scope.
|
||||
* **`$.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.
|
||||
|
||||
| Category | Available Tags |
|
||||
| :--- | :--- |
|
||||
| **Layout** | `div`, `section`, `main`, `nav`, `header`, `footer`, `span` |
|
||||
| **Typography** | `h1`, `h2`, `h3`, `p`, `label`, `a`, `li`, `ul`, `ol` |
|
||||
| **Forms** | `input`, `button`, `form`, `select`, `option` |
|
||||
| **Media** | `img`, `video`, `audio`, `canvas` |
|
||||
---
|
||||
|
||||
**Example:**
|
||||
### **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
|
||||
// No imports needed!
|
||||
div([
|
||||
h1("Title"),
|
||||
button("Ok")
|
||||
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 & Plugins
|
||||
## 4. Mounting: `$.mount`
|
||||
|
||||
Methods to initialize your application and extend the engine.
|
||||
The entry point of your application. It links your JavaScript logic to a specific DOM element.
|
||||
|
||||
| Method | Signature | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| **`$.mount`** | `(node, target)` | Wipes the target (default: `body`) and renders the component. |
|
||||
| **`$.plugin`** | `(source)` | Registers a function or loads external `.js` scripts as plugins. |
|
||||
```html
|
||||
<div id="app"></div>
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
$.plugin([UI, Router]);
|
||||
$.mount(App, '#root');
|
||||
// In your main.js
|
||||
const App = () => main([
|
||||
h1("Welcome to SigPro"),
|
||||
p("Everything here is reactive.")
|
||||
]);
|
||||
|
||||
// Usage: $.mount(component, selectorOrElement)
|
||||
$.mount(App, '#app');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Reactive Syntax Cheat Sheet
|
||||
## 5. Navigation: `$.router`
|
||||
|
||||
| Feature | Syntax | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| **Text Binding** | `p(["Value: ", $sig])` | Updates text content automatically. |
|
||||
| **Attributes** | `div({ id: $sig })` | Static attribute assignment. |
|
||||
| **Reactive Attr** | `div({ $class: $sig })` | Attribute updates when `$sig` changes. |
|
||||
| **Two-way Binding**| `input({ $value: $sig })`| Syncs input value and signal automatically. |
|
||||
| **Conditional** | `div(() => $sig() > 0 ? "Yes" : "No")` | Re-renders only the content when the condition changes. |
|
||||
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.
|
||||
|
||||
## Summary Table
|
||||
```javascript
|
||||
// 1. Function-based plugin
|
||||
$.plugin(($) => {
|
||||
$.myHelper = () => console.log("Plugin active!");
|
||||
});
|
||||
|
||||
| Feature | SigPro Approach | Benefit |
|
||||
| :--- | :--- | :--- |
|
||||
| **Update Logic** | Fine-grained (Surgical) | Blazing fast updates. |
|
||||
| **DOM** | Native Nodes | Zero abstraction cost. |
|
||||
| **Syntax** | Pure JavaScript | No build-tool lock-in. |
|
||||
| **Footprint** | Modular | Load only what you use. |
|
||||
// 2. Load external scripts
|
||||
await $.plugin('https://cdn.example.com/library.js');
|
||||
```
|
||||
|
||||
@@ -1,160 +1,128 @@
|
||||
# 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.
|
||||
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.
|
||||
|
||||
## 1. How it Works
|
||||
|
||||
When SigPro initializes, it runs a proxy loop that creates a function for every common HTML tag and attaches it to the `window` object.
|
||||
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**.
|
||||
|
||||
* **Traditional:** `$.html('button', { onclick: ... }, 'Click')`
|
||||
* **Under the hood:** `$.html('button', { onclick: ... }, 'Click')`
|
||||
* **SigPro Style:** `button({ onclick: ... }, 'Click')`
|
||||
|
||||
This approach gives you a "DSL" (Domain Specific Language) that feels like HTML but is actually **pure JavaScript**.
|
||||
|
||||
---
|
||||
|
||||
## 2. The Global Registry
|
||||
## 2. The Complete Global Registry
|
||||
|
||||
The following tags are available globally by default:
|
||||
The following tags are injected into the global scope and are ready to use as soon as SigPro loads:
|
||||
|
||||
| Category | Available Functions |
|
||||
| Category | Available Global Functions |
|
||||
| :--- | :--- |
|
||||
| **Layout** | `div`, `span`, `section`, `main`, `nav`, `header`, `footer`, `article`, `aside` |
|
||||
| **Typography** | `h1`, `h2`, `h3`, `p`, `ul`, `ol`, `li`, `a`, `label`, `strong`, `em` |
|
||||
| **Forms** | `form`, `input`, `button`, `select`, `option`, `textarea` |
|
||||
| **Table** | `table`, `thead`, `tbody`, `tr`, `th`, `td` |
|
||||
| **Media** | `img`, `video`, `audio`, `canvas`, `svg` |
|
||||
| **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` |
|
||||
|
||||
> "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.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. Usage Patterns
|
||||
## 3. Usage Patterns (Argument Flexibility)
|
||||
|
||||
The tag functions are highly flexible and accept arguments in different orders to suit your coding style.
|
||||
The tag functions are "smart". They detect whether you are passing attributes, content, or both.
|
||||
|
||||
### A. Attributes + Content
|
||||
The most common pattern.
|
||||
The standard way to build complex nodes.
|
||||
```javascript
|
||||
div({ class: 'card' }, [
|
||||
h1("Title"),
|
||||
p("Description")
|
||||
div({ class: 'container', id: 'main-wrapper' }, [
|
||||
h1("Welcome"),
|
||||
p("This is SigPro.")
|
||||
]);
|
||||
```
|
||||
|
||||
### B. Content Only
|
||||
If you don't need attributes, you can skip the object entirely.
|
||||
### 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.
|
||||
```javascript
|
||||
div([
|
||||
h1("Just Content"),
|
||||
p("No attributes object needed here.")
|
||||
section([
|
||||
h2("No Attributes Needed"),
|
||||
button("Click Me")
|
||||
]);
|
||||
```
|
||||
|
||||
### C. Simple Text
|
||||
For elements that only contain a string.
|
||||
### C. Primitive Content
|
||||
For simple tags, you can just pass a string or a number.
|
||||
```javascript
|
||||
button("Submit"); // Equivalent to <button>Submit</button>
|
||||
h1("Hello World");
|
||||
span(42);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Reactive Tags
|
||||
## 4. Reactive Attributes & Content
|
||||
|
||||
Since these helpers are just wrappers around `$.html`, they support full reactivity out of the box.
|
||||
These helpers fully support SigPro's reactivity. Attributes starting with `$` are automatically tracked.
|
||||
|
||||
```javascript
|
||||
const $loading = $(true);
|
||||
const $count = $(0);
|
||||
|
||||
div([
|
||||
$loading() ? span("Loading...") : h1("Data Ready!"),
|
||||
div({ class: 'counter-app' }, [
|
||||
h2(["Current Count: ", $count]), // Auto-unwrapping text content
|
||||
|
||||
button({
|
||||
$disabled: $loading, // Reactive attribute
|
||||
onclick: () => $loading(false)
|
||||
}, "Stop Loading")
|
||||
onclick: () => $count(c => c + 1),
|
||||
$style: () => $count() > 5 ? "color: red" : "color: green" // Reactive style
|
||||
}, "Increment")
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Under the Hood
|
||||
## 5. Technical Implementation
|
||||
|
||||
If you are curious about how this happens without a compiler, here is the logic inside the SigPro core:
|
||||
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
|
||||
const tags = ['div', 'span', 'p', 'button', ...];
|
||||
|
||||
tags.forEach(tag => {
|
||||
window[tag] = (props, content) => $.html(tag, props, content);
|
||||
});
|
||||
// Internal SigPro loop
|
||||
tags.forEach(t => window[t] = (p, c) => $.html(t, p, c));
|
||||
```
|
||||
|
||||
Because these are attached to `window`, they are available in any file in your project as soon as SigPro is loaded, making your components look like this:
|
||||
|
||||
```javascript
|
||||
// No imports required for tags!
|
||||
export default () =>
|
||||
section({ id: 'hero' }, [
|
||||
h1("Fast. Atomic. Simple."),
|
||||
p("Built with SigPro.")
|
||||
]);
|
||||
```
|
||||
Because they are real functions, you get full IDE autocompletion and valid JS syntax highlighting without needing special plugins like JSX.
|
||||
|
||||
---
|
||||
|
||||
## 6. Full Comparison: SigPro vs. Standard HTML
|
||||
## 6. Comparison: Logic to UI
|
||||
|
||||
To better understand the translation, here is a complete example of a **User Card** component. Notice how **SigPro** attributes with the `$` prefix map to reactive behavior, while standard attributes remain static.
|
||||
Here is how a dynamic **Task Item** component translates from SigPro logic to the final DOM structure.
|
||||
|
||||
::: code-group
|
||||
```javascript [SigPro (JS)]
|
||||
const $online = $(true);
|
||||
|
||||
export const UserCard = () => (
|
||||
div({ class: 'user-card' }, [
|
||||
img({ src: 'avatar.png', alt: 'User' }),
|
||||
|
||||
div({ class: 'info' }, [
|
||||
h2("John Doe"),
|
||||
p({
|
||||
$class: () => $online() ? 'status-on' : 'status-off'
|
||||
}, [
|
||||
"Status: ",
|
||||
() => $online() ? "Online" : "Offline"
|
||||
])
|
||||
]),
|
||||
|
||||
button({
|
||||
onclick: () => $online(!$online())
|
||||
}, "Toggle Status")
|
||||
```javascript [SigPro Component]
|
||||
const Task = (title, $done) => (
|
||||
li({ class: 'task-item' }, [
|
||||
input({
|
||||
type: 'checkbox',
|
||||
$checked: $done // Two-way reactive binding
|
||||
}),
|
||||
span({
|
||||
$style: () => $done() ? "text-decoration: line-through" : ""
|
||||
}, title)
|
||||
])
|
||||
);
|
||||
```
|
||||
|
||||
```html [Equivalent HTML Structure]
|
||||
<div class="user-card">
|
||||
<img src="avatar.png" alt="User">
|
||||
|
||||
<div class="info">
|
||||
<h2>John Doe</h2>
|
||||
<p class="status-on">
|
||||
Status: Online
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button>Toggle Status</button>
|
||||
</div>
|
||||
```html [Rendered HTML]
|
||||
<li class="task-item">
|
||||
<input type="checkbox" checked>
|
||||
<style="text-decoration: line-through">Buy milk</span>
|
||||
</li>
|
||||
```
|
||||
:::
|
||||
|
||||
### What is happening here?
|
||||
|
||||
1. **Structure:** The hierarchy is identical. `div([...])` in JS translates directly to nested tags in HTML.
|
||||
2. **Attributes:** `class` is set once. `$class` is "live"; SigPro listens to the `$online` signal and updates the class name without re-rendering the whole card.
|
||||
3. **Content:** The array `[...]` in SigPro is the equivalent of the children inside an HTML tag.
|
||||
4. **Reactivity:** The function `() => $online() ? ...` creates a **TextNode** in the HTML that changes its text content surgically whenever the signal toggles.
|
||||
|
||||
---
|
||||
|
||||
## 💡 Best Practices
|
||||
|
||||
1. **Destructuring:** If you prefer not to rely on global variables, you can destructure them from `window` or `$` (though in SigPro, using them globally is the intended "clean" way).
|
||||
2. **Custom Tags:** If you need a tag that isn't in the default list (like a Web Component), you can still use the base engine: `$.html('my-custom-element', { ... })`.
|
||||
|
||||
55
src/docs/examples.md
Normal file
55
src/docs/examples.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Live Playground
|
||||
|
||||
Experience **SigPro's** fine-grained reactivity in real-time. Feel free to tweak the signal values in the editor!
|
||||
|
||||
<iframe width="100%" height="600" src="//jsfiddle.net/natxocc/spwran02/4/embedded/" frameborder="0" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Best Practices for Documentation
|
||||
|
||||
* **Tab Selection:** You can control which tabs are active by default by changing the URL segment after `/embedded/`.
|
||||
* `js,result`: Shows the logic and the output.
|
||||
* `html,js,result`: Shows the base structure, the logic, and the output.
|
||||
* **Height Management:** For complex Store examples, increase the `height` attribute to `500` or `600` so the code is readable without internal scrolling.
|
||||
* **Responsive Width:** Keeping `width="100%"` ensures the fiddle scales correctly on tablets and mobile devices.
|
||||
|
||||
---
|
||||
|
||||
### 3. Advanced: The "Fiddle" Component (Optional)
|
||||
If you plan to have 10+ examples, you can create a global Vue component in VitePress. This keeps your Markdown files clean and allows you to change the theme or default height for all fiddles at once.
|
||||
|
||||
**Create `.vitepress/theme/components/Fiddle.vue`:**
|
||||
```vue
|
||||
<template>
|
||||
<div class="fiddle-wrapper" style="margin: 20px 0;">
|
||||
<iframe
|
||||
width="100%"
|
||||
:height="height"
|
||||
:src="`//jsfiddle.net/natxocc/${id}/embedded/${tabs}/dark/`"
|
||||
frameborder="0"
|
||||
loading="lazy">
|
||||
</iframe>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
id: String, // e.g., "spwran02/4"
|
||||
height: { default: '400' },
|
||||
tabs: { default: 'js,result' }
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
**Usage in Markdown:**
|
||||
```markdown
|
||||
Check out this store example:
|
||||
<Fiddle id="spwran02/4" height="500" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Why this is perfect for SigPro:
|
||||
Because SigPro is **zero-dependency** and runs directly in the browser, your JSFiddle code will be exactly what the user copies into their own `index.html`. There is no hidden "build step" confusing the learner.
|
||||
@@ -1,76 +0,0 @@
|
||||
# Getting Started
|
||||
|
||||
**SigPro** is a lightweight, atomic reactive engine designed to build modern web interfaces with zero overhead. It focuses on high performance through fine-grained reactivity.
|
||||
|
||||
## 1. Installation
|
||||
|
||||
You can install SigPro via your favorite package manager:
|
||||
|
||||
::: code-group
|
||||
```bash [npm]
|
||||
npm install sigpro
|
||||
````
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add sigpro
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add sigpro
|
||||
```
|
||||
```bash [bun]
|
||||
bun add sigpro
|
||||
```
|
||||
:::
|
||||
|
||||
## 2\. Basic Usage
|
||||
|
||||
The core of SigPro is the `$` function, which creates reactive state (Signals) and computed effects.
|
||||
|
||||
Create a `main.js` file and try this:
|
||||
|
||||
```javascript
|
||||
import { $ } from 'SigPro';
|
||||
|
||||
// 1. Create a reactive signal
|
||||
const $name = $("World");
|
||||
|
||||
// 2. Define a reactive component
|
||||
const App = () => div({ class: 'container' }, [
|
||||
h1(["Hello, ", $name, "!"]),
|
||||
|
||||
input({
|
||||
type: 'text',
|
||||
$value: $name, // Two-way binding
|
||||
placeholder: 'Enter your name...'
|
||||
}),
|
||||
|
||||
button({
|
||||
onclick: () => $name("SigPro")
|
||||
}, "Set to SigPro")
|
||||
]);
|
||||
|
||||
// 3. Mount the application
|
||||
$.mount(App, '#app');
|
||||
```
|
||||
|
||||
## 3\. How it Works
|
||||
|
||||
SigPro doesn't use a Virtual DOM. Instead, it creates real DOM nodes and binds them directly to your data:
|
||||
|
||||
1. **Signals**: `$(value)` creates a getter/setter function.
|
||||
2. **Reactivity**: When you pass a signal or a function to a DOM element, SigPro automatically creates a subscription.
|
||||
3. **Fine-Grained Updates**: Only the specific text node or attribute linked to the signal updates when the value changes.
|
||||
|
||||
## 4\. Global Tags
|
||||
|
||||
By default, SigPro exports common HTML tags to the global scope (`window`) when initialized. This allows you to write clean, declarative UI without importing every single tag:
|
||||
|
||||
```javascript
|
||||
// Instead of $.html('div', ...), just use:
|
||||
div([
|
||||
h1("Clean Syntax"),
|
||||
p("No more boilerplate.")
|
||||
]);
|
||||
```
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
# Why SigPro?
|
||||
|
||||
After years of building applications with React, Vue, and Svelte—investing countless hours mastering unique mental models, proprietary syntaxes, and complex build tools—we reached a realization: the web platform has evolved, but frameworks have become layers of abstraction that often move us further away from the browser.
|
||||
|
||||
**SigPro** is the answer to a simple question: **Why fight the platform when we can embrace it?**
|
||||
|
||||
## The Modern Web is Ready
|
||||
|
||||
SigPro bypasses the overhead of the Virtual DOM and heavy compilers by using modern browser primitives. It treats the DOM as a first-class citizen, not as a side effect of a state change.
|
||||
|
||||
| Browser Primitive | What It Enables |
|
||||
| :--- | :--- |
|
||||
| **Closures & Proxies** | Automatic dependency tracking without heavy overhead. |
|
||||
| **ES Modules** | Native modularity and lazy loading without complex bundlers. |
|
||||
| **Direct DOM APIs** | Surgical updates that are faster than any reconciliation algorithm. |
|
||||
| **Microtask Queues** | Batching updates efficiently to ensure 60fps performance. |
|
||||
|
||||
---
|
||||
|
||||
## The SigPro Philosophy
|
||||
|
||||
SigPro strips away the complexity, delivering a reactive programming model that feels like a framework but stays remarkably close to Vanilla JS:
|
||||
|
||||
* **No JSX transformations** – Pure JavaScript functions.
|
||||
* **No Virtual DOM** – Direct, fine-grained DOM manipulation.
|
||||
* **No proprietary syntax** – If you know JS, you know SigPro.
|
||||
* **Zero Build Step Required** – It can run directly in the browser via ESM.
|
||||
|
||||
```javascript
|
||||
// Pure, Atomic, Reactive.
|
||||
const $count = $(0);
|
||||
|
||||
const Counter = () => div([
|
||||
p(["Count: ", $count]),
|
||||
button({ onclick: () => $count(c => c + 1) }, "Increment")
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
SigPro isn't just lighter; it's architecturally faster because it skips the "diffing" phase entirely.
|
||||
|
||||
| Metric | SigPro | SolidJS | Svelte | Vue | React |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| **Bundle Size (gzip)** | 🥇 **< 2KB** | 🥈 7KB | 🥉 16KB | 20KB | 45KB |
|
||||
| **Architecture** | **Atomic** | **Atomic** | **Compiled** | **V-DOM** | **V-DOM** |
|
||||
| **Initial Render** | 🥇 **Fastest** | 🥈 Fast | 🥉 Fast | Average | Slow |
|
||||
| **Update Perf** | 🥇 **Surgical** | 🥇 Surgical | 🥈 Fast | 🥉 Average | Slow |
|
||||
| **Dependencies** | 🥇 **0** | 🥇 0 | 🥇 0 | 🥈 2 | 🥉 5+ |
|
||||
| **Build Step** | 🥇 **Optional** | 🥈 Required | 🥈 Required | 🥇 Optional | 🥈 Required |
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Core Principles
|
||||
|
||||
SigPro is built on four fundamental pillars:
|
||||
|
||||
### 📡 Atomic Reactivity
|
||||
Automatic dependency tracking with no manual subscriptions. When a signal changes, only the **exact** text nodes or attributes that depend on it update—instantly and surgically.
|
||||
|
||||
### ⚡ Surgical DOM Updates
|
||||
No Virtual DOM diffing. No tree reconciliation. We don't guess what changed; we know exactly where the update needs to happen. Performance scales with your data, not the size of your component tree.
|
||||
|
||||
### 🧩 Plugin-First Architecture
|
||||
The core is a tiny, powerful engine. Need Routing? Fetching? Global UI? Just plug it in. This keeps your production bundles "pay-only-for-what-you-use."
|
||||
|
||||
### 🔬 Predictable & Transparent
|
||||
There is no "magic" hidden in a black-box compiler. What you write is what the browser executes. Debugging is straightforward because there is no framework layer between your code and the DevTools.
|
||||
|
||||
---
|
||||
|
||||
> "SigPro returns the joy of web development by making the browser the hero again."
|
||||
|
||||
|
||||
@@ -4,93 +4,58 @@ layout: home
|
||||
hero:
|
||||
name: SigPro
|
||||
text: Atomic Unified Reactive Engine
|
||||
tagline: Fine-grained reactivity, built-in routing, and modular plugins. All under 2KB.
|
||||
tagline: High-precision atomic reactivity. No Virtual DOM. No compiler. No dependencies.
|
||||
image:
|
||||
src: /logo.svg
|
||||
alt: SigPro Logo
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Get Started
|
||||
link: /guide/getting-started
|
||||
link: /install
|
||||
- theme: alt
|
||||
text: View on GitHub
|
||||
link: https://github.com/natxocc/sigpro
|
||||
|
||||
features:
|
||||
- title: Atomic Reactivity
|
||||
details: Powered by Signals. Only updates what changes. No Virtual DOM overhead, no heavy re-renders.
|
||||
- title: Zero Dependencies
|
||||
details: Written in pure Vanilla JS. Maximum performance with the smallest footprint possible.
|
||||
- title: Modular Ecosystem
|
||||
details: Official plugins for UI components, dynamic Routing, Fetch, and Storage. Load only what you need.
|
||||
- title: ⚛️ Atomic Reactivity
|
||||
details: Powered by fine-grained Signals. Forget about whole-component re-renders; SigPro updates only the specific text node or attribute that changed.
|
||||
- title: 🚀 Zero Virtual DOM
|
||||
details: By eliminating the V-DOM diffing layer, SigPro performs surgical, direct manipulations on the real DOM, removing memory and CPU overhead.
|
||||
- title: 🛠️ No Compiler Required
|
||||
details: Pure Vanilla JS. No Babel, no JSX, no complex build steps. Standard JavaScript that runs natively in the browser with maximum performance.
|
||||
- title: 📦 Ultra-Lightweight
|
||||
details: The core engine—including reactivity, DOM creation, persistence, and routing—is under 2KB. Perfect for performance-critical applications.
|
||||
---
|
||||
|
||||
## Why SigPro?
|
||||
## Redefining Modern Reactivity
|
||||
|
||||
SigPro isn't just another framework; it's a high-performance engine. It strips away the complexity of massive bundles and returns to the essence of the web, enhanced with reactive superpowers.
|
||||
SigPro is not just another framework; it is a **high-performance engine**. While other libraries add layers of abstraction that slow down execution, SigPro returns to the essence of the web, leveraging the power of modern browser engines.
|
||||
|
||||
### The Core in Action
|
||||
### Why SigPro?
|
||||
|
||||
```javascript
|
||||
import { $ } from 'sigpro2';
|
||||
#### ⚡️ Surgical DOM Efficiency
|
||||
Unlike React or Vue, SigPro doesn't compare element trees. When a signal changes, SigPro knows exactly which DOM node depends on it and updates it instantly. It is **reactive precision** at its finest.
|
||||
|
||||
// A reactive state Signal
|
||||
const $count = $(0);
|
||||
#### 🔌 Modular Plugin System
|
||||
The core is sacred. Any extra functionality—Routing, UI Helpers, or State Persistence—is integrated through a polymorphic plugin system. Load only what your application truly needs.
|
||||
|
||||
// A Computed signal that updates automatically
|
||||
const $double = $(() => $count() * 2);
|
||||
#### 💾 Native Persistence
|
||||
SigPro features first-class support for `localStorage`. Synchronizing your application state with persistent storage is as simple as providing a key when initializing your Signal.
|
||||
|
||||
// UI that breathes with your data
|
||||
const Counter = () => div([
|
||||
h1(["Count: ", $count]),
|
||||
p(["Double: ", $double]),
|
||||
button({ onclick: () => $count(c => c + 1) }, "Increment")
|
||||
]);
|
||||
|
||||
$.mount(Counter);
|
||||
```
|
||||
#### 🚦 Built-in Hash Routing
|
||||
A robust routing system that supports **Native Lazy Loading** out of the box. Load your components only when the user navigates to them, keeping initial load times near zero.
|
||||
|
||||
---
|
||||
|
||||
### Key Features
|
||||
### The "No-Build" Philosophy
|
||||
In an ecosystem obsessed with compilers, SigPro bets on **standardization**. Write code today that will still run 10 years from now, without depending on build tools that will eventually become obsolete.
|
||||
|
||||
#### ⚡️ Fine-Grained Reactivity
|
||||
Unlike frameworks that diff complex trees (V-DOM), SigPro binds your signals directly to real DOM text nodes and attributes. If the data changes, the node changes. Period.
|
||||
|
||||
#### 🔌 Polymorphic Plugin System
|
||||
Extend core capabilities in a single line. Add global UI helpers, routing, or state persistence seamlessly.
|
||||
```javascript
|
||||
import { UI, Router } from 'sigpro/plugins';
|
||||
$.plugin([UI, Router]);
|
||||
```
|
||||
|
||||
#### 📂 File-Based Routing
|
||||
With our dedicated Vite plugin, manage your routes simply by creating files in `src/pages/`. It supports native **Lazy Loading** out of the box for lightning-fast initial loads.
|
||||
> "The best way to optimize code is to not have to process it at all."
|
||||
|
||||
---
|
||||
|
||||
### Quick Install
|
||||
## Community & Vision
|
||||
SigPro is an open-source project focused on simplicity and extreme speed. Designed for developers who love the web platform and hate unnecessary "bloatware".
|
||||
|
||||
::: code-group
|
||||
```bash [npm]
|
||||
npm install sigpro
|
||||
```
|
||||
```bash [pnpm]
|
||||
pnpm add sigpro
|
||||
```
|
||||
```bash [yarn]
|
||||
yarn add sigpro
|
||||
```
|
||||
```bash [bun]
|
||||
bun add sigpro
|
||||
```
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Community & Support
|
||||
SigPro is an open-source project. Whether you want to contribute, report a bug, or just talk about reactivity, join us on our official repository.
|
||||
|
||||
```
|
||||
Built with ❤️ by NatxoCC
|
||||
```
|
||||
```text
|
||||
Built with ❤️ by NatxoCC for the Modern Web.
|
||||
110
src/docs/install.md
Normal file
110
src/docs/install.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# 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.
|
||||
|
||||
## 1. Installation
|
||||
|
||||
Choose the method that best fits your workflow:
|
||||
|
||||
::: code-group
|
||||
|
||||
```bash [npm]
|
||||
npm install sigpro
|
||||
````
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add sigpro
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add sigpro
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add sigpro
|
||||
```
|
||||
|
||||
```html [CDN (ESM)]
|
||||
<script type="module">
|
||||
import { $ } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/+esm';
|
||||
</script>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
-----
|
||||
|
||||
## 2\. Quick Start Examples
|
||||
|
||||
Depending on your installation method, here is how you can get SigPro running in seconds.
|
||||
|
||||
::: code-group
|
||||
|
||||
```javascript [Mainstream (Bundlers)]
|
||||
// File: App.js
|
||||
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")
|
||||
]);
|
||||
};
|
||||
|
||||
// File: main.js
|
||||
import { $ } from 'sigpro';
|
||||
import { App } from './App.js';
|
||||
|
||||
$.mount(App, '#app');
|
||||
```
|
||||
|
||||
```html [Classic (Direct CDN)]
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<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({
|
||||
type: 'text',
|
||||
$value: $name, // Automatic two-way binding
|
||||
placeholder: 'Type your name...'
|
||||
})
|
||||
]);
|
||||
|
||||
$.mount(App, '#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
-----
|
||||
|
||||
## 3\. Global by Design
|
||||
|
||||
One of SigPro's core strengths is its **Global API**.
|
||||
|
||||
* **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.
|
||||
|
||||
## 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.
|
||||
|
||||
* **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.
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
# Development Tool: `_debug`
|
||||
|
||||
The **Debug Plugin** is a lightweight reactive listener. Once attached to a signal or a computed function, it automatically monitors changes, compares values, and formats the output in the browser console.
|
||||
|
||||
## 1. Core Features
|
||||
|
||||
* **Reactive Tracking:** Automatically logs whenever the tracked signal updates.
|
||||
* **Visual Grouping:** Uses styled console groups to keep your dev tools organized.
|
||||
* **Object Inspection:** Automatically uses `console.table()` when the signal contains an object or array.
|
||||
* **Efficient Comparison:** Uses `Object.is` to prevent redundant logging if the value hasn't actually changed.
|
||||
|
||||
---
|
||||
|
||||
## 2. Installation
|
||||
|
||||
To use `_debug`, you only need the SigPro core. Register the plugin in your `main.js`. You can conditionally load it so it only runs during development.
|
||||
|
||||
```javascript
|
||||
import { $ } from 'sigpro';
|
||||
import { Debug } from 'sigpro/plugins';
|
||||
|
||||
// Only load Debug in development mode
|
||||
const plugins = [];
|
||||
if (import.meta.env.DEV) plugins.push(Debug);
|
||||
|
||||
$.plugin(plugins).then(() => {
|
||||
import('./App.js').then(app => $.mount(app.default));
|
||||
});
|
||||
```
|
||||
|
||||
::: code-group
|
||||
```bash [NPM]
|
||||
npm install sigpro
|
||||
```
|
||||
|
||||
```bash [PNPM]
|
||||
pnpm add sigpro
|
||||
```
|
||||
|
||||
```bash [Yarn]
|
||||
yarn add sigpro
|
||||
```
|
||||
|
||||
```bash [Bun]
|
||||
bun add sigpro
|
||||
```
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. Basic Usage
|
||||
|
||||
Call `_debug` anywhere in your component. It stays active in the background, watching the signal's lifecycle.
|
||||
|
||||
```javascript
|
||||
export default () => {
|
||||
const $count = $(0);
|
||||
const $user = $({ name: "Guest", role: "Viewer" });
|
||||
|
||||
// Start tracking
|
||||
_debug($count, "Main Counter");
|
||||
_debug($user, "User Session");
|
||||
|
||||
return div([
|
||||
button({ onclick: () => $count(c => c + 1) }, "Increment"),
|
||||
button({ onclick: () => $user({ name: "Admin", role: "Super" }) }, "Promote")
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Console Output Breakdown
|
||||
|
||||
When a signal changes, the console displays a structured block:
|
||||
|
||||
1. **Header:** A styled badge with the name (e.g., `SigPro Debug: Main Counter`).
|
||||
2. **Previous Value:** The value before the update (in red).
|
||||
3. **Current Value:** The new value (in green).
|
||||
4. **Table View:** If the value is an object, a formatted table appears automatically.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 5. Debugging Computed Values
|
||||
|
||||
You can also debug **computed functions** to see exactly when derived state is recalculated.
|
||||
|
||||
```javascript
|
||||
const $price = $(100);
|
||||
const $tax = $(0.21);
|
||||
const $total = $(() => $price() * (1 + $tax()));
|
||||
|
||||
// Monitor the result of the calculation
|
||||
_debug($total, "Final Invoice Total");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Why use `_debug`?
|
||||
|
||||
1. **Clean Logic:** No need to scatter `console.log` inside your reactive functions.
|
||||
2. **State History:** Instantly see the "Before" and "After" of any user action.
|
||||
3. **No-Noise:** It only logs when a real change occurs, keeping the console clean.
|
||||
4. **Deep Inspection:** The automatic `console.table` makes debugging large API responses much faster.
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
# Data Fetching: `_fetch`
|
||||
|
||||
The **Fetch Plugin** provides a reactive wrapper around the native browser Fetch API. Instead of managing complex `async/await` flows within your UI, `_fetch` returns a "Reactive Tripod" (Data, Loading, and Error) that your components can listen to automatically.
|
||||
|
||||
## 1. Core Concept
|
||||
|
||||
When you call `_fetch`, it returns three signals immediately. Your UI declares how to react to these signals as they change from their initial state to the final response.
|
||||
|
||||
* **`$data`**: Initialized as `null`. Automatically holds the JSON response on success.
|
||||
* **`$loading`**: Initialized as `true`. Flips to `false` once the request settles.
|
||||
* **`$error`**: Initialized as `null`. Holds the error message if the request fails.
|
||||
|
||||
---
|
||||
|
||||
## 2. Installation
|
||||
|
||||
Register the `Fetch` plugin in your `main.js`. By convention, we load it alongside the UI and Router to have the full SigPro ecosystem ready.
|
||||
|
||||
```javascript
|
||||
import { $ } from 'sigpro';
|
||||
import { Fetch } from 'sigpro/plugins';
|
||||
|
||||
$.plugin([Fetch]).then(() => {
|
||||
// Now _fetch() is available globally
|
||||
import('./App.js').then(app => $.mount(app.default));
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 3. Basic Usage
|
||||
|
||||
Use `_fetch` inside your component to get live updates. The UI updates surgically whenever a signal changes.
|
||||
|
||||
```javascript
|
||||
export default () => {
|
||||
const { $data, $loading, $error } = _fetch('https://api.github.com/users/octocat');
|
||||
|
||||
return div({ class: 'p-6 flex flex-col gap-4' }, [
|
||||
h1("Profile Details"),
|
||||
|
||||
// 1. Loading State (using SigPro UI button)
|
||||
() => $loading() && _button({ $loading: true }, "Fetching..."),
|
||||
|
||||
// 2. Error State
|
||||
() => $error() && div({ class: 'alert alert-error' }, $error()),
|
||||
|
||||
// 3. Success State
|
||||
() => $data() && div({ class: 'card bg-base-200 p-4' }, [
|
||||
img({ src: $data().avatar_url, class: 'w-16 rounded-full' }),
|
||||
h2($data().name),
|
||||
p($data().bio)
|
||||
])
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Advanced Configuration
|
||||
|
||||
`_fetch` accepts the same `RequestInit` options as the standard `fetch()` (methods, headers, body, etc.).
|
||||
|
||||
```javascript
|
||||
const { $data, $loading } = _fetch('/api/v1/update', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ status: 'active' })
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Why use `_fetch` instead of native Fetch?
|
||||
|
||||
1. **Declarative UI**: You define the "Loading", "Error", and "Success" templates once, and they swap automatically.
|
||||
2. **No `useEffect` required**: Since SigPro is natively reactive, you don't need lifecycle hooks to trigger re-renders; the signals handle it.
|
||||
3. **Consistency**: It follows the same `_prefix` pattern as the rest of the official plugin ecosystem.
|
||||
4. **Automatic JSON Parsing**: It assumes JSON by default and handles 404/500 errors by populating the `$error` signal.
|
||||
@@ -1,110 +0,0 @@
|
||||
# Navigation Plugin: `Router`
|
||||
|
||||
The SigPro Router handles URL changes via hashes (`#`) and maps them to components. It supports dynamic parameters (like `:id`) and asynchronous loading for heavy pages.
|
||||
|
||||
## 1. Core Features
|
||||
|
||||
* **Hash-based:** Works everywhere without special server configuration.
|
||||
* **Lazy Loading:** Pages are only downloaded when the user visits the route.
|
||||
* **Reactive:** The view updates automatically when the hash changes.
|
||||
* **Dynamic Routes:** Supports paths like `/user/:id`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Installation
|
||||
|
||||
The Router is usually included in the official plugins package.
|
||||
|
||||
::: code-group
|
||||
```bash [NPM]
|
||||
npm install -D tailwindcss @tailwindcss/vite daisyui@next
|
||||
```
|
||||
|
||||
```bash [PNPM]
|
||||
pnpm add -D tailwindcss @tailwindcss/vite daisyui@next
|
||||
```
|
||||
|
||||
```bash [Yarn]
|
||||
yarn add -D tailwindcss @tailwindcss/vite daisyui@next
|
||||
```
|
||||
|
||||
```bash [Bun]
|
||||
bun add -d tailwindcss @tailwindcss/vite daisyui@next
|
||||
```
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. Setting Up Routes
|
||||
|
||||
In your `App.js` (or a dedicated routes file), define your navigation map.
|
||||
|
||||
```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([
|
||||
_navbar({ title: "My App" }),
|
||||
_router(routes) // The router is now a global tag
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Navigation (`_router.go`)
|
||||
|
||||
To move between pages programmatically (e.g., inside an `onclick` event), use the global `_router.go` helper.
|
||||
|
||||
```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.
|
||||
2. **Resolve:** * If it's a standard function, it executes it immediately.
|
||||
* If it's a **Promise** (via `import()`), it shows a loading state and swaps the content once the module arrives.
|
||||
3. **Inject:** It replaces the previous DOM node with the new page content surgically.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 6. Integration with UI Components
|
||||
|
||||
Since you are using the **UI Plugin**, you can easily create active states in your navigation menus by checking the current hash.
|
||||
|
||||
```javascript
|
||||
// Example of a reactive sidebar menu
|
||||
_menu({
|
||||
items: [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
active: () => window.location.hash === '#/',
|
||||
onclick: () => _router.go('/')
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
active: () => window.location.hash === '#/settings',
|
||||
onclick: () => _router.go('/settings')
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
# Persistence Tool: `_storage`
|
||||
|
||||
The Storage plugin synchronizes a signal with a specific key in your browser's `localStorage`. It handles both the **initial hydration** (loading data when the app starts) and **automatic saving** whenever the signal's value changes.
|
||||
|
||||
## 1. Core Concept
|
||||
|
||||
When you "attach" a signal to `_storage`, two things happen:
|
||||
1. **Hydration:** The plugin checks if the key already exists in `localStorage`. If it does, it parses the JSON and updates the signal immediately.
|
||||
2. **Reactive Sync:** It creates a reactive watcher that stringifies and saves the signal's value to the disk every time it is updated.
|
||||
|
||||
---
|
||||
|
||||
## 2. Installation
|
||||
|
||||
Register the `Storage` plugin in your `main.js`. Since this is a logic-only plugin, it doesn't require any CSS or UI dependencies.
|
||||
|
||||
```javascript
|
||||
import { $ } from 'sigpro';
|
||||
import { Storage } from 'sigpro/plugins';
|
||||
|
||||
$.plugin(Storage).then(() => {
|
||||
import('./App.js').then(app => $.mount(app.default));
|
||||
});
|
||||
```
|
||||
|
||||
::: code-group
|
||||
```bash [NPM]
|
||||
npm install sigpro
|
||||
```
|
||||
|
||||
```bash [PNPM]
|
||||
pnpm add sigpro
|
||||
```
|
||||
|
||||
```bash [Yarn]
|
||||
yarn add sigpro
|
||||
```
|
||||
|
||||
```bash [Bun]
|
||||
bun add sigpro
|
||||
```
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. Basic Usage
|
||||
|
||||
You can wrap any signal with `_storage`. It is common practice to do this right after creating the signal.
|
||||
|
||||
```javascript
|
||||
export default () => {
|
||||
// 1. Create a signal with a default value
|
||||
const $theme = $( 'light' );
|
||||
|
||||
// 2. Persist it. If 'user_theme' exists in localStorage,
|
||||
// $theme will be updated to that value instantly.
|
||||
_storage($theme, 'user_theme');
|
||||
|
||||
return div({ class: () => `app-${$theme()}` }, [
|
||||
h1(`Current Theme: ${$theme()}`),
|
||||
button({
|
||||
onclick: () => $theme(t => t === 'light' ? 'dark' : 'light')
|
||||
}, "Toggle Theme")
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Complex Data (Objects & Arrays)
|
||||
|
||||
Since the plugin uses `JSON.parse` and `JSON.stringify` internally, it works perfectly with complex state structures.
|
||||
|
||||
```javascript
|
||||
const $settings = $({
|
||||
notifications: true,
|
||||
fontSize: 16
|
||||
});
|
||||
|
||||
// Automatically saves the whole object whenever any property changes
|
||||
_storage($settings, 'app_settings');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Why use `_storage`?
|
||||
|
||||
1. **Zero Boilerplate:** You don't need to manually write `localStorage.getItem` or `setItem` logic inside your components.
|
||||
2. **Chaining:** Because `_storage` returns the signal, you can persist it inline.
|
||||
3. **Error Resilience:** It includes a built-in `try/catch` block to prevent your app from crashing if the stored JSON is corrupted.
|
||||
4. **Surgical Persistence:** Only the signals you explicitly mark for storage are saved, keeping your `localStorage` clean.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 6. Pro Tip: Combining with Debug
|
||||
|
||||
You can chain plugins to create a fully monitored and persistent state:
|
||||
|
||||
```javascript
|
||||
const $score = _storage($(0), 'high_score');
|
||||
|
||||
// Now it's saved to disk AND logged to console on every change
|
||||
_debug($score, "Game Score");
|
||||
```
|
||||
@@ -1,956 +0,0 @@
|
||||
# SigPro UI Plugin - Complete Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The **SigPro UI** plugin is a comprehensive, reactive component library built on SigPro's atomic reactivity system. It seamlessly integrates **Tailwind CSS v4** for utility-first styling and **daisyUI v5** for semantic, themeable components. Every component is reactive by nature, automatically responding to signal changes without manual DOM updates.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Installation & Setup](#installation--setup)
|
||||
2. [Core Concepts](#core-concepts)
|
||||
3. [Form Components](#form-components)
|
||||
4. [Action Components](#action-components)
|
||||
5. [Layout Components](#layout-components)
|
||||
6. [Navigation Components](#navigation-components)
|
||||
7. [Feedback Components](#feedback-components)
|
||||
8. [Container Components](#container-components)
|
||||
9. [Complete Examples](#complete-examples)
|
||||
10. [Styling Guide](#styling-guide)
|
||||
11. [Best Practices](#best-practices)
|
||||
|
||||
---
|
||||
|
||||
## Installation & Setup
|
||||
|
||||
### Step 1: Install Dependencies
|
||||
|
||||
```bash
|
||||
npm install -D tailwindcss @tailwindcss/vite daisyui@next
|
||||
```
|
||||
|
||||
### Step 2: Configure Tailwind CSS v4
|
||||
|
||||
Create `src/app.css`:
|
||||
|
||||
```css
|
||||
/* src/app.css */
|
||||
@import "tailwindcss";
|
||||
@plugin "daisyui";
|
||||
|
||||
/* Optional: Custom themes */
|
||||
@theme {
|
||||
--color-primary: oklch(0.65 0.2 250);
|
||||
--color-secondary: oklch(0.7 0.15 150);
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
@custom-variant dark (&:where(.dark, [data-theme="dark"], [data-theme="dark"] *)));
|
||||
```
|
||||
|
||||
### Step 3: Initialize in Your Entry Point
|
||||
|
||||
```javascript
|
||||
// main.js
|
||||
import './app.css';
|
||||
import { $ } from 'sigpro';
|
||||
import { UI } from 'sigpro/plugins';
|
||||
|
||||
// Load the UI plugin - makes all _components globally available
|
||||
$.plugin(UI).then(() => {
|
||||
// All UI components are now registered
|
||||
import('./App.js').then(app => $.mount(app.default));
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Reactive Props
|
||||
|
||||
All UI components accept reactive props using the `$` prefix. When you pass a signal, the component automatically updates:
|
||||
|
||||
```javascript
|
||||
const $username = $('John');
|
||||
const $error = $(null);
|
||||
|
||||
// Reactive input with two-way binding
|
||||
_input({
|
||||
$value: $username, // Auto-updates when signal changes
|
||||
$error: $error // Shows error message when signal has value
|
||||
})
|
||||
```
|
||||
|
||||
### The `parseClass` Helper
|
||||
|
||||
All components intelligently merge base classes with user-provided classes, supporting both static strings and reactive functions:
|
||||
|
||||
```javascript
|
||||
// Static class merging
|
||||
_button({ class: 'btn-primary' }, 'Click me')
|
||||
// Result: class="btn btn-primary"
|
||||
|
||||
// Reactive classes
|
||||
const $theme = $('btn-primary');
|
||||
_button({ class: () => $theme() }, 'Dynamic Button')
|
||||
// Updates when $theme changes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Form Components
|
||||
|
||||
### `_input` - Smart Input Field
|
||||
|
||||
A complete input wrapper with label, tooltip, error handling, and two-way binding.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `label` | `string` | Field label text |
|
||||
| `tip` | `string` | Tooltip text shown on hover of a "?" badge |
|
||||
| `$value` | `signal` | Two-way bound value signal |
|
||||
| `$error` | `signal` | Error message signal (shows red border + message) |
|
||||
| `type` | `string` | Input type: 'text', 'email', 'password', etc. |
|
||||
| `placeholder` | `string` | Placeholder text |
|
||||
| `class` | `string\|function` | Additional CSS classes |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```javascript
|
||||
// Basic usage
|
||||
const $email = $('');
|
||||
_input({
|
||||
label: 'Email Address',
|
||||
type: 'email',
|
||||
placeholder: 'user@example.com',
|
||||
$value: $email
|
||||
})
|
||||
|
||||
// With validation
|
||||
const $password = $('');
|
||||
const $passwordError = $(null);
|
||||
|
||||
_input({
|
||||
label: 'Password',
|
||||
type: 'password',
|
||||
$value: $password,
|
||||
$error: $passwordError,
|
||||
oninput: (e) => {
|
||||
if (e.target.value.length < 6) {
|
||||
$passwordError('Password must be at least 6 characters');
|
||||
} else {
|
||||
$passwordError(null);
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `_select` - Dropdown Selector
|
||||
|
||||
Reactive select component with options array.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `label` | `string` | Field label |
|
||||
| `options` | `Array<{value: any, label: string}>` | Select options |
|
||||
| `$value` | `signal` | Two-way bound selected value |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const $role = $('user');
|
||||
const roles = [
|
||||
{ value: 'admin', label: 'Administrator' },
|
||||
{ value: 'user', label: 'Standard User' },
|
||||
{ value: 'guest', label: 'Guest' }
|
||||
];
|
||||
|
||||
_select({
|
||||
label: 'User Role',
|
||||
options: roles,
|
||||
$value: $role
|
||||
})
|
||||
|
||||
// Reactive selection
|
||||
console.log($role()); // 'user'
|
||||
```
|
||||
|
||||
### `_checkbox` - Toggle Checkbox
|
||||
|
||||
Styled checkbox with label support.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `label` | `string` | Checkbox label text |
|
||||
| `$value` | `signal` | Boolean signal for checked state |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const $remember = $(true);
|
||||
|
||||
_checkbox({
|
||||
label: 'Remember me',
|
||||
$value: $remember
|
||||
})
|
||||
```
|
||||
|
||||
### `_radio` - Radio Button Group
|
||||
|
||||
Radio button with group value binding.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `label` | `string` | Radio option label |
|
||||
| `value` | `any` | Value for this radio option |
|
||||
| `$value` | `signal` | Group signal holding selected value |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const $paymentMethod = $('credit');
|
||||
|
||||
['credit', 'paypal', 'crypto'].forEach(method => {
|
||||
_radio({
|
||||
name: 'payment',
|
||||
label: method.toUpperCase(),
|
||||
value: method,
|
||||
$value: $paymentMethod
|
||||
})
|
||||
})
|
||||
|
||||
// Selected: $paymentMethod() === 'credit'
|
||||
```
|
||||
|
||||
### `_range` - Slider Control
|
||||
|
||||
Reactive range slider with optional label.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `label` | `string` | Slider label |
|
||||
| `min` | `number` | Minimum value |
|
||||
| `max` | `number` | Maximum value |
|
||||
| `step` | `number` | Step increment |
|
||||
| `$value` | `signal` | Current value signal |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const $volume = $(50);
|
||||
|
||||
_range({
|
||||
label: 'Volume',
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
$value: $volume
|
||||
})
|
||||
|
||||
// Display current value
|
||||
span(() => `Volume: ${$volume()}%`)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Action Components
|
||||
|
||||
### `_button` - Smart Action Button
|
||||
|
||||
Feature-rich button with loading states, icons, and badges.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `$loading` | `signal` | Shows spinner + disables when true |
|
||||
| `$disabled` | `signal` | Manual disabled state |
|
||||
| `icon` | `string\|HTMLElement` | Icon element or emoji/unicode |
|
||||
| `badge` | `string` | Badge text to display |
|
||||
| `badgeClass` | `string` | Additional badge styling |
|
||||
| `type` | `string` | Button type: 'button', 'submit', etc. |
|
||||
| `onclick` | `function` | Click handler |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```javascript
|
||||
// Basic button
|
||||
_button({ onclick: () => alert('Clicked!') }, 'Click Me')
|
||||
|
||||
// Loading state
|
||||
const $saving = $(false);
|
||||
_button({
|
||||
$loading: $saving,
|
||||
icon: '💾',
|
||||
onclick: async () => {
|
||||
$saving(true);
|
||||
await saveData();
|
||||
$saving(false);
|
||||
}
|
||||
}, 'Save Changes')
|
||||
|
||||
// With badge notification
|
||||
_button({
|
||||
badge: '3',
|
||||
badgeClass: 'badge-secondary',
|
||||
icon: '🔔'
|
||||
}, 'Notifications')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layout Components
|
||||
|
||||
### `_fieldset` - Form Section Group
|
||||
|
||||
Groups related form fields with a legend.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `legend` | `string` | Fieldset title |
|
||||
| `class` | `string\|function` | Additional classes |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
_fieldset({ legend: 'Personal Information' }, [
|
||||
_input({ label: 'First Name', $value: $firstName }),
|
||||
_input({ label: 'Last Name', $value: $lastName }),
|
||||
_input({ label: 'Email', type: 'email', $value: $email })
|
||||
])
|
||||
```
|
||||
|
||||
### `_accordion` - Collapsible Section
|
||||
|
||||
Expandable/collapsible content panel.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `title` | `string` | Accordion header text |
|
||||
| `name` | `string` | Optional group name (radio behavior) |
|
||||
| `open` | `boolean` | Initially open state |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```javascript
|
||||
// Single accordion (checkbox behavior)
|
||||
_accordion({ title: 'Frequently Asked Questions' }, [
|
||||
p('This is the collapsible content...')
|
||||
])
|
||||
|
||||
// Grouped accordions (radio behavior - only one open)
|
||||
_accordion({ title: 'Section 1', name: 'faq' }, [
|
||||
p('Content for section 1')
|
||||
]),
|
||||
_accordion({ title: 'Section 2', name: 'faq' }, [
|
||||
p('Content for section 2')
|
||||
])
|
||||
```
|
||||
|
||||
### `_drawer` - Sidebar Drawer
|
||||
|
||||
Responsive drawer component that can be toggled programmatically.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `id` | `string` | Unique ID for checkbox toggle |
|
||||
| `$open` | `signal` | Boolean signal for drawer state |
|
||||
| `content` | `HTMLElement` | Main content area |
|
||||
| `side` | `HTMLElement` | Sidebar content |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const $drawerOpen = $(false);
|
||||
|
||||
_drawer({
|
||||
id: 'main-drawer',
|
||||
$open: $drawerOpen,
|
||||
content: [
|
||||
_button({ onclick: () => $drawerOpen(true) }, 'Open Menu'),
|
||||
div('Main content goes here')
|
||||
],
|
||||
side: [
|
||||
_menu({ items: [
|
||||
{ label: 'Home', onclick: () => $drawerOpen(false) },
|
||||
{ label: 'Settings', onclick: () => $drawerOpen(false) }
|
||||
]})
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Navigation Components
|
||||
|
||||
### `_navbar` - Application Header
|
||||
|
||||
Responsive navigation bar with built-in styling.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `class` | `string\|function` | Additional classes |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
_navbar([
|
||||
div({ class: 'flex-1' }, [
|
||||
a({ class: 'text-xl font-bold' }, 'MyApp')
|
||||
]),
|
||||
div({ class: 'flex-none' }, [
|
||||
_button({ class: 'btn-ghost btn-sm' }, 'Login'),
|
||||
_button({ class: 'btn-primary btn-sm' }, 'Sign Up')
|
||||
])
|
||||
])
|
||||
```
|
||||
|
||||
### `_menu` - Vertical Navigation
|
||||
|
||||
Sidebar or dropdown menu with active state support.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `items` | `Array<{label: string, icon?: any, active?: boolean\|function, onclick: function}>` | Menu items |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const $currentPage = $('home');
|
||||
|
||||
_menu({ items: [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
icon: '📊',
|
||||
active: () => $currentPage() === 'dashboard',
|
||||
onclick: () => $currentPage('dashboard')
|
||||
},
|
||||
{
|
||||
label: 'Profile',
|
||||
icon: '👤',
|
||||
active: () => $currentPage() === 'profile',
|
||||
onclick: () => $currentPage('profile')
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: '⚙️',
|
||||
active: () => $currentPage() === 'settings',
|
||||
onclick: () => $currentPage('settings')
|
||||
}
|
||||
]})
|
||||
```
|
||||
|
||||
### `_tabs` - Tab Navigation
|
||||
|
||||
Horizontal tabs with lifted styling.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `items` | `Array<{label: string, active: boolean\|function, onclick: function}>` | Tab items |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const $activeTab = $('profile');
|
||||
|
||||
_tabs({ items: [
|
||||
{
|
||||
label: 'Profile',
|
||||
active: () => $activeTab() === 'profile',
|
||||
onclick: () => $activeTab('profile')
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
active: () => $activeTab() === 'settings',
|
||||
onclick: () => $activeTab('settings')
|
||||
}
|
||||
]})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Feedback Components
|
||||
|
||||
### `_badge` - Status Indicator
|
||||
|
||||
Small badge for counts, statuses, or labels.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `class` | `string\|function` | Badge style (badge-primary, badge-success, etc.) |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
_badge({ class: 'badge-success' }, 'Active')
|
||||
_badge({ class: 'badge-error' }, '3 Errors')
|
||||
_badge({ class: 'badge-warning' }, 'Pending')
|
||||
```
|
||||
|
||||
### `_tooltip` - Hover Information
|
||||
|
||||
Wrapper that shows tooltip text on hover.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `tip` | `string` | Tooltip text |
|
||||
| `position` | `string` | Tooltip position (top, bottom, left, right) |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
_tooltip({ tip: 'Click to save changes', class: 'tooltip-primary' }, [
|
||||
_button({}, 'Save')
|
||||
])
|
||||
|
||||
_tooltip({ tip: 'Your email will not be shared', class: 'tooltip-bottom' }, [
|
||||
span('ⓘ')
|
||||
])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Container Components
|
||||
|
||||
### `_modal` - Dialog Window
|
||||
|
||||
Programmatically controlled modal dialog.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `$open` | `signal` | Boolean signal controlling visibility |
|
||||
| `title` | `string` | Modal title |
|
||||
| `class` | `string\|function` | Additional styling |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const $showModal = $(false);
|
||||
|
||||
_modal({
|
||||
$open: $showModal,
|
||||
title: 'Confirm Action'
|
||||
}, [
|
||||
p('Are you sure you want to delete this item?'),
|
||||
div({ class: 'flex gap-2 justify-end mt-4' }, [
|
||||
_button({ onclick: () => $showModal(false) }, 'Cancel'),
|
||||
_button({
|
||||
class: 'btn-error',
|
||||
onclick: () => {
|
||||
deleteItem();
|
||||
$showModal(false);
|
||||
}
|
||||
}, 'Delete')
|
||||
])
|
||||
])
|
||||
|
||||
// Trigger modal
|
||||
_button({ onclick: () => $showModal(true) }, 'Delete Item')
|
||||
```
|
||||
|
||||
### `_dropdown` - Context Menu
|
||||
|
||||
Dropdown menu that appears on click.
|
||||
|
||||
**Properties:**
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `label` | `string` | Dropdown trigger text |
|
||||
| `class` | `string\|function` | Additional classes |
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
_dropdown({ label: 'Options' }, [
|
||||
li([a({ onclick: () => edit() }, 'Edit')]),
|
||||
li([a({ onclick: () => duplicate() }, 'Duplicate')]),
|
||||
li([a({ class: 'text-error', onclick: () => delete() }, 'Delete')])
|
||||
])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Example 1: User Registration Form
|
||||
|
||||
```javascript
|
||||
// Signals
|
||||
const $username = $('');
|
||||
const $email = $('');
|
||||
const $password = $('');
|
||||
const $terms = $(false);
|
||||
const $loading = $(false);
|
||||
|
||||
// Validation signals
|
||||
const $usernameError = $(null);
|
||||
const $emailError = $(null);
|
||||
const $passwordError = $(null);
|
||||
|
||||
// Form submission
|
||||
const handleSubmit = async () => {
|
||||
$loading(true);
|
||||
|
||||
// Validate
|
||||
if ($username().length < 3) $usernameError('Username too short');
|
||||
if (!$email().includes('@')) $emailError('Invalid email');
|
||||
if ($password().length < 6) $passwordError('Password too short');
|
||||
if (!$terms()) alert('Accept terms');
|
||||
|
||||
if (!$usernameError() && !$emailError() && !$passwordError()) {
|
||||
await api.register({
|
||||
username: $username(),
|
||||
email: $email(),
|
||||
password: $password()
|
||||
});
|
||||
}
|
||||
|
||||
$loading(false);
|
||||
};
|
||||
|
||||
// Component
|
||||
div({ class: 'max-w-md mx-auto p-6' }, [
|
||||
_fieldset({ legend: 'Create Account' }, [
|
||||
_input({
|
||||
label: 'Username',
|
||||
$value: $username,
|
||||
$error: $usernameError,
|
||||
placeholder: 'johndoe'
|
||||
}),
|
||||
_input({
|
||||
label: 'Email',
|
||||
type: 'email',
|
||||
$value: $email,
|
||||
$error: $emailError,
|
||||
placeholder: 'john@example.com'
|
||||
}),
|
||||
_input({
|
||||
label: 'Password',
|
||||
type: 'password',
|
||||
$value: $password,
|
||||
$error: $passwordError
|
||||
}),
|
||||
_checkbox({
|
||||
label: 'I agree to the Terms of Service',
|
||||
$value: $terms
|
||||
}),
|
||||
_button({
|
||||
$loading: $loading,
|
||||
class: 'btn-primary w-full mt-4',
|
||||
onclick: handleSubmit
|
||||
}, 'Sign Up')
|
||||
])
|
||||
])
|
||||
```
|
||||
|
||||
### Example 2: Dashboard with Router Integration
|
||||
|
||||
```javascript
|
||||
// App.js
|
||||
export default () => {
|
||||
const $activeRoute = $('dashboard');
|
||||
|
||||
return div({ class: 'min-h-screen' }, [
|
||||
_navbar([
|
||||
div({ class: 'flex-1' }, [
|
||||
a({ class: 'text-xl font-bold' }, 'Dashboard')
|
||||
]),
|
||||
_button({
|
||||
class: 'btn-ghost btn-circle',
|
||||
onclick: () => $.router.go('/settings')
|
||||
}, '⚙️')
|
||||
]),
|
||||
div({ class: 'flex' }, [
|
||||
// Sidebar
|
||||
div({ class: 'w-64 p-4' }, [
|
||||
_menu({ items: [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
icon: '📊',
|
||||
active: () => $activeRoute() === 'dashboard',
|
||||
onclick: () => {
|
||||
$activeRoute('dashboard');
|
||||
$.router.go('/');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Analytics',
|
||||
icon: '📈',
|
||||
active: () => $activeRoute() === 'analytics',
|
||||
onclick: () => {
|
||||
$activeRoute('analytics');
|
||||
$.router.go('/analytics');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
icon: '⚙️',
|
||||
active: () => $activeRoute() === 'settings',
|
||||
onclick: () => {
|
||||
$activeRoute('settings');
|
||||
$.router.go('/settings');
|
||||
}
|
||||
}
|
||||
]})
|
||||
]),
|
||||
|
||||
// Main content
|
||||
div({ class: 'flex-1 p-6' }, [
|
||||
$.router([
|
||||
{ path: '/', component: () => DashboardComponent() },
|
||||
{ path: '/analytics', component: () => AnalyticsComponent() },
|
||||
{ path: '/settings', component: () => SettingsComponent() }
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
||||
### Example 3: E-commerce Product Card
|
||||
|
||||
```javascript
|
||||
const ProductCard = ({ product }) => {
|
||||
const $quantity = $(1);
|
||||
const $inCart = $(false);
|
||||
|
||||
return div({ class: 'card bg-base-100 shadow-xl' }, [
|
||||
figure([img({ src: product.image, alt: product.name })]),
|
||||
div({ class: 'card-body' }, [
|
||||
h2({ class: 'card-title' }, product.name),
|
||||
p(product.description),
|
||||
div({ class: 'flex justify-between items-center mt-4' }, [
|
||||
span({ class: 'text-2xl font-bold' }, `$${product.price}`),
|
||||
div({ class: 'flex gap-2' }, [
|
||||
_range({
|
||||
min: 1,
|
||||
max: 10,
|
||||
$value: $quantity,
|
||||
class: 'w-32'
|
||||
}),
|
||||
_button({
|
||||
$loading: $inCart,
|
||||
class: 'btn-primary',
|
||||
onclick: async () => {
|
||||
$inCart(true);
|
||||
await addToCart(product.id, $quantity());
|
||||
$inCart(false);
|
||||
}
|
||||
}, 'Add to Cart')
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Styling Guide
|
||||
|
||||
### Theme Configuration
|
||||
|
||||
DaisyUI v5 supports extensive theming. Configure in `tailwind.config.js` or CSS:
|
||||
|
||||
```css
|
||||
/* app.css */
|
||||
@import "tailwindcss";
|
||||
@plugin "daisyui";
|
||||
|
||||
/* Custom theme */
|
||||
[data-theme="corporate"] {
|
||||
--color-primary: oklch(0.6 0.2 250);
|
||||
--color-secondary: oklch(0.7 0.15 150);
|
||||
--color-accent: oklch(0.8 0.1 50);
|
||||
--color-neutral: oklch(0.3 0.01 260);
|
||||
--color-base-100: oklch(0.98 0.01 260);
|
||||
--color-info: oklch(0.65 0.2 220);
|
||||
--color-success: oklch(0.65 0.2 140);
|
||||
--color-warning: oklch(0.7 0.2 85);
|
||||
--color-error: oklch(0.65 0.25 25);
|
||||
}
|
||||
```
|
||||
|
||||
### Component Modifiers
|
||||
|
||||
Each component accepts Tailwind/daisyUI classes:
|
||||
|
||||
```javascript
|
||||
// Button variants
|
||||
_button({ class: 'btn-primary' }, 'Primary')
|
||||
_button({ class: 'btn-secondary' }, 'Secondary')
|
||||
_button({ class: 'btn-accent' }, 'Accent')
|
||||
_button({ class: 'btn-outline' }, 'Outline')
|
||||
_button({ class: 'btn-ghost' }, 'Ghost')
|
||||
_button({ class: 'btn-sm' }, 'Small')
|
||||
_button({ class: 'btn-lg' }, 'Large')
|
||||
_button({ class: 'btn-block' }, 'Full Width')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Reactive Performance
|
||||
Always use signals for values that change, not direct variable assignments:
|
||||
|
||||
```javascript
|
||||
// ❌ Bad
|
||||
let name = 'John';
|
||||
_input({ $value: () => name }); // Won't update
|
||||
|
||||
// ✅ Good
|
||||
const $name = $('John');
|
||||
_input({ $value: $name });
|
||||
```
|
||||
|
||||
### 2. Error Handling
|
||||
Use `$error` signals with validation:
|
||||
|
||||
```javascript
|
||||
const $error = $(null);
|
||||
|
||||
_input({
|
||||
$error: $error,
|
||||
onchange: (e) => {
|
||||
if (!validate(e.target.value)) {
|
||||
$error('Invalid input');
|
||||
} else {
|
||||
$error(null);
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Modal Management
|
||||
Keep modals conditionally rendered based on `$open`:
|
||||
|
||||
```javascript
|
||||
// Modal only exists in DOM when open
|
||||
_modal({ $open: $showModal }, content)
|
||||
```
|
||||
|
||||
### 4. Form Submissions
|
||||
Combine loading states with error handling:
|
||||
|
||||
```javascript
|
||||
const $loading = $(false);
|
||||
const $error = $(null);
|
||||
|
||||
_button({
|
||||
$loading: $loading,
|
||||
onclick: async () => {
|
||||
$loading(true);
|
||||
try {
|
||||
await submit();
|
||||
$error(null);
|
||||
} catch (err) {
|
||||
$error(err.message);
|
||||
}
|
||||
$loading(false);
|
||||
}
|
||||
}, 'Submit')
|
||||
```
|
||||
|
||||
### 5. Component Composition
|
||||
Build reusable components by combining UI primitives:
|
||||
|
||||
```javascript
|
||||
const FormField = ({ label, $value, type = 'text' }) => {
|
||||
return _fieldset({ legend: label }, [
|
||||
_input({ type, $value, class: 'w-full' })
|
||||
]);
|
||||
};
|
||||
|
||||
// Usage
|
||||
FormField({ label: 'Email', $value: $email });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
All components are globally available after plugin initialization:
|
||||
|
||||
| Component | Function Signature |
|
||||
|-----------|-------------------|
|
||||
| `_button` | `(props, children) => HTMLElement` |
|
||||
| `_input` | `(props) => HTMLElement` |
|
||||
| `_select` | `(props) => HTMLElement` |
|
||||
| `_checkbox` | `(props) => HTMLElement` |
|
||||
| `_radio` | `(props) => HTMLElement` |
|
||||
| `_range` | `(props) => HTMLElement` |
|
||||
| `_fieldset` | `(props, children) => HTMLElement` |
|
||||
| `_accordion` | `(props, children) => HTMLElement` |
|
||||
| `_modal` | `(props, children) => HTMLElement` |
|
||||
| `_drawer` | `(props) => HTMLElement` |
|
||||
| `_navbar` | `(props, children) => HTMLElement` |
|
||||
| `_menu` | `(props) => HTMLElement` |
|
||||
| `_tabs` | `(props) => HTMLElement` |
|
||||
| `_badge` | `(props, children) => HTMLElement` |
|
||||
| `_tooltip` | `(props, children) => HTMLElement` |
|
||||
| `_dropdown` | `(props, children) => HTMLElement` |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Styles Not Applying
|
||||
Ensure Tailwind CSS is properly configured and imported before your app code:
|
||||
|
||||
```javascript
|
||||
import './app.css'; // Must be first
|
||||
import { $ } from 'sigpro';
|
||||
```
|
||||
|
||||
### Components Not Found
|
||||
Verify plugin is loaded before using components:
|
||||
|
||||
```javascript
|
||||
$.plugin(UI).then(() => {
|
||||
// Components are ready
|
||||
$.mount(App);
|
||||
});
|
||||
```
|
||||
|
||||
### Reactive Updates Not Working
|
||||
Ensure you're using signals, not primitive values:
|
||||
|
||||
```javascript
|
||||
// Wrong
|
||||
let count = 0;
|
||||
_button({}, () => count)
|
||||
|
||||
// Correct
|
||||
const $count = $(0);
|
||||
_button({}, () => $count())
|
||||
```
|
||||
@@ -95,29 +95,3 @@ $.plugin(ConfigLoader).then(() => {
|
||||
| **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. |
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 5. Installation
|
||||
|
||||
Custom plugins don't require extra packages, but ensure your build tool (Vite/Bun) is configured to handle the module imports.
|
||||
|
||||
::: code-group
|
||||
```bash [NPM]
|
||||
npm install sigpro
|
||||
```
|
||||
|
||||
```bash [PNPM]
|
||||
pnpm add sigpro
|
||||
```
|
||||
|
||||
```bash [Yarn]
|
||||
yarn add sigpro
|
||||
```
|
||||
|
||||
```bash [Bun]
|
||||
bun add sigpro
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
@@ -33,11 +33,11 @@ This is the standard way to build apps. It's clean, readable, and supports stand
|
||||
```javascript
|
||||
// main.js
|
||||
import { $ } from 'sigpro';
|
||||
import { UI } from 'sigpro/plugins';
|
||||
import { Fetch } from 'sigpro/plugins';
|
||||
import App from './App.js'; // Static import works perfectly!
|
||||
|
||||
// 1. Register plugins
|
||||
$.plugin(UI);
|
||||
$.plugin(Fetch);
|
||||
|
||||
// 2. Mount your app directly
|
||||
$.mount(App, '#app');
|
||||
|
||||
@@ -114,24 +114,3 @@ export const routes = [
|
||||
|
||||
Because it uses dynamic `import()`, Vite automatically performs **Code Splitting**, meaning each page is its own small JS file that only loads when the user navigates to it.
|
||||
|
||||
---
|
||||
|
||||
## 6. Installation
|
||||
|
||||
::: code-group
|
||||
```bash [NPM]
|
||||
npm install sigpro
|
||||
```
|
||||
|
||||
```bash [PNPM]
|
||||
pnpm add sigpro
|
||||
```
|
||||
|
||||
```bash [Yarn]
|
||||
yarn add sigpro
|
||||
```
|
||||
|
||||
```bash [Bun]
|
||||
bun add sigpro
|
||||
```
|
||||
:::
|
||||
|
||||
Reference in New Issue
Block a user