From 9e5b520e918722c7ab5e3aec8ef85b0087bb9884 Mon Sep 17 00:00:00 2001 From: natxocc Date: Sun, 22 Mar 2026 03:04:31 +0100 Subject: [PATCH] Upload docs --- Readme.md | 156 +-- docs/404.html | 3 +- docs/api/$.html | 36 +- docs/api/html.html | 7 +- docs/api/mount.html | 32 +- docs/api/quick.html | 5 +- docs/api/router.html | 62 ++ docs/api/tags.html | 9 +- docs/assets/api__.md.BVVMY-2O.js | 27 - docs/assets/api__.md.CJ6A9esy.js | 22 + ...Y-2O.lean.js => api__.md.CJ6A9esy.lean.js} | 2 +- docs/assets/api_mount.md.DK1GUmQ8.js | 29 + docs/assets/api_mount.md.DK1GUmQ8.lean.js | 1 + docs/assets/api_mount.md.eGRwkZvh.js | 28 - docs/assets/api_mount.md.eGRwkZvh.lean.js | 1 - docs/assets/api_router.md.BV6vPWg-.js | 37 + docs/assets/api_router.md.BV6vPWg-.lean.js | 1 + ...md.Ch1qUgrw.js => api_tags.md.BAYRMzRh.js} | 2 +- ...w.lean.js => api_tags.md.BAYRMzRh.lean.js} | 0 ...s => guide_getting-started.md.BJ3Lg3i9.js} | 4 +- ...guide_getting-started.md.BJ3Lg3i9.lean.js} | 2 +- ...ex.md.BX4IouPL.js => index.md.DTGopCPx.js} | 2 +- ...ouPL.lean.js => index.md.DTGopCPx.lean.js} | 0 ...8.js => plugins_core.debug.md.BKuhgPoj.js} | 4 +- ...=> plugins_core.debug.md.BKuhgPoj.lean.js} | 2 +- ....js => plugins_core.router.md.Bg_zmVp3.js} | 2 +- ...> plugins_core.router.md.Bg_zmVp3.lean.js} | 0 ...js => plugins_core.storage.md.C6UMGg_o.js} | 6 +- ... plugins_core.storage.md.C6UMGg_o.lean.js} | 2 +- docs/assets/plugins_core.ui.md.C434Uobv.js | 452 ++++++++ .../plugins_core.ui.md.C434Uobv.lean.js | 1 + docs/assets/plugins_core.ui.md.DAV-GYyQ.js | 30 - .../plugins_core.ui.md.DAV-GYyQ.lean.js | 1 - ...aLrm0.js => plugins_custom.md.D6eJnwEa.js} | 2 +- ....js => plugins_custom.md.D6eJnwEa.lean.js} | 0 docs/assets/plugins_quick.md.BBIL_nLZ.js | 27 + ...n.js => plugins_quick.md.BBIL_nLZ.lean.js} | 2 +- docs/assets/plugins_quick.md.ODjl7edh.js | 36 - docs/assets/ui_button.md.B087poC6.js | 34 + docs/assets/ui_button.md.B087poC6.lean.js | 1 + docs/assets/ui_form.md.CZVTxszG.js | 28 + docs/assets/ui_form.md.CZVTxszG.lean.js | 1 + docs/assets/ui_input.md.mJqBxkG3.js | 60 ++ docs/assets/ui_input.md.mJqBxkG3.lean.js | 1 + docs/assets/ui_installation.md.C7ubLVYa.js | 10 + .../ui_installation.md.C7ubLVYa.lean.js | 1 + docs/assets/ui_introduction.md.CpBz5t8n.js | 1 + .../ui_introduction.md.CpBz5t8n.lean.js | 1 + docs/assets/ui_layout.md.DLaYXca7.js | 29 + docs/assets/ui_layout.md.DLaYXca7.lean.js | 1 + docs/assets/ui_modal.md.Hjip_i1A.js | 29 + docs/assets/ui_modal.md.Hjip_i1A.lean.js | 1 + docs/assets/ui_navigation.md.CK3sbH-I.js | 51 + docs/assets/ui_navigation.md.CK3sbH-I.lean.js | 1 + docs/assets/vite_plugin.md.BxGCvKkk.js | 49 + ...ean.js => vite_plugin.md.BxGCvKkk.lean.js} | 2 +- docs/assets/vite_plugin.md.CtukKq_1.js | 50 - docs/guide/getting-started.html | 7 +- docs/guide/why.html | 5 +- docs/hashmap.json | 2 +- docs/index.html | 9 +- docs/plugins/core.debug.html | 11 +- docs/plugins/core.fetch.html | 7 +- docs/plugins/core.router.html | 9 +- docs/plugins/core.storage.html | 11 +- docs/plugins/core.ui.html | 469 ++++++++- docs/plugins/custom.html | 9 +- docs/plugins/quick.html | 38 +- docs/sigpro.js | 223 ++++ docs/ui/button.html | 59 ++ docs/ui/form.html | 53 + docs/ui/input.html | 85 ++ docs/ui/installation.html | 35 + docs/ui/introduction.html | 26 + docs/ui/layout.html | 54 + docs/ui/modal.html | 54 + docs/ui/navigation.html | 76 ++ docs/vite/plugin.html | 54 +- plugins/ui.js | 2 +- sigpro/sigpro.js | 109 +- src/docs/.vitepress/cache/deps/_metadata.json | 6 +- src/docs/.vitepress/config.js | 27 +- src/docs/api/$.md | 85 +- src/docs/api/mount.md | 53 +- src/docs/api/router.md | 106 ++ src/docs/guide/getting-started.md | 8 +- src/docs/plugins/core.ui.md | 975 ++++++++++++++++-- src/docs/plugins/quick.md | 83 +- src/docs/public/sigpro.js | 223 ++++ src/docs/ui/button.md | 114 ++ src/docs/ui/form.md | 112 ++ src/docs/ui/input.md | 137 +++ src/docs/ui/installation.md | 53 + src/docs/ui/introduction.md | 33 + src/docs/ui/layout.md | 117 +++ src/docs/ui/modal.md | 90 ++ src/docs/ui/navigation.md | 124 +++ src/docs/vite/plugin.md | 85 +- 98 files changed, 4540 insertions(+), 684 deletions(-) create mode 100644 docs/api/router.html delete mode 100644 docs/assets/api__.md.BVVMY-2O.js create mode 100644 docs/assets/api__.md.CJ6A9esy.js rename docs/assets/{api__.md.BVVMY-2O.lean.js => api__.md.CJ6A9esy.lean.js} (50%) create mode 100644 docs/assets/api_mount.md.DK1GUmQ8.js create mode 100644 docs/assets/api_mount.md.DK1GUmQ8.lean.js delete mode 100644 docs/assets/api_mount.md.eGRwkZvh.js delete mode 100644 docs/assets/api_mount.md.eGRwkZvh.lean.js create mode 100644 docs/assets/api_router.md.BV6vPWg-.js create mode 100644 docs/assets/api_router.md.BV6vPWg-.lean.js rename docs/assets/{api_tags.md.Ch1qUgrw.js => api_tags.md.BAYRMzRh.js} (98%) rename docs/assets/{api_tags.md.Ch1qUgrw.lean.js => api_tags.md.BAYRMzRh.lean.js} (100%) rename docs/assets/{guide_getting-started.md.RUz_X0AL.js => guide_getting-started.md.BJ3Lg3i9.js} (92%) rename docs/assets/{guide_getting-started.md.RUz_X0AL.lean.js => guide_getting-started.md.BJ3Lg3i9.lean.js} (59%) rename docs/assets/{index.md.BX4IouPL.js => index.md.DTGopCPx.js} (96%) rename docs/assets/{index.md.BX4IouPL.lean.js => index.md.DTGopCPx.lean.js} (100%) rename docs/assets/{plugins_core.debug.md.BRT_r6k8.js => plugins_core.debug.md.BKuhgPoj.js} (96%) rename docs/assets/{plugins_core.debug.md.BRT_r6k8.lean.js => plugins_core.debug.md.BKuhgPoj.lean.js} (70%) rename docs/assets/{plugins_core.router.md.CWklH7Gb.js => plugins_core.router.md.Bg_zmVp3.js} (97%) rename docs/assets/{plugins_core.router.md.CWklH7Gb.lean.js => plugins_core.router.md.Bg_zmVp3.lean.js} (100%) rename docs/assets/{plugins_core.storage.md.lAkbO1ib.js => plugins_core.storage.md.C6UMGg_o.js} (95%) rename docs/assets/{plugins_core.storage.md.lAkbO1ib.lean.js => plugins_core.storage.md.C6UMGg_o.lean.js} (56%) create mode 100644 docs/assets/plugins_core.ui.md.C434Uobv.js create mode 100644 docs/assets/plugins_core.ui.md.C434Uobv.lean.js delete mode 100644 docs/assets/plugins_core.ui.md.DAV-GYyQ.js delete mode 100644 docs/assets/plugins_core.ui.md.DAV-GYyQ.lean.js rename docs/assets/{plugins_custom.md.DZNaLrm0.js => plugins_custom.md.D6eJnwEa.js} (97%) rename docs/assets/{plugins_custom.md.DZNaLrm0.lean.js => plugins_custom.md.D6eJnwEa.lean.js} (100%) create mode 100644 docs/assets/plugins_quick.md.BBIL_nLZ.js rename docs/assets/{plugins_quick.md.ODjl7edh.lean.js => plugins_quick.md.BBIL_nLZ.lean.js} (61%) delete mode 100644 docs/assets/plugins_quick.md.ODjl7edh.js create mode 100644 docs/assets/ui_button.md.B087poC6.js create mode 100644 docs/assets/ui_button.md.B087poC6.lean.js create mode 100644 docs/assets/ui_form.md.CZVTxszG.js create mode 100644 docs/assets/ui_form.md.CZVTxszG.lean.js create mode 100644 docs/assets/ui_input.md.mJqBxkG3.js create mode 100644 docs/assets/ui_input.md.mJqBxkG3.lean.js create mode 100644 docs/assets/ui_installation.md.C7ubLVYa.js create mode 100644 docs/assets/ui_installation.md.C7ubLVYa.lean.js create mode 100644 docs/assets/ui_introduction.md.CpBz5t8n.js create mode 100644 docs/assets/ui_introduction.md.CpBz5t8n.lean.js create mode 100644 docs/assets/ui_layout.md.DLaYXca7.js create mode 100644 docs/assets/ui_layout.md.DLaYXca7.lean.js create mode 100644 docs/assets/ui_modal.md.Hjip_i1A.js create mode 100644 docs/assets/ui_modal.md.Hjip_i1A.lean.js create mode 100644 docs/assets/ui_navigation.md.CK3sbH-I.js create mode 100644 docs/assets/ui_navigation.md.CK3sbH-I.lean.js create mode 100644 docs/assets/vite_plugin.md.BxGCvKkk.js rename docs/assets/{vite_plugin.md.CtukKq_1.lean.js => vite_plugin.md.BxGCvKkk.lean.js} (61%) delete mode 100644 docs/assets/vite_plugin.md.CtukKq_1.js create mode 100644 docs/sigpro.js create mode 100644 docs/ui/button.html create mode 100644 docs/ui/form.html create mode 100644 docs/ui/input.html create mode 100644 docs/ui/installation.html create mode 100644 docs/ui/introduction.html create mode 100644 docs/ui/layout.html create mode 100644 docs/ui/modal.html create mode 100644 docs/ui/navigation.html create mode 100644 src/docs/api/router.md create mode 100644 src/docs/public/sigpro.js create mode 100644 src/docs/ui/button.md create mode 100644 src/docs/ui/form.md create mode 100644 src/docs/ui/input.md create mode 100644 src/docs/ui/installation.md create mode 100644 src/docs/ui/introduction.md create mode 100644 src/docs/ui/layout.md create mode 100644 src/docs/ui/modal.md create mode 100644 src/docs/ui/navigation.md diff --git a/Readme.md b/Readme.md index 704dc25..ed46c02 100644 --- a/Readme.md +++ b/Readme.md @@ -1,47 +1,38 @@ + # SigPro ⚑ -**The Ultra-Lightweight, Reactive Plugin-Based Framework.** +**The Ultra-Lightweight, Atomic Reactive Engine.** -SigPro 2 is a modern, minimalist JavaScript framework designed for developers who want the power of reactivity without the overhead of heavy runtimes. It weighs less than **2KB**, uses a signal-based architecture, and is fully extensible through a modular plugin system. +SigPro is a modern, minimalist JavaScript engine designed for developers who want surgical reactivity without the overhead. It weighs less than **2KB**, uses a high-performance signal-based architecture, and features a built-in router and native state persistence. -[![npm version](https://img.shields.io/npm/v/sigpro.svg)](https://www.npmjs.com/package/sigpro) -[![bundle size](https://img.shields.io/bundlephobia/minzip/sigpro)](https://bundlephobia.com/package/sigpro) -[![license](https://img.shields.io/npm/l/sigpro)](https://github.com/natxocc/sigpro/blob/main/LICENSE) --- -## πŸš€ Key Features +## πŸš€ Why SigPro? -* **Nano-Sized:** Optimized for speed and minimal bundle impact. -* **Signal-Based Reactivity:** Surgical DOM updatesβ€”only what changes is re-rendered. -* **Plugin Ecosystem:** Modular by design. Only load what you need (Router, Fetch, Storage, etc.). -* **File-Based Routing:** Automated route generation via Vite. -* **Zero Boilerplate:** No complex build steps or proprietary syntax. Just JavaScript. +Unlike traditional frameworks that use a **Virtual DOM** (React) or complex **Compilers** (Svelte), SigPro uses **Atomic Reactivity**. + +* **Surgical Updates:** When a Signal changes, SigPro knows exactly which text node or attribute needs to update. It doesn't "diff" trees; it directly touches the DOM. +* **Zero Dependencies:** No extra libraries, no heavy runtimes. Just pure, optimized JavaScript. +* **Synchronous by Design:** Eliminates the "async-bottleneck". Your app environment is ready the moment you import it. --- ## πŸ“¦ Installation -Install the core engine and the essential plugins. - -::: code-group -```bash [NPM] +```bash npm install sigpro ``` -```bash [Bun] -bun add sigpro -``` -::: - --- ## πŸ› οΈ Quick Start -### 1. Configure Vite -Add the automatic router to your `vite.config.js`. +### 1. Automatic File-Based Routing (Vite) +SigPro leverages Vite to turn your folder structure into your app's navigation. ```javascript +// vite.config.js import { defineConfig } from 'vite'; import { sigproRouter } from 'sigpro/vite'; @@ -50,93 +41,108 @@ export default defineConfig({ }); ``` -### 2. Initialize the App -Register plugins and mount your application shell. +### 2. The Entry Point +Forget about complex bootstrap sequences. In SigPro, you just mount and go. ```javascript // src/main.js import { $ } from 'sigpro'; -import { Router, Fetch, UI } from 'sigpro/plugins'; +import App from './App.js'; -$.plugin([Router, Fetch, UI]).then(() => { - import('./App.js').then(app => $.mount(app.default, '#app')); -}); +$.mount(App, '#app'); // Your reactive journey starts here. ``` --- -## 🧩 Official Plugins +## πŸ’Ž Core Pillars -SigPro is powered by a "Pay-only-for-what-you-use" architecture. +### 🚦 Built-in Router (`$.router`) +The router is now part of the core. It supports dynamic parameters and **Automatic Code Splitting**. Each page in your `src/pages` folder is only loaded when visited. -### 🚦 Router (`_router`) -Automated file-based routing. Just create a file in `src/pages/` and it becomes a route. ```javascript import { routes } from 'virtual:sigpro-routes'; -const App = () => main(_router(routes)); + +export default () => div({ class: 'app-container' }, [ + header([ + h1("SigPro App"), + nav([ + button({ onclick: () => $.router.go('/') }, "Home"), + button({ onclick: () => $.router.go('/user/42') }, "Profile") + ]) + ]), + // $.router acts as a reactive "portal" for your pages + main($.router(routes)) +]); ``` -### πŸ“‘ Fetch (`_fetch`) -Reactive data fetching with built-in loading and error states. +### πŸ’Ύ Native State Persistence +Stop writing boilerplate for `localStorage`. SigPro handles it at the engine level. If you provide a second argument to a Signal, it becomes persistent. + ```javascript -const { $data, $loading } = _fetch('https://api.example.com/data'); +// This value survives page refreshes automatically +const $theme = $('light', 'app-settings-theme'); + +$theme('dark'); // Saved to localStorage instantly and reactively. ``` -### πŸ’Ύ Storage (`_storage`) -Automatic synchronization between Signals and `localStorage`. -```javascript -const $theme = _storage($('light'), 'app_theme'); -``` -### 🐞 Debug (`_debug`) -Beautifully formatted console logs for tracking state changes in real-time. -```javascript -_debug($mySignal, "User Profile"); -``` --- -## πŸ“‚ Project Structure +## 🎨 Component Anatomy -A typical SigPro project follows this clean convention: - -```text -my-app/ -β”œβ”€β”€ src/ -β”‚ β”œβ”€β”€ pages/ # Automatic Routes -β”‚ β”‚ β”œβ”€β”€ index.js # -> #/ -β”‚ β”‚ └── about.js # -> #/about -β”‚ β”œβ”€β”€ plugins/ # Custom Plugins -β”‚ β”œβ”€β”€ App.js # Main Layout -β”‚ └── main.js # Entry Point -β”œβ”€β”€ vite.config.js -└── package.json -``` - ---- - -## 🎨 Example Component - -SigPro uses standard JavaScript functions and a clean, declarative syntax. +A SigPro component is just a function. No `this`, no complex hooks, just clean logic. ```javascript export default () => { - const $count = $(0); // Create a Signal + // 1. State + const $count = $(0); - return div({ class: 'p-8 text-center' }, [ - h1("Hello SigPro!"), - p(`Current count is: ${$count()}`), + // 2. Derived Logic (Computed) + const $isEven = $(() => $count() % 2 === 0); + + // 3. UI Template (Declarative) + return div({ class: 'counter-card' }, [ + h2("Counter Example"), + p(() => `Current value: ${$count()} (${$isEven() ? 'Even' : 'Odd'})`), - _button({ + button({ onclick: () => $count(c => c + 1), - class: 'btn-primary' - }, "Increment") + class: 'btn-add' + }, "Add +1") ]); }; ``` --- +## πŸ“‚ Project Structure + +```text +my-app/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ pages/ # Files here become routes (e.g., about.js -> #/about) +β”‚ β”œβ”€β”€ components/ # Your reusable UI atoms +β”‚ β”œβ”€β”€ App.js # The "Shell" (Navbar, Sidebar, Footer) +β”‚ └── main.js # The Glue (Imports $ and mounts App) +β”œβ”€β”€ vite.config.js +└── package.json +``` + +--- + +## ⚑ Performance Summary + +| Feature | SigPro | Other Frameworks | +| :--- | :--- | :--- | +| **Bundle Size** | **~2KB** | 30KB - 100KB+ | +| **Reactivity** | Atomic Signals | Virtual DOM Diffing | +| **Routing** | Built-in / File-based | External Plugin Required | +| **Persistence** | Native | Manual / Plugin Required | + +--- + ## πŸ“„ License -MIT Β© 2026 SigPro Team. \ No newline at end of file +MIT Β© 2026 SigPro Team. Designed with ❀️ for speed. + diff --git a/docs/404.html b/docs/404.html index d6677be..53d9921 100644 --- a/docs/404.html +++ b/docs/404.html @@ -11,12 +11,13 @@ +
- + \ No newline at end of file diff --git a/docs/api/$.html b/docs/api/$.html index 2e0f27b..6816a9b 100644 --- a/docs/api/$.html +++ b/docs/api/$.html @@ -13,39 +13,35 @@ - + + -
Skip to content

The Reactive Core: $( ) ​

The $ function is the heart of SigPro. It is a Unified Reactive Constructor that handles state, derivations, and side effects through a single, consistent interface.

1. The Constructor: $( input ) ​

Depending on what you pass into $( ), SigPro creates a different type of reactive primitive:

Input TypeResultInternal Behavior
Value (String, Number, Object...)SignalCreates a piece of mutable state.
FunctionComputed / EffectCreates a derived value that tracks dependencies.

2. Signal (State) ​

A Signal is a "box" that holds a value. It provides a getter/setter function to interact with that value.

  • When to use: For data that changes over time (counters, user input, toggle states, API data).
  • Syntax: const $state = $(initialValue);

Example: ​

javascript
const $name = $("Alice");
+    
Skip to content

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:

ArgumentTypeRequiredDescription
inputValue / FunctionYesInitial state or reactive logic.
keystringNoIf provided, the signal persists in localStorage.

2. Signal (State & Persistence) ​

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: ​

javascript
const $user = $("Guest", "session-user"); // Automatically saved/loaded
 
-// Read the value (Getter)
-console.log($name()); // "Alice"
+// Read (Getter)
+console.log($user()); 
 
-// Update the value (Setter)
-$name("Bob"); 
+// Update (Setter + Auto-save to Disk)
+$user("Alice"); 
 
-// Update based on previous value
-$name(current => current + " Smith");

3. Computed (Derived State) ​

When you pass a function to $( ) that returns a value, SigPro creates a Computed Signal. It automatically tracks which signals are used inside it and re-runs only when they change.

  • When to use: For values that depend on other signals (totals, filtered lists, formatted strings).
  • Syntax: const $derived = $(() => logic);

Example: ​

javascript
const $price = $(100);
+// Functional Update
+$user(prev => prev.toUpperCase());

3. Computed (Derived State) ​

When you pass a function that returns a value, SigPro creates a Computed Signal. It tracks dependencies and recalculates only when necessary.

  • Syntax: const $derived = $(() => logic);

Example: ​

javascript
const $price = $(100);
 const $qty = $(2);
 
-// Automatically tracks $price and $qty
+// Auto-tracks $price and $qty
 const $total = $(() => $price() * $qty());
 
-console.log($total()); // 200
+$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");
 
-$qty(3); // $total updates to 300 automatically

4. Effects (Side Effects) ​

An Effect is a function passed to $( ) that does not return a value (or returns undefined). SigPro treats this as a subscription that performs an action whenever its dependencies change.

  • When to use: For DOM manipulations, logging, or syncing with external APIs (LocalStorage, Fetch).
  • Syntax: $(() => { action });

Example: ​

javascript
const $theme = $("light");
-
-// This effect runs every time $theme changes
+// Runs every time $status changes
 $(() => {
-  document.body.className = $theme();
-  console.log("Theme updated to:", $theme());
-});
-
-$theme("dark"); // Logs: Theme updated to: dark

5. Summary Table: Usage Guide ​

PrimitiveLogic TypeReturns Value?Typical Use Case
SignalStaticYes (Mutable)const $user = $("Guest")
ComputedRead-onlyYes (Automatic)const $isLoggedIn = $(() => $user() !== "Guest")
EffectImperativeNo$(() => localStorage.setItem('user', $user()))

πŸ’‘ Pro Tip: Naming Convention ​

In SigPro, we use the $ prefix (e.g., $count) for variables that hold a reactive function. This makes it easy to distinguish between a standard variable and a reactive one at a glance:

javascript
let count = 0;   // Static
-const $count = $(0); // Reactive (Function)
- + console.log("System status is now:", $status()); +});

5. Summary Table: Usage Guide ​

PrimitiveLogic TypePersistence?Typical Use Case
SignalMutable StateYes (Optional)$(0, 'counter')
ComputedDerived / Read-onlyNo$(() => $a() + $b())
EffectImperative ActionNo$(() => 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:

javascript
let count = 0;       // Static
+const $count = $(0); // Reactive Signal
+ \ No newline at end of file diff --git a/docs/api/html.html b/docs/api/html.html index 897eb7d..cf8d50c 100644 --- a/docs/api/html.html +++ b/docs/api/html.html @@ -14,11 +14,12 @@ + -
Skip to content

Rendering Engine: $.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.

1. Syntax: $.html(tag, [props], [content]) ​

ParameterTypeRequiredDescription
tagstringYesAny valid HTML5 tag (e.g., 'div', 'button', 'input').
propsObjectNoAttributes, event listeners, and reactive bindings.
contentanyNoText, Nodes, Arrays, or Reactive Functions.

Example: ​

javascript
const myButton = $.html('button', { class: 'btn-primary' }, 'Click me');

2. Global Tag Helpers ​

To avoid repetitive $.html calls, SigPro automatically exposes common tags to the global window object. This allows for a clean, declarative syntax.

javascript
// Instead of $.html('div', ...), just use:
+    
Skip to content

Rendering Engine: $.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.

1. Syntax: $.html(tag, [props], [content]) ​

ParameterTypeRequiredDescription
tagstringYesAny valid HTML5 tag (e.g., 'div', 'button', 'input').
propsObjectNoAttributes, event listeners, and reactive bindings.
contentanyNoText, Nodes, Arrays, or Reactive Functions.

Example: ​

javascript
const myButton = $.html('button', { class: 'btn-primary' }, 'Click me');

2. Global Tag Helpers ​

To avoid repetitive $.html calls, SigPro automatically exposes common tags to the global window object. This allows for a clean, declarative syntax.

javascript
// Instead of $.html('div', ...), just use:
 div({ id: 'wrapper' }, [
   h1("Welcome"),
   p("This is SigPro.")
@@ -39,8 +40,8 @@
   return $count() > 10 
     ? h1("High Score!") 
     : p("Keep going...");
-});

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.

6. Summary: Content Types ​

InputBehavior
String / NumberAppended as a TextNode.
HTMLElementAppended directly to the parent.
ArrayEach item is processed and appended in order.
Function () => ...Creates a live reactive zone that updates automatically.
- +});

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.

6. Summary: Content Types ​

InputBehavior
String / NumberAppended as a TextNode.
HTMLElementAppended directly to the parent.
ArrayEach item is processed and appended in order.
Function () => ...Creates a live reactive zone that updates automatically.
+ \ No newline at end of file diff --git a/docs/api/mount.html b/docs/api/mount.html index fd2fd28..6e520a7 100644 --- a/docs/api/mount.html +++ b/docs/api/mount.html @@ -3,7 +3,7 @@ - Application Mounter: $.mount | SigPro + Application Mounter: $.router.mount (Core) | SigPro @@ -13,22 +13,24 @@ - + + -
Skip to content

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.

1. Syntax: $.mount(node, [target]) ​

ParameterTypeDefaultDescription
nodeHTMLElement or FunctionRequiredThe component or element to render.
targetstring or HTMLElementdocument.bodyWhere to mount the app (CSS selector or Element).

2. Usage Scenarios ​

A. The "Clean Slate" (Main Entry) ​

In a modern app (like our main.js example), you usually want to control the entire page. By default, $.mount clears the target's existing HTML before mounting.

javascript
// src/main.js
-import { $ } from 'SigPro';
+    
Skip to content

Application Mounter: $.router.mount (Core) ​

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. Syntax: $.mount(node, [target]) ​

ParameterTypeDefaultDescription
nodeHTMLElement or FunctionRequiredThe component or element to render.
targetstring or HTMLElementdocument.bodyWhere to mount the app (CSS selector or Element).

2. 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.

javascript
// src/main.js
+import { $ } from 'sigpro';
 import App from './App.js';
 
-$.mount(App); // Mounts to <body> by default

B. Targeting a Specific Container ​

If you have an existing HTML structure and only want SigPro to manage a specific part (like a #root div), pass a CSS selector or a reference.

html
<div id="sidebar"></div>
-<div id="app-root"></div>
javascript
// Local mount to a specific ID
+// SigPro: No .then() needed, global tags are ready immediately
+$.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.

html
<div id="sidebar"></div>
+<div id="app-root"></div>
javascript
// Mount to a specific ID
 $.mount(MyComponent, '#app-root');
 
 // Or using a direct DOM reference
 const sidebar = document.getElementById('sidebar');
-$.mount(SidebarComponent, sidebar);

3. Mounting with Pure HTML ​

One of SigPro's strengths is that it works perfectly alongside "Old School" HTML. You can create a reactive "island" inside a static page.

javascript
// A small reactive widget in a static .js file
+$.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.

javascript
// A small reactive widget
 const CounterWidget = () => {
   const $c = $(0);
   return button({ onclick: () => $c(v => v + 1) }, [
@@ -36,17 +38,17 @@
   ]);
 };
 
-// Mount it into an existing div in your HTML
-$.mount(CounterWidget, '#counter-container');

4. How it Works (The "Wipe" Logic) ​

When $.mount is called, it performs two critical steps:

  1. Clearance: It sets target.innerHTML = ''. This ensures no "zombie" HTML from previous renders or static placeholders interferes with your app.
  2. Injection: It appends your component. If you passed a Function, it executes it first to get the DOM node.

5. Global vs. Local Scope ​

Global (The "Framework" Way) ​

In a standard Vite/ESM project, you initialize SigPro globally in main.js. This makes the $ and the tag helpers (div, button, etc.) available everywhere in your project.

javascript
// main.js - Global Initialization
-import 'SigPro'; 
+// Mount it into an existing div in your static HTML
+$.mount(CounterWidget, '#counter-container');

4. How it Works (Lifecycle) ​

When $.mount is called, it performs three critical steps:

  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.

5. 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.

javascript
// src/main.js
+import { $ } from 'sigpro'; 
 
-// Now any other file can just use:
-$.mount(() => h1("Global App"));

Local (The "Library" Way) ​

If you are worried about polluting the global window object, you can import and use SigPro locally within a specific module.

javascript
// widget.js - Local usage
-import { $ } from 'SigPro';
+// Any component in any file can now use:
+$.mount(() => h1("Global App"));

Local (The "Library" Way) ​

If you prefer to avoid polluting the window object, you can import and use SigPro locally within specific modules.

javascript
// widget.js
+import { $ } from 'sigpro';
 
 const myNode = $.html('div', 'Local Widget');
-$.mount(myNode, '#widget-target');

Summary Cheat Sheet ​

GoalCode
Mount to body$.mount(App)
Mount to ID$.mount(App, '#id')
Mount to Element$.mount(App, myElement)
Reactive Widget$.mount(() => div("Hi"), '#widget')
- +$.mount(myNode, '#widget-target');

6. Summary Cheat Sheet ​

GoalCode
Mount to body$.mount(App)
Mount to ID$.mount(App, '#id')
Mount to Element$.mount(App, myElement)
Direct Function$.mount(() => div("Hi"), '#widget')
+ \ No newline at end of file diff --git a/docs/api/quick.html b/docs/api/quick.html index e31203a..1892e97 100644 --- a/docs/api/quick.html +++ b/docs/api/quick.html @@ -14,18 +14,19 @@ + -
Skip to content

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.

1. Core Reactivity: $( ) ​

The $ function is a polymorphic constructor. It creates Signals (state) or Computed Effects (logic) based on the input type.

UsageInput TypeReturnsDescription
SignalanyFunctionA getter/setter for reactive state.
ComputedFunctionFunctionA read-only signal that auto-updates when its dependencies change.

Example:

javascript
const $count = $(0);             // Signal
+    
Skip to content

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.

1. Core Reactivity: $( ) ​

The $ function is a polymorphic constructor. It creates Signals (state) or Computed Effects (logic) based on the input type.

UsageInput TypeReturnsDescription
SignalanyFunctionA getter/setter for reactive state.
ComputedFunctionFunctionA read-only signal that auto-updates when its dependencies change.

Example:

javascript
const $count = $(0);             // Signal
 const $double = $(() => $count() * 2); // Computed

2. Rendering Engine: $.html ​

SigPro uses a hyperscript-style engine to create live DOM nodes.

ArgumentTypeRequiredDescription
tagstringYesStandard HTML tag (e.g., 'div', 'button').
propsObjectNoAttributes (id), Events (onclick), or Reactive Props ($value).
contentanyNoString, Node, Array, or Reactive Function.

Example:

javascript
$.html('button', { onclick: () => alert('Hi!') }, 'Click Me');

3. Global Helpers (Tag Proxies) ​

To keep your code clean, SigPro automatically exposes common HTML tags to the global scope.

CategoryAvailable Tags
Layoutdiv, section, main, nav, header, footer, span
Typographyh1, h2, h3, p, label, a, li, ul, ol
Formsinput, button, form, select, option
Mediaimg, video, audio, canvas

Example:

javascript
// No imports needed!
 div([ 
   h1("Title"), 
   button("Ok") 
 ]);

4. Mounting & Plugins ​

Methods to initialize your application and extend the engine.

MethodSignatureDescription
$.mount(node, target)Wipes the target (default: body) and renders the component.
$.plugin(source)Registers a function or loads external .js scripts as plugins.

Example:

javascript
$.plugin([UI, Router]);
 $.mount(App, '#root');

5. Reactive Syntax Cheat Sheet ​

FeatureSyntaxDescription
Text Bindingp(["Value: ", $sig])Updates text content automatically.
Attributesdiv({ id: $sig })Static attribute assignment.
Reactive Attrdiv({ $class: $sig })Attribute updates when $sig changes.
Two-way Bindinginput({ $value: $sig })Syncs input value and signal automatically.
Conditionaldiv(() => $sig() > 0 ? "Yes" : "No")Re-renders only the content when the condition changes.

Summary Table ​

FeatureSigPro ApproachBenefit
Update LogicFine-grained (Surgical)Blazing fast updates.
DOMNative NodesZero abstraction cost.
SyntaxPure JavaScriptNo build-tool lock-in.
FootprintModularLoad only what you use.
- + \ No newline at end of file diff --git a/docs/api/router.html b/docs/api/router.html new file mode 100644 index 0000000..dda62bd --- /dev/null +++ b/docs/api/router.html @@ -0,0 +1,62 @@ + + + + + + Routing Engine: $.router | SigPro + + + + + + + + + + + + + + + +
Skip to content

Routing Engine: $.router ​

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.

1. Core Features ​

  • 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.

2. Syntax: $.router(routes) ​

ParameterTypeRequiredDescription
routesArray<Object>YesAn 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.

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)) 
+]);

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.

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);
+};
+
+nav([
+  NavLink('/', 'Home'),
+  NavLink('/settings', 'Settings')
+]);

7. Summary: Route Component Types ​

Component TypeBehavior
HTMLElementRendered immediately.
Function (params) => ...Executed with URL parameters and rendered.
Promise / import()Triggers Lazy Loading with a loading state.
String / NumberRendered as simple text inside a span.
+ + + + \ No newline at end of file diff --git a/docs/api/tags.html b/docs/api/tags.html index baeecef..636f29b 100644 --- a/docs/api/tags.html +++ b/docs/api/tags.html @@ -13,12 +13,13 @@ - + + -
Skip to content

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.

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.

  • Traditional: $.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 ​

The following tags are available globally by default:

CategoryAvailable Functions
Layoutdiv, span, section, main, nav, header, footer, article, aside
Typographyh1, h2, h3, p, ul, ol, li, a, label, strong, em
Formsform, input, button, select, option, textarea
Tabletable, thead, tbody, tr, th, td
Mediaimg, video, audio, canvas, svg

3. Usage Patterns ​

The tag functions are highly flexible and accept arguments in different orders to suit your coding style.

A. Attributes + Content ​

The most common pattern.

javascript
div({ class: 'card' }, [
+    
Skip to content

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.

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.

  • Traditional: $.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 ​

The following tags are available globally by default:

CategoryAvailable Functions
Layoutdiv, span, section, main, nav, header, footer, article, aside
Typographyh1, h2, h3, p, ul, ol, li, a, label, strong, em
Formsform, input, button, select, option, textarea
Tabletable, thead, tbody, tr, th, td
Mediaimg, video, audio, canvas, svg

3. Usage Patterns ​

The tag functions are highly flexible and accept arguments in different orders to suit your coding style.

A. Attributes + Content ​

The most common pattern.

javascript
div({ class: 'card' }, [
   h1("Title"),
   p("Description")
 ]);

B. Content Only ​

If you don't need attributes, you can skip the object entirely.

javascript
div([
@@ -41,7 +42,7 @@
   section({ id: 'hero' }, [
     h1("Fast. Atomic. Simple."),
     p("Built with SigPro.")
-  ]);

6. Full Comparison: SigPro vs. Standard HTML ​

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.

javascript
const $online = $(true);
+  ]);

6. Full Comparison: SigPro vs. Standard HTML ​

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.

javascript
const $online = $(true);
 
 export const UserCard = () => (
   div({ class: 'user-card' }, [
@@ -73,7 +74,7 @@
   
   <button>Toggle Status</button>
 </div>

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', { ... }).
- + \ No newline at end of file diff --git a/docs/assets/api__.md.BVVMY-2O.js b/docs/assets/api__.md.BVVMY-2O.js deleted file mode 100644 index 87cc389..0000000 --- a/docs/assets/api__.md.BVVMY-2O.js +++ /dev/null @@ -1,27 +0,0 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"The Reactive Core: $( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/$.md","filePath":"api/$.md"}'),n={name:"api/$.md"};function l(h,s,p,r,k,d){return t(),a("div",null,[...s[0]||(s[0]=[e(`

The Reactive Core: $( ) ​

The $ function is the heart of SigPro. It is a Unified Reactive Constructor that handles state, derivations, and side effects through a single, consistent interface.

1. The Constructor: $( input ) ​

Depending on what you pass into $( ), SigPro creates a different type of reactive primitive:

Input TypeResultInternal Behavior
Value (String, Number, Object...)SignalCreates a piece of mutable state.
FunctionComputed / EffectCreates a derived value that tracks dependencies.

2. Signal (State) ​

A Signal is a "box" that holds a value. It provides a getter/setter function to interact with that value.

  • When to use: For data that changes over time (counters, user input, toggle states, API data).
  • Syntax: const $state = $(initialValue);

Example: ​

javascript
const $name = $("Alice");
-
-// Read the value (Getter)
-console.log($name()); // "Alice"
-
-// Update the value (Setter)
-$name("Bob"); 
-
-// Update based on previous value
-$name(current => current + " Smith");

3. Computed (Derived State) ​

When you pass a function to $( ) that returns a value, SigPro creates a Computed Signal. It automatically tracks which signals are used inside it and re-runs only when they change.

  • When to use: For values that depend on other signals (totals, filtered lists, formatted strings).
  • Syntax: const $derived = $(() => logic);

Example: ​

javascript
const $price = $(100);
-const $qty = $(2);
-
-// Automatically tracks $price and $qty
-const $total = $(() => $price() * $qty());
-
-console.log($total()); // 200
-
-$qty(3); // $total updates to 300 automatically

4. Effects (Side Effects) ​

An Effect is a function passed to $( ) that does not return a value (or returns undefined). SigPro treats this as a subscription that performs an action whenever its dependencies change.

  • When to use: For DOM manipulations, logging, or syncing with external APIs (LocalStorage, Fetch).
  • Syntax: $(() => { action });

Example: ​

javascript
const $theme = $("light");
-
-// This effect runs every time $theme changes
-$(() => {
-  document.body.className = $theme();
-  console.log("Theme updated to:", $theme());
-});
-
-$theme("dark"); // Logs: Theme updated to: dark

5. Summary Table: Usage Guide ​

PrimitiveLogic TypeReturns Value?Typical Use Case
SignalStaticYes (Mutable)const $user = $("Guest")
ComputedRead-onlyYes (Automatic)const $isLoggedIn = $(() => $user() !== "Guest")
EffectImperativeNo$(() => localStorage.setItem('user', $user()))

πŸ’‘ Pro Tip: Naming Convention ​

In SigPro, we use the $ prefix (e.g., $count) for variables that hold a reactive function. This makes it easy to distinguish between a standard variable and a reactive one at a glance:

javascript
let count = 0;   // Static
-const $count = $(0); // Reactive (Function)
`,30)])])}const c=i(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api__.md.CJ6A9esy.js b/docs/assets/api__.md.CJ6A9esy.js new file mode 100644 index 0000000..0d4f9c5 --- /dev/null +++ b/docs/assets/api__.md.CJ6A9esy.js @@ -0,0 +1,22 @@ +import{_ as t,o as i,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"The Reactive Core: $( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/$.md","filePath":"api/$.md"}'),n={name:"api/$.md"};function l(h,s,r,p,o,d){return i(),a("div",null,[...s[0]||(s[0]=[e(`

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:

ArgumentTypeRequiredDescription
inputValue / FunctionYesInitial state or reactive logic.
keystringNoIf provided, the signal persists in localStorage.

2. Signal (State & Persistence) ​

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: ​

javascript
const $user = $("Guest", "session-user"); // Automatically saved/loaded
+
+// Read (Getter)
+console.log($user()); 
+
+// Update (Setter + Auto-save to Disk)
+$user("Alice"); 
+
+// Functional Update
+$user(prev => prev.toUpperCase());

3. Computed (Derived State) ​

When you pass a function that returns a value, SigPro creates a Computed Signal. It tracks dependencies and recalculates only when necessary.

  • Syntax: const $derived = $(() => logic);

Example: ​

javascript
const $price = $(100);
+const $qty = $(2);
+
+// 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());
+});

5. Summary Table: Usage Guide ​

PrimitiveLogic TypePersistence?Typical Use Case
SignalMutable StateYes (Optional)$(0, 'counter')
ComputedDerived / Read-onlyNo$(() => $a() + $b())
EffectImperative ActionNo$(() => 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:

javascript
let count = 0;       // Static
+const $count = $(0); // Reactive Signal
`,34)])])}const c=t(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api__.md.BVVMY-2O.lean.js b/docs/assets/api__.md.CJ6A9esy.lean.js similarity index 50% rename from docs/assets/api__.md.BVVMY-2O.lean.js rename to docs/assets/api__.md.CJ6A9esy.lean.js index 727d251..b120fd4 100644 --- a/docs/assets/api__.md.BVVMY-2O.lean.js +++ b/docs/assets/api__.md.CJ6A9esy.lean.js @@ -1 +1 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"The Reactive Core: $( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/$.md","filePath":"api/$.md"}'),n={name:"api/$.md"};function l(h,s,p,r,k,d){return t(),a("div",null,[...s[0]||(s[0]=[e("",30)])])}const c=i(n,[["render",l]]);export{g as __pageData,c as default}; +import{_ as t,o as i,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"The Reactive Core: $( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/$.md","filePath":"api/$.md"}'),n={name:"api/$.md"};function l(h,s,r,p,o,d){return i(),a("div",null,[...s[0]||(s[0]=[e("",34)])])}const c=t(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api_mount.md.DK1GUmQ8.js b/docs/assets/api_mount.md.DK1GUmQ8.js new file mode 100644 index 0000000..5a969ec --- /dev/null +++ b/docs/assets/api_mount.md.DK1GUmQ8.js @@ -0,0 +1,29 @@ +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Application Mounter: $.router.mount (Core)","description":"","frontmatter":{},"headers":[],"relativePath":"api/mount.md","filePath":"api/mount.md"}'),n={name:"api/mount.md"};function l(h,s,p,r,o,k){return a(),t("div",null,[...s[0]||(s[0]=[e(`

Application Mounter: $.router.mount (Core) ​

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. Syntax: $.mount(node, [target]) ​

ParameterTypeDefaultDescription
nodeHTMLElement or FunctionRequiredThe component or element to render.
targetstring or HTMLElementdocument.bodyWhere to mount the app (CSS selector or Element).

2. 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.

javascript
// src/main.js
+import { $ } from 'sigpro';
+import App from './App.js';
+
+// SigPro: No .then() needed, global tags are ready immediately
+$.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.

html
<div id="sidebar"></div>
+<div id="app-root"></div>
javascript
// Mount to a specific ID
+$.mount(MyComponent, '#app-root');
+
+// Or using a direct DOM reference
+const sidebar = document.getElementById('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.

javascript
// A small reactive widget
+const CounterWidget = () => {
+  const $c = $(0);
+  return button({ onclick: () => $c(v => v + 1) }, [
+    "Clicks: ", $c
+  ]);
+};
+
+// Mount it into an existing div in your static HTML
+$.mount(CounterWidget, '#counter-container');

4. How it Works (Lifecycle) ​

When $.mount is called, it performs three critical steps:

  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.

5. 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.

javascript
// src/main.js
+import { $ } from 'sigpro'; 
+
+// Any component in any file can now use:
+$.mount(() => h1("Global App"));

Local (The "Library" Way) ​

If you prefer to avoid polluting the window object, you can import and use SigPro locally within specific modules.

javascript
// widget.js
+import { $ } from 'sigpro';
+
+const myNode = $.html('div', 'Local Widget');
+$.mount(myNode, '#widget-target');

6. Summary Cheat Sheet ​

GoalCode
Mount to body$.mount(App)
Mount to ID$.mount(App, '#id')
Mount to Element$.mount(App, myElement)
Direct Function$.mount(() => div("Hi"), '#widget')
`,32)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/api_mount.md.DK1GUmQ8.lean.js b/docs/assets/api_mount.md.DK1GUmQ8.lean.js new file mode 100644 index 0000000..f7481f1 --- /dev/null +++ b/docs/assets/api_mount.md.DK1GUmQ8.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Application Mounter: $.router.mount (Core)","description":"","frontmatter":{},"headers":[],"relativePath":"api/mount.md","filePath":"api/mount.md"}'),n={name:"api/mount.md"};function l(h,s,p,r,o,k){return a(),t("div",null,[...s[0]||(s[0]=[e("",32)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/api_mount.md.eGRwkZvh.js b/docs/assets/api_mount.md.eGRwkZvh.js deleted file mode 100644 index 49d4057..0000000 --- a/docs/assets/api_mount.md.eGRwkZvh.js +++ /dev/null @@ -1,28 +0,0 @@ -import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Application Mounter: $.mount","description":"","frontmatter":{},"headers":[],"relativePath":"api/mount.md","filePath":"api/mount.md"}'),n={name:"api/mount.md"};function l(h,s,p,o,r,k){return a(),t("div",null,[...s[0]||(s[0]=[e(`

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.

1. Syntax: $.mount(node, [target]) ​

ParameterTypeDefaultDescription
nodeHTMLElement or FunctionRequiredThe component or element to render.
targetstring or HTMLElementdocument.bodyWhere to mount the app (CSS selector or Element).

2. Usage Scenarios ​

A. The "Clean Slate" (Main Entry) ​

In a modern app (like our main.js example), you usually want to control the entire page. By default, $.mount clears the target's existing HTML before mounting.

javascript
// src/main.js
-import { $ } from 'SigPro';
-import App from './App.js';
-
-$.mount(App); // Mounts to <body> by default

B. Targeting a Specific Container ​

If you have an existing HTML structure and only want SigPro to manage a specific part (like a #root div), pass a CSS selector or a reference.

html
<div id="sidebar"></div>
-<div id="app-root"></div>
javascript
// Local mount to a specific ID
-$.mount(MyComponent, '#app-root');
-
-// Or using a direct DOM reference
-const sidebar = document.getElementById('sidebar');
-$.mount(SidebarComponent, sidebar);

3. Mounting with Pure HTML ​

One of SigPro's strengths is that it works perfectly alongside "Old School" HTML. You can create a reactive "island" inside a static page.

javascript
// A small reactive widget in a static .js file
-const CounterWidget = () => {
-  const $c = $(0);
-  return button({ onclick: () => $c(v => v + 1) }, [
-    "Clicks: ", $c
-  ]);
-};
-
-// Mount it into an existing div in your HTML
-$.mount(CounterWidget, '#counter-container');

4. How it Works (The "Wipe" Logic) ​

When $.mount is called, it performs two critical steps:

  1. Clearance: It sets target.innerHTML = ''. This ensures no "zombie" HTML from previous renders or static placeholders interferes with your app.
  2. Injection: It appends your component. If you passed a Function, it executes it first to get the DOM node.

5. Global vs. Local Scope ​

Global (The "Framework" Way) ​

In a standard Vite/ESM project, you initialize SigPro globally in main.js. This makes the $ and the tag helpers (div, button, etc.) available everywhere in your project.

javascript
// main.js - Global Initialization
-import 'SigPro'; 
-
-// Now any other file can just use:
-$.mount(() => h1("Global App"));

Local (The "Library" Way) ​

If you are worried about polluting the global window object, you can import and use SigPro locally within a specific module.

javascript
// widget.js - Local usage
-import { $ } from 'SigPro';
-
-const myNode = $.html('div', 'Local Widget');
-$.mount(myNode, '#widget-target');

Summary Cheat Sheet ​

GoalCode
Mount to body$.mount(App)
Mount to ID$.mount(App, '#id')
Mount to Element$.mount(App, myElement)
Reactive Widget$.mount(() => div("Hi"), '#widget')
`,32)])])}const c=i(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api_mount.md.eGRwkZvh.lean.js b/docs/assets/api_mount.md.eGRwkZvh.lean.js deleted file mode 100644 index 427b068..0000000 --- a/docs/assets/api_mount.md.eGRwkZvh.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Application Mounter: $.mount","description":"","frontmatter":{},"headers":[],"relativePath":"api/mount.md","filePath":"api/mount.md"}'),n={name:"api/mount.md"};function l(h,s,p,o,r,k){return a(),t("div",null,[...s[0]||(s[0]=[e("",32)])])}const c=i(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api_router.md.BV6vPWg-.js b/docs/assets/api_router.md.BV6vPWg-.js new file mode 100644 index 0000000..2514d34 --- /dev/null +++ b/docs/assets/api_router.md.BV6vPWg-.js @@ -0,0 +1,37 @@ +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const E=JSON.parse('{"title":"Routing Engine: $.router","description":"","frontmatter":{},"headers":[],"relativePath":"api/router.md","filePath":"api/router.md"}'),e={name:"api/router.md"};function h(l,s,k,p,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

Routing Engine: $.router ​

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.

1. Core Features ​

  • 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.

2. Syntax: $.router(routes) ​

ParameterTypeRequiredDescription
routesArray<Object>YesAn 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.

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)) 
+]);

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.

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);
+};
+
+nav([
+  NavLink('/', 'Home'),
+  NavLink('/settings', 'Settings')
+]);

7. Summary: Route Component Types ​

Component TypeBehavior
HTMLElementRendered immediately.
Function (params) => ...Executed with URL parameters and rendered.
Promise / import()Triggers Lazy Loading with a loading state.
String / NumberRendered as simple text inside a span.
`,26)])])}const g=i(e,[["render",h]]);export{E as __pageData,g as default}; diff --git a/docs/assets/api_router.md.BV6vPWg-.lean.js b/docs/assets/api_router.md.BV6vPWg-.lean.js new file mode 100644 index 0000000..92a0835 --- /dev/null +++ b/docs/assets/api_router.md.BV6vPWg-.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const E=JSON.parse('{"title":"Routing Engine: $.router","description":"","frontmatter":{},"headers":[],"relativePath":"api/router.md","filePath":"api/router.md"}'),e={name:"api/router.md"};function h(l,s,k,p,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n("",26)])])}const g=i(e,[["render",h]]);export{E as __pageData,g as default}; diff --git a/docs/assets/api_tags.md.Ch1qUgrw.js b/docs/assets/api_tags.md.BAYRMzRh.js similarity index 98% rename from docs/assets/api_tags.md.Ch1qUgrw.js rename to docs/assets/api_tags.md.BAYRMzRh.js index 3fce715..f07e963 100644 --- a/docs/assets/api_tags.md.Ch1qUgrw.js +++ b/docs/assets/api_tags.md.BAYRMzRh.js @@ -21,7 +21,7 @@ import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const E section({ id: 'hero' }, [ h1("Fast. Atomic. Simple."), p("Built with SigPro.") - ]);

6. Full Comparison: SigPro vs. Standard HTML ​

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.

javascript
const $online = $(true);
+  ]);

6. Full Comparison: SigPro vs. Standard HTML ​

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.

javascript
const $online = $(true);
 
 export const UserCard = () => (
   div({ class: 'user-card' }, [
diff --git a/docs/assets/api_tags.md.Ch1qUgrw.lean.js b/docs/assets/api_tags.md.BAYRMzRh.lean.js
similarity index 100%
rename from docs/assets/api_tags.md.Ch1qUgrw.lean.js
rename to docs/assets/api_tags.md.BAYRMzRh.lean.js
diff --git a/docs/assets/guide_getting-started.md.RUz_X0AL.js b/docs/assets/guide_getting-started.md.BJ3Lg3i9.js
similarity index 92%
rename from docs/assets/guide_getting-started.md.RUz_X0AL.js
rename to docs/assets/guide_getting-started.md.BJ3Lg3i9.js
index 1134116..187149f 100644
--- a/docs/assets/guide_getting-started.md.RUz_X0AL.js
+++ b/docs/assets/guide_getting-started.md.BJ3Lg3i9.js
@@ -1,4 +1,4 @@
-import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"guide/getting-started.md","filePath":"guide/getting-started.md"}'),e={name:"guide/getting-started.md"};function l(h,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

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:

bash
npm install SigPro
bash
pnpm add SigPro
bash
yarn add SigPro
bash
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';
+import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"guide/getting-started.md","filePath":"guide/getting-started.md"}'),e={name:"guide/getting-started.md"};function l(p,s,h,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

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:

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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");
@@ -23,4 +23,4 @@ import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g
 div([
   h1("Clean Syntax"),
   p("No more boilerplate.")
-]);
`,15)])])}const E=i(e,[["render",l]]);export{g as __pageData,E as default}; +]);
`,15)])])}const c=i(e,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/guide_getting-started.md.RUz_X0AL.lean.js b/docs/assets/guide_getting-started.md.BJ3Lg3i9.lean.js similarity index 59% rename from docs/assets/guide_getting-started.md.RUz_X0AL.lean.js rename to docs/assets/guide_getting-started.md.BJ3Lg3i9.lean.js index 39b0785..38ed860 100644 --- a/docs/assets/guide_getting-started.md.RUz_X0AL.lean.js +++ b/docs/assets/guide_getting-started.md.BJ3Lg3i9.lean.js @@ -1 +1 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"guide/getting-started.md","filePath":"guide/getting-started.md"}'),e={name:"guide/getting-started.md"};function l(h,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n("",15)])])}const E=i(e,[["render",l]]);export{g as __pageData,E as default}; +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"guide/getting-started.md","filePath":"guide/getting-started.md"}'),e={name:"guide/getting-started.md"};function l(p,s,h,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n("",15)])])}const c=i(e,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/index.md.BX4IouPL.js b/docs/assets/index.md.DTGopCPx.js similarity index 96% rename from docs/assets/index.md.BX4IouPL.js rename to docs/assets/index.md.DTGopCPx.js index f456ae3..4697eaf 100644 --- a/docs/assets/index.md.BX4IouPL.js +++ b/docs/assets/index.md.DTGopCPx.js @@ -14,4 +14,4 @@ import{_ as s,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const g ]); $.mount(Counter);

Key Features ​

⚑️ 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.


Quick Install ​

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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
`,20)])])}const c=s(n,[["render",l]]);export{g as __pageData,c as default}; +$.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.


Quick Install ​

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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
`,20)])])}const c=s(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/index.md.BX4IouPL.lean.js b/docs/assets/index.md.DTGopCPx.lean.js similarity index 100% rename from docs/assets/index.md.BX4IouPL.lean.js rename to docs/assets/index.md.DTGopCPx.lean.js diff --git a/docs/assets/plugins_core.debug.md.BRT_r6k8.js b/docs/assets/plugins_core.debug.md.BKuhgPoj.js similarity index 96% rename from docs/assets/plugins_core.debug.md.BRT_r6k8.js rename to docs/assets/plugins_core.debug.md.BKuhgPoj.js index 3082bf3..2600a21 100644 --- a/docs/assets/plugins_core.debug.md.BRT_r6k8.js +++ b/docs/assets/plugins_core.debug.md.BKuhgPoj.js @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Development Tool: _debug","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.debug.md","filePath":"plugins/core.debug.md"}'),e={name:"plugins/core.debug.md"};function l(h,s,p,k,r,o){return a(),t("div",null,[...s[0]||(s[0]=[n(`

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{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Development Tool: _debug","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.debug.md","filePath":"plugins/core.debug.md"}'),e={name:"plugins/core.debug.md"};function l(h,s,p,k,r,o){return a(),n("div",null,[...s[0]||(s[0]=[t(`

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
@@ -7,7 +7,7 @@ import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g
 
 $.plugin(plugins).then(() => {
   import('./App.js').then(app => $.mount(app.default));
-});
bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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 () => {
+});
bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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" });
 
diff --git a/docs/assets/plugins_core.debug.md.BRT_r6k8.lean.js b/docs/assets/plugins_core.debug.md.BKuhgPoj.lean.js
similarity index 70%
rename from docs/assets/plugins_core.debug.md.BRT_r6k8.lean.js
rename to docs/assets/plugins_core.debug.md.BKuhgPoj.lean.js
index add7b6e..83a18f7 100644
--- a/docs/assets/plugins_core.debug.md.BRT_r6k8.lean.js
+++ b/docs/assets/plugins_core.debug.md.BKuhgPoj.lean.js
@@ -1 +1 @@
-import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Development Tool: _debug","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.debug.md","filePath":"plugins/core.debug.md"}'),e={name:"plugins/core.debug.md"};function l(h,s,p,k,r,o){return a(),t("div",null,[...s[0]||(s[0]=[n("",24)])])}const E=i(e,[["render",l]]);export{g as __pageData,E as default};
+import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Development Tool: _debug","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.debug.md","filePath":"plugins/core.debug.md"}'),e={name:"plugins/core.debug.md"};function l(h,s,p,k,r,o){return a(),n("div",null,[...s[0]||(s[0]=[t("",24)])])}const E=i(e,[["render",l]]);export{g as __pageData,E as default};
diff --git a/docs/assets/plugins_core.router.md.CWklH7Gb.js b/docs/assets/plugins_core.router.md.Bg_zmVp3.js
similarity index 97%
rename from docs/assets/plugins_core.router.md.CWklH7Gb.js
rename to docs/assets/plugins_core.router.md.Bg_zmVp3.js
index 063b74b..8d4fab9 100644
--- a/docs/assets/plugins_core.router.md.CWklH7Gb.js
+++ b/docs/assets/plugins_core.router.md.Bg_zmVp3.js
@@ -1,4 +1,4 @@
-import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const E=JSON.parse('{"title":"Navigation Plugin: Router","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.router.md","filePath":"plugins/core.router.md"}'),e={name:"plugins/core.router.md"};function h(l,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

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.

bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
bash
pnpm add -D tailwindcss @tailwindcss/vite daisyui@next
bash
yarn add -D tailwindcss @tailwindcss/vite daisyui@next
bash
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 = [
+import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const E=JSON.parse('{"title":"Navigation Plugin: Router","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.router.md","filePath":"plugins/core.router.md"}'),e={name:"plugins/core.router.md"};function h(l,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

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.

bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
bash
pnpm add -D tailwindcss @tailwindcss/vite daisyui@next
bash
yarn add -D tailwindcss @tailwindcss/vite daisyui@next
bash
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', 
diff --git a/docs/assets/plugins_core.router.md.CWklH7Gb.lean.js b/docs/assets/plugins_core.router.md.Bg_zmVp3.lean.js
similarity index 100%
rename from docs/assets/plugins_core.router.md.CWklH7Gb.lean.js
rename to docs/assets/plugins_core.router.md.Bg_zmVp3.lean.js
diff --git a/docs/assets/plugins_core.storage.md.lAkbO1ib.js b/docs/assets/plugins_core.storage.md.C6UMGg_o.js
similarity index 95%
rename from docs/assets/plugins_core.storage.md.lAkbO1ib.js
rename to docs/assets/plugins_core.storage.md.C6UMGg_o.js
index 6e4e434..9886b20 100644
--- a/docs/assets/plugins_core.storage.md.lAkbO1ib.js
+++ b/docs/assets/plugins_core.storage.md.C6UMGg_o.js
@@ -1,9 +1,9 @@
-import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Persistence Tool: _storage","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.storage.md","filePath":"plugins/core.storage.md"}'),e={name:"plugins/core.storage.md"};function h(l,s,p,k,r,o){return a(),t("div",null,[...s[0]||(s[0]=[n(`

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{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Persistence Tool: _storage","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.storage.md","filePath":"plugins/core.storage.md"}'),n={name:"plugins/core.storage.md"};function h(l,s,p,k,r,o){return a(),t("div",null,[...s[0]||(s[0]=[e(`

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));
-});
bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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 () => {
+});
bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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' );
 
@@ -26,4 +26,4 @@ import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g
 _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");
`,25)])])}const c=i(e,[["render",h]]);export{g as __pageData,c as default}; +_debug($score, "Game Score");
`,25)])])}const c=i(n,[["render",h]]);export{g as __pageData,c as default}; diff --git a/docs/assets/plugins_core.storage.md.lAkbO1ib.lean.js b/docs/assets/plugins_core.storage.md.C6UMGg_o.lean.js similarity index 56% rename from docs/assets/plugins_core.storage.md.lAkbO1ib.lean.js rename to docs/assets/plugins_core.storage.md.C6UMGg_o.lean.js index be7046b..00ff4c3 100644 --- a/docs/assets/plugins_core.storage.md.lAkbO1ib.lean.js +++ b/docs/assets/plugins_core.storage.md.C6UMGg_o.lean.js @@ -1 +1 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Persistence Tool: _storage","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.storage.md","filePath":"plugins/core.storage.md"}'),e={name:"plugins/core.storage.md"};function h(l,s,p,k,r,o){return a(),t("div",null,[...s[0]||(s[0]=[n("",25)])])}const c=i(e,[["render",h]]);export{g as __pageData,c as default}; +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Persistence Tool: _storage","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.storage.md","filePath":"plugins/core.storage.md"}'),n={name:"plugins/core.storage.md"};function h(l,s,p,k,r,o){return a(),t("div",null,[...s[0]||(s[0]=[e("",25)])])}const c=i(n,[["render",h]]);export{g as __pageData,c as default}; diff --git a/docs/assets/plugins_core.ui.md.C434Uobv.js b/docs/assets/plugins_core.ui.md.C434Uobv.js new file mode 100644 index 0000000..9433d66 --- /dev/null +++ b/docs/assets/plugins_core.ui.md.C434Uobv.js @@ -0,0 +1,452 @@ +import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"SigPro UI Plugin - Complete Documentation","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.ui.md","filePath":"plugins/core.ui.md"}'),h={name:"plugins/core.ui.md"};function l(p,s,k,e,E,d){return a(),n("div",null,[...s[0]||(s[0]=[t(`

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
  2. Core Concepts
  3. Form Components
  4. Action Components
  5. Layout Components
  6. Navigation Components
  7. Feedback Components
  8. Container Components
  9. Complete Examples
  10. Styling Guide
  11. 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:

PropertyTypeDescription
labelstringField label text
tipstringTooltip text shown on hover of a "?" badge
$valuesignalTwo-way bound value signal
$errorsignalError message signal (shows red border + message)
typestringInput type: 'text', 'email', 'password', etc.
placeholderstringPlaceholder text
classstring|functionAdditional 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:

PropertyTypeDescription
labelstringField label
optionsArray<{value: any, label: string}>Select options
$valuesignalTwo-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:

PropertyTypeDescription
labelstringCheckbox label text
$valuesignalBoolean 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:

PropertyTypeDescription
labelstringRadio option label
valueanyValue for this radio option
$valuesignalGroup 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:

PropertyTypeDescription
labelstringSlider label
minnumberMinimum value
maxnumberMaximum value
stepnumberStep increment
$valuesignalCurrent 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:

PropertyTypeDescription
$loadingsignalShows spinner + disables when true
$disabledsignalManual disabled state
iconstring|HTMLElementIcon element or emoji/unicode
badgestringBadge text to display
badgeClassstringAdditional badge styling
typestringButton type: 'button', 'submit', etc.
onclickfunctionClick 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:

PropertyTypeDescription
legendstringFieldset title
classstring|functionAdditional 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:

PropertyTypeDescription
titlestringAccordion header text
namestringOptional group name (radio behavior)
openbooleanInitially 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:

PropertyTypeDescription
idstringUnique ID for checkbox toggle
$opensignalBoolean signal for drawer state
contentHTMLElementMain content area
sideHTMLElementSidebar 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) }
+    ]})
+  ]
+})

Responsive navigation bar with built-in styling.

Properties:

PropertyTypeDescription
classstring|functionAdditional 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')
+  ])
+])

Sidebar or dropdown menu with active state support.

Properties:

PropertyTypeDescription
itemsArray<{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:

PropertyTypeDescription
itemsArray<{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:

PropertyTypeDescription
classstring|functionBadge 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:

PropertyTypeDescription
tipstringTooltip text
positionstringTooltip 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 ​

Programmatically controlled modal dialog.

Properties:

PropertyTypeDescription
$opensignalBoolean signal controlling visibility
titlestringModal title
classstring|functionAdditional 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 menu that appears on click.

Properties:

PropertyTypeDescription
labelstringDropdown trigger text
classstring|functionAdditional 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:

ComponentFunction 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())
`,178)])])}const o=i(h,[["render",l]]);export{g as __pageData,o as default}; diff --git a/docs/assets/plugins_core.ui.md.C434Uobv.lean.js b/docs/assets/plugins_core.ui.md.C434Uobv.lean.js new file mode 100644 index 0000000..058cac5 --- /dev/null +++ b/docs/assets/plugins_core.ui.md.C434Uobv.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"SigPro UI Plugin - Complete Documentation","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.ui.md","filePath":"plugins/core.ui.md"}'),h={name:"plugins/core.ui.md"};function l(p,s,k,e,E,d){return a(),n("div",null,[...s[0]||(s[0]=[t("",178)])])}const o=i(h,[["render",l]]);export{g as __pageData,o as default}; diff --git a/docs/assets/plugins_core.ui.md.DAV-GYyQ.js b/docs/assets/plugins_core.ui.md.DAV-GYyQ.js deleted file mode 100644 index 2ff6f49..0000000 --- a/docs/assets/plugins_core.ui.md.DAV-GYyQ.js +++ /dev/null @@ -1,30 +0,0 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Official UI Plugin: UI","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.ui.md","filePath":"plugins/core.ui.md"}'),n={name:"plugins/core.ui.md"};function l(h,s,p,o,d,r){return t(),a("div",null,[...s[0]||(s[0]=[e(`

Official UI Plugin: UI ​

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 components.

1. Prerequisites & Installation ​

To use these components, you must install the styling engine. SigPro UI provides the logic, but Tailwind and daisyUI provide the visuals.

bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
bash
pnpm add -D tailwindcss @tailwindcss/vite daisyui@next
bash
yarn add -D tailwindcss @tailwindcss/vite daisyui@next
bash
bun add -d tailwindcss @tailwindcss/vite daisyui@next

Would you like to continue with the Router.md documentation now?

CSS Configuration (app.css) ​

In Tailwind v4, configuration is handled directly in your CSS. Create a src/app.css file:

css
/* src/app.css */
-@import "tailwindcss";
-
-/* Import daisyUI v5 as a Tailwind v4 plugin */
-@plugin "daisyui";
-
-/* Optional: Configure themes */
-@custom-variant dark (&:where(.dark, [data-theme="dark"], [data-theme="dark"] *)));

2. Initialization ​

You must import your CSS and register the UI plugin in your entry point. This populates the global scope with reactive component helpers (prefixed with _).

javascript
// main.js
-import './app.css';
-import { $ } from 'sigpro';
-import { UI } from 'sigpro/plugins';
-
-$.plugin(UI).then(() => {
-  // Global components like _button and _input are now ready
-  import('./App.js').then(app => $.mount(app.default));
-});

3. Core Component Tags (_tags) ​

SigPro UI components are more than just HTML; they are Reactive Functional Components that manage complex states (loading, errors, accessibility) automatically.

A. Action Components (_button) ​

The _button automatically handles spinners and disabled states based on signals.

PropertyTypeDescription
$loadingsignalIf true, shows a spinner and disables the button.
$disabledsignalManually disables the button (logic-bound).
iconnode/strPrepends an icon to the text.
badgestringAppends a small badge to the button.
javascript
_button({ 
-  $loading: $isSaving, 
-  icon: 'πŸ’Ύ', 
-  class: 'btn-primary' 
-}, "Save Data")

B. High-Density Forms (_input, _select, _checkbox) ​

These components wrap the raw input in a fieldset with integrated labels and tooltips.

  • label: Field title displayed above the input.
  • tip: Displays a ? badge that shows a tooltip on hover.
  • $error: A signal that, when populated, turns the input red and displays the message.
  • $value: Two-way binding. Updates the signal on input and the input on signal change.
javascript
_input({
-  label: "Username",
-  tip: "Choose a unique name",
-  $value: $name,
-  $error: $nameError
-})

4. Complex UI Patterns ​

Reactive Modals (_modal) ​

The _modal is surgically mounted. If the $open signal is false, the component is completely removed from the DOM, optimizing performance.

javascript
const $showModal = $(false);
-
-_modal({ $open: $showModal, title: "Alert" }, [
-  p("Are you sure you want to proceed?"),
-  _button({ onclick: () => doAction() }, "Confirm")
-])

Designed to work seamlessly with the Router.

ComponentKey Logic
_tabsAccepts an active property (signal or function) to highlight the current tab.
_drawerA responsive sidebar that toggles via an ID or an $open signal.
_navbarStandard top bar with shadow and glass effect support.
_menuVertical navigation list with active state support.

5. Summary Table: UI Globals ​

Once $.plugin(UI) is active, these tags are available project-wide:

TagCategoryUse Case
_fieldsetLayoutGrouping related inputs with a legend.
_accordionContentCollapsible sections (FAQs).
_badgeFeedbackStatus indicators (Success, Warning).
_tooltipFeedbackDescriptive text on hover.
_rangeInputReactive slider for numerical values.

What's next? ​

With the UI ready and styled via Tailwind v4, we can move to the Router.md. We will explain how to link _tabs and _menu to different URL paths for a full SPA experience.

Would you like to start with the Router configuration?

`,40)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/plugins_core.ui.md.DAV-GYyQ.lean.js b/docs/assets/plugins_core.ui.md.DAV-GYyQ.lean.js deleted file mode 100644 index 69a4077..0000000 --- a/docs/assets/plugins_core.ui.md.DAV-GYyQ.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Official UI Plugin: UI","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/core.ui.md","filePath":"plugins/core.ui.md"}'),n={name:"plugins/core.ui.md"};function l(h,s,p,o,d,r){return t(),a("div",null,[...s[0]||(s[0]=[e("",40)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/plugins_custom.md.DZNaLrm0.js b/docs/assets/plugins_custom.md.D6eJnwEa.js similarity index 97% rename from docs/assets/plugins_custom.md.DZNaLrm0.js rename to docs/assets/plugins_custom.md.D6eJnwEa.js index 3a6c31d..cf4ad7e 100644 --- a/docs/assets/plugins_custom.md.DZNaLrm0.js +++ b/docs/assets/plugins_custom.md.D6eJnwEa.js @@ -45,4 +45,4 @@ import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const o $.plugin(ConfigLoader).then(() => { console.log("Config loaded:", $.config); $.mount(App); -});

4. Best Practices for Plugin Authors ​

RuleDescription
PrefixingUse _ for UI components (_modal) and $. for logic ($.fetch).
IdempotencyEnsure calling $.plugin(MyPlugin) twice doesn't break the app.
EncapsulationUse the $ instance passed as an argument rather than importing it again inside the plugin.
ReactivityAlways 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.

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
`,24)])])}const E=i(l,[["render",h]]);export{o as __pageData,E as default}; +});

4. Best Practices for Plugin Authors ​

RuleDescription
PrefixingUse _ for UI components (_modal) and $. for logic ($.fetch).
IdempotencyEnsure calling $.plugin(MyPlugin) twice doesn't break the app.
EncapsulationUse the $ instance passed as an argument rather than importing it again inside the plugin.
ReactivityAlways 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.

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
`,24)])])}const E=i(l,[["render",h]]);export{o as __pageData,E as default}; diff --git a/docs/assets/plugins_custom.md.DZNaLrm0.lean.js b/docs/assets/plugins_custom.md.D6eJnwEa.lean.js similarity index 100% rename from docs/assets/plugins_custom.md.DZNaLrm0.lean.js rename to docs/assets/plugins_custom.md.D6eJnwEa.lean.js diff --git a/docs/assets/plugins_quick.md.BBIL_nLZ.js b/docs/assets/plugins_quick.md.BBIL_nLZ.js new file mode 100644 index 0000000..c87fad4 --- /dev/null +++ b/docs/assets/plugins_quick.md.BBIL_nLZ.js @@ -0,0 +1,27 @@ +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Extending SigPro: $.plugin","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/quick.md","filePath":"plugins/quick.md"}'),e={name:"plugins/quick.md"};function l(h,s,p,r,o,k){return a(),t("div",null,[...s[0]||(s[0]=[n(`

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.

This is the standard way to build apps. It's clean, readable, and supports standard ESM imports.

javascript
// main.js
+import { $ } from 'sigpro';
+import { UI } from 'sigpro/plugins';
+import App from './App.js'; // Static import works perfectly!
+
+// 1. Register plugins
+$.plugin(UI);
+
+// 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 TypeActionBehavior
FunctionExecutes fn($)Synchronous: Immediate availability.
String (URL)Injects <script src="...">Asynchronous: Returns a Promise.
ArrayProcesses each item in the listReturns 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 ​

GoalCode
Local Plugin$.plugin(myPlugin)
Multiple Plugins$.plugin([UI, Router])
External Library$.plugin('https://...').then(...)
Hybrid Load$.plugin([UI, 'https://...']).then(...)
`,28)])])}const c=i(e,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/plugins_quick.md.ODjl7edh.lean.js b/docs/assets/plugins_quick.md.BBIL_nLZ.lean.js similarity index 61% rename from docs/assets/plugins_quick.md.ODjl7edh.lean.js rename to docs/assets/plugins_quick.md.BBIL_nLZ.lean.js index f909f8f..88c117b 100644 --- a/docs/assets/plugins_quick.md.ODjl7edh.lean.js +++ b/docs/assets/plugins_quick.md.BBIL_nLZ.lean.js @@ -1 +1 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Extending SigPro: $.plugin","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/quick.md","filePath":"plugins/quick.md"}'),e={name:"plugins/quick.md"};function l(h,s,p,k,r,o){return a(),t("div",null,[...s[0]||(s[0]=[n("",28)])])}const E=i(e,[["render",l]]);export{g as __pageData,E as default}; +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Extending SigPro: $.plugin","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/quick.md","filePath":"plugins/quick.md"}'),e={name:"plugins/quick.md"};function l(h,s,p,r,o,k){return a(),t("div",null,[...s[0]||(s[0]=[n("",28)])])}const c=i(e,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/plugins_quick.md.ODjl7edh.js b/docs/assets/plugins_quick.md.ODjl7edh.js deleted file mode 100644 index 123478f..0000000 --- a/docs/assets/plugins_quick.md.ODjl7edh.js +++ /dev/null @@ -1,36 +0,0 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Extending SigPro: $.plugin","description":"","frontmatter":{},"headers":[],"relativePath":"plugins/quick.md","filePath":"plugins/quick.md"}'),e={name:"plugins/quick.md"};function l(h,s,p,k,r,o){return a(),t("div",null,[...s[0]||(s[0]=[n(`

Extending SigPro: $.plugin ​

The plugin system is the engine's way of growing. It allows you to inject new functionality directly into the $ object or load external resources.

1. How Plugins Work ​

A plugin in SigPro is simply a function that receives the core instance. When you run $.plugin(MyPlugin), the engine hands over the $ object so the plugin can attach new methods or register global tags (like div(), span(), etc.).

Functional Plugin Example ​

javascript
// A plugin that adds a simple logger 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 ​

Since plugins often set up global variables (like the HTML tags), the order of initialization is critical. Here are the two ways to start your app:

This is the most robust way. It ensures all global tags (div, button, etc.) are created before your App code is even read by the browser.

javascript
// main.js
-import { $ } from 'sigpro';
-import { UI, Router } from 'sigpro/plugins';
-
-// 1. Load plugins first
-$.plugin([UI, Router]).then(() => {
-  
-  // 2. Import your app only after the environment is ready
-  import('./App.js').then(appFile => {
-    const MyApp = appFile.default;
-    $.mount(MyApp, '#app');
-  });
-
-});

Option B: Static Start (No Global Tags) ​

Use this only if you prefer not to use global tags and want to use $.html directly in your components. This allows for standard static imports.

javascript
// main.js
-import { $ } from 'sigpro';
-import { UI } from 'sigpro/plugins';
-import MyApp from './App.js'; // Static import works here
-
-$.plugin(UI);
-$.mount(MyApp, '#app');

Warning: In this mode, if App.js uses div() instead of $.html('div'), it will throw a ReferenceError.


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 and executed.

javascript
// Loading external libraries as plugins
-await $.plugin([
-  'https://cdn.jsdelivr.net/npm/chart.js',
-  'https://cdn.example.com/custom-ui-lib.js'
-]);
-
-console.log("External resources are ready to use!");

4. Polymorphic Loading Reference ​

The $.plugin method adapts to whatever you throw at it:

Input TypeActionBehavior
FunctionExecutes fn($)Synchronous / Immediate
String (URL)Injects <script src="...">Asynchronous (Returns Promise)
ArrayProcesses each item in the listReturns Promise if any item is Async

πŸ’‘ Pro Tip: Why the .then()? ​

Using $.plugin([...]).then(...) is like giving your app a "Pre-flight Check". It guarantees that:

  1. All reactive methods are attached.
  2. Global HTML tags are defined.
  3. External libraries (like Chart.js) are loaded.
  4. The result: Your components are cleaner, smaller, and error-free.
`,28)])])}const E=i(e,[["render",l]]);export{g as __pageData,E as default}; diff --git a/docs/assets/ui_button.md.B087poC6.js b/docs/assets/ui_button.md.B087poC6.js new file mode 100644 index 0000000..484e8ef --- /dev/null +++ b/docs/assets/ui_button.md.B087poC6.js @@ -0,0 +1,34 @@ +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"Button Component","description":"","frontmatter":{},"headers":[],"relativePath":"ui/button.md","filePath":"ui/button.md"}'),h={name:"ui/button.md"};function l(e,s,k,p,d,E){return a(),t("div",null,[...s[0]||(s[0]=[n(`

Button Component ​

The _button component creates reactive buttons with built-in support for loading states, icons, badges, and disabled states.

Basic Usage ​

javascript
_button({ onclick: () => alert('Clicked!') }, 'Click Me')

Loading State ​

The $loading signal automatically shows a spinner and disables the button.

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.

javascript
_button({ icon: '⭐' }, 'Favorite')
+_button({ icon: 'πŸ’Ύ' }, 'Save')
+_button({ icon: 'πŸ—‘οΈ', class: 'btn-error' }, 'Delete')

Badges ​

Add badges to buttons for notifications or status indicators.

javascript
_button({ badge: '3' }, 'Notifications')
+_button({ badge: 'New', badgeClass: 'badge-secondary' }, 'Update Available')

Button Variants ​

Use daisyUI classes to style your buttons.

javascript
_button({ class: 'btn-primary' }, 'Primary')
+_button({ class: 'btn-secondary' }, 'Secondary')
+_button({ class: 'btn-outline' }, 'Outline')
+_button({ class: 'btn-sm' }, 'Small')

Counter Example ​

javascript
const $count = $(0)
+
+_button({ 
+  onclick: () => $count($count() + 1),
+  icon: 'πŸ”’'
+}, () => \`Count: \${$count()}\`)

Async Action Example ​

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 ​

PropTypeDescription
$loadingSignal<boolean>Shows spinner and disables button
$disabledSignal<boolean>Disables the button
iconstring | NodeIcon to display before text
badgestringBadge text to display
badgeClassstringAdditional CSS classes for badge
classstring | functionAdditional CSS classes
onclickfunctionClick event handler
typestringButton type ('button', 'submit', etc.)
`,29)])])}const g=i(h,[["render",l]]);export{o as __pageData,g as default}; diff --git a/docs/assets/ui_button.md.B087poC6.lean.js b/docs/assets/ui_button.md.B087poC6.lean.js new file mode 100644 index 0000000..fc55151 --- /dev/null +++ b/docs/assets/ui_button.md.B087poC6.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"Button Component","description":"","frontmatter":{},"headers":[],"relativePath":"ui/button.md","filePath":"ui/button.md"}'),h={name:"ui/button.md"};function l(e,s,k,p,d,E){return a(),t("div",null,[...s[0]||(s[0]=[n("",29)])])}const g=i(h,[["render",l]]);export{o as __pageData,g as default}; diff --git a/docs/assets/ui_form.md.CZVTxszG.js b/docs/assets/ui_form.md.CZVTxszG.js new file mode 100644 index 0000000..c90abf1 --- /dev/null +++ b/docs/assets/ui_form.md.CZVTxszG.js @@ -0,0 +1,28 @@ +import{_ as a,o as i,c as e,ae as t}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Form Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/form.md","filePath":"ui/form.md"}'),n={name:"ui/form.md"};function l(h,s,d,p,r,k){return i(),e("div",null,[...s[0]||(s[0]=[t(`

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.

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.

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.

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.

javascript
const $volume = $(50)
+
+_range({
+  label: 'Volume',
+  min: 0,
+  max: 100,
+  step: 1,
+  $value: $volume
+})

Complete Form Example ​

API Reference ​

_select ​

PropTypeDescription
labelstringField label
optionsArray<{value: any, label: string}>Select options
$valueSignal<any>Selected value signal

_checkbox ​

PropTypeDescription
labelstringCheckbox label
$valueSignal<boolean>Checked state signal

_radio ​

PropTypeDescription
namestringRadio group name
labelstringRadio option label
valueanyValue for this option
$valueSignal<any>Group selected value signal

_range ​

PropTypeDescription
labelstringSlider label
minnumberMinimum value
maxnumberMaximum value
stepnumberStep increment
$valueSignal<number>Current value signal
`,29)])])}const E=a(n,[["render",l]]);export{c as __pageData,E as default}; diff --git a/docs/assets/ui_form.md.CZVTxszG.lean.js b/docs/assets/ui_form.md.CZVTxszG.lean.js new file mode 100644 index 0000000..24312ad --- /dev/null +++ b/docs/assets/ui_form.md.CZVTxszG.lean.js @@ -0,0 +1 @@ +import{_ as a,o as i,c as e,ae as t}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Form Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/form.md","filePath":"ui/form.md"}'),n={name:"ui/form.md"};function l(h,s,d,p,r,k){return i(),e("div",null,[...s[0]||(s[0]=[t("",29)])])}const E=a(n,[["render",l]]);export{c as __pageData,E as default}; diff --git a/docs/assets/ui_input.md.mJqBxkG3.js b/docs/assets/ui_input.md.mJqBxkG3.js new file mode 100644 index 0000000..56c1375 --- /dev/null +++ b/docs/assets/ui_input.md.mJqBxkG3.js @@ -0,0 +1,60 @@ +import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Input Component","description":"","frontmatter":{},"headers":[],"relativePath":"ui/input.md","filePath":"ui/input.md"}'),l={name:"ui/input.md"};function h(e,s,p,k,d,E){return a(),n("div",null,[...s[0]||(s[0]=[t(`

Input Component ​

The _input component creates reactive form inputs with built-in support for labels, tooltips, error messages, and two-way binding.

Basic Usage ​

javascript
const $name = $('')
+
+_input({
+  label: 'Name',
+  placeholder: 'Enter your name',
+  $value: $name
+})

With Tooltip ​

The tip prop adds an info badge with a tooltip.

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.

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.

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.

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 ​

PropTypeDescription
labelstringField label text
tipstringTooltip text shown on hover
$valueSignal<any>Two-way bound value signal
$errorSignal<string|null>Error message signal
typestringInput type (text, email, password, number, etc.)
placeholderstringPlaceholder text
classstring | functionAdditional CSS classes
oninputfunctionInput event handler
onchangefunctionChange event handler
disabledbooleanDisabled state

Examples ​

Registration Form Field ​

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)
+})
`,27)])])}const o=i(l,[["render",h]]);export{g as __pageData,o as default}; diff --git a/docs/assets/ui_input.md.mJqBxkG3.lean.js b/docs/assets/ui_input.md.mJqBxkG3.lean.js new file mode 100644 index 0000000..a43c16a --- /dev/null +++ b/docs/assets/ui_input.md.mJqBxkG3.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Input Component","description":"","frontmatter":{},"headers":[],"relativePath":"ui/input.md","filePath":"ui/input.md"}'),l={name:"ui/input.md"};function h(e,s,p,k,d,E){return a(),n("div",null,[...s[0]||(s[0]=[t("",27)])])}const o=i(l,[["render",h]]);export{g as __pageData,o as default}; diff --git a/docs/assets/ui_installation.md.C7ubLVYa.js b/docs/assets/ui_installation.md.C7ubLVYa.js new file mode 100644 index 0000000..45bba36 --- /dev/null +++ b/docs/assets/ui_installation.md.C7ubLVYa.js @@ -0,0 +1,10 @@ +import{_ as s,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Installation","description":"","frontmatter":{},"headers":[],"relativePath":"ui/installation.md","filePath":"ui/installation.md"}'),e={name:"ui/installation.md"};function l(p,i,h,r,o,k){return a(),t("div",null,[...i[0]||(i[0]=[n(`

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 ​

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
`,19)])])}const g=s(e,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/ui_installation.md.C7ubLVYa.lean.js b/docs/assets/ui_installation.md.C7ubLVYa.lean.js new file mode 100644 index 0000000..c776321 --- /dev/null +++ b/docs/assets/ui_installation.md.C7ubLVYa.lean.js @@ -0,0 +1 @@ +import{_ as s,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Installation","description":"","frontmatter":{},"headers":[],"relativePath":"ui/installation.md","filePath":"ui/installation.md"}'),e={name:"ui/installation.md"};function l(p,i,h,r,o,k){return a(),t("div",null,[...i[0]||(i[0]=[n("",19)])])}const g=s(e,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/ui_introduction.md.CpBz5t8n.js b/docs/assets/ui_introduction.md.CpBz5t8n.js new file mode 100644 index 0000000..ae61593 --- /dev/null +++ b/docs/assets/ui_introduction.md.CpBz5t8n.js @@ -0,0 +1 @@ +import{_ as e,o,c as a,ae as r}from"./chunks/framework.C8AWLET_.js";const p=JSON.parse('{"title":"UI Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/introduction.md","filePath":"ui/introduction.md"}'),n={name:"ui/introduction.md"};function d(i,t,s,c,l,u){return o(),a("div",null,[...t[0]||(t[0]=[r('

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 ​

What's Included ​

The UI plugin provides a comprehensive set of reactive components:

CategoryComponents
Actions_button
Forms_input, _select, _checkbox, _radio, _range
Layout_fieldset, _accordion, _drawer
Navigation_navbar, _menu, _tabs
Overlays_modal, _dropdown
Feedback_badge, _tooltip

Next Steps ​

',11)])])}const m=e(n,[["render",d]]);export{p as __pageData,m as default}; diff --git a/docs/assets/ui_introduction.md.CpBz5t8n.lean.js b/docs/assets/ui_introduction.md.CpBz5t8n.lean.js new file mode 100644 index 0000000..911091b --- /dev/null +++ b/docs/assets/ui_introduction.md.CpBz5t8n.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as a,ae as r}from"./chunks/framework.C8AWLET_.js";const p=JSON.parse('{"title":"UI Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/introduction.md","filePath":"ui/introduction.md"}'),n={name:"ui/introduction.md"};function d(i,t,s,c,l,u){return o(),a("div",null,[...t[0]||(t[0]=[r("",11)])])}const m=e(n,[["render",d]]);export{p as __pageData,m as default}; diff --git a/docs/assets/ui_layout.md.DLaYXca7.js b/docs/assets/ui_layout.md.DLaYXca7.js new file mode 100644 index 0000000..8b9d7b5 --- /dev/null +++ b/docs/assets/ui_layout.md.DLaYXca7.js @@ -0,0 +1,29 @@ +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Layout Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/layout.md","filePath":"ui/layout.md"}'),n={name:"ui/layout.md"};function l(h,s,p,d,o,k){return a(),t("div",null,[...s[0]||(s[0]=[e(`

Layout Components ​

Layout components for structuring your application with containers, sections, and collapsible panels.

Fieldset (_fieldset) ​

Groups related form fields with a legend.

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 ​

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.

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.

javascript
_accordion({ title: 'Open by Default', open: true }, [
+  $.html('p', {}, 'This accordion starts open.')
+])

Complete Layout Example ​

API Reference ​

_fieldset ​

PropTypeDescription
legendstringFieldset title/legend text
classstring | functionAdditional CSS classes

_accordion ​

PropTypeDescription
titlestringAccordion header text
namestringGroup name for radio behavior (optional)
openbooleanInitially 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 })
+  ])
+])
`,34)])])}const E=i(n,[["render",l]]);export{c as __pageData,E as default}; diff --git a/docs/assets/ui_layout.md.DLaYXca7.lean.js b/docs/assets/ui_layout.md.DLaYXca7.lean.js new file mode 100644 index 0000000..8964d8e --- /dev/null +++ b/docs/assets/ui_layout.md.DLaYXca7.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"Layout Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/layout.md","filePath":"ui/layout.md"}'),n={name:"ui/layout.md"};function l(h,s,p,d,o,k){return a(),t("div",null,[...s[0]||(s[0]=[e("",34)])])}const E=i(n,[["render",l]]);export{c as __pageData,E as default}; diff --git a/docs/assets/ui_modal.md.Hjip_i1A.js b/docs/assets/ui_modal.md.Hjip_i1A.js new file mode 100644 index 0000000..c66465e --- /dev/null +++ b/docs/assets/ui_modal.md.Hjip_i1A.js @@ -0,0 +1,29 @@ +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"Modal & Drawer Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/modal.md","filePath":"ui/modal.md"}'),h={name:"ui/modal.md"};function l(e,s,k,p,d,r){return a(),t("div",null,[...s[0]||(s[0]=[n(`

Modal & Drawer Components ​

Overlay components for dialogs, side panels, and popups with reactive control.

A dialog component that appears on top of the page. The modal is completely removed from the DOM when closed, optimizing performance.

Basic Modal ​

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')
+])
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')
+])

Drawer (_drawer) ​

A sidebar panel that slides in from the side.

Basic Drawer ​

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 ​

API Reference ​

PropTypeDescription
$openSignal<boolean>Controls modal visibility
titlestringModal title text

_drawer ​

PropTypeDescription
idstringUnique identifier for the drawer
$openSignal<boolean>Controls drawer visibility
contentHTMLElementMain content area
sideHTMLElementSidebar content
`,24)])])}const g=i(h,[["render",l]]);export{o as __pageData,g as default}; diff --git a/docs/assets/ui_modal.md.Hjip_i1A.lean.js b/docs/assets/ui_modal.md.Hjip_i1A.lean.js new file mode 100644 index 0000000..f2caf03 --- /dev/null +++ b/docs/assets/ui_modal.md.Hjip_i1A.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"Modal & Drawer Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/modal.md","filePath":"ui/modal.md"}'),h={name:"ui/modal.md"};function l(e,s,k,p,d,r){return a(),t("div",null,[...s[0]||(s[0]=[n("",24)])])}const g=i(h,[["render",l]]);export{o as __pageData,g as default}; diff --git a/docs/assets/ui_navigation.md.CK3sbH-I.js b/docs/assets/ui_navigation.md.CK3sbH-I.js new file mode 100644 index 0000000..8f1e904 --- /dev/null +++ b/docs/assets/ui_navigation.md.CK3sbH-I.js @@ -0,0 +1,51 @@ +import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Navigation Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/navigation.md","filePath":"ui/navigation.md"}'),h={name:"ui/navigation.md"};function l(k,s,p,e,d,E){return a(),n("div",null,[...s[0]||(s[0]=[t(`

Navigation Components ​

Navigation components for building menus, navbars, and tabs with reactive active states.

A responsive navigation bar with built-in styling.

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')
+  ])
+])

Vertical navigation menu with active state highlighting.

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.

javascript
const $activeTab = $('profile')
+
+_tabs({ items: [
+  { 
+    label: 'Profile', 
+    active: () => $activeTab() === 'profile',
+    onclick: () => $activeTab('profile')
+  },
+  { 
+    label: 'Settings', 
+    active: () => $activeTab() === 'settings',
+    onclick: () => $activeTab('settings')
+  }
+]})

A dropdown menu that appears on click.

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 ​

API Reference ​

PropTypeDescription
classstring | functionAdditional CSS classes
PropTypeDescription
itemsArray<{label: string, icon?: any, active?: boolean|function, onclick: function}>Menu items

_tabs ​

PropTypeDescription
itemsArray<{label: string, active: boolean|function, onclick: function}>Tab items
PropTypeDescription
labelstringDropdown trigger text
classstring | functionAdditional CSS classes
`,29)])])}const o=i(h,[["render",l]]);export{g as __pageData,o as default}; diff --git a/docs/assets/ui_navigation.md.CK3sbH-I.lean.js b/docs/assets/ui_navigation.md.CK3sbH-I.lean.js new file mode 100644 index 0000000..6c13f24 --- /dev/null +++ b/docs/assets/ui_navigation.md.CK3sbH-I.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as n,ae as t}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Navigation Components","description":"","frontmatter":{},"headers":[],"relativePath":"ui/navigation.md","filePath":"ui/navigation.md"}'),h={name:"ui/navigation.md"};function l(k,s,p,e,d,E){return a(),n("div",null,[...s[0]||(s[0]=[t("",29)])])}const o=i(h,[["render",l]]);export{g as __pageData,o as default}; diff --git a/docs/assets/vite_plugin.md.BxGCvKkk.js b/docs/assets/vite_plugin.md.BxGCvKkk.js new file mode 100644 index 0000000..2229591 --- /dev/null +++ b/docs/assets/vite_plugin.md.BxGCvKkk.js @@ -0,0 +1,49 @@ +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Vite Plugin: File-based Routing","description":"","frontmatter":{},"headers":[],"relativePath":"vite/plugin.md","filePath":"vite/plugin.md"}'),e={name:"vite/plugin.md"};function l(p,s,h,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

Vite Plugin: File-based Routing ​

The sigproRouter plugin for Vite automates route generation by scanning your pages directory. It creates a virtual module that you can import directly into your code, eliminating the need to maintain a manual routes array.

1. Project Structure ​

To use the plugin, organize your files within the src/pages directory. The folder hierarchy directly determines your application's URL structure. SigPro uses brackets [param] for dynamic segments.

text
my-sigpro-app/
+β”œβ”€β”€ src/
+β”‚   β”œβ”€β”€ pages/
+β”‚   β”‚   β”œβ”€β”€ index.js          β†’  #/
+β”‚   β”‚   β”œβ”€β”€ about.js          β†’  #/about
+β”‚   β”‚   β”œβ”€β”€ users/
+β”‚   β”‚   β”‚   └── [id].js       β†’  #/users/:id
+β”‚   β”‚   └── blog/
+β”‚   β”‚       β”œβ”€β”€ index.js      β†’  #/blog
+β”‚   β”‚       └── [slug].js     β†’  #/blog/:slug
+β”‚   β”œβ”€β”€ App.js                (Main Layout)
+β”‚   └── main.js               (Entry Point)
+β”œβ”€β”€ vite.config.js
+└── package.json

2. Setup & Configuration ​

Add the plugin to your vite.config.js. It works out of the box with zero configuration.

javascript
// vite.config.js
+import { defineConfig } from 'vite';
+import { sigproRouter } from 'sigpro/vite';
+
+export default defineConfig({
+  plugins: [sigproRouter()]
+});

3. Implementation ​

Thanks to SigPro's synchronous initialization, you no longer need to wrap your mounting logic in .then() blocks.

Option A: Direct in main.js ​

Ideal for single-page applications where the router controls the entire viewport.

javascript
// src/main.js
+import { $ } from 'sigpro';
+import { routes } from 'virtual:sigpro-routes';
+
+// The Core already has $.router ready
+$.mount($.router(routes), '#app');

Option B: Inside App.js (Persistent Layout) ​

Recommended for professional apps with a fixed Sidebar or Navbar that should not re-render when changing pages.

javascript
// src/main.js
+import { $ } from 'sigpro';
+import App from './App.js';
+
+$.mount(App, '#app');
+
+// src/App.js
+import { routes } from 'virtual:sigpro-routes';
+
+export default () => div({ class: 'layout' }, [
+  header([
+    h1("SigPro App"),
+    nav([
+      button({ onclick: () => $.router.go('/') }, "Home"),
+      button({ onclick: () => $.router.go('/blog') }, "Blog")
+    ])
+  ]),
+  // Only the content inside <main> will be swapped reactively
+  main($.router(routes))
+]);

4. Route Mapping Reference ​

The plugin follows a simple convention to transform your file system into a routing map.

File PathGenerated PathDescription
index.js/The application root.
about.js/aboutA static page.
[id].js/:idDynamic parameter (passed to the component).
blog/index.js/blogFolder index page.
_utils.jsIgnoredFiles starting with _ are excluded from routing.

5. How it Works (Vite Virtual Module) ​

The plugin generates a virtual module named virtual:sigpro-routes. This module exports an array of objects compatible with $.router():

javascript
// Internal representation generated by the plugin
+export const routes = [
+  { path: '/', component: () => import('/src/pages/index.js') },
+  { path: '/users/:id', component: () => import('/src/pages/users/[id].js') },
+  // ...
+];

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 ​

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
`,30)])])}const c=i(e,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/vite_plugin.md.CtukKq_1.lean.js b/docs/assets/vite_plugin.md.BxGCvKkk.lean.js similarity index 61% rename from docs/assets/vite_plugin.md.CtukKq_1.lean.js rename to docs/assets/vite_plugin.md.BxGCvKkk.lean.js index 8544b5e..72fd347 100644 --- a/docs/assets/vite_plugin.md.CtukKq_1.lean.js +++ b/docs/assets/vite_plugin.md.BxGCvKkk.lean.js @@ -1 +1 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Vite Plugin: File-based Routing","description":"","frontmatter":{},"headers":[],"relativePath":"vite/plugin.md","filePath":"vite/plugin.md"}'),e={name:"vite/plugin.md"};function p(l,s,h,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n("",24)])])}const E=i(e,[["render",p]]);export{g as __pageData,E as default}; +import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Vite Plugin: File-based Routing","description":"","frontmatter":{},"headers":[],"relativePath":"vite/plugin.md","filePath":"vite/plugin.md"}'),e={name:"vite/plugin.md"};function l(p,s,h,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n("",30)])])}const c=i(e,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/vite_plugin.md.CtukKq_1.js b/docs/assets/vite_plugin.md.CtukKq_1.js deleted file mode 100644 index edaaf0c..0000000 --- a/docs/assets/vite_plugin.md.CtukKq_1.js +++ /dev/null @@ -1,50 +0,0 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Vite Plugin: File-based Routing","description":"","frontmatter":{},"headers":[],"relativePath":"vite/plugin.md","filePath":"vite/plugin.md"}'),e={name:"vite/plugin.md"};function p(l,s,h,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

Vite Plugin: File-based Routing ​

The sigproRouter plugin for Vite automates route generation by scanning your pages directory. It creates a virtual module that you can import directly into your code, eliminating the need to maintain a manual routes array.

1. Project Structure ​

To use the plugin, organize your files within the src/pages directory. The folder hierarchy directly determines your application's URL structure.

text
my-sigpro-app/
-β”œβ”€β”€ src/
-β”‚   β”œβ”€β”€ pages/
-β”‚   β”‚   β”œβ”€β”€ index.js          β†’  #/
-β”‚   β”‚   β”œβ”€β”€ about.js          β†’  #/about
-β”‚   β”‚   β”œβ”€β”€ users/
-β”‚   β”‚   β”‚   └── [id].js       β†’  #/users/:id
-β”‚   β”‚   └── blog/
-β”‚   β”‚       β”œβ”€β”€ index.js      β†’  #/blog
-β”‚   β”‚       └── [slug].js     β†’  #/blog/:slug
-β”‚   β”œβ”€β”€ App.js                (Optional App Shell)
-β”‚   └── main.js               (Entry Point)
-β”œβ”€β”€ vite.config.js
-└── package.json

2. Setup & Configuration ​

Add the plugin to your vite.config.js.

javascript
// vite.config.js
-import { defineConfig } from 'vite';
-import { sigproRouter } from 'sigpro/vite';
-
-export default defineConfig({
-  plugins: [sigproRouter()]
-});

3. Implementation ​

You can implement the router either directly in your entry point or inside an App component to support persistent layouts (like a navbar that doesn't re-render).

Option A: Direct in main.js ​

Best for simple apps where the router occupies the entire viewport.

javascript
// src/main.js
-import { $ } from 'sigpro';
-import { Router } from 'sigpro/plugins';
-import { routes } from 'virtual:sigpro-routes';
-
-$.plugin(Router).then(() => {
-  $.mount(_router(routes), '#app');
-});

Option B: Inside App.js (With Layout) ​

Recommended for apps with a fixed Sidebar or Navbar.

javascript
// src/main.js
-import { $ } from 'sigpro';
-import { Router } from 'sigpro/plugins';
-
-$.plugin(Router).then(() => {
-  import('./App.js').then(app => $.mount(app.default, '#app'));
-});
-
-// src/App.js
-import { routes } from 'virtual:sigpro-routes';
-
-export default () => {
-  return div({ class: 'layout' }, [
-    header([
-      h1("SigPro App"),
-      nav([
-        a({ href: '#/' }, "Home"),
-        a({ href: '#/blog' }, "Blog")
-      ])
-    ]),
-    // The router only swaps the content inside this <main> tag
-    main(_router(routes))
-  ]);
-};

4. Route Mapping Reference ​

File PathGenerated RouteLogic
index.js/Home page
about.js/aboutStatic path
[id].js/:idDynamic parameter
blog/index.js/blogFolder index
_utils.jsIgnoredFiles starting with _ are skipped

5. Installation ​

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
`,24)])])}const E=i(e,[["render",p]]);export{g as __pageData,E as default}; diff --git a/docs/guide/getting-started.html b/docs/guide/getting-started.html index 14f5208..cdf0e15 100644 --- a/docs/guide/getting-started.html +++ b/docs/guide/getting-started.html @@ -13,12 +13,13 @@ - + + -
Skip to content

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:

bash
npm install SigPro
bash
pnpm add SigPro
bash
yarn add SigPro
bash
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';
+    
Skip to content

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:

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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");
@@ -44,7 +45,7 @@
   h1("Clean Syntax"),
   p("No more boilerplate.")
 ]);
- + \ No newline at end of file diff --git a/docs/guide/why.html b/docs/guide/why.html index a66d10b..6b210e6 100644 --- a/docs/guide/why.html +++ b/docs/guide/why.html @@ -14,18 +14,19 @@ + -
Skip to content

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 PrimitiveWhat It Enables
Closures & ProxiesAutomatic dependency tracking without heavy overhead.
ES ModulesNative modularity and lazy loading without complex bundlers.
Direct DOM APIsSurgical updates that are faster than any reconciliation algorithm.
Microtask QueuesBatching 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.
+    
Skip to content

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 PrimitiveWhat It Enables
Closures & ProxiesAutomatic dependency tracking without heavy overhead.
ES ModulesNative modularity and lazy loading without complex bundlers.
Direct DOM APIsSurgical updates that are faster than any reconciliation algorithm.
Microtask QueuesBatching 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.

MetricSigProSolidJSSvelteVueReact
Bundle Size (gzip)πŸ₯‡ < 2KBπŸ₯ˆ 7KBπŸ₯‰ 16KB20KB45KB
ArchitectureAtomicAtomicCompiledV-DOMV-DOM
Initial RenderπŸ₯‡ FastestπŸ₯ˆ FastπŸ₯‰ FastAverageSlow
Update PerfπŸ₯‡ SurgicalπŸ₯‡ SurgicalπŸ₯ˆ FastπŸ₯‰ AverageSlow
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."

- + \ No newline at end of file diff --git a/docs/hashmap.json b/docs/hashmap.json index 62468aa..1b853b5 100644 --- a/docs/hashmap.json +++ b/docs/hashmap.json @@ -1 +1 @@ -{"api__.md":"BVVMY-2O","api_html.md":"-lEpgX-Z","api_mount.md":"eGRwkZvh","api_quick.md":"Cy_XozKR","api_tags.md":"Ch1qUgrw","guide_getting-started.md":"RUz_X0AL","guide_why.md":"lyU7T5_c","index.md":"BX4IouPL","plugins_core.debug.md":"BRT_r6k8","plugins_core.fetch.md":"BIc8aMQh","plugins_core.router.md":"CWklH7Gb","plugins_core.storage.md":"lAkbO1ib","plugins_core.ui.md":"DAV-GYyQ","plugins_custom.md":"DZNaLrm0","plugins_quick.md":"ODjl7edh","vite_plugin.md":"CtukKq_1"} +{"api__.md":"CJ6A9esy","api_html.md":"-lEpgX-Z","api_mount.md":"DK1GUmQ8","api_quick.md":"Cy_XozKR","api_router.md":"BV6vPWg-","api_tags.md":"BAYRMzRh","guide_getting-started.md":"BJ3Lg3i9","guide_why.md":"lyU7T5_c","index.md":"DTGopCPx","plugins_core.debug.md":"BKuhgPoj","plugins_core.fetch.md":"BIc8aMQh","plugins_core.router.md":"Bg_zmVp3","plugins_core.storage.md":"C6UMGg_o","plugins_core.ui.md":"C434Uobv","plugins_custom.md":"D6eJnwEa","plugins_quick.md":"BBIL_nLZ","ui_button.md":"B087poC6","ui_form.md":"CZVTxszG","ui_input.md":"mJqBxkG3","ui_installation.md":"C7ubLVYa","ui_introduction.md":"CpBz5t8n","ui_layout.md":"DLaYXca7","ui_modal.md":"Hjip_i1A","ui_navigation.md":"CK3sbH-I","vite_plugin.md":"BxGCvKkk"} diff --git a/docs/index.html b/docs/index.html index a728d16..403ce1e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -13,12 +13,13 @@ - + + -
Skip to content

SigProAtomic Unified Reactive Engine

Fine-grained reactivity, built-in routing, and modular plugins. All under 2KB.

SigPro Logo

Why SigPro? ​

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.

The Core in Action ​

javascript
import { $ } from 'sigpro2';
+    
Skip to content

SigProAtomic Unified Reactive Engine

Fine-grained reactivity, built-in routing, and modular plugins. All under 2KB.

SigPro Logo

Why SigPro? ​

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.

The Core in Action ​

javascript
import { $ } from 'sigpro2';
 
 // A reactive state Signal
 const $count = $(0);
@@ -34,8 +35,8 @@
 ]);
 
 $.mount(Counter);

Key Features ​

⚑️ 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.


Quick Install ​

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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
- +$.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.


Quick Install ​

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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
+ \ No newline at end of file diff --git a/docs/plugins/core.debug.html b/docs/plugins/core.debug.html index d620294..21bd7ea 100644 --- a/docs/plugins/core.debug.html +++ b/docs/plugins/core.debug.html @@ -13,12 +13,13 @@ - + + -
Skip to content

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';
+    
Skip to content

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
@@ -27,7 +28,7 @@
 
 $.plugin(plugins).then(() => {
   import('./App.js').then(app => $.mount(app.default));
-});
bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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 () => {
+});
bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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" });
 
@@ -44,8 +45,8 @@
 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.
- +_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.
+ \ No newline at end of file diff --git a/docs/plugins/core.fetch.html b/docs/plugins/core.fetch.html index 50ce9dd..7d06003 100644 --- a/docs/plugins/core.fetch.html +++ b/docs/plugins/core.fetch.html @@ -14,11 +14,12 @@ + -
Skip to content

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';
+    
Skip to content

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(() => {
@@ -47,8 +48,8 @@
   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.
- +});

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.
+ \ No newline at end of file diff --git a/docs/plugins/core.router.html b/docs/plugins/core.router.html index 651381d..c030b88 100644 --- a/docs/plugins/core.router.html +++ b/docs/plugins/core.router.html @@ -13,12 +13,13 @@ - + + -
Skip to content

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.

bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
bash
pnpm add -D tailwindcss @tailwindcss/vite daisyui@next
bash
yarn add -D tailwindcss @tailwindcss/vite daisyui@next
bash
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 = [
+    
Skip to content

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.

bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
bash
pnpm add -D tailwindcss @tailwindcss/vite daisyui@next
bash
yarn add -D tailwindcss @tailwindcss/vite daisyui@next
bash
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', 
@@ -48,8 +49,8 @@
       onclick: () => _router.go('/settings') 
     }
   ]
-})
- +})
+ \ No newline at end of file diff --git a/docs/plugins/core.storage.html b/docs/plugins/core.storage.html index f53634e..bb2f6ab 100644 --- a/docs/plugins/core.storage.html +++ b/docs/plugins/core.storage.html @@ -13,17 +13,18 @@ - + + -
Skip to content

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';
+    
Skip to content

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));
-});
bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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 () => {
+});
bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
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' );
 
@@ -46,8 +47,8 @@
 _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");
- +_debug($score, "Game Score");
+ \ No newline at end of file diff --git a/docs/plugins/core.ui.html b/docs/plugins/core.ui.html index 8a340b7..bb4d510 100644 --- a/docs/plugins/core.ui.html +++ b/docs/plugins/core.ui.html @@ -3,7 +3,7 @@ - Official UI Plugin: UI | SigPro + SigPro UI Plugin - Complete Documentation | SigPro @@ -13,42 +13,465 @@ - + + -
Skip to content

Official UI Plugin: UI ​

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 components.

1. Prerequisites & Installation ​

To use these components, you must install the styling engine. SigPro UI provides the logic, but Tailwind and daisyUI provide the visuals.

bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
bash
pnpm add -D tailwindcss @tailwindcss/vite daisyui@next
bash
yarn add -D tailwindcss @tailwindcss/vite daisyui@next
bash
bun add -d tailwindcss @tailwindcss/vite daisyui@next

Would you like to continue with the Router.md documentation now?

CSS Configuration (app.css) ​

In Tailwind v4, configuration is handled directly in your CSS. Create a src/app.css file:

css
/* src/app.css */
+    
Skip to content

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
  2. Core Concepts
  3. Form Components
  4. Action Components
  5. Layout Components
  6. Navigation Components
  7. Feedback Components
  8. Container Components
  9. Complete Examples
  10. Styling Guide
  11. 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";
-
-/* Import daisyUI v5 as a Tailwind v4 plugin */
 @plugin "daisyui";
 
-/* Optional: Configure themes */
-@custom-variant dark (&:where(.dark, [data-theme="dark"], [data-theme="dark"] *)));

2. Initialization ​

You must import your CSS and register the UI plugin in your entry point. This populates the global scope with reactive component helpers (prefixed with _).

javascript
// main.js
+/* 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(() => {
-  // Global components like _button and _input are now ready
+  // All UI components are now registered
   import('./App.js').then(app => $.mount(app.default));
-});

3. Core Component Tags (_tags) ​

SigPro UI components are more than just HTML; they are Reactive Functional Components that manage complex states (loading, errors, accessibility) automatically.

A. Action Components (_button) ​

The _button automatically handles spinners and disabled states based on signals.

PropertyTypeDescription
$loadingsignalIf true, shows a spinner and disables the button.
$disabledsignalManually disables the button (logic-bound).
iconnode/strPrepends an icon to the text.
badgestringAppends a small badge to the button.
javascript
_button({ 
-  $loading: $isSaving, 
-  icon: 'πŸ’Ύ', 
-  class: 'btn-primary' 
-}, "Save Data")

B. High-Density Forms (_input, _select, _checkbox) ​

These components wrap the raw input in a fieldset with integrated labels and tooltips.

  • label: Field title displayed above the input.
  • tip: Displays a ? badge that shows a tooltip on hover.
  • $error: A signal that, when populated, turns the input red and displays the message.
  • $value: Two-way binding. Updates the signal on input and the input on signal change.
javascript
_input({
-  label: "Username",
-  tip: "Choose a unique name",
-  $value: $name,
-  $error: $nameError
-})

4. Complex UI Patterns ​

Reactive Modals (_modal) ​

The _modal is surgically mounted. If the $open signal is false, the component is completely removed from the DOM, optimizing performance.

javascript
const $showModal = $(false);
+});

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);
 
-_modal({ $open: $showModal, title: "Alert" }, [
-  p("Are you sure you want to proceed?"),
-  _button({ onclick: () => doAction() }, "Confirm")
-])

Designed to work seamlessly with the Router.

ComponentKey Logic
_tabsAccepts an active property (signal or function) to highlight the current tab.
_drawerA responsive sidebar that toggles via an ID or an $open signal.
_navbarStandard top bar with shadow and glass effect support.
_menuVertical navigation list with active state support.

5. Summary Table: UI Globals ​

Once $.plugin(UI) is active, these tags are available project-wide:

TagCategoryUse Case
_fieldsetLayoutGrouping related inputs with a legend.
_accordionContentCollapsible sections (FAQs).
_badgeFeedbackStatus indicators (Success, Warning).
_tooltipFeedbackDescriptive text on hover.
_rangeInputReactive slider for numerical values.

What's next? ​

With the UI ready and styled via Tailwind v4, we can move to the Router.md. We will explain how to link _tabs and _menu to different URL paths for a full SPA experience.

Would you like to start with the Router configuration?

- +// 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:

PropertyTypeDescription
labelstringField label text
tipstringTooltip text shown on hover of a "?" badge
$valuesignalTwo-way bound value signal
$errorsignalError message signal (shows red border + message)
typestringInput type: 'text', 'email', 'password', etc.
placeholderstringPlaceholder text
classstring|functionAdditional 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:

PropertyTypeDescription
labelstringField label
optionsArray<{value: any, label: string}>Select options
$valuesignalTwo-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:

PropertyTypeDescription
labelstringCheckbox label text
$valuesignalBoolean 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:

PropertyTypeDescription
labelstringRadio option label
valueanyValue for this radio option
$valuesignalGroup 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:

PropertyTypeDescription
labelstringSlider label
minnumberMinimum value
maxnumberMaximum value
stepnumberStep increment
$valuesignalCurrent 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:

PropertyTypeDescription
$loadingsignalShows spinner + disables when true
$disabledsignalManual disabled state
iconstring|HTMLElementIcon element or emoji/unicode
badgestringBadge text to display
badgeClassstringAdditional badge styling
typestringButton type: 'button', 'submit', etc.
onclickfunctionClick 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:

PropertyTypeDescription
legendstringFieldset title
classstring|functionAdditional 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:

PropertyTypeDescription
titlestringAccordion header text
namestringOptional group name (radio behavior)
openbooleanInitially 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:

PropertyTypeDescription
idstringUnique ID for checkbox toggle
$opensignalBoolean signal for drawer state
contentHTMLElementMain content area
sideHTMLElementSidebar 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) }
+    ]})
+  ]
+})

Responsive navigation bar with built-in styling.

Properties:

PropertyTypeDescription
classstring|functionAdditional 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')
+  ])
+])

Sidebar or dropdown menu with active state support.

Properties:

PropertyTypeDescription
itemsArray<{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:

PropertyTypeDescription
itemsArray<{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:

PropertyTypeDescription
classstring|functionBadge 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:

PropertyTypeDescription
tipstringTooltip text
positionstringTooltip 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 ​

Programmatically controlled modal dialog.

Properties:

PropertyTypeDescription
$opensignalBoolean signal controlling visibility
titlestringModal title
classstring|functionAdditional 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 menu that appears on click.

Properties:

PropertyTypeDescription
labelstringDropdown trigger text
classstring|functionAdditional 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:

ComponentFunction 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())
+ \ No newline at end of file diff --git a/docs/plugins/custom.html b/docs/plugins/custom.html index f4bd76f..8772e5b 100644 --- a/docs/plugins/custom.html +++ b/docs/plugins/custom.html @@ -13,12 +13,13 @@ - + + -
Skip to content

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
+    
Skip to content

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
@@ -65,8 +66,8 @@
 $.plugin(ConfigLoader).then(() => {
   console.log("Config loaded:", $.config);
   $.mount(App);
-});

4. Best Practices for Plugin Authors ​

RuleDescription
PrefixingUse _ for UI components (_modal) and $. for logic ($.fetch).
IdempotencyEnsure calling $.plugin(MyPlugin) twice doesn't break the app.
EncapsulationUse the $ instance passed as an argument rather than importing it again inside the plugin.
ReactivityAlways 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.

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
- +});

4. Best Practices for Plugin Authors ​

RuleDescription
PrefixingUse _ for UI components (_modal) and $. for logic ($.fetch).
IdempotencyEnsure calling $.plugin(MyPlugin) twice doesn't break the app.
EncapsulationUse the $ instance passed as an argument rather than importing it again inside the plugin.
ReactivityAlways 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.

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
+ \ No newline at end of file diff --git a/docs/plugins/quick.html b/docs/plugins/quick.html index df5bbd9..4f5a715 100644 --- a/docs/plugins/quick.html +++ b/docs/plugins/quick.html @@ -13,12 +13,13 @@ - + + -
Skip to content

Extending SigPro: $.plugin ​

The plugin system is the engine's way of growing. It allows you to inject new functionality directly into the $ object or load external resources.

1. How Plugins Work ​

A plugin in SigPro is simply a function that receives the core instance. When you run $.plugin(MyPlugin), the engine hands over the $ object so the plugin can attach new methods or register global tags (like div(), span(), etc.).

Functional Plugin Example ​

javascript
// A plugin that adds a simple logger to any signal
+    
Skip to content

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()));
@@ -28,33 +29,24 @@
 // Activation
 $.plugin(Logger);
 const $count = $(0);
-$.watch($count, "Counter"); // Now available globally via $

2. Initialization Patterns ​

Since plugins often set up global variables (like the HTML tags), the order of initialization is critical. Here are the two ways to start your app:

This is the most robust way. It ensures all global tags (div, button, etc.) are created before your App code is even read by the browser.

javascript
// main.js
-import { $ } from 'sigpro';
-import { UI, Router } from 'sigpro/plugins';
-
-// 1. Load plugins first
-$.plugin([UI, Router]).then(() => {
-  
-  // 2. Import your app only after the environment is ready
-  import('./App.js').then(appFile => {
-    const MyApp = appFile.default;
-    $.mount(MyApp, '#app');
-  });
-
-});

Option B: Static Start (No Global Tags) ​

Use this only if you prefer not to use global tags and want to use $.html directly in your components. This allows for standard static imports.

javascript
// main.js
+$.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.

This is the standard way to build apps. It's clean, readable, and supports standard ESM imports.

javascript
// main.js
 import { $ } from 'sigpro';
 import { UI } from 'sigpro/plugins';
-import MyApp from './App.js'; // Static import works here
+import App from './App.js'; // Static import works perfectly!
 
+// 1. Register plugins
 $.plugin(UI);
-$.mount(MyApp, '#app');

Warning: In this mode, if App.js uses div() instead of $.html('div'), it will throw a ReferenceError.


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 and executed.

javascript
// Loading external libraries as plugins
-await $.plugin([
+
+// 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'
-]);
-
-console.log("External resources are ready to use!");

4. Polymorphic Loading Reference ​

The $.plugin method adapts to whatever you throw at it:

Input TypeActionBehavior
FunctionExecutes fn($)Synchronous / Immediate
String (URL)Injects <script src="...">Asynchronous (Returns Promise)
ArrayProcesses each item in the listReturns Promise if any item is Async

πŸ’‘ Pro Tip: Why the .then()? ​

Using $.plugin([...]).then(...) is like giving your app a "Pre-flight Check". It guarantees that:

  1. All reactive methods are attached.
  2. Global HTML tags are defined.
  3. External libraries (like Chart.js) are loaded.
  4. The result: Your components are cleaner, smaller, and error-free.
- +]).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 TypeActionBehavior
FunctionExecutes fn($)Synchronous: Immediate availability.
String (URL)Injects <script src="...">Asynchronous: Returns a Promise.
ArrayProcesses each item in the listReturns 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 ​

GoalCode
Local Plugin$.plugin(myPlugin)
Multiple Plugins$.plugin([UI, Router])
External Library$.plugin('https://...').then(...)
Hybrid Load$.plugin([UI, 'https://...']).then(...)
+ \ No newline at end of file diff --git a/docs/sigpro.js b/docs/sigpro.js new file mode 100644 index 0000000..995ed40 --- /dev/null +++ b/docs/sigpro.js @@ -0,0 +1,223 @@ +/** + * SigPro - Atomic Unified Reactive Engine + * A lightweight, fine-grained reactivity system with built-in routing and plugin support. + * @author Gemini & User + */ +(() => { + /** @type {Function|null} Internal tracker for the currently executing reactive effect. */ + let activeEffect = null; + + /** + * @typedef {Object} SigPro + * @property {function(any|function, string=): Function} $ - Creates a Signal or Computed. Optional key for localStorage. + * @property {function(string, Object=, any=): HTMLElement} html - Creates a reactive HTML element. + * @property {function((HTMLElement|function), (HTMLElement|string)=): void} mount - Mounts a component to the DOM. + * @property {function(Array): HTMLElement} router - Initializes a hash-based router. + * @property {function(string): void} router.go - Programmatic navigation to a hash path. + * @property {function((function|string|Array)): (Promise|SigPro)} plugin - Extends SigPro or loads external scripts. + */ + + /** + * Creates a Signal (state) or a Computed/Effect (reaction). + * Supports optional persistence in localStorage. + * * @param {any|function} initial - Initial value or a function for computed logic. + * @param {string} [key] - Optional localStorage key for automatic state persistence. + * @returns {Function} A reactive accessor/mutator function. + */ + const $ = (initial, key) => { + const subs = new Set(); + + if (typeof initial === 'function') { + let cached; + const runner = () => { + const prev = activeEffect; + activeEffect = runner; + try { + const next = initial(); + if (!Object.is(cached, next)) { + cached = next; + subs.forEach(s => s()); + } + } finally { activeEffect = prev; } + }; + runner(); + return () => { + if (activeEffect) subs.add(activeEffect); + return cached; + }; + } + + if (key) { + const saved = localStorage.getItem(key); + if (saved !== null) { + try { initial = JSON.parse(saved); } catch (e) { } + } + } + + return (...args) => { + if (args.length) { + const next = typeof args[0] === 'function' ? args[0](initial) : args[0]; + if (!Object.is(initial, next)) { + initial = next; + if (key) localStorage.setItem(key, JSON.stringify(initial)); + subs.forEach(s => s()); + } + } + if (activeEffect) subs.add(activeEffect); + return initial; + }; + }; + + /** + * Hyperscript engine to render reactive HTML nodes. + * @param {string} tag - The HTML tag name (e.g., 'div', 'button'). + * @param {Object} [props] - Attributes, events (onclick), or reactive props ($value, $class). + * @param {any} [content] - String, Node, Array of nodes, or reactive function. + * @returns {HTMLElement} A live DOM element linked to SigPro signals. + */ + $.html = (tag, props = {}, content = []) => { + const el = document.createElement(tag); + if (typeof props !== 'object' || props instanceof Node || Array.isArray(props) || typeof props === 'function') { + content = props; + props = {}; + } + + for (let [key, val] of Object.entries(props)) { + if (key.startsWith('on')) { + el.addEventListener(key.toLowerCase().slice(2), val); + } else if (key.startsWith('$')) { + const attr = key.slice(1); + // Two-way binding for inputs + if ((attr === 'value' || attr === 'checked') && typeof val === 'function') { + const ev = attr === 'checked' ? 'change' : 'input'; + el.addEventListener(ev, e => val(attr === 'checked' ? e.target.checked : e.target.value)); + } + // Reactive attribute update + $(() => { + const v = typeof val === 'function' ? val() : val; + if (attr === 'value' || attr === 'checked') el[attr] = v; + else if (typeof v === 'boolean') el.toggleAttribute(attr, v); + else el.setAttribute(attr, v ?? ''); + }); + } else el.setAttribute(key, val); + } + + const append = (c) => { + if (Array.isArray(c)) return c.forEach(append); + if (typeof c === 'function') { + const node = document.createTextNode(''); + $(() => { + const res = c(); + if (res instanceof Node) { + if (node.parentNode) node.replaceWith(res); + } else { + node.textContent = res ?? ''; + } + }); + return el.appendChild(node); + } + el.appendChild(c instanceof Node ? c : document.createTextNode(c ?? '')); + }; + append(content); + return el; + }; + + const tags = ['div', 'span', 'p', 'button', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'label', 'section', 'nav', 'main', 'header', 'footer', 'input', 'form', 'img', 'select', 'option', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'canvas', 'video', 'audio']; + tags.forEach(t => window[t] = (p, c) => $.html(t, p, c)); + + /** + * Application mounter. + * @param {HTMLElement|function} node - Root component or element to mount. + * @param {HTMLElement|string} [target=document.body] - Target element or CSS selector. + */ + $.mount = (node, target = document.body) => { + const el = typeof target === 'string' ? document.querySelector(target) : target; + if (el) { + el.innerHTML = ''; + el.appendChild(typeof node === 'function' ? node() : node); + } + }; + + /** + * Initializes a reactive hash-based router. + * Maps URL hash changes to component rendering and supports Vite's dynamic imports. + * * @param {Array<{path: string, component: Function|Promise|HTMLElement}>} routes - Array of route objects. + * @returns {HTMLElement} A reactive div container that swaps content based on the current hash. + */ + $.router = (routes) => { + const sPath = $(window.location.hash.replace(/^#/, "") || "/"); + window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/")); + + return $.html('div', [ + () => { + const current = sPath(); + const cP = current.split('/').filter(Boolean); + + const route = routes.find(r => { + const rP = r.path.split('/').filter(Boolean); + if (rP.length !== cP.length) return false; + return rP.every((part, i) => part.startsWith(':') || part === cP[i]); + }) || routes.find(r => r.path === "*"); + + if (!route) return $.html('h1', "404 - Not Found"); + + const rP = route.path.split('/').filter(Boolean); + const params = {}; + rP.forEach((part, i) => { + if (part.startsWith(':')) params[part.slice(1)] = cP[i]; + }); + + const result = typeof route.component === 'function' ? route.component(params) : route.component; + + if (result instanceof Promise) { + const $lazyNode = $($.html('span', "Loading...")); + result.then(m => { + const content = m.default || m; + const finalView = typeof content === 'function' ? content(params) : content; + $lazyNode(finalView); + }); + return () => $lazyNode(); + } + + return result instanceof Node ? result : $.html('span', String(result)); + } + ]); + }; + + /** + * Programmatically navigates to a specific path using the hash. + * * @param {string} path - The destination path (e.g., '/home' or 'settings'). + * @example + * $.router.go('/profile/42'); + */ + $.router.go = (path) => { + window.location.hash = path.startsWith('/') ? path : `/${path}`; + }; + + /** + * Polymorphic Plugin System. + * Registers internal functions or loads external .js files as plugins. + * @param {function|string|Array} source - Plugin function or URL(s). + * @returns {Promise|SigPro} Resolves with the $ instance after loading or registering. + */ + $.plugin = (source) => { + if (typeof source === 'function') { + source($); + return $; + } + const urls = Array.isArray(source) ? source : [source]; + return Promise.all(urls.map(url => new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = url; + script.async = true; + script.onload = () => { + console.log(`%c[SigPro] Plugin Loaded: ${url}`, "color: #51cf66; font-weight: bold;"); + resolve(); + }; + script.onerror = () => reject(new Error(`[SigPro] Failed to load: ${url}`)); + document.head.appendChild(script); + }))).then(() => $); + }; + + window.$ = $; +})(); \ No newline at end of file diff --git a/docs/ui/button.html b/docs/ui/button.html new file mode 100644 index 0000000..130d561 --- /dev/null +++ b/docs/ui/button.html @@ -0,0 +1,59 @@ + + + + + + Button Component | SigPro + + + + + + + + + + + + + + + +
Skip to content

Button Component ​

The _button component creates reactive buttons with built-in support for loading states, icons, badges, and disabled states.

Basic Usage ​

javascript
_button({ onclick: () => alert('Clicked!') }, 'Click Me')

Loading State ​

The $loading signal automatically shows a spinner and disables the button.

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.

javascript
_button({ icon: '⭐' }, 'Favorite')
+_button({ icon: 'πŸ’Ύ' }, 'Save')
+_button({ icon: 'πŸ—‘οΈ', class: 'btn-error' }, 'Delete')

Badges ​

Add badges to buttons for notifications or status indicators.

javascript
_button({ badge: '3' }, 'Notifications')
+_button({ badge: 'New', badgeClass: 'badge-secondary' }, 'Update Available')

Button Variants ​

Use daisyUI classes to style your buttons.

javascript
_button({ class: 'btn-primary' }, 'Primary')
+_button({ class: 'btn-secondary' }, 'Secondary')
+_button({ class: 'btn-outline' }, 'Outline')
+_button({ class: 'btn-sm' }, 'Small')

Counter Example ​

javascript
const $count = $(0)
+
+_button({ 
+  onclick: () => $count($count() + 1),
+  icon: 'πŸ”’'
+}, () => `Count: ${$count()}`)

Async Action Example ​

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 ​

PropTypeDescription
$loadingSignal<boolean>Shows spinner and disables button
$disabledSignal<boolean>Disables the button
iconstring | NodeIcon to display before text
badgestringBadge text to display
badgeClassstringAdditional CSS classes for badge
classstring | functionAdditional CSS classes
onclickfunctionClick event handler
typestringButton type ('button', 'submit', etc.)
+ + + + \ No newline at end of file diff --git a/docs/ui/form.html b/docs/ui/form.html new file mode 100644 index 0000000..73ebd62 --- /dev/null +++ b/docs/ui/form.html @@ -0,0 +1,53 @@ + + + + + + Form Components | SigPro + + + + + + + + + + + + + + + +
Skip to content

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.

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.

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.

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.

javascript
const $volume = $(50)
+
+_range({
+  label: 'Volume',
+  min: 0,
+  max: 100,
+  step: 1,
+  $value: $volume
+})

Complete Form Example ​

API Reference ​

_select ​

PropTypeDescription
labelstringField label
optionsArray<{value: any, label: string}>Select options
$valueSignal<any>Selected value signal

_checkbox ​

PropTypeDescription
labelstringCheckbox label
$valueSignal<boolean>Checked state signal

_radio ​

PropTypeDescription
namestringRadio group name
labelstringRadio option label
valueanyValue for this option
$valueSignal<any>Group selected value signal

_range ​

PropTypeDescription
labelstringSlider label
minnumberMinimum value
maxnumberMaximum value
stepnumberStep increment
$valueSignal<number>Current value signal
+ + + + \ No newline at end of file diff --git a/docs/ui/input.html b/docs/ui/input.html new file mode 100644 index 0000000..8adac5a --- /dev/null +++ b/docs/ui/input.html @@ -0,0 +1,85 @@ + + + + + + Input Component | SigPro + + + + + + + + + + + + + + + +
Skip to content

Input Component ​

The _input component creates reactive form inputs with built-in support for labels, tooltips, error messages, and two-way binding.

Basic Usage ​

javascript
const $name = $('')
+
+_input({
+  label: 'Name',
+  placeholder: 'Enter your name',
+  $value: $name
+})

With Tooltip ​

The tip prop adds an info badge with a tooltip.

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.

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.

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.

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 ​

PropTypeDescription
labelstringField label text
tipstringTooltip text shown on hover
$valueSignal<any>Two-way bound value signal
$errorSignal<string|null>Error message signal
typestringInput type (text, email, password, number, etc.)
placeholderstringPlaceholder text
classstring | functionAdditional CSS classes
oninputfunctionInput event handler
onchangefunctionChange event handler
disabledbooleanDisabled state

Examples ​

Registration Form Field ​

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)
+})
+ + + + \ No newline at end of file diff --git a/docs/ui/installation.html b/docs/ui/installation.html new file mode 100644 index 0000000..b94bd26 --- /dev/null +++ b/docs/ui/installation.html @@ -0,0 +1,35 @@ + + + + + + Installation | SigPro + + + + + + + + + + + + + + + +
Skip to content

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 ​

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
+ + + + \ No newline at end of file diff --git a/docs/ui/introduction.html b/docs/ui/introduction.html new file mode 100644 index 0000000..d0d0205 --- /dev/null +++ b/docs/ui/introduction.html @@ -0,0 +1,26 @@ + + + + + + UI Components | SigPro + + + + + + + + + + + + + + + +
Skip to content

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 ​

What's Included ​

The UI plugin provides a comprehensive set of reactive components:

CategoryComponents
Actions_button
Forms_input, _select, _checkbox, _radio, _range
Layout_fieldset, _accordion, _drawer
Navigation_navbar, _menu, _tabs
Overlays_modal, _dropdown
Feedback_badge, _tooltip

Next Steps ​

+ + + + \ No newline at end of file diff --git a/docs/ui/layout.html b/docs/ui/layout.html new file mode 100644 index 0000000..5b1cde6 --- /dev/null +++ b/docs/ui/layout.html @@ -0,0 +1,54 @@ + + + + + + Layout Components | SigPro + + + + + + + + + + + + + + + +
Skip to content

Layout Components ​

Layout components for structuring your application with containers, sections, and collapsible panels.

Fieldset (_fieldset) ​

Groups related form fields with a legend.

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 ​

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.

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.

javascript
_accordion({ title: 'Open by Default', open: true }, [
+  $.html('p', {}, 'This accordion starts open.')
+])

Complete Layout Example ​

API Reference ​

_fieldset ​

PropTypeDescription
legendstringFieldset title/legend text
classstring | functionAdditional CSS classes

_accordion ​

PropTypeDescription
titlestringAccordion header text
namestringGroup name for radio behavior (optional)
openbooleanInitially 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 })
+  ])
+])
+ + + + \ No newline at end of file diff --git a/docs/ui/modal.html b/docs/ui/modal.html new file mode 100644 index 0000000..1f14255 --- /dev/null +++ b/docs/ui/modal.html @@ -0,0 +1,54 @@ + + + + + + Modal & Drawer Components | SigPro + + + + + + + + + + + + + + + +
Skip to content

Modal & Drawer Components ​

Overlay components for dialogs, side panels, and popups with reactive control.

A dialog component that appears on top of the page. The modal is completely removed from the DOM when closed, optimizing performance.

Basic Modal ​

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')
+])
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')
+])

Drawer (_drawer) ​

A sidebar panel that slides in from the side.

Basic Drawer ​

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 ​

API Reference ​

PropTypeDescription
$openSignal<boolean>Controls modal visibility
titlestringModal title text

_drawer ​

PropTypeDescription
idstringUnique identifier for the drawer
$openSignal<boolean>Controls drawer visibility
contentHTMLElementMain content area
sideHTMLElementSidebar content
+ + + + \ No newline at end of file diff --git a/docs/ui/navigation.html b/docs/ui/navigation.html new file mode 100644 index 0000000..807bbdc --- /dev/null +++ b/docs/ui/navigation.html @@ -0,0 +1,76 @@ + + + + + + Navigation Components | SigPro + + + + + + + + + + + + + + + +
Skip to content

Navigation Components ​

Navigation components for building menus, navbars, and tabs with reactive active states.

A responsive navigation bar with built-in styling.

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')
+  ])
+])

Vertical navigation menu with active state highlighting.

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.

javascript
const $activeTab = $('profile')
+
+_tabs({ items: [
+  { 
+    label: 'Profile', 
+    active: () => $activeTab() === 'profile',
+    onclick: () => $activeTab('profile')
+  },
+  { 
+    label: 'Settings', 
+    active: () => $activeTab() === 'settings',
+    onclick: () => $activeTab('settings')
+  }
+]})

A dropdown menu that appears on click.

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 ​

API Reference ​

PropTypeDescription
classstring | functionAdditional CSS classes
PropTypeDescription
itemsArray<{label: string, icon?: any, active?: boolean|function, onclick: function}>Menu items

_tabs ​

PropTypeDescription
itemsArray<{label: string, active: boolean|function, onclick: function}>Tab items
PropTypeDescription
labelstringDropdown trigger text
classstring | functionAdditional CSS classes
+ + + + \ No newline at end of file diff --git a/docs/vite/plugin.html b/docs/vite/plugin.html index d37897e..3522ae8 100644 --- a/docs/vite/plugin.html +++ b/docs/vite/plugin.html @@ -13,12 +13,13 @@ - + + -
Skip to content

Vite Plugin: File-based Routing ​

The sigproRouter plugin for Vite automates route generation by scanning your pages directory. It creates a virtual module that you can import directly into your code, eliminating the need to maintain a manual routes array.

1. Project Structure ​

To use the plugin, organize your files within the src/pages directory. The folder hierarchy directly determines your application's URL structure.

text
my-sigpro-app/
+    
Skip to content

Vite Plugin: File-based Routing ​

The sigproRouter plugin for Vite automates route generation by scanning your pages directory. It creates a virtual module that you can import directly into your code, eliminating the need to maintain a manual routes array.

1. Project Structure ​

To use the plugin, organize your files within the src/pages directory. The folder hierarchy directly determines your application's URL structure. SigPro uses brackets [param] for dynamic segments.

text
my-sigpro-app/
 β”œβ”€β”€ src/
 β”‚   β”œβ”€β”€ pages/
 β”‚   β”‚   β”œβ”€β”€ index.js          β†’  #/
@@ -28,47 +29,46 @@
 β”‚   β”‚   └── blog/
 β”‚   β”‚       β”œβ”€β”€ index.js      β†’  #/blog
 β”‚   β”‚       └── [slug].js     β†’  #/blog/:slug
-β”‚   β”œβ”€β”€ App.js                (Optional App Shell)
+β”‚   β”œβ”€β”€ App.js                (Main Layout)
 β”‚   └── main.js               (Entry Point)
 β”œβ”€β”€ vite.config.js
-└── package.json

2. Setup & Configuration ​

Add the plugin to your vite.config.js.

javascript
// vite.config.js
+└── package.json

2. Setup & Configuration ​

Add the plugin to your vite.config.js. It works out of the box with zero configuration.

javascript
// vite.config.js
 import { defineConfig } from 'vite';
 import { sigproRouter } from 'sigpro/vite';
 
 export default defineConfig({
   plugins: [sigproRouter()]
-});

3. Implementation ​

You can implement the router either directly in your entry point or inside an App component to support persistent layouts (like a navbar that doesn't re-render).

Option A: Direct in main.js ​

Best for simple apps where the router occupies the entire viewport.

javascript
// src/main.js
+});

3. Implementation ​

Thanks to SigPro's synchronous initialization, you no longer need to wrap your mounting logic in .then() blocks.

Option A: Direct in main.js ​

Ideal for single-page applications where the router controls the entire viewport.

javascript
// src/main.js
 import { $ } from 'sigpro';
-import { Router } from 'sigpro/plugins';
 import { routes } from 'virtual:sigpro-routes';
 
-$.plugin(Router).then(() => {
-  $.mount(_router(routes), '#app');
-});

Option B: Inside App.js (With Layout) ​

Recommended for apps with a fixed Sidebar or Navbar.

javascript
// src/main.js
+// The Core already has $.router ready
+$.mount($.router(routes), '#app');

Option B: Inside App.js (Persistent Layout) ​

Recommended for professional apps with a fixed Sidebar or Navbar that should not re-render when changing pages.

javascript
// src/main.js
 import { $ } from 'sigpro';
-import { Router } from 'sigpro/plugins';
+import App from './App.js';
 
-$.plugin(Router).then(() => {
-  import('./App.js').then(app => $.mount(app.default, '#app'));
-});
+$.mount(App, '#app');
 
 // src/App.js
 import { routes } from 'virtual:sigpro-routes';
 
-export default () => {
-  return div({ class: 'layout' }, [
-    header([
-      h1("SigPro App"),
-      nav([
-        a({ href: '#/' }, "Home"),
-        a({ href: '#/blog' }, "Blog")
-      ])
-    ]),
-    // The router only swaps the content inside this <main> tag
-    main(_router(routes))
-  ]);
-};

4. Route Mapping Reference ​

File PathGenerated RouteLogic
index.js/Home page
about.js/aboutStatic path
[id].js/:idDynamic parameter
blog/index.js/blogFolder index
_utils.jsIgnoredFiles starting with _ are skipped

5. Installation ​

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
- +export default () => div({ class: 'layout' }, [ + header([ + h1("SigPro App"), + nav([ + button({ onclick: () => $.router.go('/') }, "Home"), + button({ onclick: () => $.router.go('/blog') }, "Blog") + ]) + ]), + // Only the content inside <main> will be swapped reactively + main($.router(routes)) +]);

4. Route Mapping Reference ​

The plugin follows a simple convention to transform your file system into a routing map.

File PathGenerated PathDescription
index.js/The application root.
about.js/aboutA static page.
[id].js/:idDynamic parameter (passed to the component).
blog/index.js/blogFolder index page.
_utils.jsIgnoredFiles starting with _ are excluded from routing.

5. How it Works (Vite Virtual Module) ​

The plugin generates a virtual module named virtual:sigpro-routes. This module exports an array of objects compatible with $.router():

javascript
// Internal representation generated by the plugin
+export const routes = [
+  { path: '/', component: () => import('/src/pages/index.js') },
+  { path: '/users/:id', component: () => import('/src/pages/users/[id].js') },
+  // ...
+];

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 ​

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
+ \ No newline at end of file diff --git a/plugins/ui.js b/plugins/ui.js index b73d62d..0b75cc7 100644 --- a/plugins/ui.js +++ b/plugins/ui.js @@ -1,5 +1,5 @@ /** - * SigPro UI 2.0 - daisyUI v5 & Tailwind v4 Plugin + * SigPro UI - daisyUI v5 & Tailwind v4 Plugin * Provides a set of reactive functional components. */ diff --git a/sigpro/sigpro.js b/sigpro/sigpro.js index 32019c6..995ed40 100644 --- a/sigpro/sigpro.js +++ b/sigpro/sigpro.js @@ -1,5 +1,5 @@ /** - * SigPro 2.0 - Atomic Unified Reactive Engine + * SigPro - Atomic Unified Reactive Engine * A lightweight, fine-grained reactivity system with built-in routing and plugin support. * @author Gemini & User */ @@ -8,32 +8,31 @@ let activeEffect = null; /** - * @typedef {Object} SigPro - * @property {function(string, Object=, any=): HTMLElement} html - Creates a reactive HTML element. - * @property {function((HTMLElement|function), (HTMLElement|string)=): void} mount - Mounts a component to the DOM. - * @property {function(Array): HTMLElement} router - Initializes a hash-based router. - * @property {function(string): void} router.go - Programmatic navigation to a hash path. - * @property {function((function|string|Array)): (Promise|SigPro)} plugin - Extends SigPro or loads external scripts. - */ + * @typedef {Object} SigPro + * @property {function(any|function, string=): Function} $ - Creates a Signal or Computed. Optional key for localStorage. + * @property {function(string, Object=, any=): HTMLElement} html - Creates a reactive HTML element. + * @property {function((HTMLElement|function), (HTMLElement|string)=): void} mount - Mounts a component to the DOM. + * @property {function(Array): HTMLElement} router - Initializes a hash-based router. + * @property {function(string): void} router.go - Programmatic navigation to a hash path. + * @property {function((function|string|Array)): (Promise|SigPro)} plugin - Extends SigPro or loads external scripts. + */ /** - * Creates a Signal (state) or a Computed/Effect (reaction). - * @param {any|function} initial - Initial value for a signal, or a function for computed logic. - * @returns {Function} A reactive accessor/mutator function. - * @example - * const $count = $(0); // Signal: $count(5) to update, $count() to read. - * const $double = $(() => $count() * 2); // Computed: Auto-updates when $count changes. - */ - const $ = (initial) => { + * Creates a Signal (state) or a Computed/Effect (reaction). + * Supports optional persistence in localStorage. + * * @param {any|function} initial - Initial value or a function for computed logic. + * @param {string} [key] - Optional localStorage key for automatic state persistence. + * @returns {Function} A reactive accessor/mutator function. + */ + const $ = (initial, key) => { const subs = new Set(); - - // Logic for Computed Signals (Functions) + if (typeof initial === 'function') { let cached; const runner = () => { const prev = activeEffect; activeEffect = runner; - try { + try { const next = initial(); if (!Object.is(cached, next)) { cached = next; @@ -48,12 +47,19 @@ }; } - // Logic for Standard Signals (State) + if (key) { + const saved = localStorage.getItem(key); + if (saved !== null) { + try { initial = JSON.parse(saved); } catch (e) { } + } + } + return (...args) => { if (args.length) { const next = typeof args[0] === 'function' ? args[0](initial) : args[0]; if (!Object.is(initial, next)) { initial = next; + if (key) localStorage.setItem(key, JSON.stringify(initial)); subs.forEach(s => s()); } } @@ -116,6 +122,9 @@ return el; }; + const tags = ['div', 'span', 'p', 'button', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'label', 'section', 'nav', 'main', 'header', 'footer', 'input', 'form', 'img', 'select', 'option', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'canvas', 'video', 'audio']; + tags.forEach(t => window[t] = (p, c) => $.html(t, p, c)); + /** * Application mounter. * @param {HTMLElement|function} node - Root component or element to mount. @@ -129,6 +138,62 @@ } }; + /** + * Initializes a reactive hash-based router. + * Maps URL hash changes to component rendering and supports Vite's dynamic imports. + * * @param {Array<{path: string, component: Function|Promise|HTMLElement}>} routes - Array of route objects. + * @returns {HTMLElement} A reactive div container that swaps content based on the current hash. + */ + $.router = (routes) => { + const sPath = $(window.location.hash.replace(/^#/, "") || "/"); + window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/")); + + return $.html('div', [ + () => { + const current = sPath(); + const cP = current.split('/').filter(Boolean); + + const route = routes.find(r => { + const rP = r.path.split('/').filter(Boolean); + if (rP.length !== cP.length) return false; + return rP.every((part, i) => part.startsWith(':') || part === cP[i]); + }) || routes.find(r => r.path === "*"); + + if (!route) return $.html('h1', "404 - Not Found"); + + const rP = route.path.split('/').filter(Boolean); + const params = {}; + rP.forEach((part, i) => { + if (part.startsWith(':')) params[part.slice(1)] = cP[i]; + }); + + const result = typeof route.component === 'function' ? route.component(params) : route.component; + + if (result instanceof Promise) { + const $lazyNode = $($.html('span', "Loading...")); + result.then(m => { + const content = m.default || m; + const finalView = typeof content === 'function' ? content(params) : content; + $lazyNode(finalView); + }); + return () => $lazyNode(); + } + + return result instanceof Node ? result : $.html('span', String(result)); + } + ]); + }; + + /** + * Programmatically navigates to a specific path using the hash. + * * @param {string} path - The destination path (e.g., '/home' or 'settings'). + * @example + * $.router.go('/profile/42'); + */ + $.router.go = (path) => { + window.location.hash = path.startsWith('/') ? path : `/${path}`; + }; + /** * Polymorphic Plugin System. * Registers internal functions or loads external .js files as plugins. @@ -154,9 +219,5 @@ }))).then(() => $); }; - // Global HTML Tag Proxy Helpers - const tags = ['div', 'span', 'p', 'button', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'label', 'section', 'nav', 'main', 'header', 'footer', 'input', 'form', 'img', 'select', 'option', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'canvas', 'video', 'audio']; - tags.forEach(t => window[t] = (p, c) => $.html(t, p, c)); - window.$ = $; })(); \ No newline at end of file diff --git a/src/docs/.vitepress/cache/deps/_metadata.json b/src/docs/.vitepress/cache/deps/_metadata.json index 9d2241e..9df0773 100644 --- a/src/docs/.vitepress/cache/deps/_metadata.json +++ b/src/docs/.vitepress/cache/deps/_metadata.json @@ -7,7 +7,7 @@ "vue": { "src": "../../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js", "file": "vue.js", - "fileHash": "5e2bcecf", + "fileHash": "505179d7", "needsInterop": false }, "vitepress > @vue/devtools-api": { @@ -19,13 +19,13 @@ "vitepress > @vueuse/core": { "src": "../../../../../node_modules/@vueuse/core/index.mjs", "file": "vitepress___@vueuse_core.js", - "fileHash": "f08e5a15", + "fileHash": "3e2c9d39", "needsInterop": false }, "@theme/index": { "src": "../../../../../node_modules/vitepress/dist/client/theme-default/index.js", "file": "@theme_index.js", - "fileHash": "442c9e5b", + "fileHash": "127b4204", "needsInterop": false } }, diff --git a/src/docs/.vitepress/config.js b/src/docs/.vitepress/config.js index 9194ee0..0a4e182 100644 --- a/src/docs/.vitepress/config.js +++ b/src/docs/.vitepress/config.js @@ -8,7 +8,11 @@ export default defineConfig({ outDir: '../../docs', base: isDev ? '/absproxy/5174/sigpro/' : '/sigpro/', - // CONFIGURACIΓ“N DE VITE (Motor interno) + // AΓ‘ADIDO: Head para estilos + head: [ + ['link', { rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/npm/daisyui@5/dist/full.css' }] + ], + vite: { outDir: '../../docs', base: isDev ? '/absproxy/5174/sigpro/' : '/sigpro/', @@ -24,6 +28,8 @@ export default defineConfig({ { text: 'Home', link: '/' }, { text: 'Guide', link: '/guide/getting-started' }, { text: 'Api', link: '/api/quick' }, + // AΓ‘ADIDO: UI en nav + { text: 'UI', link: '/ui/introduction' }, ], sidebar: [ { @@ -40,6 +46,7 @@ export default defineConfig({ { text: 'Quick Start', link: '/api/quick' }, { text: '$', link: '/api/$' }, { text: '$.html', link: '/api/html' }, + { text: '$.router', link: '/api/router' }, { text: '$.mount', link: '/api/mount' }, { text: 'Tags', link: '/api/tags' }, ] @@ -48,11 +55,8 @@ export default defineConfig({ text: 'Plugins', items: [ { text: 'Quick Start', link: '/plugins/quick' }, - { text: '@core Router Plugin', link: '/plugins/core.router' }, { text: '@core UI Plugin', link: '/plugins/core.ui' }, - { text: '@core UI Fetch', link: '/plugins/core.fetch' }, - { text: '@core UI Storage', link: '/plugins/core.storage' }, - { text: '@core UI Debug', link: '/plugins/core.debug' }, + { text: '@core Debug', link: '/plugins/core.debug' }, { text: 'Custom', link: '/plugins/custom' }, ] }, @@ -61,6 +65,19 @@ export default defineConfig({ 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' }, + ] } ], socialLinks: [ diff --git a/src/docs/api/$.md b/src/docs/api/$.md index 61ed572..0088bfd 100644 --- a/src/docs/api/$.md +++ b/src/docs/api/$.md @@ -1,46 +1,45 @@ # The Reactive Core: `$( )` -The `$` function is the heart of **SigPro**. It is a **Unified Reactive Constructor** that handles state, derivations, and side effects through a single, consistent interface. +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 )` +## 1. The Constructor: `$( input, [key] )` -Depending on what you pass into `$( )`, SigPro creates a different type of reactive primitive: +Depending on the arguments you pass, SigPro creates different reactive primitives: -| Input Type | Result | Internal Behavior | -| :--- | :--- | :--- | -| **Value** (String, Number, Object...) | **Signal** | Creates a piece of mutable state. | -| **Function** | **Computed / Effect** | Creates a derived value that tracks dependencies. | +| Argument | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **input** | `Value` / `Function` | **Yes** | Initial state or reactive logic. | +| **key** | `string` | No | If provided, the signal **persists** in `localStorage`. | --- -## 2. Signal (State) +## 2. Signal (State & Persistence) -A **Signal** is a "box" that holds a value. It provides a getter/setter function to interact with that value. +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`. -* **When to use:** For data that changes over time (counters, user input, toggle states, API data). -* **Syntax:** `const $state = $(initialValue);` +* **Standard:** `const $count = $(0);` +* **Persistent:** `const $theme = $("light", "app-theme");` (Restores value on page reload). ### Example: ```javascript -const $name = $("Alice"); +const $user = $("Guest", "session-user"); // Automatically saved/loaded -// Read the value (Getter) -console.log($name()); // "Alice" +// Read (Getter) +console.log($user()); -// Update the value (Setter) -$name("Bob"); +// Update (Setter + Auto-save to Disk) +$user("Alice"); -// Update based on previous value -$name(current => current + " Smith"); +// Functional Update +$user(prev => prev.toUpperCase()); ``` --- ## 3. Computed (Derived State) -When you pass a **function** to `$( )` that **returns a value**, SigPro creates a **Computed Signal**. It automatically tracks which signals are used inside it and re-runs only when they change. +When you pass a **function** that **returns a value**, SigPro creates a **Computed Signal**. It tracks dependencies and recalculates only when necessary. -* **When to use:** For values that depend on other signals (totals, filtered lists, formatted strings). * **Syntax:** `const $derived = $(() => logic);` ### Example: @@ -48,54 +47,56 @@ When you pass a **function** to `$( )` that **returns a value**, SigPro creates const $price = $(100); const $qty = $(2); -// Automatically tracks $price and $qty +// Auto-tracks $price and $qty const $total = $(() => $price() * $qty()); -console.log($total()); // 200 - $qty(3); // $total updates to 300 automatically ``` --- -## 4. Effects (Side Effects) +## 4. Effects (Reactive Actions) -An **Effect** is a function passed to `$( )` that **does not return a value** (or returns `undefined`). SigPro treats this as a subscription that performs an action whenever its dependencies change. +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:** For DOM manipulations, logging, or syncing with external APIs (LocalStorage, Fetch). +* **When to use:** Logging, manual DOM tweaks, or syncing with external APIs. * **Syntax:** `$(() => { action });` ### Example: ```javascript -const $theme = $("light"); +const $status = $("online"); -// This effect runs every time $theme changes +// Runs every time $status changes $(() => { - document.body.className = $theme(); - console.log("Theme updated to:", $theme()); + console.log("System status is now:", $status()); }); - -$theme("dark"); // Logs: Theme updated to: dark ``` --- ## 5. Summary Table: Usage Guide -| Primitive | Logic Type | Returns Value? | Typical Use Case | +| Primitive | Logic Type | Persistence? | Typical Use Case | | :--- | :--- | :--- | :--- | -| **Signal** | Static | Yes (Mutable) | `const $user = $("Guest")` | -| **Computed** | Read-only | Yes (Automatic) | `const $isLoggedIn = $(() => $user() !== "Guest")` | -| **Effect** | Imperative | No | `$(() => localStorage.setItem('user', $user()))` | - - +| **Signal** | Mutable State | **Yes** (Optional) | `$(0, 'counter')` | +| **Computed** | Derived / Read-only | No | `$(() => $a() + $b())` | +| **Effect** | Imperative Action | No | `$(() => alert($msg()))` | --- -## πŸ’‘ Pro Tip: Naming Convention -In SigPro, we use the **`$` prefix** (e.g., `$count`) for variables that hold a reactive function. This makes it easy to distinguish between a standard variable and a reactive one at a glance: +## πŸ’‘ 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: ```javascript -let count = 0; // Static -const $count = $(0); // Reactive (Function) +let count = 0; // Static +const $count = $(0); // Reactive Signal ``` diff --git a/src/docs/api/mount.md b/src/docs/api/mount.md index d095da4..bd75b08 100644 --- a/src/docs/api/mount.md +++ b/src/docs/api/mount.md @@ -1,6 +1,6 @@ -# Application Mounter: `$.mount` +# Application Mounter: `$.router.mount` (Core) -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. +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. Syntax: `$.mount(node, [target])` @@ -14,18 +14,19 @@ The `$.mount` function is the entry point of your reactive world. It takes a **S ## 2. Usage Scenarios ### A. The "Clean Slate" (Main Entry) -In a modern app (like our `main.js` example), you usually want to control the entire page. By default, `$.mount` clears the target's existing HTML before mounting. +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. ```javascript // src/main.js -import { $ } from 'SigPro'; +import { $ } from 'sigpro'; import App from './App.js'; -$.mount(App); // Mounts to by default +// SigPro: No .then() needed, global tags are ready immediately +$.mount(App); ``` ### B. Targeting a Specific Container -If you have an existing HTML structure and only want **SigPro** to manage a specific part (like a `#root` div), pass a CSS selector or a reference. +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. ```html @@ -33,7 +34,7 @@ If you have an existing HTML structure and only want **SigPro** to manage a spec ``` ```javascript -// Local mount to a specific ID +// Mount to a specific ID $.mount(MyComponent, '#app-root'); // Or using a direct DOM reference @@ -43,11 +44,11 @@ $.mount(SidebarComponent, sidebar); --- -## 3. Mounting with Pure HTML -One of SigPro's strengths is that it works perfectly alongside "Old School" HTML. You can create a reactive "island" inside a static page. +## 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. ```javascript -// A small reactive widget in a static .js file +// A small reactive widget const CounterWidget = () => { const $c = $(0); return button({ onclick: () => $c(v => v + 1) }, [ @@ -55,41 +56,40 @@ const CounterWidget = () => { ]); }; -// Mount it into an existing div in your HTML +// Mount it into an existing div in your static HTML $.mount(CounterWidget, '#counter-container'); ``` --- -## 4. How it Works (The "Wipe" Logic) -When `$.mount` is called, it performs two critical steps: - -1. **Clearance:** It sets `target.innerHTML = ''`. This ensures no "zombie" HTML from previous renders or static placeholders interferes with your app. -2. **Injection:** It appends your component. If you passed a **Function**, it executes it first to get the DOM node. - +## 4. How it Works (Lifecycle) +When `$.mount` is called, it performs three critical steps: +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. --- ## 5. Global vs. Local Scope ### Global (The "Framework" Way) -In a standard Vite/ESM project, you initialize SigPro globally in `main.js`. This makes the `$` and the tag helpers (`div`, `button`, etc.) available everywhere in your project. +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. ```javascript -// main.js - Global Initialization -import 'SigPro'; +// src/main.js +import { $ } from 'sigpro'; -// Now any other file can just use: +// Any component in any file can now use: $.mount(() => h1("Global App")); ``` ### Local (The "Library" Way) -If you are worried about polluting the global `window` object, you can import and use SigPro locally within a specific module. +If you prefer to avoid polluting the `window` object, you can import and use SigPro locally within specific modules. ```javascript -// widget.js - Local usage -import { $ } from 'SigPro'; +// widget.js +import { $ } from 'sigpro'; const myNode = $.html('div', 'Local Widget'); $.mount(myNode, '#widget-target'); @@ -97,12 +97,11 @@ $.mount(myNode, '#widget-target'); --- -### Summary Cheat Sheet +## 6. Summary Cheat Sheet | Goal | Code | | :--- | :--- | | **Mount to body** | `$.mount(App)` | | **Mount to ID** | `$.mount(App, '#id')` | | **Mount to Element** | `$.mount(App, myElement)` | -| **Reactive Widget** | `$.mount(() => div("Hi"), '#widget')` | - +| **Direct Function** | `$.mount(() => div("Hi"), '#widget')` | diff --git a/src/docs/api/router.md b/src/docs/api/router.md new file mode 100644 index 0000000..becee7e --- /dev/null +++ b/src/docs/api/router.md @@ -0,0 +1,106 @@ +# Routing Engine: `$.router` + +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. + +## 1. Core Features + +* **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`. + +--- + +## 2. Syntax: `$.router(routes)` + +| Parameter | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **routes** | `Array` | **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. + +```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)) +]); +``` + +--- + +## 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. + +```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); +}; + +nav([ + NavLink('/', 'Home'), + NavLink('/settings', 'Settings') +]); +``` + +--- + +## 7. Summary: Route Component Types + +| 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. | diff --git a/src/docs/guide/getting-started.md b/src/docs/guide/getting-started.md index c6519e0..e026fde 100644 --- a/src/docs/guide/getting-started.md +++ b/src/docs/guide/getting-started.md @@ -8,18 +8,18 @@ You can install SigPro via your favorite package manager: ::: code-group ```bash [npm] -npm install SigPro +npm install sigpro ```` ```bash [pnpm] -pnpm add SigPro +pnpm add sigpro ``` ```bash [yarn] -yarn add SigPro +yarn add sigpro ``` ```bash [bun] -bun add SigPro +bun add sigpro ``` ::: diff --git a/src/docs/plugins/core.ui.md b/src/docs/plugins/core.ui.md index 4371ac8..c6f2505 100644 --- a/src/docs/plugins/core.ui.md +++ b/src/docs/plugins/core.ui.md @@ -1,50 +1,53 @@ -# Official UI Plugin: `UI` +# SigPro UI Plugin - Complete Documentation -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 components. +## Overview -## 1. Prerequisites & Installation +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. -To use these components, you must install the styling engine. SigPro UI provides the logic, but Tailwind and daisyUI provide the visuals. +## Table of Contents -::: code-group -```bash [NPM] +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 ``` -```bash [PNPM] -pnpm add -D tailwindcss @tailwindcss/vite daisyui@next -``` +### Step 2: Configure Tailwind CSS v4 -```bash [Yarn] -yarn add -D tailwindcss @tailwindcss/vite daisyui@next -``` - -```bash [Bun] -bun add -d tailwindcss @tailwindcss/vite daisyui@next -``` -::: - -Would you like to continue with the **Router.md** documentation now? - -### CSS Configuration (`app.css`) -In Tailwind v4, configuration is handled directly in your CSS. Create a `src/app.css` file: +Create `src/app.css`: ```css /* src/app.css */ @import "tailwindcss"; - -/* Import daisyUI v5 as a Tailwind v4 plugin */ @plugin "daisyui"; -/* Optional: Configure themes */ +/* 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"] *))); ``` ---- - -## 2. Initialization - -You must import your CSS and register the `UI` plugin in your entry point. This populates the global scope with reactive component helpers (prefixed with `_`). +### Step 3: Initialize in Your Entry Point ```javascript // main.js @@ -52,96 +55,902 @@ import './app.css'; import { $ } from 'sigpro'; import { UI } from 'sigpro/plugins'; +// Load the UI plugin - makes all _components globally available $.plugin(UI).then(() => { - // Global components like _button and _input are now ready + // All UI components are now registered import('./App.js').then(app => $.mount(app.default)); }); ``` --- -## 3. Core Component Tags (`_tags`) +## Core Concepts -SigPro UI components are more than just HTML; they are **Reactive Functional Components** that manage complex states (loading, errors, accessibility) automatically. +### Reactive Props -### A. Action Components (`_button`) -The `_button` automatically handles spinners and disabled states based on signals. - -| Property | Type | Description | -| :--- | :--- | :--- | -| **`$loading`** | `signal` | If true, shows a spinner and disables the button. | -| **`$disabled`**| `signal` | Manually disables the button (logic-bound). | -| **`icon`** | `node/str`| Prepends an icon to the text. | -| **`badge`** | `string` | Appends a small badge to the button. | +All UI components accept reactive props using the `$` prefix. When you pass a signal, the component automatically updates: ```javascript -_button({ - $loading: $isSaving, - icon: 'πŸ’Ύ', - class: 'btn-primary' -}, "Save Data") +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 +}) ``` -### B. High-Density Forms (`_input`, `_select`, `_checkbox`) -These components wrap the raw input in a `fieldset` with integrated labels and tooltips. +### The `parseClass` Helper -* **`label`**: Field title displayed above the input. -* **`tip`**: Displays a `?` badge that shows a tooltip on hover. -* **`$error`**: A signal that, when populated, turns the input red and displays the message. -* **`$value`**: **Two-way binding**. Updates the signal on input and the input on signal change. +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: "Username", - tip: "Choose a unique name", - $value: $name, - $error: $nameError + 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) } + ]}) + ] }) ``` --- -## 4. Complex UI Patterns +## Navigation Components -### Reactive Modals (`_modal`) -The `_modal` is surgically mounted. If the `$open` signal is `false`, the component is completely removed from the DOM, optimizing performance. +### `_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: "Alert" }, [ - p("Are you sure you want to proceed?"), - _button({ onclick: () => doAction() }, "Confirm") +_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')]) ]) ``` -### Navigation & Layout (`_tabs`, `_drawer`, `_navbar`) -Designed to work seamlessly with the **Router**. +--- -| Component | Key Logic | -| :--- | :--- | -| **`_tabs`** | Accepts an `active` property (signal or function) to highlight the current tab. | -| **`_drawer`** | A responsive sidebar that toggles via an ID or an `$open` signal. | -| **`_navbar`** | Standard top bar with shadow and glass effect support. | -| **`_menu`** | Vertical navigation list with active state support. | +## 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') + ]) + ]) + ]) + ]); +}; +``` --- -## 5. Summary Table: UI Globals +## Styling Guide -Once `$.plugin(UI)` is active, these tags are available project-wide: +### Theme Configuration -| Tag | Category | Use Case | -| :--- | :--- | :--- | -| `_fieldset` | Layout | Grouping related inputs with a `legend`. | -| `_accordion`| Content | Collapsible sections (FAQs). | -| `_badge` | Feedback | Status indicators (Success, Warning). | -| `_tooltip` | Feedback | Descriptive text on hover. | -| `_range` | Input | Reactive slider for numerical values. | +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') +``` --- -### What's next? -With the UI ready and styled via **Tailwind v4**, we can move to the **Router.md**. We will explain how to link `_tabs` and `_menu` to different URL paths for a full SPA experience. +## Best Practices -**Would you like to start with the Router configuration?** \ No newline at end of file +### 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()) +``` diff --git a/src/docs/plugins/quick.md b/src/docs/plugins/quick.md index e426a31..04e67b2 100644 --- a/src/docs/plugins/quick.md +++ b/src/docs/plugins/quick.md @@ -1,14 +1,14 @@ # Extending SigPro: `$.plugin` -The plugin system is the engine's way of growing. It allows you to inject new functionality directly into the `$` object or load external resources. +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 simply a function that receives the core instance. When you run `$.plugin(MyPlugin)`, the engine hands over the `$` object so the plugin can attach new methods or register global tags (like `div()`, `span()`, etc.). +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 logger to any signal +// A plugin that adds a simple watcher to any signal const Logger = ($) => { $.watch = (target, label = "Log") => { $(() => console.log(`[${label}]:`, target())); @@ -23,79 +23,72 @@ $.watch($count, "Counter"); // Now available globally via $ --- -## 2. Initialization Patterns +## 2. Initialization Patterns (SigPro) -Since plugins often set up global variables (like the HTML tags), the order of initialization is critical. Here are the two ways to start your app: +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. -### Option A: The "Safe" Async Start (Recommended) -This is the most robust way. It ensures all global tags (`div`, `button`, etc.) are created **before** your App code is even read by the browser. - -```javascript -// main.js -import { $ } from 'sigpro'; -import { UI, Router } from 'sigpro/plugins'; - -// 1. Load plugins first -$.plugin([UI, Router]).then(() => { - - // 2. Import your app only after the environment is ready - import('./App.js').then(appFile => { - const MyApp = appFile.default; - $.mount(MyApp, '#app'); - }); - -}); -``` - -### Option B: Static Start (No Global Tags) -Use this only if you prefer **not** to use global tags and want to use `$.html` directly in your components. This allows for standard static imports. +### 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 { UI } from 'sigpro/plugins'; -import MyApp from './App.js'; // Static import works here +import App from './App.js'; // Static import works perfectly! +// 1. Register plugins $.plugin(UI); -$.mount(MyApp, '#app'); + +// 2. Mount your app directly +$.mount(App, '#app'); ``` -> **Warning:** In this mode, if `App.js` uses `div()` instead of `$.html('div')`, it will throw a `ReferenceError`. --- ## 3. Resource Plugins (External Scripts) -You can pass a **URL** or an **Array of URLs**. SigPro will inject them as `