nuclo 0.2.32 → 0.2.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/{Element-DXd2npjS.cjs → Element-BUdteOsZ.cjs} +2 -2
- package/dist/{Element-C3mU75-a.mjs.map → Element-BUdteOsZ.cjs.map} +1 -1
- package/dist/{Element-C3mU75-a.mjs → Element-Djosl17E.mjs} +2 -2
- package/dist/{Element-DXd2npjS.cjs.map → Element-Djosl17E.mjs.map} +1 -1
- package/dist/bootstrap.d.ts +5 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/element/attributes.d.ts +6 -0
- package/dist/element/attributes.d.ts.map +1 -0
- package/dist/element/class-name.d.ts +6 -0
- package/dist/element/class-name.d.ts.map +1 -0
- package/dist/element/event-attributes.d.ts +5 -0
- package/dist/element/event-attributes.d.ts.map +1 -0
- package/dist/element/events.d.ts +46 -0
- package/dist/element/events.d.ts.map +1 -0
- package/dist/element/factory-meta.d.ts +25 -0
- package/dist/element/factory-meta.d.ts.map +1 -0
- package/dist/element/factory.d.ts +32 -0
- package/dist/element/factory.d.ts.map +1 -0
- package/dist/element/inline-style.d.ts +9 -0
- package/dist/element/inline-style.d.ts.map +1 -0
- package/dist/element/lifecycle.d.ts +106 -0
- package/dist/element/lifecycle.d.ts.map +1 -0
- package/dist/element/modifiers.d.ts +11 -0
- package/dist/element/modifiers.d.ts.map +1 -0
- package/dist/element/tags.d.ts +5 -0
- package/dist/element/tags.d.ts.map +1 -0
- package/dist/hydration.d.ts +73 -0
- package/dist/hydration.d.ts.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/list/index.d.ts +7 -0
- package/dist/list/index.d.ts.map +1 -0
- package/dist/list/runtime.d.ts +6 -0
- package/dist/list/runtime.d.ts.map +1 -0
- package/dist/list/template.d.ts +113 -0
- package/dist/list/template.d.ts.map +1 -0
- package/dist/list/types.d.ts +57 -0
- package/dist/list/types.d.ts.map +1 -0
- package/dist/nuclo.cjs +1 -1
- package/dist/nuclo.cjs.map +1 -1
- package/dist/nuclo.mjs +1 -1
- package/dist/nuclo.mjs.map +1 -1
- package/dist/nuclo.umd.js +1 -1
- package/dist/nuclo.umd.js.map +1 -1
- package/dist/polyfill/Document.d.ts +19 -0
- package/dist/polyfill/Document.d.ts.map +1 -0
- package/dist/polyfill/Element.d.ts +48 -0
- package/dist/polyfill/Element.d.ts.map +1 -0
- package/dist/polyfill/Event.d.ts +43 -0
- package/dist/polyfill/Event.d.ts.map +1 -0
- package/dist/polyfill/Node.d.ts +13 -0
- package/dist/polyfill/Node.d.ts.map +1 -0
- package/dist/polyfill/Text.d.ts +11 -0
- package/dist/polyfill/Text.d.ts.map +1 -0
- package/dist/polyfill/index.d.ts +24 -0
- package/dist/polyfill/index.d.ts.map +1 -0
- package/dist/polyfill/nuclo.polyfill.cjs +1 -1
- package/dist/polyfill/nuclo.polyfill.cjs.map +1 -1
- package/dist/polyfill/nuclo.polyfill.mjs +1 -1
- package/dist/polyfill/nuclo.polyfill.mjs.map +1 -1
- package/dist/render.d.ts +33 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/shared/conditions.d.ts +4 -0
- package/dist/shared/conditions.d.ts.map +1 -0
- package/dist/shared/dom.d.ts +80 -0
- package/dist/shared/dom.d.ts.map +1 -0
- package/dist/shared/environment.d.ts +5 -0
- package/dist/shared/environment.d.ts.map +1 -0
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/renderables.d.ts +4 -0
- package/dist/shared/renderables.d.ts.map +1 -0
- package/dist/shared/strings.d.ts +19 -0
- package/dist/shared/strings.d.ts.map +1 -0
- package/dist/shared/type-guards.d.ts +16 -0
- package/dist/shared/type-guards.d.ts.map +1 -0
- package/dist/ssr/index.d.ts +19 -0
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/ssr/nuclo.ssr.cjs +1 -1
- package/dist/ssr/nuclo.ssr.cjs.map +1 -1
- package/dist/ssr/nuclo.ssr.mjs +1 -1
- package/dist/ssr/nuclo.ssr.mjs.map +1 -1
- package/dist/ssr/render-to-string.d.ts +41 -0
- package/dist/ssr/render-to-string.d.ts.map +1 -0
- package/dist/style/css.d.ts +26 -0
- package/dist/style/css.d.ts.map +1 -0
- package/dist/style/engine.d.ts +94 -0
- package/dist/style/engine.d.ts.map +1 -0
- package/dist/style/index.d.ts +3 -0
- package/dist/style/index.d.ts.map +1 -0
- package/dist/update/conditional.d.ts +3 -0
- package/dist/update/conditional.d.ts.map +1 -0
- package/dist/update/reactive-attributes.d.ts +45 -0
- package/dist/update/reactive-attributes.d.ts.map +1 -0
- package/dist/update/reactive-text.d.ts +38 -0
- package/dist/update/reactive-text.d.ts.map +1 -0
- package/dist/update/registry.d.ts +116 -0
- package/dist/update/registry.d.ts.map +1 -0
- package/dist/update/scope.d.ts +7 -0
- package/dist/update/scope.d.ts.map +1 -0
- package/dist/update/update.d.ts +4 -0
- package/dist/update/update.d.ts.map +1 -0
- package/dist/when/builder.d.ts +16 -0
- package/dist/when/builder.d.ts.map +1 -0
- package/dist/when/index.d.ts +44 -0
- package/dist/when/index.d.ts.map +1 -0
- package/dist/when/runtime.d.ts +59 -0
- package/dist/when/runtime.d.ts.map +1 -0
- package/package.json +11 -11
- package/types/core/base.d.ts +12 -0
- package/types/core/lifecycle.d.ts +28 -0
- package/types/features/on.d.ts +27 -2
- package/types/index.d.ts +8 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { UpdateScope } from "./scope";
|
|
2
|
+
import type { TextResolver } from "./registry";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a reactive text node that automatically updates when its resolver function changes.
|
|
5
|
+
*
|
|
6
|
+
* The text node will be registered for reactive updates and its content will be synchronized
|
|
7
|
+
* whenever notifyReactiveTextNodes() is called.
|
|
8
|
+
*
|
|
9
|
+
* @param resolver - Function that returns the text content (string, number, boolean, etc.)
|
|
10
|
+
* @param preEvaluated - Optional pre-evaluated value to avoid calling resolver immediately
|
|
11
|
+
* @returns A Text node that will reactively update its content
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const count = signal(0);
|
|
16
|
+
* const textNode = createReactiveTextNode(() => `Count: ${count.value}`);
|
|
17
|
+
* // Later, when count changes and notifyReactiveTextNodes() is called,
|
|
18
|
+
* // the text content automatically updates
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function createReactiveTextNode(resolver: TextResolver, preEvaluated?: unknown, sanitize?: boolean): Text | DocumentFragment;
|
|
22
|
+
/**
|
|
23
|
+
* Updates all registered reactive text nodes.
|
|
24
|
+
*
|
|
25
|
+
* Iterates through all reactive text nodes, re-evaluates their resolver functions,
|
|
26
|
+
* and updates their content if it has changed. Automatically cleans up disconnected nodes.
|
|
27
|
+
*
|
|
28
|
+
* This function should be called after state changes to synchronize the DOM with application state.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* // After updating application state
|
|
33
|
+
* count.value++;
|
|
34
|
+
* notifyReactiveTextNodes(); // All reactive text nodes update
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function notifyReactiveTextNodes(scope?: UpdateScope): void;
|
|
38
|
+
//# sourceMappingURL=reactive-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactive-text.d.ts","sourceRoot":"","sources":["../../src/update/reactive-text.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,gBAAgB,CAgClI;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CA0CjE"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types and registries for reactive nodes.
|
|
3
|
+
* Single source of truth — imported by reactiveText.ts and reactiveAttributes.ts.
|
|
4
|
+
*
|
|
5
|
+
* Registry structure: the iteration sets only hold WeakRefs; the actual info
|
|
6
|
+
* records live in WeakMaps keyed by the node. Info records may strongly
|
|
7
|
+
* reference the node (e.g. attribute applyValue closures capture the
|
|
8
|
+
* element), so storing them in a strongly-held map would pin the node in
|
|
9
|
+
* memory forever. With WeakMap storage the whole record is collectible as
|
|
10
|
+
* soon as the node is unreachable.
|
|
11
|
+
*
|
|
12
|
+
* Dead WeakRefs left in the iteration sets are pruned by the notify passes as
|
|
13
|
+
* they iterate (deref() === undefined, or node disconnected). Registration is
|
|
14
|
+
* the hottest path in list-heavy renders (one text node + often one element
|
|
15
|
+
* per row), so no per-node FinalizationRegistry is used here: its register()
|
|
16
|
+
* cost per row outweighs keeping the sets tidy between updates, and a stale
|
|
17
|
+
* husk is just an empty WeakRef until the next update() sweeps it.
|
|
18
|
+
*/
|
|
19
|
+
export type TextResolver = () => Primitive;
|
|
20
|
+
export interface ReactiveTextNodeInfo {
|
|
21
|
+
resolver: TextResolver;
|
|
22
|
+
lastValue: string;
|
|
23
|
+
/**
|
|
24
|
+
* DSL semantics: nullish and non-primitive resolver results render as ""
|
|
25
|
+
* (instead of String(value)). Set by the tag-builder text path so it can
|
|
26
|
+
* register the user's resolver directly rather than allocating a
|
|
27
|
+
* sanitizing wrapper closure per text node.
|
|
28
|
+
*/
|
|
29
|
+
sanitize?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export type AttributeResolver = () => unknown;
|
|
32
|
+
export interface AttributeResolverRecord {
|
|
33
|
+
key: string;
|
|
34
|
+
resolver: AttributeResolver;
|
|
35
|
+
applyValue: (value: unknown) => void;
|
|
36
|
+
lastValue: unknown;
|
|
37
|
+
}
|
|
38
|
+
export interface ReactiveElementInfo {
|
|
39
|
+
/**
|
|
40
|
+
* Flat keyed array rather than a Map: elements almost always carry one or
|
|
41
|
+
* two reactive attributes, so linear key lookup on registration beats a Map
|
|
42
|
+
* allocation per element, and the per-update flush iterates with a plain
|
|
43
|
+
* index loop (no iterator/destructuring allocations).
|
|
44
|
+
*/
|
|
45
|
+
attributeResolvers: AttributeResolverRecord[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Iteration set of weak references to reactive text nodes.
|
|
49
|
+
*/
|
|
50
|
+
export declare const reactiveTextNodes: Set<WeakRef<Text>>;
|
|
51
|
+
/**
|
|
52
|
+
* WeakMap for O(1) lookups of reactive text node info by node reference.
|
|
53
|
+
*/
|
|
54
|
+
export declare const reactiveTextNodesByNode: WeakMap<Text, {
|
|
55
|
+
ref: WeakRef<Text>;
|
|
56
|
+
info: ReactiveTextNodeInfo;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Iteration set of weak references to reactive elements.
|
|
60
|
+
*/
|
|
61
|
+
export declare const reactiveElements: Set<WeakRef<Element>>;
|
|
62
|
+
/**
|
|
63
|
+
* WeakMap for O(1) lookups of reactive element info by element reference.
|
|
64
|
+
*/
|
|
65
|
+
export declare const reactiveElementsByNode: WeakMap<Element, {
|
|
66
|
+
ref: WeakRef<Element>;
|
|
67
|
+
info: ReactiveElementInfo;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Registers a reactive text node in both lookup structures.
|
|
71
|
+
*/
|
|
72
|
+
export declare function registerReactiveTextNode(node: Text, info: ReactiveTextNodeInfo): void;
|
|
73
|
+
/**
|
|
74
|
+
* Registers a reactive element in both lookup structures.
|
|
75
|
+
*/
|
|
76
|
+
export declare function registerReactiveElement(element: Element, info: ReactiveElementInfo): void;
|
|
77
|
+
/**
|
|
78
|
+
* Removes a reactive text node's WeakRef entry from the iteration set.
|
|
79
|
+
*/
|
|
80
|
+
export declare function removeReactiveTextNodeRef(ref: WeakRef<Text>): void;
|
|
81
|
+
/**
|
|
82
|
+
* Removes a reactive element's WeakRef entry from the iteration set.
|
|
83
|
+
*/
|
|
84
|
+
export declare function removeReactiveElementRef(ref: WeakRef<Element>): void;
|
|
85
|
+
/**
|
|
86
|
+
* Manually removes reactive text node info for a specific text node.
|
|
87
|
+
* This should be called when a text node is removed from the DOM to prevent memory leaks.
|
|
88
|
+
*/
|
|
89
|
+
export declare function cleanupReactiveTextNode(node: Text): void;
|
|
90
|
+
/**
|
|
91
|
+
* Manually removes reactive element info for a specific element.
|
|
92
|
+
* This should be called when an element is removed from the DOM to prevent memory leaks.
|
|
93
|
+
*/
|
|
94
|
+
export declare function cleanupReactiveElement(element: Element): void;
|
|
95
|
+
export interface ConditionalInfo<TTagName extends ElementTagName = ElementTagName> {
|
|
96
|
+
condition: () => boolean;
|
|
97
|
+
tagName: TTagName;
|
|
98
|
+
modifiers: Array<NodeMod<TTagName> | NodeModFn<TTagName>>;
|
|
99
|
+
isSvg: boolean;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Attach conditional info to a node and register it.
|
|
103
|
+
*/
|
|
104
|
+
export declare function storeConditionalInfo<TTagName extends ElementTagName>(node: Node, info: ConditionalInfo<TTagName>): void;
|
|
105
|
+
/**
|
|
106
|
+
* Explicit unregister helper (optional use on teardown if needed).
|
|
107
|
+
* O(1) via reverse lookup WeakMap.
|
|
108
|
+
*/
|
|
109
|
+
export declare function unregisterConditionalNode(node: Node): void;
|
|
110
|
+
/**
|
|
111
|
+
* Returns an array of currently tracked conditional nodes that are still alive.
|
|
112
|
+
* Automatically cleans up garbage-collected nodes.
|
|
113
|
+
*/
|
|
114
|
+
export declare function getActiveConditionalNodes(): Node[];
|
|
115
|
+
export declare function getConditionalInfo(node: Node): ConditionalInfo<ElementTagName> | null;
|
|
116
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/update/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC;AAE3C,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,CAAC;AAE9C,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,kBAAkB,EAAE,uBAAuB,EAAE,CAAC;CAC/C;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,oBAA2B,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,uBAAuB;SAA4B,OAAO,CAAC,IAAI,CAAC;UAAQ,oBAAoB;EAAK,CAAC;AAE/G;;GAEG;AACH,eAAO,MAAM,gBAAgB,uBAA8B,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,sBAAsB;SAA+B,OAAO,CAAC,OAAO,CAAC;UAAQ,mBAAmB;EAAK,CAAC;AAEnH;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAYrF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,GAAG,IAAI,CASzF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAElE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAEpE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAMxD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAM7D;AAOD,MAAM,WAAW,eAAe,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc;IAChF,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,OAAO,EAAE,QAAQ,CAAC;IAClB,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1D,KAAK,EAAE,OAAO,CAAC;CACf;AAyBD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,SAAS,cAAc,EACnE,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,eAAe,CAAC,QAAQ,CAAC,GAC7B,IAAI,CAON;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAQ1D;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,EAAE,CAalD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,eAAe,CAAC,cAAc,CAAC,GAAG,IAAI,CAErF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface UpdateScope {
|
|
2
|
+
roots: ReadonlyArray<Element>;
|
|
3
|
+
contains(node: Node): boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function getScopeRoots(ids: readonly string[]): Element[];
|
|
6
|
+
export declare function scope<TTagName extends ElementTagName = ElementTagName>(...ids: string[]): NodeModFn<TTagName>;
|
|
7
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/update/scope.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;CAC/B;AAoCD,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,EAAE,CAoC/D;AAED,wBAAgB,KAAK,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc,EACpE,GAAG,GAAG,EAAE,MAAM,EAAE,GACf,SAAS,CAAC,QAAQ,CAAC,CAQrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/update/update.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAY3C,wBAAgB,MAAM,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAiClD;AAED,wBAAgB,yBAAyB,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAqBnE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { WhenCondition, WhenContent, WhenGroup } from "./runtime";
|
|
2
|
+
declare class WhenBuilderImpl<TTagName extends ElementTagName = ElementTagName> {
|
|
3
|
+
private groups;
|
|
4
|
+
private elseContent;
|
|
5
|
+
constructor(initialCondition: WhenCondition, ...content: WhenContent<TTagName>[]);
|
|
6
|
+
cloneWith(additionalGroup?: WhenGroup<TTagName>, newElseContent?: WhenContent<TTagName>[]): WhenBuilderImpl<TTagName>;
|
|
7
|
+
when(condition: WhenCondition, ...content: WhenContent<TTagName>[]): WhenBuilderImpl<TTagName>;
|
|
8
|
+
else(...content: WhenContent<TTagName>[]): WhenBuilderImpl<TTagName>;
|
|
9
|
+
render(host: ExpandedElement<TTagName>, index: number): Node | null;
|
|
10
|
+
private freshRender;
|
|
11
|
+
private hydrateRender;
|
|
12
|
+
private createRuntimeFromMarkers;
|
|
13
|
+
}
|
|
14
|
+
export declare function createWhenBuilderFunction<TTagName extends ElementTagName>(builder: WhenBuilderImpl<TTagName>): WhenBuilder<TTagName>;
|
|
15
|
+
export { WhenBuilderImpl };
|
|
16
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/when/builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAe,MAAM,WAAW,CAAC;AAyBpF,cAAM,eAAe,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc;IACpE,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,WAAW,CAA+B;IAElD,YAAY,gBAAgB,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,EAE/E;IAED,SAAS,CACP,eAAe,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EACrC,cAAc,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,GACvC,eAAe,CAAC,QAAQ,CAAC,CAM3B;IAED,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,CAG7F;IAED,IAAI,CAAC,GAAG,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,CAGnE;IAED,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAUlE;IAED,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,aAAa;IAgFrB,OAAO,CAAC,wBAAwB;CAwBjC;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,SAAS,cAAc,EACvE,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,GACjC,WAAW,CAAC,QAAQ,CAAC,CAavB;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { WhenCondition, WhenContent } from "./runtime";
|
|
2
|
+
export { updateWhenRuntimes } from "./runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a conditional rendering block (when/else logic).
|
|
5
|
+
*
|
|
6
|
+
* Renders different content based on boolean conditions, similar to if/else statements.
|
|
7
|
+
* Conditions can be static booleans or reactive functions that are re-evaluated on updates.
|
|
8
|
+
*
|
|
9
|
+
* @param condition - Boolean value or function returning a boolean
|
|
10
|
+
* @param content - Content to render when condition is true
|
|
11
|
+
* @returns A builder that allows chaining additional .when() or .else() branches
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const isLoggedIn = signal(false);
|
|
16
|
+
*
|
|
17
|
+
* div(
|
|
18
|
+
* when(() => isLoggedIn.value,
|
|
19
|
+
* span('Welcome back!')
|
|
20
|
+
* )
|
|
21
|
+
* .else(
|
|
22
|
+
* button('Login', on('click', () => login()))
|
|
23
|
+
* )
|
|
24
|
+
* )
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* // Multiple conditions
|
|
30
|
+
* div(
|
|
31
|
+
* when(() => status.value === 'loading',
|
|
32
|
+
* spinner()
|
|
33
|
+
* )
|
|
34
|
+
* .when(() => status.value === 'error',
|
|
35
|
+
* errorMessage()
|
|
36
|
+
* )
|
|
37
|
+
* .else(
|
|
38
|
+
* contentView()
|
|
39
|
+
* )
|
|
40
|
+
* )
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function when<TTagName extends ElementTagName = ElementTagName>(condition: WhenCondition, ...content: WhenContent<TTagName>[]): WhenBuilder<TTagName>;
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/when/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,IAAI,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc,EACnE,SAAS,EAAE,aAAa,EACxB,GAAG,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,GAClC,WAAW,CAAC,QAAQ,CAAC,CAGvB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { UpdateScope } from "../update/scope";
|
|
2
|
+
export type WhenCondition = boolean | (() => boolean);
|
|
3
|
+
export type WhenContent<TTagName extends ElementTagName = ElementTagName> = NodeModLike<TTagName>;
|
|
4
|
+
export interface WhenGroup<TTagName extends ElementTagName = ElementTagName> {
|
|
5
|
+
condition: WhenCondition;
|
|
6
|
+
content: WhenContent<TTagName>[];
|
|
7
|
+
}
|
|
8
|
+
export interface WhenRuntime<TTagName extends ElementTagName = ElementTagName> {
|
|
9
|
+
startMarker: Comment;
|
|
10
|
+
endMarker: Comment;
|
|
11
|
+
host: ExpandedElement<TTagName>;
|
|
12
|
+
index: number;
|
|
13
|
+
groups: WhenGroup<TTagName>[];
|
|
14
|
+
elseContent: WhenContent<TTagName>[];
|
|
15
|
+
/**
|
|
16
|
+
* Tracks which branch is currently rendered:
|
|
17
|
+
* - null: nothing rendered yet (initial state)
|
|
18
|
+
* - -1: else branch is active
|
|
19
|
+
* - >= 0: groups[activeIndex] is active
|
|
20
|
+
*/
|
|
21
|
+
activeIndex: number | null;
|
|
22
|
+
update(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Evaluates which condition branch should be active.
|
|
26
|
+
* Returns the index of the first truthy condition, -1 for else branch, or null for no match.
|
|
27
|
+
*/
|
|
28
|
+
export declare function evaluateActiveCondition<TTagName extends ElementTagName>(groups: ReadonlyArray<WhenGroup<TTagName>>, elseContent: ReadonlyArray<WhenContent<TTagName>>): number | null;
|
|
29
|
+
/**
|
|
30
|
+
* Main render function for when/else conditionals.
|
|
31
|
+
* Evaluates conditions, clears old content, and renders the active branch.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderWhenContent<TTagName extends ElementTagName>(runtime: WhenRuntime<TTagName>): void;
|
|
34
|
+
/**
|
|
35
|
+
* Registers a when runtime for tracking and updates.
|
|
36
|
+
* Uses WeakRef to prevent memory leaks when elements are removed.
|
|
37
|
+
* The runtime object itself is mutable and will be updated in place.
|
|
38
|
+
*/
|
|
39
|
+
export declare function registerWhenRuntime<TTagName extends ElementTagName>(runtime: WhenRuntime<TTagName>): void;
|
|
40
|
+
/**
|
|
41
|
+
* Updates all active when/else conditional runtimes.
|
|
42
|
+
*
|
|
43
|
+
* Re-evaluates all conditional branches and re-renders if the active branch has changed.
|
|
44
|
+
* Automatically cleans up runtimes that are garbage collected or disconnected from DOM.
|
|
45
|
+
*
|
|
46
|
+
* This function should be called after state changes that affect conditional expressions.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* isLoggedIn.value = true;
|
|
51
|
+
* updateWhenRuntimes(); // All when() conditionals re-evaluate
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function updateWhenRuntimes(scope?: UpdateScope): void;
|
|
55
|
+
/**
|
|
56
|
+
* Renders a list of content items and collects the resulting nodes.
|
|
57
|
+
*/
|
|
58
|
+
export declare function renderContentItems<TTagName extends ElementTagName>(items: ReadonlyArray<WhenContent<TTagName>>, host: ExpandedElement<TTagName>, index: number, endMarker: Comment): Node[];
|
|
59
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/when/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAKnD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;AACtD,MAAM,MAAM,WAAW,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc,IACtE,WAAW,CAAC,QAAQ,CAAC,CAAC;AAExB,MAAM,WAAW,SAAS,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc;IACzE,SAAS,EAAE,aAAa,CAAC;IACzB,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,WAAW,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc;IAC3E,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC9B,WAAW,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;IACrC;;;;;OAKG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,IAAI,IAAI,CAAC;CAChB;AAmBD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,SAAS,cAAc,EACrE,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,GAChD,MAAM,GAAG,IAAI,CAOf;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,SAAS,cAAc,EAC/D,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,GAC7B,IAAI,CAoBN;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAS,cAAc,EACjE,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,GAC7B,IAAI,CAKN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CA0C5D;AA8BD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,cAAc,EAChE,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAC3C,IAAI,EAAE,eAAe,CAAC,QAAQ,CAAC,EAC/B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,GACjB,IAAI,EAAE,CASR"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuclo",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.34",
|
|
5
5
|
"description": "Lightweight, type-safe DOM library with plain mutable state and explicit updates — global tag builders, SSR, hydration, and typed CSS, no virtual DOM, no compiler.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Danilo Celestino de Castro (https://github.com/dan2dev)",
|
|
@@ -62,20 +62,20 @@
|
|
|
62
62
|
"lint:fix": "eslint . --ext .ts,.js,.json,.jsonc,.json5,.md,.css --fix"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@eslint/css": "^
|
|
65
|
+
"@eslint/css": "^2.0.0",
|
|
66
66
|
"@eslint/js": "^10.0.1",
|
|
67
|
-
"@eslint/json": "^2.0
|
|
67
|
+
"@eslint/json": "^2.1.0",
|
|
68
68
|
"@eslint/markdown": "^8.0.3",
|
|
69
|
-
"@types/node": "^26.2
|
|
70
|
-
"@vitest/coverage-v8": "
|
|
71
|
-
"eslint": "^10.
|
|
72
|
-
"globals": "^17.
|
|
69
|
+
"@types/node": "^26.6.2",
|
|
70
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
71
|
+
"eslint": "^10.11.0",
|
|
72
|
+
"globals": "^17.12.0",
|
|
73
73
|
"jiti": "^2.7.0",
|
|
74
|
-
"jsdom": "^30.0
|
|
75
|
-
"tsdown": "^0.
|
|
74
|
+
"jsdom": "^30.1.0",
|
|
75
|
+
"tsdown": "^0.23.0",
|
|
76
76
|
"typescript": "^7.0.2",
|
|
77
|
-
"typescript-eslint": "^8.
|
|
78
|
-
"vitest": "^
|
|
77
|
+
"typescript-eslint": "^8.70.0",
|
|
78
|
+
"vitest": "^5.0.1"
|
|
79
79
|
},
|
|
80
80
|
"repository": {
|
|
81
81
|
"type": "git",
|
package/types/core/base.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "./events";
|
|
2
|
+
import "./lifecycle";
|
|
2
3
|
|
|
3
4
|
declare const anyParentNodeModifierTag: unique symbol;
|
|
4
5
|
|
|
@@ -13,6 +14,16 @@ type LowercaseEventAttributes<TTagName extends ElementTagName> = {
|
|
|
13
14
|
[K in Extract<keyof HTMLElementTagNameMap[TTagName], `on${string}`>]?: never;
|
|
14
15
|
};
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* `onMount` / `onDestroy` attributes — not real DOM events (no native
|
|
19
|
+
* `on${string}` IDL property exists for them), so they're typed here rather
|
|
20
|
+
* than derived from HTMLElementEventAttributeNameMap. See src/element/lifecycle.ts.
|
|
21
|
+
*/
|
|
22
|
+
type LifecycleElementAttributes<TTagName extends ElementTagName> = {
|
|
23
|
+
onMount?: MountCallback<HTMLElementTagNameMap[TTagName]>;
|
|
24
|
+
onDestroy?: DestroyCallback<HTMLElementTagNameMap[TTagName]>;
|
|
25
|
+
};
|
|
26
|
+
|
|
16
27
|
declare global {
|
|
17
28
|
export type Primitive = string | number | bigint | boolean | symbol | null | undefined;
|
|
18
29
|
export type ElementTagName = keyof HTMLElementTagNameMap;
|
|
@@ -43,6 +54,7 @@ declare global {
|
|
|
43
54
|
> = NativeElementAttributes<TTagName>
|
|
44
55
|
& CamelCaseEventAttributes<TTagName>
|
|
45
56
|
& LowercaseEventAttributes<TTagName>
|
|
57
|
+
& LifecycleElementAttributes<TTagName>
|
|
46
58
|
& {
|
|
47
59
|
// Allow custom attributes (data-*, aria-*, etc.)
|
|
48
60
|
[key: string]: ValueOrFactory<unknown>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount/destroy lifecycle hook types.
|
|
3
|
+
*
|
|
4
|
+
* Two equivalent ways to register them (both typed from these two aliases):
|
|
5
|
+
* - `on("mount", cb)` / `on("destroy", cb)` (types/features/on.d.ts)
|
|
6
|
+
* - `{ onMount: cb }` / `{ onDestroy: cb }` attributes (LifecycleElementAttributes below)
|
|
7
|
+
*
|
|
8
|
+
* See src/element/lifecycle.ts for exactly when each callback fires.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
declare global {
|
|
12
|
+
/** Cleanup returned by a mount callback, run once the element is destroyed. */
|
|
13
|
+
export type MountCleanup = () => void;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Called once, after the element has been created and connected to the
|
|
17
|
+
* live document. May return a cleanup function — equivalent to also
|
|
18
|
+
* registering it with `on("destroy", ...)` / `{ onDestroy }`.
|
|
19
|
+
*/
|
|
20
|
+
export type MountCallback<TElement extends Element = HTMLElement> =
|
|
21
|
+
(element: TElement) => void | MountCleanup;
|
|
22
|
+
|
|
23
|
+
/** Called once the element has been removed. */
|
|
24
|
+
export type DestroyCallback<TElement extends Element = HTMLElement> =
|
|
25
|
+
(element: TElement) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {};
|
package/types/features/on.d.ts
CHANGED
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
* })
|
|
11
11
|
* );
|
|
12
12
|
*
|
|
13
|
-
* Overloads
|
|
13
|
+
* Overloads (order matters — TS resolves the first structural match, and the
|
|
14
|
+
* "mount"/"destroy" and custom-event overloads all accept `string`, so the
|
|
15
|
+
* more specific ones must come first):
|
|
14
16
|
* 1. DOM standard events (strongly typed via the tag's native event map)
|
|
15
|
-
* 2.
|
|
17
|
+
* 2. "mount" / "destroy" lifecycle pseudo-events (not real DOM events)
|
|
18
|
+
* 3. Custom / arbitrary event names with user-specified event type
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
21
|
declare global {
|
|
@@ -37,6 +40,28 @@ declare global {
|
|
|
37
40
|
options?: boolean | AddEventListenerOptions
|
|
38
41
|
): NodeModFn<TTagName>;
|
|
39
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Fires once, after the element has been created and connected to the live
|
|
45
|
+
* document. Equivalent to `{ onMount: ... }`. May return a cleanup function
|
|
46
|
+
* — equivalent to also registering it with `on("destroy", ...)`.
|
|
47
|
+
*
|
|
48
|
+
* See src/element/lifecycle.ts for exactly when "connected" is guaranteed.
|
|
49
|
+
*/
|
|
50
|
+
function on<TTagName extends ElementTagName = ElementTagName>(
|
|
51
|
+
type: "mount",
|
|
52
|
+
listener: MountCallback<HTMLElementTagNameMap[TTagName]>,
|
|
53
|
+
): NodeModFn<TTagName>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Fires once the element has been removed. Equivalent to `{ onDestroy: ... }`.
|
|
57
|
+
*
|
|
58
|
+
* See src/element/lifecycle.ts for exactly when this is guaranteed to run.
|
|
59
|
+
*/
|
|
60
|
+
function on<TTagName extends ElementTagName = ElementTagName>(
|
|
61
|
+
type: "destroy",
|
|
62
|
+
listener: DestroyCallback<HTMLElementTagNameMap[TTagName]>,
|
|
63
|
+
): NodeModFn<TTagName>;
|
|
64
|
+
|
|
40
65
|
/**
|
|
41
66
|
* Add a custom / arbitrary event listener (e.g. "my-event").
|
|
42
67
|
*
|
package/types/index.d.ts
CHANGED
|
@@ -85,6 +85,14 @@ export function on<
|
|
|
85
85
|
>,
|
|
86
86
|
options?: boolean | AddEventListenerOptions
|
|
87
87
|
): NodeModFn<TTagName>;
|
|
88
|
+
export function on<TTagName extends ElementTagName = ElementTagName>(
|
|
89
|
+
type: "mount",
|
|
90
|
+
listener: MountCallback<HTMLElementTagNameMap[TTagName]>,
|
|
91
|
+
): NodeModFn<TTagName>;
|
|
92
|
+
export function on<TTagName extends ElementTagName = ElementTagName>(
|
|
93
|
+
type: "destroy",
|
|
94
|
+
listener: DestroyCallback<HTMLElementTagNameMap[TTagName]>,
|
|
95
|
+
): NodeModFn<TTagName>;
|
|
88
96
|
export function on<K extends string, E extends Event = Event, TTagName extends ElementTagName = ElementTagName>(
|
|
89
97
|
type: K,
|
|
90
98
|
listener: TypedEventListener<HTMLElementTagNameMap[TTagName], E>,
|