Update Funcions
This commit is contained in:
@@ -81,7 +81,7 @@ const Counter = () => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(Counter, "#app");
|
Mount(Counter, "#app");
|
||||||
```
|
```
|
||||||
|
|
||||||
-----
|
-----
|
||||||
@@ -96,7 +96,7 @@ $mount(Counter, "#app");
|
|||||||
| **Native Persistence** | **Included ($)** | Requires Plugins | Manual |
|
| **Native Persistence** | **Included ($)** | Requires Plugins | Manual |
|
||||||
| **Dependencies** | **Zero** | Many | Build Toolchain |
|
| **Dependencies** | **Zero** | Many | Build Toolchain |
|
||||||
| **Lifecycle Mgmt** | **Automatic (Cleanup Root)** | Manual / Hook-based | Manual / Hook-based |
|
| **Lifecycle Mgmt** | **Automatic (Cleanup Root)** | Manual / Hook-based | Manual / Hook-based |
|
||||||
| **Routing** | **Reactive Hash ($router) + File-based (Vite)** | Virtual Router (External) | File-based / External |
|
| **Routing** | **Reactive Hash (Router) + File-based (Vite)** | Virtual Router (External) | File-based / External |
|
||||||
| **Learning Curve** | **Zero (Vanilla JS)** | Steep (JSX/Templates) | Medium (Directives) |
|
| **Learning Curve** | **Zero (Vanilla JS)** | Steep (JSX/Templates) | Medium (Directives) |
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|||||||
18
dist/sigpro.esm.js
vendored
18
dist/sigpro.esm.js
vendored
@@ -337,15 +337,19 @@ var For = (source, renderFn, keyFn, tag = "div", props = { style: "display:conte
|
|||||||
for (let i = 0;i < items.length; i++) {
|
for (let i = 0;i < items.length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
const key = keyFn ? keyFn(item, i) : i;
|
const key = keyFn ? keyFn(item, i) : i;
|
||||||
let view = viewCache.get(key) || Render(() => renderFn(item, i));
|
let view = viewCache.get(key);
|
||||||
|
if (!view) {
|
||||||
|
const result = renderFn(item, i);
|
||||||
|
view = result instanceof Node ? { container: result, destroy: () => {
|
||||||
|
cleanupNode(result);
|
||||||
|
result.remove();
|
||||||
|
} } : Render(() => result);
|
||||||
|
}
|
||||||
viewCache.delete(key);
|
viewCache.delete(key);
|
||||||
nextCache.set(key, view);
|
nextCache.set(key, view);
|
||||||
order.push(key);
|
order.push(key);
|
||||||
}
|
}
|
||||||
viewCache.forEach((view) => {
|
viewCache.forEach((v) => v.destroy());
|
||||||
view.destroy();
|
|
||||||
view.container.remove();
|
|
||||||
});
|
|
||||||
let anchor = marker;
|
let anchor = marker;
|
||||||
for (let i = order.length - 1;i >= 0; i--) {
|
for (let i = order.length - 1;i >= 0; i--) {
|
||||||
const view = nextCache.get(order[i]);
|
const view = nextCache.get(order[i]);
|
||||||
@@ -411,8 +415,7 @@ var Mount = (component, target) => {
|
|||||||
MOUNTED_NODES.set(targetEl, instance);
|
MOUNTED_NODES.set(targetEl, instance);
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
var Fragment = ({ children }) => children;
|
var SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
||||||
var SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount, Fragment };
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
assign(window, SigPro);
|
assign(window, SigPro);
|
||||||
const tags = `div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter`.split(" ");
|
const tags = `div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter`.split(" ");
|
||||||
@@ -430,7 +433,6 @@ export {
|
|||||||
Render,
|
Render,
|
||||||
Mount,
|
Mount,
|
||||||
If,
|
If,
|
||||||
Fragment,
|
|
||||||
For,
|
For,
|
||||||
$$,
|
$$,
|
||||||
$
|
$
|
||||||
|
|||||||
2
dist/sigpro.esm.min.js
vendored
2
dist/sigpro.esm.min.js
vendored
File diff suppressed because one or more lines are too long
18
dist/sigpro.js
vendored
18
dist/sigpro.js
vendored
@@ -36,7 +36,6 @@
|
|||||||
Render: () => Render,
|
Render: () => Render,
|
||||||
Mount: () => Mount,
|
Mount: () => Mount,
|
||||||
If: () => If,
|
If: () => If,
|
||||||
Fragment: () => Fragment,
|
|
||||||
For: () => For,
|
For: () => For,
|
||||||
$$: () => $$,
|
$$: () => $$,
|
||||||
$: () => $
|
$: () => $
|
||||||
@@ -381,15 +380,19 @@
|
|||||||
for (let i = 0;i < items.length; i++) {
|
for (let i = 0;i < items.length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
const key = keyFn ? keyFn(item, i) : i;
|
const key = keyFn ? keyFn(item, i) : i;
|
||||||
let view = viewCache.get(key) || Render(() => renderFn(item, i));
|
let view = viewCache.get(key);
|
||||||
|
if (!view) {
|
||||||
|
const result = renderFn(item, i);
|
||||||
|
view = result instanceof Node ? { container: result, destroy: () => {
|
||||||
|
cleanupNode(result);
|
||||||
|
result.remove();
|
||||||
|
} } : Render(() => result);
|
||||||
|
}
|
||||||
viewCache.delete(key);
|
viewCache.delete(key);
|
||||||
nextCache.set(key, view);
|
nextCache.set(key, view);
|
||||||
order.push(key);
|
order.push(key);
|
||||||
}
|
}
|
||||||
viewCache.forEach((view) => {
|
viewCache.forEach((v) => v.destroy());
|
||||||
view.destroy();
|
|
||||||
view.container.remove();
|
|
||||||
});
|
|
||||||
let anchor = marker;
|
let anchor = marker;
|
||||||
for (let i = order.length - 1;i >= 0; i--) {
|
for (let i = order.length - 1;i >= 0; i--) {
|
||||||
const view = nextCache.get(order[i]);
|
const view = nextCache.get(order[i]);
|
||||||
@@ -455,8 +458,7 @@
|
|||||||
MOUNTED_NODES.set(targetEl, instance);
|
MOUNTED_NODES.set(targetEl, instance);
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
var Fragment = ({ children }) => children;
|
var SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
||||||
var SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount, Fragment };
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
assign(window, SigPro);
|
assign(window, SigPro);
|
||||||
const tags = `div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter`.split(" ");
|
const tags = `div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter`.split(" ");
|
||||||
|
|||||||
2
dist/sigpro.min.js
vendored
2
dist/sigpro.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -7,13 +7,13 @@
|
|||||||
|
|
||||||
* **API Reference**
|
* **API Reference**
|
||||||
* [Quick Start](api/quick.md)
|
* [Quick Start](api/quick.md)
|
||||||
* [$ signals](api/signal.md)
|
* [Signals & Proxies](api/signal.md)
|
||||||
* [$watch](api/watch.md)
|
* [Watch](api/watch.md)
|
||||||
* [$if](api/if.md)
|
* [If](api/if.md)
|
||||||
* [$for](api/for.md)
|
* [For](api/for.md)
|
||||||
* [$router](api/router.md)
|
* [Router](api/router.md)
|
||||||
* [$mount](api/mount.md)
|
* [Mount](api/mount.md)
|
||||||
* [$html](api/html.md)
|
* [Tag](api/html.md)
|
||||||
* [Tags](api/tags.md)
|
* [Tags](api/tags.md)
|
||||||
* [Global Store](api/global.md)
|
* [Global Store](api/global.md)
|
||||||
* [JSX Style](api/jsx.md)
|
* [JSX Style](api/jsx.md)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Reactive Lists: `$for( )`
|
# Reactive Lists: `For( )`
|
||||||
|
|
||||||
The `$for` function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple `.map()`, `$for` is **keyed**, meaning it only updates, moves, or deletes the specific items that changed.
|
The `For` function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple `.map()`, `For` is **keyed**, meaning it only updates, moves, or deletes the specific items that changed.
|
||||||
|
|
||||||
## Function Signature
|
## Function Signature
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
$for(
|
For(
|
||||||
source: Signal<any[]> | Function | any[],
|
source: Signal<any[]> | Function | any[],
|
||||||
render: (item: any, index: number) => HTMLElement,
|
render: (item: any, index: number) => HTMLElement,
|
||||||
keyFn?: (item: any, index: number) => string | number
|
keyFn?: (item: any, index: number) => string | number
|
||||||
@@ -34,7 +34,7 @@ const users = $([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
Ul({ class: "list" }, [
|
Ul({ class: "list" }, [
|
||||||
$for(users,
|
For(users,
|
||||||
(user) => Li({ class: "p-2" }, user.name),
|
(user) => Li({ class: "p-2" }, user.name),
|
||||||
(user) => user.id // Stable and unique key
|
(user) => user.id // Stable and unique key
|
||||||
)
|
)
|
||||||
@@ -42,14 +42,14 @@ Ul({ class: "list" }, [
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 2. Simplified Usage (Automatic Key)
|
### 2. Simplified Usage (Automatic Key)
|
||||||
If you omit the third parameter, `$for` will automatically use the array index as the key. This is ideal for simple lists that don't change order frequently.
|
If you omit the third parameter, `For` will automatically use the array index as the key. This is ideal for simple lists that don't change order frequently.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const tags = $(["Tech", "JS", "Web"]);
|
const tags = $(["Tech", "JS", "Web"]);
|
||||||
|
|
||||||
// No need to pass keyFn if the index is sufficient
|
// No need to pass keyFn if the index is sufficient
|
||||||
Div({ class: "flex gap-1" }, [
|
Div({ class: "flex gap-1" }, [
|
||||||
$for(tags, (tag) => Badge(tag))
|
For(tags, (tag) => Badge(tag))
|
||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ Div({ class: "flex gap-1" }, [
|
|||||||
|
|
||||||
## How it Works (The Reconciliation)
|
## How it Works (The Reconciliation)
|
||||||
|
|
||||||
When the `source` signal changes, `$for` performs the following steps:
|
When the `source` signal changes, `For` performs the following steps:
|
||||||
|
|
||||||
1. **Key Diffing**: It compares the new keys with the previous ones stored in an internal `Map`.
|
1. **Key Diffing**: It compares the new keys with the previous ones stored in an internal `Map`.
|
||||||
2. **Node Reuse**: If a key already exists, the DOM node is **reused** and moved to its new position. No new elements are created.
|
2. **Node Reuse**: If a key already exists, the DOM node is **reused** and moved to its new position. No new elements are created.
|
||||||
@@ -68,13 +68,13 @@ When the `source` signal changes, `$for` performs the following steps:
|
|||||||
## Performance Tips
|
## Performance Tips
|
||||||
|
|
||||||
* **Stable Keys**: Never use `Math.random()` as a key. This will force SigPro to destroy and recreate the entire list on every update, killing performance.
|
* **Stable Keys**: Never use `Math.random()` as a key. This will force SigPro to destroy and recreate the entire list on every update, killing performance.
|
||||||
* **State Preservation**: If your list items have internal state (like an input with text), `$for` ensures that state is preserved even if the list is reordered, as long as the key (`id`) remains the same.
|
* **State Preservation**: If your list items have internal state (like an input with text), `For` ensures that state is preserved even if the list is reordered, as long as the key (`id`) remains the same.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Summary Comparison
|
## Summary Comparison
|
||||||
|
|
||||||
| Feature | Standard `Array.map` | SigPro `$for` |
|
| Feature | Standard `Array.map` | SigPro `For` |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| **Re-render** | Re-renders everything | Only updates changes |
|
| **Re-render** | Re-renders everything | Only updates changes |
|
||||||
| **DOM Nodes** | Re-created every time | **Reused via Keys** |
|
| **DOM Nodes** | Re-created every time | **Reused via Keys** |
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# The DOM Factory: `$html( )`
|
# The DOM Factory: `Tag( )`
|
||||||
|
|
||||||
`$html` is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses `$watch` to maintain a live, high-performance link between your Signals and the Document Object Model.
|
`Tag` is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses `Watch` to maintain a live, high-performance link between your Signals and the Document Object Model.
|
||||||
|
|
||||||
## Function Signature
|
## Function Signature
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
$html(tagName: string, props?: Object, children?: any[] | any): HTMLElement
|
Tag(tagName: string, props?: Object, children?: any[] | any): HTMLElement
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Type | Required | Description |
|
| Parameter | Type | Required | Description |
|
||||||
@@ -50,7 +50,7 @@ Button({
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 4. Reactive Attributes (One-Way)
|
### 4. Reactive Attributes (One-Way)
|
||||||
If an attribute value is a **function** (like a Signal), `$html` creates an internal **`$watch`** to keep the DOM in sync with the state.
|
If an attribute value is a **function** (like a Signal), `Tag` creates an internal **`Watch`** to keep the DOM in sync with the state.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
Div({
|
Div({
|
||||||
@@ -72,7 +72,7 @@ Input({
|
|||||||
> **Note:** To use a Signal as **read-only** in an input, wrap it in an anonymous function: `value: () => username()`.
|
> **Note:** To use a Signal as **read-only** in an input, wrap it in an anonymous function: `value: () => username()`.
|
||||||
|
|
||||||
### 6. Reactive Children
|
### 6. Reactive Children
|
||||||
Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary using `$watch` for that specific part of the DOM.
|
Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary using `Watch` for that specific part of the DOM.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
Div({}, [
|
Div({}, [
|
||||||
@@ -85,20 +85,20 @@ Div({}, [
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Memory Management (Internal)
|
## Memory Management (Internal)
|
||||||
Every element created with `$html` is "self-aware" regarding its reactive dependencies.
|
Every element created with `Tag` is "self-aware" regarding its reactive dependencies.
|
||||||
* **`._cleanups`**: A hidden `Set` attached to the element that stores all `stop()` functions from its internal `$watch` calls and event listeners.
|
* **`._cleanups`**: A hidden `Set` attached to the element that stores all `stop()` functions from its internal `Watch` calls and event listeners.
|
||||||
* **Lifecycle**: When an element is removed by a Controller (`$if`, `$for`, or `$router`), SigPro performs a recursive **"sweep"** to execute these cleanups, ensuring **zero memory leaks**.
|
* **Lifecycle**: When an element is removed by a Controller (`If`, `For`, or `Router`), SigPro performs a recursive **"sweep"** to execute these cleanups, ensuring **zero memory leaks**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Tag Constructors (The Shortcuts)
|
## Tag Constructors (The Shortcuts)
|
||||||
|
|
||||||
Instead of writing `$html("div", ...)` every time, SigPro provides PascalCase global functions for all standard HTML tags. These are direct mappings to the `$html` factory.
|
Instead of writing `Tag("div", ...)` every time, SigPro provides PascalCase global functions for all standard HTML tags. These are direct mappings to the `Tag` factory.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// This:
|
// This:
|
||||||
Div({ class: "wrapper" }, [ Span("Hello") ])
|
Div({ class: "wrapper" }, [ Span("Hello") ])
|
||||||
|
|
||||||
// Is exactly equivalent to:
|
// Is exactly equivalent to:
|
||||||
$html("div", { class: "wrapper" }, [ $html("span", {}, "Hello") ])
|
Tag("div", { class: "wrapper" }, [ Tag("span", {}, "Hello") ])
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Reactive Branching: `$if( )`
|
# Reactive Branching: `If( )`
|
||||||
|
|
||||||
The `$if` function is a reactive control flow operator. It manages the conditional rendering of components with optional smooth transitions, ensuring that only the active branch exists in the DOM and in memory.
|
The `If` function is a reactive control flow operator. It manages the conditional rendering of components with optional smooth transitions, ensuring that only the active branch exists in the DOM and in memory.
|
||||||
|
|
||||||
## Function Signature
|
## Function Signature
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
$if(
|
If(
|
||||||
condition: Signal<boolean> | Function,
|
condition: Signal<boolean> | Function,
|
||||||
thenVal: Component | Node,
|
thenVal: Component | Node,
|
||||||
otherwiseVal?: Component | Node,
|
otherwiseVal?: Component | Node,
|
||||||
@@ -53,7 +53,7 @@ const fade = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$if(show, Modal, null, fade);
|
If(show, Modal, null, fade);
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -68,7 +68,7 @@ const isVisible = $(false);
|
|||||||
Div([
|
Div([
|
||||||
Button({ onclick: () => isVisible(!isVisible()) }, "Toggle Message"),
|
Button({ onclick: () => isVisible(!isVisible()) }, "Toggle Message"),
|
||||||
|
|
||||||
$if(isVisible,
|
If(isVisible,
|
||||||
P("Now you see me!"),
|
P("Now you see me!"),
|
||||||
P("Now you don't...")
|
P("Now you don't...")
|
||||||
)
|
)
|
||||||
@@ -83,7 +83,7 @@ const showModal = $(false);
|
|||||||
Div([
|
Div([
|
||||||
Button({ onclick: () => showModal(true) }, "Open Modal"),
|
Button({ onclick: () => showModal(true) }, "Open Modal"),
|
||||||
|
|
||||||
$if(showModal,
|
If(showModal,
|
||||||
() => Modal({ onClose: () => showModal(false) }),
|
() => Modal({ onClose: () => showModal(false) }),
|
||||||
null,
|
null,
|
||||||
fade // ← Smooth enter/exit animation
|
fade // ← Smooth enter/exit animation
|
||||||
@@ -93,10 +93,10 @@ Div([
|
|||||||
|
|
||||||
### 3. Lazy Component Loading
|
### 3. Lazy Component Loading
|
||||||
|
|
||||||
Unlike CSS `display: none`, `$if` is **lazy**. The inactive branch is never created, saving memory.
|
Unlike CSS `display: none`, `If` is **lazy**. The inactive branch is never created, saving memory.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
$if(() => user.isLogged(),
|
If(() => user.isLogged(),
|
||||||
() => Dashboard(), // Only executed if logged in
|
() => Dashboard(), // Only executed if logged in
|
||||||
() => LoginGate() // Only executed if guest
|
() => LoginGate() // Only executed if guest
|
||||||
)
|
)
|
||||||
@@ -105,15 +105,15 @@ $if(() => user.isLogged(),
|
|||||||
### 4. Complex Conditions
|
### 4. Complex Conditions
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
$if(() => count() > 10 && status() === 'ready',
|
If(() => count() > 10 && status() === 'ready',
|
||||||
Span("Threshold reached!")
|
Span("Threshold reached!")
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. $if.not Helper
|
### 5. If.not Helper
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
$if.not(loading,
|
If.not(loading,
|
||||||
() => Content(), // Shows when loading is FALSE
|
() => Content(), // Shows when loading is FALSE
|
||||||
() => Spinner() // Shows when loading is TRUE
|
() => Spinner() // Shows when loading is TRUE
|
||||||
)
|
)
|
||||||
@@ -123,10 +123,10 @@ $if.not(loading,
|
|||||||
|
|
||||||
## Automatic Cleanup
|
## Automatic Cleanup
|
||||||
|
|
||||||
One of the core strengths of `$if` is its integrated **Cleanup** logic. SigPro ensures that when a branch is swapped out, it is completely purged.
|
One of the core strengths of `If` is its integrated **Cleanup** logic. SigPro ensures that when a branch is swapped out, it is completely purged.
|
||||||
|
|
||||||
1. **Stop Watchers**: All `$watch` calls inside the inactive branch are permanently stopped.
|
1. **Stop Watchers**: All `Watch` calls inside the inactive branch are permanently stopped.
|
||||||
2. **Unbind Events**: Event listeners attached via `$html` are removed.
|
2. **Unbind Events**: Event listeners attached via `Tag` are removed.
|
||||||
3. **Recursive Sweep**: SigPro performs a deep "sweep" of the removed branch.
|
3. **Recursive Sweep**: SigPro performs a deep "sweep" of the removed branch.
|
||||||
4. **Transition Respect**: When using transitions, destruction only happens AFTER the `out` animation completes.
|
4. **Transition Respect**: When using transitions, destruction only happens AFTER the `out` animation completes.
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ One of the core strengths of `$if` is its integrated **Cleanup** logic. SigPro e
|
|||||||
|
|
||||||
## Technical Comparison
|
## Technical Comparison
|
||||||
|
|
||||||
| Feature | Standard CSS `hidden` | SigPro `$if` |
|
| Feature | Standard CSS `hidden` | SigPro `If` |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| **DOM Presence** | Always present | Only if active |
|
| **DOM Presence** | Always present | Only if active |
|
||||||
| **Reactivity** | Still processing | **Paused/Destroyed** |
|
| **Reactivity** | Still processing | **Paused/Destroyed** |
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ SigPro works seamlessly with JSX.
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"jsxFactory": "$html",
|
"jsxFactory": "Tag",
|
||||||
"jsxFragmentFactory": "Fragment"
|
"jsxFragmentFactory": "Fragment"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ import { defineConfig } from 'vite'
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
esbuild: {
|
esbuild: {
|
||||||
jsxFactory: '$html',
|
jsxFactory: 'Tag',
|
||||||
jsxFragmentFactory: 'Fragment'
|
jsxFragmentFactory: 'Fragment'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -38,7 +38,7 @@ export default defineConfig({
|
|||||||
export default {
|
export default {
|
||||||
plugins: [
|
plugins: [
|
||||||
['@babel/plugin-transform-react-jsx', {
|
['@babel/plugin-transform-react-jsx', {
|
||||||
pragma: '$html',
|
pragma: 'Tag',
|
||||||
pragmaFrag: 'Fragment'
|
pragmaFrag: 'Fragment'
|
||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
@@ -49,7 +49,7 @@ export default {
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
// App.jsx
|
// App.jsx
|
||||||
import { $, $mount, Fragment } from 'sigpro';
|
import { $, Mount, Fragment } from 'sigpro';
|
||||||
|
|
||||||
const Button = ({ onClick, children }) => (
|
const Button = ({ onClick, children }) => (
|
||||||
<button class="btn btn-primary" onclick={onClick}>
|
<button class="btn btn-primary" onclick={onClick}>
|
||||||
@@ -76,7 +76,7 @@ const App = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(App, '#app');
|
Mount(App, '#app');
|
||||||
```
|
```
|
||||||
|
|
||||||
## What Gets Compiled
|
## What Gets Compiled
|
||||||
@@ -90,8 +90,8 @@ Your JSX:
|
|||||||
|
|
||||||
Compiles to:
|
Compiles to:
|
||||||
```javascript
|
```javascript
|
||||||
$html('div', { class: "container" },
|
Tag('div', { class: "container" },
|
||||||
$html(Button, {}, "Click")
|
Tag(Button, {}, "Click")
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ SigPro automatically injects `Div()`, `Button()`, `Span()`, and all other HTML t
|
|||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const { $, $mount, Fragment } = SigPro;
|
const { $, Mount, Fragment } = SigPro;
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const count = $(0);
|
const count = $(0);
|
||||||
@@ -127,7 +127,7 @@ SigPro automatically injects `Div()`, `Button()`, `Span()`, and all other HTML t
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(App, '#app');
|
Mount(App, '#app');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -138,10 +138,10 @@ SigPro automatically injects `Div()`, `Button()`, `Span()`, and all other HTML t
|
|||||||
For a JSX-like syntax without a build step, use `htm`:
|
For a JSX-like syntax without a build step, use `htm`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { $, $mount } from 'https://unpkg.com/sigpro';
|
import { $, Mount } from 'https://unpkg.com/sigpro';
|
||||||
import htm from 'https://esm.sh/htm';
|
import htm from 'https://esm.sh/htm';
|
||||||
|
|
||||||
const html = htm.bind($html);
|
const html = htm.bind(Tag);
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const count = $(0);
|
const count = $(0);
|
||||||
@@ -156,7 +156,7 @@ const App = () => {
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(App, '#app');
|
Mount(App, '#app');
|
||||||
```
|
```
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Application Mounter: `$mount( )`
|
# Application Mounter: `Mount( )`
|
||||||
|
|
||||||
The `$mount` function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle.
|
The `Mount` function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle.
|
||||||
|
|
||||||
## Function Signature
|
## Function Signature
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
$mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
|
Mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
| Parameter | Type | Default | Description |
|
||||||
@@ -27,7 +27,7 @@ import SigPro from 'sigpro';
|
|||||||
import App from './App.js';
|
import App from './App.js';
|
||||||
|
|
||||||
// Mounts your main App component
|
// Mounts your main App component
|
||||||
$mount(App, '#app-root');
|
Mount(App, '#app-root');
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Reactive "Islands"
|
### 2. Reactive "Islands"
|
||||||
@@ -42,17 +42,17 @@ const Counter = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Mount only the counter into a specific sidebar div
|
// Mount only the counter into a specific sidebar div
|
||||||
$mount(Counter, '#sidebar-widget');
|
Mount(Counter, '#sidebar-widget');
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## How it Works (Lifecycle & Cleanup)
|
## How it Works (Lifecycle & Cleanup)
|
||||||
|
|
||||||
When `$mount` is executed, it performs these critical steps to ensure a leak-free environment:
|
When `Mount` is executed, it performs these critical steps to ensure a leak-free environment:
|
||||||
|
|
||||||
1. **Duplicate Detection**: If you call `$mount` on a target that already has a SigPro instance, it automatically calls `.destroy()` on the previous instance. This prevents "Zombie Effects" from stacking in memory.
|
1. **Duplicate Detection**: If you call `Mount` on a target that already has a SigPro instance, it automatically calls `.destroy()` on the previous instance. This prevents "Zombie Effects" from stacking in memory.
|
||||||
2. **Internal Scoping**: It executes the component function inside an internal **Reactive Owner**. This captures every `$watch` and event listener created during the render.
|
2. **Internal Scoping**: It executes the component function inside an internal **Reactive Owner**. This captures every `Watch` and event listener created during the render.
|
||||||
3. **Target Injection**: It clears the target using `replaceChildren()` and appends the new component.
|
3. **Target Injection**: It clears the target using `replaceChildren()` and appends the new component.
|
||||||
4. **Runtime Creation**: It returns a control object:
|
4. **Runtime Creation**: It returns a control object:
|
||||||
* `container`: The actual DOM element created.
|
* `container`: The actual DOM element created.
|
||||||
@@ -62,10 +62,10 @@ When `$mount` is executed, it performs these critical steps to ensure a leak-fre
|
|||||||
|
|
||||||
## Manual Unmounting
|
## Manual Unmounting
|
||||||
|
|
||||||
While SigPro handles most cleanups automatically (via `$if`, `$for`, and `$router`), you can manually destroy any mounted instance. This is vital for imperatively managed UI like **Toasts** or **Modals**.
|
While SigPro handles most cleanups automatically (via `If`, `For`, and `Router`), you can manually destroy any mounted instance. This is vital for imperatively managed UI like **Toasts** or **Modals**.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const instance = $mount(MyToast, '#toast-container');
|
const instance = Mount(MyToast, '#toast-container');
|
||||||
|
|
||||||
// Later, to remove the toast and kill its reactivity:
|
// Later, to remove the toast and kill its reactivity:
|
||||||
instance.destroy();
|
instance.destroy();
|
||||||
@@ -77,9 +77,9 @@ instance.destroy();
|
|||||||
|
|
||||||
| Goal | Code Pattern |
|
| Goal | Code Pattern |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| **Mount to body** | `$mount(App)` |
|
| **Mount to body** | `Mount(App)` |
|
||||||
| **Mount to CSS Selector** | `$mount(App, '#root')` |
|
| **Mount to CSS Selector** | `Mount(App, '#root')` |
|
||||||
| **Mount to DOM Node** | `$mount(App, myElement)` |
|
| **Mount to DOM Node** | `Mount(App, myElement)` |
|
||||||
| **Clean & Re-mount** | Calling `$mount` again on the same target |
|
| **Clean & Re-mount** | Calling `Mount` again on the same target |
|
||||||
| **Total Cleanup** | `const app = $mount(App); app.destroy();` |
|
| **Total Cleanup** | `const app = Mount(App); app.destroy();` |
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,16 @@ SigPro is a high-performance micro-framework that updates the **Real DOM** surgi
|
|||||||
<div class="text-center my-8">
|
<div class="text-center my-8">
|
||||||
<div class="flex justify-center gap-2 flex-wrap mb-4">
|
<div class="flex justify-center gap-2 flex-wrap mb-4">
|
||||||
<span class="badge badge-primary badge-lg font-mono text-lg">$-$$</span>
|
<span class="badge badge-primary badge-lg font-mono text-lg">$-$$</span>
|
||||||
<span class="badge badge-secondary badge-lg font-mono text-lg">$watch</span>
|
<span class="badge badge-secondary badge-lg font-mono text-lg">Watch</span>
|
||||||
<span class="badge badge-accent badge-lg font-mono text-lg">$html</span>
|
<span class="badge badge-accent badge-lg font-mono text-lg">Tag</span>
|
||||||
<span class="badge badge-info badge-lg font-mono text-lg">$if</span>
|
<span class="badge badge-info badge-lg font-mono text-lg">If</span>
|
||||||
<span class="badge badge-success badge-lg font-mono text-lg">$for</span>
|
<span class="badge badge-success badge-lg font-mono text-lg">For</span>
|
||||||
<span class="badge badge-warning badge-lg font-mono text-lg">$router</span>
|
<span class="badge badge-warning badge-lg font-mono text-lg">Router</span>
|
||||||
<span class="badge badge-error badge-lg font-mono text-lg">$mount</span>
|
<span class="badge badge-error badge-lg font-mono text-lg">Mount</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="text-5xl font-black bg-gradient-to-r from-primary via-secondary to-accent bg-clip-text text-transparent">
|
<h1 class="text-5xl font-black bg-gradient-to-r from-primary via-secondary to-accent bg-clip-text text-transparent">
|
||||||
⚡ All the power! ⚡
|
⚡ All the power! ⚡
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-xl opacity-70 mt-2">6 functions that will change the way you code</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Core Functions
|
## Core Functions
|
||||||
@@ -48,32 +47,32 @@ Explore the reactive building blocks of SigPro.
|
|||||||
<td>Creates a <b>Deep Reactive Proxy</b>. Track nested property access automatically. No need for manual signals.</td>
|
<td>Creates a <b>Deep Reactive Proxy</b>. Track nested property access automatically. No need for manual signals.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code class="text-secondary font-bold">$watch(fn)</code></td>
|
<td><code class="text-secondary font-bold">Watch(fn)</code></td>
|
||||||
<td class="font-mono text-xs opacity-70">(function) => stopFn</td>
|
<td class="font-mono text-xs opacity-70">(function) => stopFn</td>
|
||||||
<td><b>Auto Mode:</b> Tracks any signal touched inside. Returns a stop function.</td>
|
<td><b>Auto Mode:</b> Tracks any signal touched inside. Returns a stop function.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code class="text-secondary font-bold">$watch(deps, fn)</code></td>
|
<td><code class="text-secondary font-bold">Watch(deps, fn)</code></td>
|
||||||
<td class="font-mono text-xs opacity-70">(Array, function) => stopFn</td>
|
<td class="font-mono text-xs opacity-70">(Array, function) => stopFn</td>
|
||||||
<td><b>Explicit Mode:</b> Only runs when signals in <code>deps</code> change.</td>
|
<td><b>Explicit Mode:</b> Only runs when signals in <code>deps</code> change.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code class="text-accent font-bold">$if(cond, then, else?)</code></td>
|
<td><code class="text-accent font-bold">If(cond, then, else?)</code></td>
|
||||||
<td class="font-mono text-xs opacity-70">(Signal|bool, fn, fn?) => Node</td>
|
<td class="font-mono text-xs opacity-70">(Signal|bool, fn, fn?) => Node</td>
|
||||||
<td>Reactive conditional. Automatically destroys "else" branch memory.</td>
|
<td>Reactive conditional. Automatically destroys "else" branch memory.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code class="text-accent font-bold">$for(src, render, key)</code></td>
|
<td><code class="text-accent font-bold">For(src, render, key)</code></td>
|
||||||
<td class="font-mono text-xs opacity-70">(Signal, fn, fn) => Node</td>
|
<td class="font-mono text-xs opacity-70">(Signal, fn, fn) => Node</td>
|
||||||
<td><b>Keyed Loop:</b> Optimized list renderer. Uses the key function for surgical DOM moves.</td>
|
<td><b>Keyed Loop:</b> Optimized list renderer. Uses the key function for surgical DOM moves.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code class="text-info font-bold">$router(routes)</code></td>
|
<td><code class="text-info font-bold">Router(routes)</code></td>
|
||||||
<td class="font-mono text-xs opacity-70">(Array) => Node</td>
|
<td class="font-mono text-xs opacity-70">(Array) => Node</td>
|
||||||
<td><b>SPA Router:</b> Hash-based routing with dynamic params (<code>:id</code>) and auto-cleanup.</td>
|
<td><b>SPA Router:</b> Hash-based routing with dynamic params (<code>:id</code>) and auto-cleanup.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><code class="font-bold">$mount(node, target)</code></td>
|
<td><code class="font-bold">Mount(node, target)</code></td>
|
||||||
<td class="font-mono text-xs opacity-70">(any, string|Node) => Runtime</td>
|
<td class="font-mono text-xs opacity-70">(any, string|Node) => Runtime</td>
|
||||||
<td>Entry point. Cleans the target and mounts the app with full lifecycle management.</td>
|
<td>Entry point. Cleans the target and mounts the app with full lifecycle management.</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -107,8 +106,8 @@ SigPro provides **PascalCase** wrappers for all standard HTML5 tags (e.g., `Div`
|
|||||||
<div class="card bg-base-200 border border-base-300 shadow-sm">
|
<div class="card bg-base-200 border border-base-300 shadow-sm">
|
||||||
<div class="card-body p-5">
|
<div class="card-body p-5">
|
||||||
<h3 class="text-xs font-black uppercase tracking-widest opacity-60">Dynamic Routing</h3>
|
<h3 class="text-xs font-black uppercase tracking-widest opacity-60">Dynamic Routing</h3>
|
||||||
<code class="text-info font-bold text-sm bg-base-300/50 p-2 rounded-lg">$router.to('/user/1')</code>
|
<code class="text-info font-bold text-sm bg-base-300/50 p-2 rounded-lg">Router.to('/user/1')</code>
|
||||||
<p class="text-xs mt-3 leading-relaxed">Navigate programmatically. Access params via <code>$router.params().id</code>.</p>
|
<p class="text-xs mt-3 leading-relaxed">Navigate programmatically. Access params via <code>Router.params().id</code>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -131,14 +130,14 @@ SigPro provides **PascalCase** wrappers for all standard HTML5 tags (e.g., `Div`
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Custom API BYOS (Bring Your Own Syntax)
|
## Custom API (Bring Your Own Syntax)
|
||||||
|
|
||||||
SigPro's core functions are intentionally simple and can be easily renamed in **one line** to match your preferred coding style.
|
SigPro's core functions are intentionally simple and can be easily renamed in **one line** to match your preferred coding style.
|
||||||
|
|
||||||
### One-Line Renaming
|
### One-Line Renaming
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { $ as signal, $mount as render, $html as tag, $if as when, $for as each, $watch as effect } from 'sigpro';
|
import { $ as signal, Mount as render, Tag as tag, If as when, For as each, Watch as effect } from 'sigpro';
|
||||||
|
|
||||||
// Now use your custom names
|
// Now use your custom names
|
||||||
const count = signal(0);
|
const count = signal(0);
|
||||||
@@ -166,21 +165,21 @@ const useState = (initial) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const useEffect = (fn, deps) => {
|
const useEffect = (fn, deps) => {
|
||||||
deps ? SigPro.$watch(deps, fn) : SigPro.$watch(fn);
|
deps ? SigPro.Watch(deps, fn) : SigPro.Watch(fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Usage
|
// Usage
|
||||||
const Counter = () => {
|
const Counter = () => {
|
||||||
const [count, setCount] = useState(0);
|
const [count, setCount] = useState(0);
|
||||||
useEffect(() => console.log(count()), [count]);
|
useEffect(() => console.log(count()), [count]);
|
||||||
return SigPro.$html('button', { onClick: () => setCount(count() + 1) }, count);
|
return SigPro.Tag('button', { onClick: () => setCount(count() + 1) }, count);
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create Vue-like API
|
### Create Vue-like API
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { $ as ref, $watch as watch, $mount as mount } from 'sigpro';
|
import { $ as ref, Watch as watch, Mount as mount } from 'sigpro';
|
||||||
|
|
||||||
const computed = (fn) => ref(fn);
|
const computed = (fn) => ref(fn);
|
||||||
const createApp = (component) => ({ mount: (selector) => mount(component, selector) });
|
const createApp = (component) => ({ mount: (selector) => mount(component, selector) });
|
||||||
@@ -197,7 +196,7 @@ Create a central configuration file to reuse your custom naming across the entir
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// config/sigpro.config.js
|
// config/sigpro.config.js
|
||||||
import { $ as signal, $mount as render, $html as tag, $if as when, $for as each, $watch as effect } from 'sigpro';
|
import { $ as signal, Mount as render, Tag as tag, If as when, For as each, Watch as effect } from 'sigpro';
|
||||||
|
|
||||||
// Re-export everything with your custom names
|
// Re-export everything with your custom names
|
||||||
export { signal, render, tag, when, each, effect };
|
export { signal, render, tag, when, each, effect };
|
||||||
@@ -229,7 +228,7 @@ render(App, '#app');
|
|||||||
> **Why rename?** Team preferences, framework migration, or just personal taste. SigPro adapts to you, not the other way around.
|
> **Why rename?** Team preferences, framework migration, or just personal taste. SigPro adapts to you, not the other way around.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> **Performance Hint:** For lists (`$for`), always provide a unique key function `(item) => item.id` to prevent unnecessary node creation and enable reordering.
|
> **Performance Hint:** For lists (`For`), always provide a unique key function `(item) => item.id` to prevent unnecessary node creation and enable reordering.
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> **Pro Tip:** Use `$$()` for complex nested state objects instead of multiple `$()` signals. It's cleaner and automatically tracks deep properties.
|
> **Pro Tip:** Use `$$()` for complex nested state objects instead of multiple `$()` signals. It's cleaner and automatically tracks deep properties.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Routing: `$router()` & Utilities
|
# Routing: `Router()` & Utilities
|
||||||
|
|
||||||
SigPro includes a built-in, lightweight **Hash Router** to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically.
|
SigPro includes a built-in, lightweight **Hash Router** to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically.
|
||||||
|
|
||||||
## Router Signature
|
## Router Signature
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
$router(routes: Route[]): HTMLElement
|
Router(routes: Route[]): HTMLElement
|
||||||
```
|
```
|
||||||
|
|
||||||
### Route Object
|
### Route Object
|
||||||
@@ -19,13 +19,13 @@ $router(routes: Route[]): HTMLElement
|
|||||||
## Usage Patterns
|
## Usage Patterns
|
||||||
|
|
||||||
### 1. Defining Routes
|
### 1. Defining Routes
|
||||||
The `$router` returns a `div` element with the class `.router-outlet`. When the hash changes, the router destroys the previous view and mounts the new one inside this container.
|
The `Router` returns a `div` element with the class `.router-outlet`. When the hash changes, the router destroys the previous view and mounts the new one inside this container.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const App = () => Div({ class: "app-layout" }, [
|
const App = () => Div({ class: "app-layout" }, [
|
||||||
Navbar(),
|
Navbar(),
|
||||||
// The router outlet is placed here
|
// The router outlet is placed here
|
||||||
$router([
|
Router([
|
||||||
{ path: "/", component: Home },
|
{ path: "/", component: Home },
|
||||||
{ path: "/profile/:id", component: (params) => UserProfile(params.id) },
|
{ path: "/profile/:id", component: (params) => UserProfile(params.id) },
|
||||||
{ path: "*", component: () => H1("404 Not Found") }
|
{ path: "*", component: () => H1("404 Not Found") }
|
||||||
@@ -34,7 +34,7 @@ const App = () => Div({ class: "app-layout" }, [
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 2. Dynamic Segments (`:id`)
|
### 2. Dynamic Segments (`:id`)
|
||||||
The router automatically parses URL parameters (like `:id`) and passes them as an object to the component function. You can also access them globally via `$router.params()`.
|
The router automatically parses URL parameters (like `:id`) and passes them as an object to the component function. You can also access them globally via `Router.params()`.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// If the URL is #/profile/42
|
// If the URL is #/profile/42
|
||||||
@@ -49,23 +49,23 @@ const UserProfile = (params) => {
|
|||||||
|
|
||||||
SigPro provides a set of programmatic methods to control the history and read the state.
|
SigPro provides a set of programmatic methods to control the history and read the state.
|
||||||
|
|
||||||
### `$router.to(path)`
|
### `Router.to(path)`
|
||||||
Navigates to a specific path. It automatically formats the hash (e.g., `/home` becomes `#/home`).
|
Navigates to a specific path. It automatically formats the hash (e.g., `/home` becomes `#/home`).
|
||||||
```javascript
|
```javascript
|
||||||
Button({ onclick: () => $router.to("/dashboard") }, "Go to Dashboard")
|
Button({ onclick: () => Router.to("/dashboard") }, "Go to Dashboard")
|
||||||
```
|
```
|
||||||
|
|
||||||
### `$router.back()`
|
### `Router.back()`
|
||||||
Goes back to the previous page in the browser history.
|
Goes back to the previous page in the browser history.
|
||||||
```javascript
|
```javascript
|
||||||
Button({ onclick: () => $router.back() }, "Back")
|
Button({ onclick: () => Router.back() }, "Back")
|
||||||
```
|
```
|
||||||
|
|
||||||
### `$router.path()`
|
### `Router.path()`
|
||||||
Returns the current path (without the `#`).
|
Returns the current path (without the `#`).
|
||||||
```javascript
|
```javascript
|
||||||
$watch(() => {
|
Watch(() => {
|
||||||
console.log("Current path is:", $router.path());
|
console.log("Current path is:", Router.path());
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ $watch(() => {
|
|||||||
## Technical Behavior
|
## Technical Behavior
|
||||||
|
|
||||||
* **Automatic Cleanup**: Every time you navigate, the router calls `.destroy()` on the previous view. This ensures that all **signals, effects, and event listeners** from the old page are purged from memory.
|
* **Automatic Cleanup**: Every time you navigate, the router calls `.destroy()` on the previous view. This ensures that all **signals, effects, and event listeners** from the old page are purged from memory.
|
||||||
* **Reactive Params**: `$router.params` is a signal (`$`). You can react to parameter changes without re-mounting the entire router outlet.
|
* **Reactive Params**: `Router.params` is a signal (`$`). You can react to parameter changes without re-mounting the entire router outlet.
|
||||||
* **Hash-Based**: By using `window.location.hash`, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects.
|
* **Hash-Based**: By using `window.location.hash`, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ $$<T extends object>(obj: T): T
|
|||||||
```javascript
|
```javascript
|
||||||
const state = $$({ count: 0, name: "Juan" });
|
const state = $$({ count: 0, name: "Juan" });
|
||||||
|
|
||||||
$watch(() => state.count, () => {
|
Watch(() => state.count, () => {
|
||||||
console.log(`Count is now ${state.count}`);
|
console.log(`Count is now ${state.count}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ const user = $$({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// This works! Tracks deep property access
|
// This works! Tracks deep property access
|
||||||
$watch(() => user.profile.address.city, () => {
|
Watch(() => user.profile.address.city, () => {
|
||||||
console.log("City changed");
|
console.log("City changed");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ const todos = $$([
|
|||||||
{ id: 2, text: "Build an app", done: false }
|
{ id: 2, text: "Build an app", done: false }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$watch(() => todos.length, () => {
|
Watch(() => todos.length, () => {
|
||||||
console.log(`You have ${todos.length} todos`);
|
console.log(`You have ${todos.length} todos`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ const canSubmit = $(() =>
|
|||||||
form.fields.password.length > 6
|
form.fields.password.length > 6
|
||||||
);
|
);
|
||||||
|
|
||||||
$watch(canSubmit, (valid) => {
|
Watch(canSubmit, (valid) => {
|
||||||
form.isValid(valid); // Update signal inside reactive object
|
form.isValid(valid); // Update signal inside reactive object
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -224,8 +224,8 @@ state.user.name = "Ana";
|
|||||||
todos.push(newItem);
|
todos.push(newItem);
|
||||||
|
|
||||||
// Track in effects
|
// Track in effects
|
||||||
$watch(() => state.count, () => {});
|
Watch(() => state.count, () => {});
|
||||||
$watch(() => state.user.name, () => {});
|
Watch(() => state.user.name, () => {});
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❌ DON'T:
|
### ❌ DON'T:
|
||||||
@@ -248,7 +248,7 @@ Like all SigPro reactive primitives, `$$()` integrates with the cleanup system:
|
|||||||
|
|
||||||
- Effects tracking reactive properties are automatically disposed
|
- Effects tracking reactive properties are automatically disposed
|
||||||
- No manual cleanup needed
|
- No manual cleanup needed
|
||||||
- Works with `$watch`, `$if`, and `$for`
|
- Works with `Watch`, `If`, and `For`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ const app = {
|
|||||||
// UI component
|
// UI component
|
||||||
const UserProfile = () => {
|
const UserProfile = () => {
|
||||||
return Div({}, [
|
return Div({}, [
|
||||||
$if(() => app.isLoggedIn(),
|
If(() => app.isLoggedIn(),
|
||||||
() => Div({}, [
|
() => Div({}, [
|
||||||
H2(`Welcome ${app.user.name}`),
|
H2(`Welcome ${app.user.name}`),
|
||||||
P(`Email: ${app.user.email}`),
|
P(`Email: ${app.user.email}`),
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Global Tag Helpers
|
# Global Tag Helpers
|
||||||
|
|
||||||
In **SigPro**, you don't need to manually type `$html('div', ...)` for every element. To keep your code declarative and readable, the engine automatically generates **Global Helper Functions** for all standard HTML5 tags upon initialization.
|
In **SigPro**, you don't need to manually type `Tag('div', ...)` for every element. To keep your code declarative and readable, the engine automatically generates **Global Helper Functions** for all standard HTML5 tags upon initialization.
|
||||||
|
|
||||||
## 1. How it Works
|
## 1. How it Works
|
||||||
|
|
||||||
SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the `window` object. This creates a specialized **DSL** (Domain Specific Language) that looks like a template engine but is **100% standard JavaScript**.
|
SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the `window` object. This creates a specialized **DSL** (Domain Specific Language) that looks like a template engine but is **100% standard JavaScript**.
|
||||||
|
|
||||||
* **Under the hood:** `$html('button', { onclick: ... }, 'Click')`
|
* **Under the hood:** `Tag('button', { onclick: ... }, 'Click')`
|
||||||
* **SigPro Style:** `Button({ onclick: ... }, 'Click')`
|
* **SigPro Style:** `Button({ onclick: ... }, 'Click')`
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -51,10 +51,10 @@ Section([
|
|||||||
|
|
||||||
## 4. Reactive Power
|
## 4. Reactive Power
|
||||||
|
|
||||||
These helpers are natively wired into SigPro's **`$watch`** engine.
|
These helpers are natively wired into SigPro's **`Watch`** engine.
|
||||||
|
|
||||||
### Reactive Attributes (One-Way)
|
### Reactive Attributes (One-Way)
|
||||||
Simply pass a Signal (function) to any attribute. SigPro creates an internal `$watch` to keep the DOM in sync.
|
Simply pass a Signal (function) to any attribute. SigPro creates an internal `Watch` to keep the DOM in sync.
|
||||||
```javascript
|
```javascript
|
||||||
const theme = $("light");
|
const theme = $("light");
|
||||||
|
|
||||||
@@ -80,12 +80,12 @@ Input({
|
|||||||
> **Pro Tip:** If you want an input to be **read-only** but still reactive, wrap the signal in an anonymous function: `value: () => search()`. This prevents the "backwards" synchronization.
|
> **Pro Tip:** If you want an input to be **read-only** but still reactive, wrap the signal in an anonymous function: `value: () => search()`. This prevents the "backwards" synchronization.
|
||||||
|
|
||||||
### Dynamic Flow & Cleanup
|
### Dynamic Flow & Cleanup
|
||||||
Combine tags with Core controllers. SigPro automatically cleans up the `$watch` instances and event listeners when nodes are removed from the DOM.
|
Combine tags with Core controllers. SigPro automatically cleans up the `Watch` instances and event listeners when nodes are removed from the DOM.
|
||||||
```javascript
|
```javascript
|
||||||
const items = $(["Apple", "Banana", "Cherry"]);
|
const items = $(["Apple", "Banana", "Cherry"]);
|
||||||
|
|
||||||
Ul({ class: "list-disc" }, [
|
Ul({ class: "list-disc" }, [
|
||||||
$for(items, (item) => Li(item), (item) => item)
|
For(items, (item) => Li(item), (item) => item)
|
||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -128,15 +128,15 @@ const UserStatus = (name, online) => (
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Custom Tags with `$html`
|
## 6. Custom Tags with `Tag`
|
||||||
|
|
||||||
Create reusable components using `$html`. All reactivity auto-cleans itself.
|
Create reusable components using `Tag`. All reactivity auto-cleans itself.
|
||||||
|
|
||||||
### Basic Example
|
### Basic Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const UserCard = (props, children) =>
|
const UserCard = (props, children) =>
|
||||||
$html('div', { class: 'card p-4', 'data-id': props.id }, children);
|
Tag('div', { class: 'card p-4', 'data-id': props.id }, children);
|
||||||
|
|
||||||
UserCard({ id: 123 }, [H3("John Doe"), P("john@example.com")]);
|
UserCard({ id: 123 }, [H3("John Doe"), P("john@example.com")]);
|
||||||
```
|
```
|
||||||
@@ -146,7 +146,7 @@ UserCard({ id: 123 }, [H3("John Doe"), P("john@example.com")]);
|
|||||||
```javascript
|
```javascript
|
||||||
const Counter = (initial = 0) => {
|
const Counter = (initial = 0) => {
|
||||||
const count = $(initial);
|
const count = $(initial);
|
||||||
return $html('div', { class: 'flex gap-2' }, [
|
return Tag('div', { class: 'flex gap-2' }, [
|
||||||
Button({ onclick: () => count(count() - 1) }, '-'),
|
Button({ onclick: () => count(count() - 1) }, '-'),
|
||||||
Span(() => count()),
|
Span(() => count()),
|
||||||
Button({ onclick: () => count(count() + 1) }, '+')
|
Button({ onclick: () => count(count() + 1) }, '+')
|
||||||
@@ -161,7 +161,7 @@ Only for external resources (intervals, sockets, third-party libs):
|
|||||||
```javascript
|
```javascript
|
||||||
const Timer = () => {
|
const Timer = () => {
|
||||||
const time = $(new Date().toLocaleTimeString());
|
const time = $(new Date().toLocaleTimeString());
|
||||||
const el = $html('span', {}, () => time());
|
const el = Tag('span', {}, () => time());
|
||||||
|
|
||||||
const interval = setInterval(() => time(new Date().toLocaleTimeString()), 1000);
|
const interval = setInterval(() => time(new Date().toLocaleTimeString()), 1000);
|
||||||
el._cleanups.add(() => clearInterval(interval)); // Manual cleanup
|
el._cleanups.add(() => clearInterval(interval)); // Manual cleanup
|
||||||
@@ -170,15 +170,15 @@ const Timer = () => {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### `$html` vs Tag Helpers
|
### `Tag` vs Tag Helpers
|
||||||
|
|
||||||
| Use | Recommendation |
|
| Use | Recommendation |
|
||||||
|:---|:---|
|
|:---|:---|
|
||||||
| Standard tags (`div`, `span`) | `Div()`, `Span()` helpers |
|
| Standard tags (`div`, `span`) | `Div()`, `Span()` helpers |
|
||||||
| Reusable components | Function returning `$html` |
|
| Reusable components | Function returning `Tag` |
|
||||||
| Dynamic tag names | `$html(tagName, props, children)` |
|
| Dynamic tag names | `Tag(tagName, props, children)` |
|
||||||
|
|
||||||
> **Auto-cleanup**: `$html` automatically destroys watchers, events, and nested components. Only add to `_cleanups` for external resources.
|
> **Auto-cleanup**: `Tag` automatically destroys watchers, events, and nested components. Only add to `_cleanups` for external resources.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# Reactivity Control: `$watch( )`
|
# Reactivity Control: `Watch( )`
|
||||||
|
|
||||||
The `$watch` function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. `$watch` is **polymorphic**: it can track dependencies automatically or follow an explicit list.
|
The `Watch` function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. `Watch` is **polymorphic**: it can track dependencies automatically or follow an explicit list.
|
||||||
|
|
||||||
## Function Signature
|
## Function Signature
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Automatic Mode (Magic Tracking)
|
// Automatic Mode (Magic Tracking)
|
||||||
$watch(callback: Function): StopFunction
|
Watch(callback: Function): StopFunction
|
||||||
|
|
||||||
// Explicit Mode (Isolated Dependencies)
|
// Explicit Mode (Isolated Dependencies)
|
||||||
$watch(deps: Signal[], callback: Function): StopFunction
|
Watch(deps: Signal[], callback: Function): StopFunction
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Type | Required | Description |
|
| Parameter | Type | Required | Description |
|
||||||
@@ -29,7 +29,7 @@ Any signal you "touch" inside the callback becomes a dependency. SigPro tracks t
|
|||||||
```javascript
|
```javascript
|
||||||
const count = $(0);
|
const count = $(0);
|
||||||
|
|
||||||
$watch(() => {
|
Watch(() => {
|
||||||
// Re-runs every time 'count' changes
|
// Re-runs every time 'count' changes
|
||||||
console.log(`Count is: ${count()}`);
|
console.log(`Count is: ${count()}`);
|
||||||
});
|
});
|
||||||
@@ -42,7 +42,7 @@ This mode **isolates** execution. The callback only triggers when the signals in
|
|||||||
const sPath = $("/home");
|
const sPath = $("/home");
|
||||||
const user = $("Admin");
|
const user = $("Admin");
|
||||||
|
|
||||||
$watch([sPath], () => {
|
Watch([sPath], () => {
|
||||||
// Only triggers when 'sPath' changes.
|
// Only triggers when 'sPath' changes.
|
||||||
// Changes to 'user' will NOT trigger this, preventing accidental re-renders.
|
// Changes to 'user' will NOT trigger this, preventing accidental re-renders.
|
||||||
console.log(`Navigating to ${sPath()} as ${user()}`);
|
console.log(`Navigating to ${sPath()} as ${user()}`);
|
||||||
@@ -53,11 +53,11 @@ $watch([sPath], () => {
|
|||||||
If your logic creates timers, event listeners, or other reactive effects, SigPro tracks them as "children" of the current watch. When the watcher re-runs or stops, it kills everything inside automatically.
|
If your logic creates timers, event listeners, or other reactive effects, SigPro tracks them as "children" of the current watch. When the watcher re-runs or stops, it kills everything inside automatically.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
$watch(() => {
|
Watch(() => {
|
||||||
const timer = setInterval(() => console.log("Tick"), 1000);
|
const timer = setInterval(() => console.log("Tick"), 1000);
|
||||||
|
|
||||||
// Register a manual cleanup if needed
|
// Register a manual cleanup if needed
|
||||||
// Or simply rely on SigPro to kill nested $watch() calls
|
// Or simply rely on SigPro to kill nested Watch() calls
|
||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -68,7 +68,7 @@ $watch(() => {
|
|||||||
Call the returned function to manually kill the watcher. This is essential for manual DOM injections (like Toasts) or long-lived background processes.
|
Call the returned function to manually kill the watcher. This is essential for manual DOM injections (like Toasts) or long-lived background processes.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const stop = $watch(() => console.log(count()));
|
const stop = Watch(() => console.log(count()));
|
||||||
|
|
||||||
// Later...
|
// Later...
|
||||||
stop(); // The link between the signal and this code is physically severed.
|
stop(); // The link between the signal and this code is physically severed.
|
||||||
@@ -83,7 +83,7 @@ SigPro batches updates. If you update multiple signals in the same execution blo
|
|||||||
const a = $(0);
|
const a = $(0);
|
||||||
const b = $(0);
|
const b = $(0);
|
||||||
|
|
||||||
$watch(() => console.log(a(), b()));
|
Watch(() => console.log(a(), b()));
|
||||||
|
|
||||||
// This triggers only ONE re-run.
|
// This triggers only ONE re-run.
|
||||||
a(1);
|
a(1);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const Counter = () => {
|
|||||||
Button({ class: 'btn btn-circle btn-primary', onclick: () => $count(c => c + 1) }, "+")
|
Button({ class: 'btn btn-circle btn-primary', onclick: () => $count(c => c + 1) }, "+")
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(Counter, '#demo-counter');
|
Mount(Counter, '#demo-counter');
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -48,12 +48,12 @@ const ComputedDemo = () => {
|
|||||||
P({ class: 'text-center' }, ["Base: ", $count, " ⮕ ", Span({class: 'text-primary font-bold'}, ["Double: ", $double])])
|
P({ class: 'text-center' }, ["Base: ", $count, " ⮕ ", Span({class: 'text-primary font-bold'}, ["Double: ", $double])])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ComputedDemo, '#demo-computed');
|
Mount(ComputedDemo, '#demo-computed');
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Lists and Loops ($for)
|
## 3. Lists and Loops (For)
|
||||||
SigPro handles lists efficiently, only updating specific DOM nodes.
|
SigPro handles lists efficiently, only updating specific DOM nodes.
|
||||||
|
|
||||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
@@ -79,16 +79,16 @@ const ListDemo = () => {
|
|||||||
Button({ class: 'btn btn-primary', onclick: addTodo }, "Add")
|
Button({ class: 'btn btn-primary', onclick: addTodo }, "Add")
|
||||||
]),
|
]),
|
||||||
Ul({ class: 'menu bg-base-200 rounded-box p-2' },
|
Ul({ class: 'menu bg-base-200 rounded-box p-2' },
|
||||||
$for($todos, (item) => Li([A(item)]), (item) => item)
|
For($todos, (item) => Li([A(item)]), (item) => item)
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ListDemo, '#demo-list');
|
Mount(ListDemo, '#demo-list');
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Conditional Rendering ($if)
|
## 4. Conditional Rendering (If)
|
||||||
Toggle visibility without re-rendering the entire parent.
|
Toggle visibility without re-rendering the entire parent.
|
||||||
|
|
||||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
@@ -103,13 +103,13 @@ const ConditionalDemo = () => {
|
|||||||
const $isVisible = $(false);
|
const $isVisible = $(false);
|
||||||
return Div({ class: 'text-center w-full' }, [
|
return Div({ class: 'text-center w-full' }, [
|
||||||
Button({ class: 'btn btn-outline mb-4', onclick: () => $isVisible(! $isVisible()) }, "Toggle Secret"),
|
Button({ class: 'btn btn-outline mb-4', onclick: () => $isVisible(! $isVisible()) }, "Toggle Secret"),
|
||||||
$if($isVisible,
|
If($isVisible,
|
||||||
() => Div({ class: 'p-4 bg-warning text-warning-content rounded-lg shadow-inner' }, "🤫 SigPro is Awesome!"),
|
() => Div({ class: 'p-4 bg-warning text-warning-content rounded-lg shadow-inner' }, "🤫 SigPro is Awesome!"),
|
||||||
() => Div({ class: 'p-4 opacity-50' }, "Nothing to see here...")
|
() => Div({ class: 'p-4 opacity-50' }, "Nothing to see here...")
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ConditionalDemo, '#demo-if');
|
Mount(ConditionalDemo, '#demo-if');
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -133,7 +133,7 @@ const PersistDemo = () => {
|
|||||||
P({ class: 'text-xs opacity-50' }, "Refresh the page to see magic!")
|
P({ class: 'text-xs opacity-50' }, "Refresh the page to see magic!")
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PersistDemo, '#demo-persist');
|
Mount(PersistDemo, '#demo-persist');
|
||||||
```
|
```
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -150,7 +150,7 @@ $mount(PersistDemo, '#demo-persist');
|
|||||||
Button({ class: 'btn btn-circle btn-primary', onclick: () => $count(c => c + 1) }, "+")
|
Button({ class: 'btn btn-circle btn-primary', onclick: () => $count(c => c + 1) }, "+")
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(Counter, counterTarget);
|
Mount(Counter, counterTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Computed
|
// 2. Computed
|
||||||
@@ -164,7 +164,7 @@ $mount(PersistDemo, '#demo-persist');
|
|||||||
P({ class: 'text-center' }, ["Base: ", $count, " ⮕ ", Span({class: 'text-primary font-bold'}, ["Double: ", $double])])
|
P({ class: 'text-center' }, ["Base: ", $count, " ⮕ ", Span({class: 'text-primary font-bold'}, ["Double: ", $double])])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ComputedDemo, computedTarget);
|
Mount(ComputedDemo, computedTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. List
|
// 3. List
|
||||||
@@ -179,10 +179,10 @@ $mount(PersistDemo, '#demo-persist');
|
|||||||
Input({ class: 'input input-bordered flex-1', value: $input, placeholder: 'New task...' }),
|
Input({ class: 'input input-bordered flex-1', value: $input, placeholder: 'New task...' }),
|
||||||
Button({ class: 'btn btn-primary', onclick: addTodo }, "Add")
|
Button({ class: 'btn btn-primary', onclick: addTodo }, "Add")
|
||||||
]),
|
]),
|
||||||
Ul({ class: 'menu bg-base-200 rounded-box p-2' }, $for($todos, (item) => Li([A(item)]), (item) => item))
|
Ul({ class: 'menu bg-base-200 rounded-box p-2' }, For($todos, (item) => Li([A(item)]), (item) => item))
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ListDemo, listTarget);
|
Mount(ListDemo, listTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. If
|
// 4. If
|
||||||
@@ -192,13 +192,13 @@ $mount(PersistDemo, '#demo-persist');
|
|||||||
const $isVisible = $(false);
|
const $isVisible = $(false);
|
||||||
return Div({ class: 'text-center w-full' }, [
|
return Div({ class: 'text-center w-full' }, [
|
||||||
Button({ class: 'btn btn-outline mb-4', onclick: () => $isVisible(! $isVisible()) }, "Toggle Secret"),
|
Button({ class: 'btn btn-outline mb-4', onclick: () => $isVisible(! $isVisible()) }, "Toggle Secret"),
|
||||||
$if($isVisible,
|
If($isVisible,
|
||||||
() => Div({ class: 'p-4 bg-warning text-warning-content rounded-lg' }, "🤫 SigPro is Awesome!"),
|
() => Div({ class: 'p-4 bg-warning text-warning-content rounded-lg' }, "🤫 SigPro is Awesome!"),
|
||||||
() => Div({ class: 'p-4 opacity-50' }, "Nothing to see here...")
|
() => Div({ class: 'p-4 opacity-50' }, "Nothing to see here...")
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ConditionalDemo, ifTarget);
|
Mount(ConditionalDemo, ifTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Persist
|
// 5. Persist
|
||||||
@@ -212,7 +212,7 @@ $mount(PersistDemo, '#demo-persist');
|
|||||||
P({ class: 'text-xs opacity-50' }, "Refresh the page!")
|
P({ class: 'text-xs opacity-50' }, "Refresh the page!")
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PersistDemo, persistTarget);
|
Mount(PersistDemo, persistTarget);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
name: 'SigPro',
|
name: 'SigPro',
|
||||||
repo: '',
|
repo: '',
|
||||||
loadSidebar: true,
|
loadSidebar: true,
|
||||||
subMaxLevel: 3,
|
subMaxLevel: 0,
|
||||||
sidebarDisplayLevel: 1,
|
sidebarDisplayLevel: 1,
|
||||||
executeScript: true,
|
executeScript: true,
|
||||||
copyCode: {
|
copyCode: {
|
||||||
@@ -31,9 +31,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="module">
|
<script src="./sigpro.js"></script>
|
||||||
import SigPro from 'https://cdn.jsdelivr.net/npm/sigpro@latest/+esm';
|
|
||||||
</script>
|
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export const App = () => {
|
|||||||
import "sigpro";
|
import "sigpro";
|
||||||
import { App } from "./App.js";
|
import { App } from "./App.js";
|
||||||
|
|
||||||
$mount(App, "#app");
|
Mount(App, "#app");
|
||||||
```
|
```
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -137,7 +137,7 @@ $mount(App, "#app");
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$mount(App, "#app");
|
Mount(App, "#app");
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -153,14 +153,14 @@ $mount(App, "#app");
|
|||||||
One of SigPro's core strengths is its **Global API**, which eliminates "Import Hell" while remaining ESM-compatible.
|
One of SigPro's core strengths is its **Global API**, which eliminates "Import Hell" while remaining ESM-compatible.
|
||||||
|
|
||||||
- **The "Zero-Config" Import:** By simply adding `import SigPro from "sigpro"`, the framework automatically "hydrates" the global `window` object.
|
- **The "Zero-Config" Import:** By simply adding `import SigPro from "sigpro"`, the framework automatically "hydrates" the global `window` object.
|
||||||
- **Core Functions:** You get immediate access to `$`, `$watch`, `$html`, `$if`, `$for`, and `$router` anywhere in your scripts without using the `SigPro.` prefix.
|
- **Core Functions:** You get immediate access to `$`, `Watch`, `Tag`, `If`, `For`, and `Router` anywhere in your scripts without using the `SigPro.` prefix.
|
||||||
- **Auto-Installation:** This happens instantly upon import thanks to its built-in `install()` routine, making it "Plug & Play" for both local projects and CDN usage.
|
- **Auto-Installation:** This happens instantly upon import thanks to its built-in `install()` routine, making it "Plug & Play" for both local projects and CDN usage.
|
||||||
|
|
||||||
- **PascalCase Tag Helpers:** Standard HTML tags are pre-registered as global functions (`Div`, `Span`, `Button`, `Section`, etc.).
|
- **PascalCase Tag Helpers:** Standard HTML tags are pre-registered as global functions (`Div`, `Span`, `Button`, `Section`, etc.).
|
||||||
- **Clean UI Syntax:** This allows you to write UI structures that look like HTML but are pure, reactive JavaScript: `Div({ class: "card" }, [ H1("Title") ])`.
|
- **Clean UI Syntax:** This allows you to write UI structures that look like HTML but are pure, reactive JavaScript: `Div({ class: "card" }, [ H1("Title") ])`.
|
||||||
|
|
||||||
- **Hybrid Tree Shaking:** \* For **Maximum Speed**, use `import SigPro from "sigpro"`.
|
- **Hybrid Tree Shaking:** \* For **Maximum Speed**, use `import SigPro from "sigpro"`.
|
||||||
- For **Maximum Optimization**, you can still use Named Imports: `import { $, $html } from "sigpro"`. This allows modern bundlers like Vite to prune unused code while keeping your core reactive.
|
- For **Maximum Optimization**, you can still use Named Imports: `import { $, Tag } from "sigpro"`. This allows modern bundlers like Vite to prune unused code while keeping your core reactive.
|
||||||
|
|
||||||
- **Custom Components:** We recommend using **PascalCase** for your own components (e.g., `UserCard()`) to maintain visual consistency with the built-in Tag Helpers and distinguish them from standard logic.
|
- **Custom Components:** We recommend using **PascalCase** for your own components (e.g., `UserCard()`) to maintain visual consistency with the built-in Tag Helpers and distinguish them from standard logic.
|
||||||
|
|
||||||
|
|||||||
472
docs/sigpro.js
Normal file
472
docs/sigpro.js
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
(() => {
|
||||||
|
var __defProp = Object.defineProperty;
|
||||||
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||||
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||||
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||||
|
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
||||||
|
var __toCommonJS = (from) => {
|
||||||
|
var entry = __moduleCache.get(from), desc;
|
||||||
|
if (entry)
|
||||||
|
return entry;
|
||||||
|
entry = __defProp({}, "__esModule", { value: true });
|
||||||
|
if (from && typeof from === "object" || typeof from === "function")
|
||||||
|
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
||||||
|
get: () => from[key],
|
||||||
|
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
||||||
|
}));
|
||||||
|
__moduleCache.set(from, entry);
|
||||||
|
return entry;
|
||||||
|
};
|
||||||
|
var __export = (target, all) => {
|
||||||
|
for (var name in all)
|
||||||
|
__defProp(target, name, {
|
||||||
|
get: all[name],
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
set: (newValue) => all[name] = () => newValue
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// index.js
|
||||||
|
var exports_sigpro = {};
|
||||||
|
__export(exports_sigpro, {
|
||||||
|
Watch: () => Watch,
|
||||||
|
Tag: () => Tag,
|
||||||
|
Router: () => Router,
|
||||||
|
Render: () => Render,
|
||||||
|
Mount: () => Mount,
|
||||||
|
If: () => If,
|
||||||
|
For: () => For,
|
||||||
|
$$: () => $$,
|
||||||
|
$: () => $
|
||||||
|
});
|
||||||
|
|
||||||
|
// sigpro.js
|
||||||
|
var activeEffect = null;
|
||||||
|
var currentOwner = null;
|
||||||
|
var effectQueue = new Set;
|
||||||
|
var isFlushing = false;
|
||||||
|
var MOUNTED_NODES = new WeakMap;
|
||||||
|
var doc = document;
|
||||||
|
var isArr = Array.isArray;
|
||||||
|
var assign = Object.assign;
|
||||||
|
var createEl = (t) => doc.createElement(t);
|
||||||
|
var createText = (t) => doc.createTextNode(String(t ?? ""));
|
||||||
|
var isFunc = (f) => typeof f === "function";
|
||||||
|
var isObj = (o) => typeof o === "object" && o !== null;
|
||||||
|
var runWithContext = (effect, callback) => {
|
||||||
|
const previousEffect = activeEffect;
|
||||||
|
activeEffect = effect;
|
||||||
|
try {
|
||||||
|
return callback();
|
||||||
|
} finally {
|
||||||
|
activeEffect = previousEffect;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var cleanupNode = (node) => {
|
||||||
|
if (node._cleanups) {
|
||||||
|
node._cleanups.forEach((dispose) => dispose());
|
||||||
|
node._cleanups.clear();
|
||||||
|
}
|
||||||
|
node.childNodes?.forEach(cleanupNode);
|
||||||
|
};
|
||||||
|
var flushEffects = () => {
|
||||||
|
if (isFlushing)
|
||||||
|
return;
|
||||||
|
isFlushing = true;
|
||||||
|
while (effectQueue.size > 0) {
|
||||||
|
const sortedEffects = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
||||||
|
effectQueue.clear();
|
||||||
|
for (const effect of sortedEffects) {
|
||||||
|
if (!effect._deleted)
|
||||||
|
effect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isFlushing = false;
|
||||||
|
};
|
||||||
|
var trackSubscription = (subscribers) => {
|
||||||
|
if (activeEffect && !activeEffect._deleted) {
|
||||||
|
subscribers.add(activeEffect);
|
||||||
|
activeEffect._deps.add(subscribers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var triggerUpdate = (subscribers) => {
|
||||||
|
subscribers.forEach((effect) => {
|
||||||
|
if (effect === activeEffect || effect._deleted)
|
||||||
|
return;
|
||||||
|
if (effect._isComputed) {
|
||||||
|
effect.markDirty();
|
||||||
|
if (effect._subs)
|
||||||
|
triggerUpdate(effect._subs);
|
||||||
|
} else {
|
||||||
|
effectQueue.add(effect);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!isFlushing)
|
||||||
|
queueMicrotask(flushEffects);
|
||||||
|
};
|
||||||
|
var Render = (renderFn) => {
|
||||||
|
const cleanups = new Set;
|
||||||
|
const previousOwner = currentOwner;
|
||||||
|
const container = createEl("div");
|
||||||
|
container.style.display = "contents";
|
||||||
|
currentOwner = { cleanups };
|
||||||
|
const processResult = (result) => {
|
||||||
|
if (!result)
|
||||||
|
return;
|
||||||
|
if (result._isRuntime) {
|
||||||
|
cleanups.add(result.destroy);
|
||||||
|
container.appendChild(result.container);
|
||||||
|
} else if (isArr(result)) {
|
||||||
|
result.forEach(processResult);
|
||||||
|
} else {
|
||||||
|
container.appendChild(result instanceof Node ? result : createText(result));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
||||||
|
} finally {
|
||||||
|
currentOwner = previousOwner;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
_isRuntime: true,
|
||||||
|
container,
|
||||||
|
destroy: () => {
|
||||||
|
cleanups.forEach((fn) => fn());
|
||||||
|
cleanupNode(container);
|
||||||
|
container.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var $ = (initialValue, storageKey = null) => {
|
||||||
|
const subscribers = new Set;
|
||||||
|
if (isFunc(initialValue)) {
|
||||||
|
let cachedValue, isDirty = true;
|
||||||
|
const effect = () => {
|
||||||
|
if (effect._deleted)
|
||||||
|
return;
|
||||||
|
effect._deps.forEach((dep) => dep.delete(effect));
|
||||||
|
effect._deps.clear();
|
||||||
|
runWithContext(effect, () => {
|
||||||
|
const newValue = initialValue();
|
||||||
|
if (!Object.is(cachedValue, newValue) || isDirty) {
|
||||||
|
cachedValue = newValue;
|
||||||
|
isDirty = false;
|
||||||
|
triggerUpdate(subscribers);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
assign(effect, {
|
||||||
|
_deps: new Set,
|
||||||
|
_isComputed: true,
|
||||||
|
_subs: subscribers,
|
||||||
|
_deleted: false,
|
||||||
|
markDirty: () => isDirty = true,
|
||||||
|
stop: () => {
|
||||||
|
effect._deleted = true;
|
||||||
|
effect._deps.forEach((dep) => dep.delete(effect));
|
||||||
|
subscribers.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (currentOwner)
|
||||||
|
currentOwner.cleanups.add(effect.stop);
|
||||||
|
return () => {
|
||||||
|
if (isDirty)
|
||||||
|
effect();
|
||||||
|
trackSubscription(subscribers);
|
||||||
|
return cachedValue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let value = initialValue;
|
||||||
|
if (storageKey) {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem(storageKey);
|
||||||
|
if (saved !== null)
|
||||||
|
value = JSON.parse(saved);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("SigPro Storage Lock", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (...args) => {
|
||||||
|
if (args.length) {
|
||||||
|
const nextValue = isFunc(args[0]) ? args[0](value) : args[0];
|
||||||
|
if (!Object.is(value, nextValue)) {
|
||||||
|
value = nextValue;
|
||||||
|
if (storageKey)
|
||||||
|
localStorage.setItem(storageKey, JSON.stringify(value));
|
||||||
|
triggerUpdate(subscribers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackSubscription(subscribers);
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var $$ = (object, cache = new WeakMap) => {
|
||||||
|
if (!isObj(object))
|
||||||
|
return object;
|
||||||
|
if (cache.has(object))
|
||||||
|
return cache.get(object);
|
||||||
|
const keySubscribers = {};
|
||||||
|
const proxy = new Proxy(object, {
|
||||||
|
get(target, key) {
|
||||||
|
if (activeEffect)
|
||||||
|
trackSubscription(keySubscribers[key] ??= new Set);
|
||||||
|
const value = Reflect.get(target, key);
|
||||||
|
return isObj(value) ? $$(value, cache) : value;
|
||||||
|
},
|
||||||
|
set(target, key, value) {
|
||||||
|
if (Object.is(target[key], value))
|
||||||
|
return true;
|
||||||
|
const success = Reflect.set(target, key, value);
|
||||||
|
if (keySubscribers[key])
|
||||||
|
triggerUpdate(keySubscribers[key]);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cache.set(object, proxy);
|
||||||
|
return proxy;
|
||||||
|
};
|
||||||
|
var Watch = (target, callbackFn) => {
|
||||||
|
const isExplicit = isArr(target);
|
||||||
|
const callback = isExplicit ? callbackFn : target;
|
||||||
|
if (!isFunc(callback))
|
||||||
|
return () => {};
|
||||||
|
const owner = currentOwner;
|
||||||
|
const runner = () => {
|
||||||
|
if (runner._deleted)
|
||||||
|
return;
|
||||||
|
runner._deps.forEach((dep) => dep.delete(runner));
|
||||||
|
runner._deps.clear();
|
||||||
|
runner._cleanups.forEach((cleanup) => cleanup());
|
||||||
|
runner._cleanups.clear();
|
||||||
|
const previousOwner = currentOwner;
|
||||||
|
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
||||||
|
runWithContext(runner, () => {
|
||||||
|
currentOwner = { cleanups: runner._cleanups };
|
||||||
|
if (isExplicit) {
|
||||||
|
runWithContext(null, callback);
|
||||||
|
target.forEach((dep) => isFunc(dep) && dep());
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
currentOwner = previousOwner;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
assign(runner, {
|
||||||
|
_deps: new Set,
|
||||||
|
_cleanups: new Set,
|
||||||
|
_deleted: false,
|
||||||
|
stop: () => {
|
||||||
|
if (runner._deleted)
|
||||||
|
return;
|
||||||
|
runner._deleted = true;
|
||||||
|
effectQueue.delete(runner);
|
||||||
|
runner._deps.forEach((dep) => dep.delete(runner));
|
||||||
|
runner._cleanups.forEach((cleanup) => cleanup());
|
||||||
|
if (owner)
|
||||||
|
owner.cleanups.delete(runner.stop);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (owner)
|
||||||
|
owner.cleanups.add(runner.stop);
|
||||||
|
runner();
|
||||||
|
return runner.stop;
|
||||||
|
};
|
||||||
|
var Tag = (tag, props = {}, children = []) => {
|
||||||
|
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||||
|
children = props;
|
||||||
|
props = {};
|
||||||
|
}
|
||||||
|
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
||||||
|
const element = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : createEl(tag);
|
||||||
|
element._cleanups = new Set;
|
||||||
|
element.onUnmount = (fn) => element._cleanups.add(fn);
|
||||||
|
const booleanAttributes = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
||||||
|
const updateAttribute = (name, value) => {
|
||||||
|
const sanitized = (name === "src" || name === "href") && String(value).toLowerCase().includes("javascript:") ? "#" : value;
|
||||||
|
if (booleanAttributes.includes(name)) {
|
||||||
|
element[name] = !!sanitized;
|
||||||
|
sanitized ? element.setAttribute(name, "") : element.removeAttribute(name);
|
||||||
|
} else {
|
||||||
|
sanitized == null ? element.removeAttribute(name) : element.setAttribute(name, sanitized);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (let [key, value] of Object.entries(props)) {
|
||||||
|
if (key === "ref") {
|
||||||
|
isFunc(value) ? value(element) : value.current = element;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const isSignal = isFunc(value);
|
||||||
|
if (key.startsWith("on")) {
|
||||||
|
const eventName = key.slice(2).toLowerCase().split(".")[0];
|
||||||
|
element.addEventListener(eventName, value);
|
||||||
|
element._cleanups.add(() => element.removeEventListener(eventName, value));
|
||||||
|
} else if (isSignal) {
|
||||||
|
element._cleanups.add(Watch(() => {
|
||||||
|
const currentVal = value();
|
||||||
|
key === "class" ? element.className = currentVal || "" : updateAttribute(key, currentVal);
|
||||||
|
}));
|
||||||
|
if (["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName) && (key === "value" || key === "checked")) {
|
||||||
|
const event = key === "checked" ? "change" : "input";
|
||||||
|
const handler = (e) => value(e.target[key]);
|
||||||
|
element.addEventListener(event, handler);
|
||||||
|
element._cleanups.add(() => element.removeEventListener(event, handler));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateAttribute(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const appendChildNode = (child) => {
|
||||||
|
if (isArr(child))
|
||||||
|
return child.forEach(appendChildNode);
|
||||||
|
if (isFunc(child)) {
|
||||||
|
const marker = createText("");
|
||||||
|
element.appendChild(marker);
|
||||||
|
let currentNodes = [];
|
||||||
|
element._cleanups.add(Watch(() => {
|
||||||
|
const result = child();
|
||||||
|
const nextNodes = (isArr(result) ? result : [result]).map((node) => node?._isRuntime ? node.container : node instanceof Node ? node : createText(node));
|
||||||
|
currentNodes.forEach((node) => {
|
||||||
|
cleanupNode(node);
|
||||||
|
node.remove();
|
||||||
|
});
|
||||||
|
nextNodes.forEach((node) => marker.parentNode?.insertBefore(node, marker));
|
||||||
|
currentNodes = nextNodes;
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
element.appendChild(child instanceof Node ? child : createText(child));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
appendChildNode(children);
|
||||||
|
return element;
|
||||||
|
};
|
||||||
|
var If = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
||||||
|
const marker = createText("");
|
||||||
|
const container = Tag("div", { style: "display:contents" }, [marker]);
|
||||||
|
let currentView = null, lastState = null;
|
||||||
|
Watch(() => {
|
||||||
|
const state = !!(isFunc(condition) ? condition() : condition);
|
||||||
|
if (state === lastState)
|
||||||
|
return;
|
||||||
|
lastState = state;
|
||||||
|
const dispose = () => {
|
||||||
|
if (currentView)
|
||||||
|
currentView.destroy();
|
||||||
|
currentView = null;
|
||||||
|
};
|
||||||
|
if (currentView && !state && transition?.out) {
|
||||||
|
transition.out(currentView.container, dispose);
|
||||||
|
} else {
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
const branch = state ? thenVal : otherwiseVal;
|
||||||
|
if (branch) {
|
||||||
|
currentView = Render(() => isFunc(branch) ? branch() : branch);
|
||||||
|
container.insertBefore(currentView.container, marker);
|
||||||
|
if (state && transition?.in)
|
||||||
|
transition.in(currentView.container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
};
|
||||||
|
var For = (source, renderFn, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
||||||
|
const marker = createText("");
|
||||||
|
const container = Tag(tag, props, [marker]);
|
||||||
|
let viewCache = new Map;
|
||||||
|
Watch(() => {
|
||||||
|
const items = (isFunc(source) ? source() : source) || [];
|
||||||
|
const nextCache = new Map;
|
||||||
|
const order = [];
|
||||||
|
for (let i = 0;i < items.length; i++) {
|
||||||
|
const item = items[i];
|
||||||
|
const key = keyFn ? keyFn(item, i) : i;
|
||||||
|
let view = viewCache.get(key);
|
||||||
|
if (!view) {
|
||||||
|
const result = renderFn(item, i);
|
||||||
|
view = result instanceof Node ? { container: result, destroy: () => {
|
||||||
|
cleanupNode(result);
|
||||||
|
result.remove();
|
||||||
|
} } : Render(() => result);
|
||||||
|
}
|
||||||
|
viewCache.delete(key);
|
||||||
|
nextCache.set(key, view);
|
||||||
|
order.push(key);
|
||||||
|
}
|
||||||
|
viewCache.forEach((v) => v.destroy());
|
||||||
|
let anchor = marker;
|
||||||
|
for (let i = order.length - 1;i >= 0; i--) {
|
||||||
|
const view = nextCache.get(order[i]);
|
||||||
|
if (view.container.nextSibling !== anchor) {
|
||||||
|
container.insertBefore(view.container, anchor);
|
||||||
|
}
|
||||||
|
anchor = view.container;
|
||||||
|
}
|
||||||
|
viewCache = nextCache;
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
};
|
||||||
|
var Router = (routes) => {
|
||||||
|
const currentPath = $(window.location.hash.replace(/^#/, "") || "/");
|
||||||
|
window.addEventListener("hashchange", () => currentPath(window.location.hash.replace(/^#/, "") || "/"));
|
||||||
|
const outlet = Tag("div", { class: "router-outlet" });
|
||||||
|
let currentView = null;
|
||||||
|
Watch([currentPath], async () => {
|
||||||
|
const path = currentPath();
|
||||||
|
const route = routes.find((r) => {
|
||||||
|
const routeParts = r.path.split("/").filter(Boolean);
|
||||||
|
const pathParts = path.split("/").filter(Boolean);
|
||||||
|
return routeParts.length === pathParts.length && routeParts.every((part, i) => part.startsWith(":") || part === pathParts[i]);
|
||||||
|
}) || routes.find((r) => r.path === "*");
|
||||||
|
if (route) {
|
||||||
|
let component = route.component;
|
||||||
|
if (isFunc(component) && component.toString().includes("import")) {
|
||||||
|
component = (await component()).default || await component();
|
||||||
|
}
|
||||||
|
const params = {};
|
||||||
|
route.path.split("/").filter(Boolean).forEach((part, i) => {
|
||||||
|
if (part.startsWith(":"))
|
||||||
|
params[part.slice(1)] = path.split("/").filter(Boolean)[i];
|
||||||
|
});
|
||||||
|
if (currentView)
|
||||||
|
currentView.destroy();
|
||||||
|
if (Router.params)
|
||||||
|
Router.params(params);
|
||||||
|
currentView = Render(() => {
|
||||||
|
try {
|
||||||
|
return isFunc(component) ? component(params) : component;
|
||||||
|
} catch (e) {
|
||||||
|
return Tag("div", { class: "p-4 text-error" }, "Error loading view");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
outlet.appendChild(currentView.container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return outlet;
|
||||||
|
};
|
||||||
|
Router.params = $({});
|
||||||
|
Router.to = (path) => window.location.hash = path.replace(/^#?\/?/, "#/");
|
||||||
|
Router.back = () => window.history.back();
|
||||||
|
Router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
||||||
|
var Mount = (component, target) => {
|
||||||
|
const targetEl = typeof target === "string" ? doc.querySelector(target) : target;
|
||||||
|
if (!targetEl)
|
||||||
|
return;
|
||||||
|
if (MOUNTED_NODES.has(targetEl))
|
||||||
|
MOUNTED_NODES.get(targetEl).destroy();
|
||||||
|
const instance = Render(isFunc(component) ? component : () => component);
|
||||||
|
targetEl.replaceChildren(instance.container);
|
||||||
|
MOUNTED_NODES.set(targetEl, instance);
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
|
var SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
assign(window, SigPro);
|
||||||
|
const tags = `div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter`.split(" ");
|
||||||
|
tags.forEach((tag) => {
|
||||||
|
const helper = tag[0].toUpperCase() + tag.slice(1);
|
||||||
|
if (!(helper in window))
|
||||||
|
window[helper] = (p, c) => Tag(tag, p, c);
|
||||||
|
});
|
||||||
|
window.SigPro = Object.freeze(SigPro);
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
Unlike **Tag Helpers** (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions:
|
Unlike **Tag Helpers** (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions:
|
||||||
|
|
||||||
* **Stateful**: They manage complex internal states (like date ranges, search filtering, or API lifecycles).
|
* **Stateful**: They manage complex internal states (like date ranges, search filtering, or API lifecycles).
|
||||||
* **Reactive**: Attributes prefixed with `$` are automatically tracked via `$watch`.
|
* **Reactive**: Attributes prefixed with `$` are automatically tracked via `Watch`.
|
||||||
* **Self-Sane**: They automatically use `._cleanups` to destroy observers or event listeners when removed from the DOM.
|
* **Self-Sane**: They automatically use `._cleanups` to destroy observers or event listeners when removed from the DOM.
|
||||||
* **Themed**: Fully compatible with DaisyUI v5 theme system and Tailwind v4 utility classes.
|
* **Themed**: Fully compatible with DaisyUI v5 theme system and Tailwind v4 utility classes.
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ Datepicker({
|
|||||||
```
|
```
|
||||||
|
|
||||||
### D. Imperative Toasts & Modals
|
### D. Imperative Toasts & Modals
|
||||||
Trigger complex UI elements from your logic. These components use `$mount` internally to ensure they are properly cleaned up from memory after they close.
|
Trigger complex UI elements from your logic. These components use `Mount` internally to ensure they are properly cleaned up from memory after they close.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Show a notification (Self-destroying after 3s)
|
// Show a notification (Self-destroying after 3s)
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ Thanks to **SigPro's synchronous initialization**, you no longer need to wrap yo
|
|||||||
import SigPro from 'sigpro';
|
import SigPro from 'sigpro';
|
||||||
import { routes } from 'virtual:sigpro-routes';
|
import { routes } from 'virtual:sigpro-routes';
|
||||||
|
|
||||||
// The Core already has $router ready
|
// The Core already has Router ready
|
||||||
$mount($router(routes), '#app');
|
Mount(Router(routes), '#app');
|
||||||
```
|
```
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -71,12 +71,12 @@ export default () => div({ class: 'layout' }, [
|
|||||||
header([
|
header([
|
||||||
h1("SigPro App"),
|
h1("SigPro App"),
|
||||||
nav([
|
nav([
|
||||||
button({ onclick: () => $router.go('/') }, "Home"),
|
button({ onclick: () => Router.go('/') }, "Home"),
|
||||||
button({ onclick: () => $router.go('/blog') }, "Blog")
|
button({ onclick: () => Router.go('/blog') }, "Blog")
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
// Only the content inside <main> will be swapped reactively
|
// Only the content inside <main> will be swapped reactively
|
||||||
main($router(routes))
|
main(Router(routes))
|
||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ The plugin follows a simple convention to transform your file system into a rout
|
|||||||
|
|
||||||
## 5. How it Works (Vite Virtual Module)
|
## 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()`:
|
The plugin generates a virtual module named `virtual:sigpro-routes`. This module exports an array of objects compatible with `Router()`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Internal representation generated by the plugin
|
// Internal representation generated by the plugin
|
||||||
|
|||||||
@@ -37,10 +37,11 @@
|
|||||||
"clean": "rm -rf dist",
|
"clean": "rm -rf dist",
|
||||||
"prebuild": "npm run clean",
|
"prebuild": "npm run clean",
|
||||||
"build:iife": "bun build ./index.js --bundle --outfile=./dist/sigpro.js --format=iife --global-name=SigPro",
|
"build:iife": "bun build ./index.js --bundle --outfile=./dist/sigpro.js --format=iife --global-name=SigPro",
|
||||||
|
"build:docs": "bun build ./index.js --bundle --outfile=./docs/sigpro.js --format=iife --global-name=SigPro",
|
||||||
"build:iife:min": "bun build ./index.js --bundle --outfile=./dist/sigpro.min.js --format=iife --global-name=SigPro --minify",
|
"build:iife:min": "bun build ./index.js --bundle --outfile=./dist/sigpro.min.js --format=iife --global-name=SigPro --minify",
|
||||||
"build:esm": "bun build ./index.js --bundle --outfile=./dist/sigpro.esm.js --format=esm",
|
"build:esm": "bun build ./index.js --bundle --outfile=./dist/sigpro.esm.js --format=esm",
|
||||||
"build:esm:min": "bun build ./index.js --bundle --outfile=./dist/sigpro.esm.min.js --format=esm --minify",
|
"build:esm:min": "bun build ./index.js --bundle --outfile=./dist/sigpro.esm.min.js --format=esm --minify",
|
||||||
"build": "npm run build:iife && npm run build:iife:min && npm run build:esm && npm run build:esm:min",
|
"build": "bun run build:iife && bun run build:iife:min && bun run build:esm && bun run build:esm:min && bun run build:docs",
|
||||||
"docs": "bun x serve docs",
|
"docs": "bun x serve docs",
|
||||||
"prepublishOnly": "npm run build"
|
"prepublishOnly": "npm run build"
|
||||||
},
|
},
|
||||||
|
|||||||
228
sigpro.d.ts
vendored
228
sigpro.d.ts
vendored
@@ -1,25 +1,13 @@
|
|||||||
// sigpro.d.ts
|
declare module 'sigpro' {
|
||||||
|
|
||||||
export declare module 'sigpro' {
|
|
||||||
type Signal<T> = {
|
type Signal<T> = {
|
||||||
(): T;
|
(): T;
|
||||||
(value: T | ((prev: T) => T)): void;
|
(value: T | ((prev: T) => T)): T;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ComputedSignal<T> = {
|
type ComputedSignal<T> = () => T;
|
||||||
(): T;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ReactiveObject<T extends object> = T;
|
|
||||||
|
|
||||||
type DeepReactive<T> = T extends object
|
|
||||||
? { [K in keyof T]: DeepReactive<T[K]> }
|
|
||||||
: T;
|
|
||||||
|
|
||||||
type Unsubscribe = () => void;
|
type Unsubscribe = () => void;
|
||||||
|
|
||||||
type CleanupFn = () => void;
|
|
||||||
|
|
||||||
type ViewInstance = {
|
type ViewInstance = {
|
||||||
_isRuntime: true;
|
_isRuntime: true;
|
||||||
container: HTMLDivElement;
|
container: HTMLDivElement;
|
||||||
@@ -60,53 +48,46 @@ export declare module 'sigpro' {
|
|||||||
component: ComponentFunction | (() => Promise<ComponentFunction>);
|
component: ComponentFunction | (() => Promise<ComponentFunction>);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RouterOutlet extends HTMLElement {
|
interface RouterOutlet {
|
||||||
params: Signal<Record<string, string>>;
|
params: Signal<Record<string, string>>;
|
||||||
to: (path: string) => void;
|
to: (path: string) => void;
|
||||||
back: () => void;
|
back: () => void;
|
||||||
path: () => string;
|
path: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function $<T>(initial: T, key?: string): Signal<T>;
|
|
||||||
function $<T>(computed: () => T): ComputedSignal<T>;
|
|
||||||
function $$<T extends object>(obj: T): T;
|
|
||||||
|
|
||||||
function $watch(
|
|
||||||
target: () => any,
|
|
||||||
fn?: never
|
|
||||||
): Unsubscribe;
|
|
||||||
function $watch(
|
|
||||||
target: Array<() => any>,
|
|
||||||
fn: () => void
|
|
||||||
): Unsubscribe;
|
|
||||||
|
|
||||||
function $html(
|
|
||||||
tag: string,
|
|
||||||
props?: HtmlProps | ComponentChildren,
|
|
||||||
content?: ComponentChildren
|
|
||||||
): HTMLElement;
|
|
||||||
|
|
||||||
interface Transition {
|
interface Transition {
|
||||||
in: (el: HTMLElement) => void;
|
in: (el: HTMLElement) => void;
|
||||||
out: (el: HTMLElement, done: () => void) => void;
|
out: (el: HTMLElement, done: () => void) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function $if(
|
function $<T>(initial: T, storageKey?: string): Signal<T>;
|
||||||
|
function $<T>(computed: () => T): ComputedSignal<T>;
|
||||||
|
|
||||||
|
function $$<T extends object>(obj: T): T;
|
||||||
|
|
||||||
|
function Watch(
|
||||||
|
target: () => any,
|
||||||
|
callback?: never
|
||||||
|
): Unsubscribe;
|
||||||
|
function Watch(
|
||||||
|
target: Array<() => any>,
|
||||||
|
callback: () => void
|
||||||
|
): Unsubscribe;
|
||||||
|
|
||||||
|
function Tag(
|
||||||
|
tag: string,
|
||||||
|
props?: HtmlProps | ComponentChildren,
|
||||||
|
children?: ComponentChildren
|
||||||
|
): HTMLElement;
|
||||||
|
|
||||||
|
function If(
|
||||||
condition: boolean | (() => boolean),
|
condition: boolean | (() => boolean),
|
||||||
thenVal: ComponentFunction | ComponentChild,
|
thenVal: ComponentFunction | ComponentChild,
|
||||||
otherwiseVal?: ComponentFunction | ComponentChild,
|
otherwiseVal?: ComponentFunction | ComponentChild,
|
||||||
transition?: Transition
|
transition?: Transition
|
||||||
): HTMLDivElement;
|
): HTMLDivElement;
|
||||||
|
|
||||||
namespace $if {
|
function For<T>(
|
||||||
function not(
|
|
||||||
condition: boolean | (() => boolean),
|
|
||||||
thenVal: ComponentFunction | ComponentChild,
|
|
||||||
otherwiseVal?: ComponentFunction | ComponentChild
|
|
||||||
): HTMLDivElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
function $for<T>(
|
|
||||||
source: T[] | (() => T[]),
|
source: T[] | (() => T[]),
|
||||||
render: (item: T, index: number) => ComponentChild,
|
render: (item: T, index: number) => ComponentChild,
|
||||||
keyFn?: (item: T, index: number) => string | number,
|
keyFn?: (item: T, index: number) => string | number,
|
||||||
@@ -114,70 +95,115 @@ export declare module 'sigpro' {
|
|||||||
props?: HtmlProps
|
props?: HtmlProps
|
||||||
): HTMLElement;
|
): HTMLElement;
|
||||||
|
|
||||||
function $router(routes: Route[]): RouterOutlet;
|
function Router(routes: Route[]): RouterOutlet;
|
||||||
|
|
||||||
function $mount(
|
function Mount(
|
||||||
component: ComponentFunction | HTMLElement,
|
component: ComponentFunction | HTMLElement,
|
||||||
target: string | HTMLElement
|
target: string | HTMLElement
|
||||||
): ViewInstance | undefined;
|
): ViewInstance | undefined;
|
||||||
|
|
||||||
const Div: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Div: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Span: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Span: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const P: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const P: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const H1: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const H1: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const H2: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const H2: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const H3: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const H3: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const H4: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const H4: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const H5: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const H5: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const H6: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const H6: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Button: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Button: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Input: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Input: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Textarea: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Textarea: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Select: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Select: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Option: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Option: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Label: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Label: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Form: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Form: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const A: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const A: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Img: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Img: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Ul: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Ul: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Ol: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Ol: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Li: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Li: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Nav: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Nav: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Header: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Header: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Footer: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Footer: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Section: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Section: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Article: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Article: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Aside: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Aside: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Main: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Main: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Table: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Table: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Thead: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Thead: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Tbody: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Tbody: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Tr: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Tr: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Th: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Th: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
const Td: (props?: HtmlProps | ComponentChildren, content?: ComponentChildren) => HTMLElement;
|
const Td: (props?: HtmlProps | ComponentChildren, children?: ComponentChildren) => HTMLElement;
|
||||||
|
|
||||||
const SigPro: {
|
const SigPro: {
|
||||||
$: typeof $;
|
$: typeof $;
|
||||||
$$: typeof $$;
|
$$: typeof $$;
|
||||||
$watch: typeof $watch;
|
Watch: typeof Watch;
|
||||||
$html: typeof $html;
|
Tag: typeof Tag;
|
||||||
$if: typeof $if;
|
If: typeof If;
|
||||||
$for: typeof $for;
|
For: typeof For;
|
||||||
$router: typeof $router;
|
Router: typeof Router;
|
||||||
$mount: typeof $mount;
|
Mount: typeof Mount;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SigPro;
|
||||||
|
export {
|
||||||
|
$,
|
||||||
|
$$,
|
||||||
|
Watch,
|
||||||
|
Tag,
|
||||||
|
If,
|
||||||
|
For,
|
||||||
|
Router,
|
||||||
|
Mount,
|
||||||
|
Div,
|
||||||
|
Span,
|
||||||
|
P,
|
||||||
|
H1,
|
||||||
|
H2,
|
||||||
|
H3,
|
||||||
|
H4,
|
||||||
|
H5,
|
||||||
|
H6,
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
Textarea,
|
||||||
|
Select,
|
||||||
|
Option,
|
||||||
|
Label,
|
||||||
|
Form,
|
||||||
|
A,
|
||||||
|
Img,
|
||||||
|
Ul,
|
||||||
|
Ol,
|
||||||
|
Li,
|
||||||
|
Nav,
|
||||||
|
Header,
|
||||||
|
Footer,
|
||||||
|
Section,
|
||||||
|
Article,
|
||||||
|
Aside,
|
||||||
|
Main,
|
||||||
|
Table,
|
||||||
|
Thead,
|
||||||
|
Tbody,
|
||||||
|
Tr,
|
||||||
|
Th,
|
||||||
|
Td,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare global {
|
declare global {
|
||||||
const $: typeof import('sigpro').$;
|
const $: typeof import('sigpro').$;
|
||||||
const $$: typeof import('sigpro').$$;
|
const $$: typeof import('sigpro').$$;
|
||||||
const $watch: typeof import('sigpro').$watch;
|
const Watch: typeof import('sigpro').Watch;
|
||||||
const $html: typeof import('sigpro').$html;
|
const Tag: typeof import('sigpro').Tag;
|
||||||
const $if: typeof import('sigpro').$if;
|
const If: typeof import('sigpro').If;
|
||||||
const $for: typeof import('sigpro').$for;
|
const For: typeof import('sigpro').For;
|
||||||
const $router: typeof import('sigpro').$router;
|
const Router: typeof import('sigpro').Router;
|
||||||
const $mount: typeof import('sigpro').$mount;
|
const Mount: typeof import('sigpro').Mount;
|
||||||
|
|
||||||
const Div: typeof import('sigpro').Div;
|
const Div: typeof import('sigpro').Div;
|
||||||
const Span: typeof import('sigpro').Span;
|
const Span: typeof import('sigpro').Span;
|
||||||
const P: typeof import('sigpro').P;
|
const P: typeof import('sigpro').P;
|
||||||
@@ -216,12 +242,12 @@ export declare global {
|
|||||||
interface Window {
|
interface Window {
|
||||||
$: typeof $;
|
$: typeof $;
|
||||||
$$: typeof $$;
|
$$: typeof $$;
|
||||||
$watch: typeof $watch;
|
Watch: typeof Watch;
|
||||||
$html: typeof $html;
|
Tag: typeof Tag;
|
||||||
$if: typeof $if;
|
If: typeof If;
|
||||||
$for: typeof $for;
|
For: typeof For;
|
||||||
$router: typeof $router;
|
Router: typeof Router;
|
||||||
$mount: typeof $mount;
|
Mount: typeof Mount;
|
||||||
SigPro: typeof import('sigpro').default;
|
SigPro: typeof import('sigpro').default;
|
||||||
Div: typeof Div;
|
Div: typeof Div;
|
||||||
Span: typeof Span;
|
Span: typeof Span;
|
||||||
|
|||||||
Reference in New Issue
Block a user