# ⚡ SigPro 1.2.18 – Complete API Reference SigPro is a **Real‑DOM first** reactive micro‑framework. No virtual DOM, no diffing overhead – it updates the DOM directly with surgical precision. Built‑in automatic cleanup prevents memory leaks, and the API is designed to be both tiny and powerful. ```javascript import { $, $$, watch, h, when, each, fx, router, req, mount, batch } from 'sigpro' // or use globally as window.$ etc. ``` --- ## 🔁 Core Reactivity ### `$(value, localStorageKey?)` – Signal & Computed Creates a reactive signal. If a function is passed, it becomes a **computed** signal that caches its result until dependencies change. | Usage | Description | |-------|-------------| | `const count = $(0)` | Basic signal, returns a getter/setter: `count()` reads, `count(5)` writes. | | `const double = $( () => count() * 2 )` | Computed signal – updates automatically when `count` changes. | | `const stored = $('hello', 'myKey')` | Persisted signal – reads/writes to `localStorage`. | **Example** ```javascript const count = $(0) const double = $( () => count() * 2 ) watch(() => { console.log(`count = ${count()}, double = ${double()}`) }) // logs on every change count(5) // triggers log: count=5, double=10 ``` --- ### `$$(object)` – Deep Reactive Proxy Makes a plain object (and all nested objects) deeply reactive. Any property access is tracked, any mutation triggers updates. ```javascript const state = $$({ user: { name: 'Alice', age: 30 }, items: [1,2,3] }) watch(() => { console.log(state.user.name) // tracks `user.name` }) state.user.name = 'Bob' // triggers the effect ``` > **Note**: `$$` caches proxies per original object, so calling `$$` multiple times on the same object returns the same proxy. --- ### `watch(source, callback?)` – Reactive Effect Two modes: 1. **Auto‑track mode** – pass a function: `watch(() => { /* reads signals */ })` Automatically re‑runs whenever any signal read inside changes. 2. **Explicit mode** – pass an array of signals and a callback: `watch([count, double], () => { ... })` Runs the callback when any of the listed signals change. The callback receives the new values. Both modes return a `stop` function that disposes the effect. ```javascript // auto mode const stop = watch(() => console.log(count())) // explicit mode watch([count, double], ([newCount, newDouble]) => { console.log(newCount, newDouble) }) ``` > **Important**: Effects are depth‑aware – they run in topological order, parents before children. --- ## 🧱 Components & DOM (Hyperscript) ### `h(tag, props, children)` – Create DOM Nodes The universal builder. `props` can be omitted. Children can be strings, numbers, nodes, arrays, or **dynamic functions**. | Feature | Example | |---------|---------| | Standard attributes | `h('div', { class: 'box', id: 'main' })` | | Events | `onClick: (e) => ...` (automatically cleaned up) | | Reactive attributes | `class: () => count() > 0 ? 'positive' : 'negative'` | | Two‑way binding | `value: mySignal` (works on `input`, `textarea`, `select`) | | Refs | `ref: (el) => ...` or `ref: { current: null }` | | SVG support | tag names like `svg`, `circle`, `path` – sets correct namespace | | Dangerous URL sanitising | `href` / `src` with `javascript:` or `data:` are blocked → `'#'` | **Dynamic children** – pass a function as a child, it will be re‑executed and the DOM patched automatically: ```javascript h('div', {}, [ () => count() > 0 ? h('span', {}, 'positive') : h('span', {}, 'zero or negative') ]) ``` ### Tag shortcuts SigPro defines **all standard HTML5 tags** as PascalCase globals (when run in browser) and also exports them as named exports. Example: ```javascript Div({ class: 'container' }, [ H1({}, 'Title'), Button({ onClick: () => alert('hi') }, 'Click me') ]) ``` Available tags: `a`, `abbr`, `article`, `aside`, `audio`, `b`, `blockquote`, `br`, `button`, `canvas`, `caption`, `cite`, `code`, `col`, `colgroup`, `datalist`, `dd`, `del`, `details`, `dfn`, `dialog`, `div`, `dl`, `dt`, `em`, `embed`, `fieldset`, `figcaption`, `figure`, `footer`, `form`, `h1`…`h6`, `header`, `hr`, `i`, `iframe`, `img`, `input`, `ins`, `kbd`, `label`, `legend`, `li`, `main`, `mark`, `meter`, `nav`, `object`, `ol`, `optgroup`, `option`, `output`, `p`, `picture`, `pre`, `progress`, `section`, `select`, `slot`, `small`, `source`, `span`, `strong`, `sub`, `summary`, `sup`, `svg`, `table`, `tbody`, `td`, `template`, `textarea`, `tfoot`, `th`, `thead`, `time`, `tr`, `u`, `ul`, `video`. --- ## 🧩 Flow Control Components ### `when(condition, thenComponent, elseComponent?)` Reactive conditional rendering. `condition` can be a boolean, a signal, or any function that returns a boolean. Both branches can be `Node`, `() => Node`, or `null`. Automatically disposes the unmounted branch. ```javascript when( () => user.loggedIn(), () => Div({}, 'Welcome back!'), () => Button({ onClick: () => login() }, 'Login') ) ``` --- ### `each(source, itemRenderer, keyFn)` Optimised keyed list rendering. `source` can be an array or a signal/function returning an array. `itemRenderer(item, index)` returns a Node (or a function that returns Nodes). `keyFn(item, index)` returns a unique identifier – **required** for efficient DOM reuse. ```javascript const items = $([{ id: 1, text: 'a' }, { id: 2, text: 'b' }]) each(items, (item) => Li({}, item.text), (item) => item.id ) ``` When the array changes, elements are added, removed, or reordered with minimal DOM operations. --- ## 💥 Effects & Lifecycle ### `onUnmount(fn)` Inside a component (function called from `h`), registers a cleanup function that runs when that component is removed from the DOM. ```javascript const Timer = () => { const interval = setInterval(() => console.log('tick'), 1000) onUnmount(() => clearInterval(interval)) return Div({}, 'Timer running') } ``` ### `batch(fn)` Batch multiple reactive updates into a single flush, improving performance. ```javascript batch(() => { count(1) name('John') // effects run only once after the batch ends }) ``` ### `untrack(fn)` Run a function without tracking any signal reads. ```javascript const logCount = () => { untrack(() => console.log('count is', count())) } ``` --- ## ✨ Animations – `fx(options, child)` Applies smooth enter animations (CSS transitions / keyframes). Returns the modified element. ```javascript fx({ name: 'fade', duration: 300 }, Div({}, 'Hello') ) ``` **Options** - `name` – uses predefined keyframes `${name}-in` (you must define them in your CSS) - `duration` – in ms (default 200) - `scale` – adds `scale(0.95)` → `none` - `slide` – adds `translateY(-10px)` → `none` - `rotate` – adds `rotate(-2deg)` → `none` - `blur` – adds `blur(4px)` → `none` If `name` is given, it sets `animation: ${name}-in ${duration}ms`. Otherwise it applies a smooth transition from the initial transform/filter to the final state. --- ## 🧭 Router – `router(routes)` Hash‑based SPA router. Returns a DOM node that renders the current route. ```javascript const routes = [ { path: '/', component: () => Div({}, 'Home') }, { path: '/user/:id', component: (params) => Div({}, `User ${params.id}`) }, { path: '*', component: () => Div({}, '404') } ] const App = () => Div({}, [ A({ href: '#/' }, 'Home'), A({ href: '#/user/42' }, 'User 42'), router(routes) ]) ``` **API** | Method | Description | |--------|-------------| | `router.params()` | Returns a reactive signal of current route params (e.g., `{ id: '42' }`). | | `router.to(path)` | Navigate to a new hash (e.g., `router.to('/user/5')`). Prepend `#` automatically. | | `router.back()` | Go back in history. | | `router.path()` | Returns current hash path without `#` (e.g., `/user/42`). | --- ## 🌐 HTTP Requests – `req(config)` Creates a reactive request controller with built‑in loading/error/data signals and abort support. ```javascript const fetchUser = req({ url: '/api/user/1', method: 'GET' }) // start the request fetchUser.run().catch(console.error) // reactively display state watch(() => { if (fetchUser.loading()) console.log('loading...') if (fetchUser.error()) console.error(fetchUser.error()) if (fetchUser.data()) console.log(fetchUser.data()) }) // abort if needed fetchUser.abort() ``` **Options** - `url` (required) - `method` (default `'GET'`) - `headers` (object, default `{}`) **Return value** - `run(body?)` – initiates the request, returns a promise. - `abort()` – aborts the current request (AbortController). - `loading` – signal (boolean) - `error` – signal (`null` or error message) - `data` – signal (`null` or parsed JSON) > **Note**: Automatically sets `Content-Type: application/json` unless `body` is a `FormData`. Timeout after 10 seconds aborts the request. --- ## 🚀 Mounting – `mount(component, target)` Clears the target element and mounts the application. Returns the runtime instance (which has a `.destroy()` method). ```javascript mount(() => App(), '#app') // or mount(App, document.body) ``` If you mount again on the same target, the previous instance is automatically destroyed. --- ## 🧹 Global Cleanup & Memory SigPro tracks every effect, DOM event listener, and nested component. When a component is unmounted: - All its effects are disposed. - All DOM event listeners are removed. - All `onUnmount` callbacks run. - Child components are recursively destroyed. You never need to manually clean up – just write reactive code. --- ## 📦 Full Example – Counter with Persistence ```javascript import { $, watch, h, mount } from 'sigpro' const count = $(0, 'counter') // persists in localStorage const App = () => Div({ class: 'counter' }, [ H1({}, () => `Count: ${count()}`), Button({ onClick: () => count(count() + 1) }, '+'), Button({ onClick: () => count(count() - 1) }, '-'), Button({ onClick: () => count(0) }, 'Reset') ]) mount(App, '#app') ``` --- ## 🔧 Customising the API (Renaming) You can rename everything in one line: ```javascript import { $ as signal, watch as effect, h as element, mount as render } from 'sigpro' ``` Or assign globally: ```javascript window.myReactive = $ ``` All functions are also exposed on the global `window` object when included via `