zenith 0.0.1 → 1.0.0-beta.14
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/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/types/helpers/closest.d.ts +14 -0
- package/dist/types/helpers/cls.d.ts +8 -0
- package/dist/types/helpers/derived.d.ts +19 -0
- package/dist/types/helpers/html.d.ts +20 -0
- package/dist/types/helpers/index.d.ts +13 -0
- package/dist/types/helpers/index.iife.d.ts +1 -0
- package/dist/types/helpers/inject.d.ts +11 -0
- package/dist/types/helpers/is.d.ts +14 -0
- package/dist/types/helpers/keep.d.ts +11 -0
- package/dist/types/helpers/portal.d.ts +21 -0
- package/dist/types/helpers/router.d.ts +28 -0
- package/dist/types/helpers/signal.d.ts +21 -0
- package/dist/types/helpers/slot.d.ts +29 -0
- package/dist/types/index.d.ts +28 -0
- package/dist/types/index.iife.d.ts +1 -0
- package/dist/types/vnode.defs.d.ts +82 -0
- package/dist/zenith-helpers.development.js +157 -0
- package/dist/zenith-helpers.global.js +2 -0
- package/dist/zenith-helpers.js +2 -0
- package/dist/zenith.development.js +537 -0
- package/dist/zenith.global.js +2 -0
- package/dist/zenith.js +2 -0
- package/package.json +54 -9
- package/index.js +0 -7
- package/readme.md +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Daniel Barber / Aleph1 Technologies Inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Zenith
|
|
2
|
+
|
|
3
|
+
Zenith is a tiny reactive JavaScript framework with a virtual DOM, keyed reconciliation, and targeted component redraws. Its rendering model can update an entire application or a specific component, which is useful when parts of a page manage their own content, such as canvas, WebGL, and SVG interfaces.
|
|
4
|
+
|
|
5
|
+
Zenith supports component lifecycle hooks, memoization, DOM events, SVG and MathML, and optional helpers. It is inspired by [Mithril.js](https://github.com/MithrilJS/mithril.js).
|
|
6
|
+
|
|
7
|
+
**Status:** Zenith is currently in beta. We aim to keep the API stable through 1.0.0, but breaking changes may still occur during the beta period.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Install the beta package:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install zenith@beta
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Import the functions you need:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import { bind, comp, compDef, elem } from 'zenith';
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Prebuilt browser files are also included in `dist/`. The `zenith.global.js` build is intended for direct browser use; the `zenith.js` build is an ES module.
|
|
24
|
+
|
|
25
|
+
## Example: counter
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<!doctype html>
|
|
29
|
+
<html lang="en">
|
|
30
|
+
<head>
|
|
31
|
+
<meta charset="utf-8">
|
|
32
|
+
<title>Zenith counter</title>
|
|
33
|
+
</head>
|
|
34
|
+
<body>
|
|
35
|
+
<main id="app"></main>
|
|
36
|
+
<script type="module">
|
|
37
|
+
import { bind, comp, compDef, elem } from 'zenith';
|
|
38
|
+
|
|
39
|
+
let count = 0;
|
|
40
|
+
|
|
41
|
+
const Counter = compDef({
|
|
42
|
+
draw: () => elem('button', {
|
|
43
|
+
onclick: () => { count++; }
|
|
44
|
+
}, `Count: ${count}`)
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
bind(document.querySelector('#app'), comp(Counter));
|
|
48
|
+
</script>
|
|
49
|
+
</body>
|
|
50
|
+
</html>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The example assumes an environment that resolves npm package imports, such as a bundler or development server. Event handlers automatically schedule a redraw unless the event's `draw` property is set to `false`.
|
|
54
|
+
|
|
55
|
+
## Components and redraws
|
|
56
|
+
|
|
57
|
+
Create components with `compDef()` and `comp()`, then mount them using `bind()`. Call `draw()` when external state changes outside an event handler:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
import { bind, comp, compDef, draw, elem } from 'zenith';
|
|
61
|
+
|
|
62
|
+
let message = 'Hello';
|
|
63
|
+
|
|
64
|
+
const Message = compDef({
|
|
65
|
+
draw: () => elem('p', message)
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const view = comp(Message);
|
|
69
|
+
bind(document.querySelector('#app'), view);
|
|
70
|
+
|
|
71
|
+
message = 'Updated';
|
|
72
|
+
draw(view);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Redraws are scheduled in a microtask. Call `sync()` if you need to apply queued redraws immediately. `draw(view, true)` requests a targeted redraw of a component.
|
|
76
|
+
|
|
77
|
+
Component definitions can use `init`, `draw`, `drawn`, `remove`, and `removed` lifecycle hooks. `remove` may return a Promise to defer removal. Components also support optional memoization through `$memo`.
|
|
78
|
+
|
|
79
|
+
## Package builds
|
|
80
|
+
|
|
81
|
+
The npm package includes prebuilt files under `dist/`, including:
|
|
82
|
+
|
|
83
|
+
- `zenith.js` — ES module
|
|
84
|
+
- `zenith.global.js` — global browser build
|
|
85
|
+
- `zenith.development.js` — development build
|
|
86
|
+
- `zenith-helpers.js` — optional helpers (import from `zenith/helpers`)
|
|
87
|
+
|
|
88
|
+
The library is designed for a very small compressed footprint; actual size depends on the build and compression settings.
|
|
89
|
+
|
|
90
|
+
## Documentation
|
|
91
|
+
|
|
92
|
+
Additional information: [Zenith website](https://aleph-1.com/zenith) (work in progress).
|
|
93
|
+
|
|
94
|
+
Report bugs and request features through [GitHub Issues](https://github.com/aleph1/Zenith/issues).
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { VNode } from '../vnode.defs';
|
|
2
|
+
/**
|
|
3
|
+
* Finds the closest ancestor vNode matching the predicate.
|
|
4
|
+
* Walks up the tree from the given vNode's parent.
|
|
5
|
+
* Returns undefined if no match is found.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Find the closest list component ancestor
|
|
9
|
+
* const list = closest(vNode, n => n.is === is.comp && n.$def === List);
|
|
10
|
+
*
|
|
11
|
+
* // Find the closest ancestor with a specific attribute
|
|
12
|
+
* const themed = closest(vNode, n => n.attrs?.theme);
|
|
13
|
+
*/
|
|
14
|
+
export declare const closest: (vNode: VNode, predicate: (vNode: VNode) => boolean) => VNode | undefined;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Combines class names, filtering out falsy values.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* cls('btn', isActive && 'btn-active', hasError && 'btn-error')
|
|
6
|
+
* // => 'btn btn-active' (if isActive, not hasError)
|
|
7
|
+
*/
|
|
8
|
+
export declare const cls: (...args: (string | false | null | undefined | 0)[]) => string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Signal } from './signal';
|
|
2
|
+
export interface DerivedSignal<T> extends Signal<T> {
|
|
3
|
+
destroy(): void;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Creates a computed signal that updates when any dependency changes.
|
|
7
|
+
* Call destroy() in component cleanup to prevent memory leaks.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const firstName = signal('John');
|
|
11
|
+
* const lastName = signal('Smith');
|
|
12
|
+
* const fullName = derived(
|
|
13
|
+
* () => `${firstName.val} ${lastName.val}`,
|
|
14
|
+
* [firstName, lastName]
|
|
15
|
+
* );
|
|
16
|
+
* // In component cleanup:
|
|
17
|
+
* removed: () => fullName.destroy()
|
|
18
|
+
*/
|
|
19
|
+
export declare const derived: <T>(fn: () => T, deps: Signal<any>[]) => DerivedSignal<T>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { VNodeNode } from '../vnode.defs';
|
|
2
|
+
/**
|
|
3
|
+
* Converts an HTML string to DOM nodes wrapped in node() vnodes.
|
|
4
|
+
*
|
|
5
|
+
* XSS WARNING: Only use with trusted, sanitized HTML.
|
|
6
|
+
* Never pass user-generated content without sanitization.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // Safe static content
|
|
10
|
+
* html('<div class="card"><h2>Title</h2></div>')
|
|
11
|
+
*
|
|
12
|
+
* // Unsafe user input
|
|
13
|
+
* html(`<div>${userInput}</div>`)
|
|
14
|
+
*
|
|
15
|
+
* // Safe alternative
|
|
16
|
+
* elem('div', userInput)
|
|
17
|
+
*
|
|
18
|
+
* @see https://owasp.org/www-community/attacks/xss/
|
|
19
|
+
*/
|
|
20
|
+
export declare const html: (value: string | number | bigint) => VNodeNode | VNodeNode[];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { keep } from './keep';
|
|
2
|
+
export { is } from './is';
|
|
3
|
+
export { cls } from './cls';
|
|
4
|
+
export { slot } from './slot';
|
|
5
|
+
export { html } from './html';
|
|
6
|
+
export { closest } from './closest';
|
|
7
|
+
export { inject } from './inject';
|
|
8
|
+
export { signal } from './signal';
|
|
9
|
+
export type { Signal } from './signal';
|
|
10
|
+
export { derived } from './derived';
|
|
11
|
+
export type { DerivedSignal } from './derived';
|
|
12
|
+
export { router } from './router';
|
|
13
|
+
export { Portal } from './portal';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { VNode } from '../vnode.defs';
|
|
2
|
+
/**
|
|
3
|
+
* Retrieves values provided by ancestor components.
|
|
4
|
+
* Walks up the tree collecting requested keys, with closer
|
|
5
|
+
* ancestors taking priority (shadowing).
|
|
6
|
+
* Returns an object with only the keys that were found.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const { theme, locale } = inject(vNode, 'theme', 'locale');
|
|
10
|
+
*/
|
|
11
|
+
export declare const inject: (vNode: VNode, ...keys: string[]) => Record<string, any>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vNode type constants for use with predicate checks.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* if (vNode.is === is.elem) // is a DOM element
|
|
6
|
+
* if (vNode.is === is.comp) // is a component
|
|
7
|
+
*/
|
|
8
|
+
export declare const is: {
|
|
9
|
+
elem: number;
|
|
10
|
+
text: number;
|
|
11
|
+
comp: number;
|
|
12
|
+
node: number;
|
|
13
|
+
compDef: number;
|
|
14
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A memoization predicate that prevents all re-renders.
|
|
3
|
+
* Pass to a component's $memo property to make it fully static.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const Static = compDef({
|
|
7
|
+
* $memo: keep,
|
|
8
|
+
* draw: () => elem('div', 'I never re-render')
|
|
9
|
+
* });
|
|
10
|
+
*/
|
|
11
|
+
export declare const keep: () => boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { VNodeComp, VNodeCompDefinition, VNodeAnyOrArray } from '../vnode.defs';
|
|
2
|
+
interface PortalAttrs {
|
|
3
|
+
to: string | Element;
|
|
4
|
+
children: VNodeAnyOrArray;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Renders children to a different DOM location.
|
|
8
|
+
* Useful for modals, tooltips, and overlays that need
|
|
9
|
+
* to escape their parent's stacking context or overflow.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* comp(Portal, {
|
|
13
|
+
* to: '#modals', // CSS selector
|
|
14
|
+
* to: document.body, // or Element reference
|
|
15
|
+
* children: comp(Modal)
|
|
16
|
+
* })
|
|
17
|
+
*/
|
|
18
|
+
export declare const Portal: VNodeCompDefinition<PortalAttrs> & {
|
|
19
|
+
to: (vNode: VNodeComp<PortalAttrs>) => Element | null;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { VNodeCompDefinition, VNodeAnyOrArray } from '../vnode.defs';
|
|
2
|
+
interface RouterOptions {
|
|
3
|
+
mode?: 'hash' | 'history';
|
|
4
|
+
}
|
|
5
|
+
type RouteComponent = (params: Record<string, string>) => VNodeAnyOrArray;
|
|
6
|
+
interface RouterRoutes {
|
|
7
|
+
[pattern: string]: RouteComponent;
|
|
8
|
+
'*'?: RouteComponent;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a hash or history-based router as a component definition.
|
|
12
|
+
* Supports dynamic params (:id) and wildcard routes (*).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const App = router({
|
|
16
|
+
* '/': () => comp(Home),
|
|
17
|
+
* '/users/:id': ({ id }) => comp(User, { id }),
|
|
18
|
+
* '*': () => comp(NotFound)
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Hash mode (default)
|
|
22
|
+
* draw(comp(App), document.body);
|
|
23
|
+
*
|
|
24
|
+
* // History mode
|
|
25
|
+
* draw(comp(router(routes, { mode: 'history' })), document.body);
|
|
26
|
+
*/
|
|
27
|
+
export declare const router: (routes: RouterRoutes, options?: RouterOptions) => VNodeCompDefinition;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type SignalSubscriber<T> = (val: T) => void;
|
|
2
|
+
export interface Signal<T> {
|
|
3
|
+
get val(): T;
|
|
4
|
+
set val(newVal: T);
|
|
5
|
+
sub(fn: SignalSubscriber<T>): () => void;
|
|
6
|
+
notify(): void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Creates a reactive value that notifies subscribers on change.
|
|
10
|
+
* Uses strict inequality (!==) to detect changes, so object
|
|
11
|
+
* mutations won't trigger notifications — use immutable updates
|
|
12
|
+
* or call notify() manually.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const count = signal(0);
|
|
16
|
+
* const unsub = count.sub(val => console.log(val));
|
|
17
|
+
* count.val = 1; // logs 1
|
|
18
|
+
* unsub(); // stop listening
|
|
19
|
+
*/
|
|
20
|
+
export declare const signal: <T>(val: T) => Signal<T>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { VNode, VNodeAttrs } from '../vnode.defs';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a content slot to a renderable vNode.
|
|
4
|
+
* Supports multiple content formats for flexible component APIs.
|
|
5
|
+
*
|
|
6
|
+
* - string → returned as-is
|
|
7
|
+
* - function → called with attrs, returns result
|
|
8
|
+
* - array → each item recursively resolved
|
|
9
|
+
* - vNode → returned as-is
|
|
10
|
+
* - null / undefined → returns nothing
|
|
11
|
+
*
|
|
12
|
+
* Note: component definitions must be wrapped in comp() by the caller.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const Card = compDef({
|
|
16
|
+
* draw: (vNode) => elem('div', { class: 'card' },
|
|
17
|
+
* slot(vNode.attrs.header, vNode.attrs),
|
|
18
|
+
* slot(vNode.attrs.children, vNode.attrs)
|
|
19
|
+
* )
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Usage
|
|
23
|
+
* comp(Card, {
|
|
24
|
+
* header: elem('h2', 'Title'), // vNode
|
|
25
|
+
* children: (attrs) => elem('p', 'Hi'), // function
|
|
26
|
+
* children: comp(Modal, { ... }), // wrap in comp() explicitly
|
|
27
|
+
* })
|
|
28
|
+
*/
|
|
29
|
+
export declare const slot: (content: VNode | VNode[] | string | ((attrs: VNodeAttrs) => VNode | VNode[] | string) | null | undefined, attrs: VNodeAttrs) => VNode | VNode[] | string | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Window {
|
|
3
|
+
DEBUG?: boolean;
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
import { VNodeElem, VNodeComp, VNodeNode, VNodeAny, VNodeArray, VNodeElemAttributes, VNodeCompAttributes, VNodeCompDefinition, VNodeContainer } from './vnode.defs';
|
|
7
|
+
declare const ns: {
|
|
8
|
+
math: string;
|
|
9
|
+
svg: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const diff: {
|
|
12
|
+
none: number;
|
|
13
|
+
self: number;
|
|
14
|
+
nodes: number;
|
|
15
|
+
all: number;
|
|
16
|
+
};
|
|
17
|
+
export declare const sync: () => void;
|
|
18
|
+
export declare const draw: (vNode: VNodeContainer, surgical?: boolean) => void;
|
|
19
|
+
export declare const elem: {
|
|
20
|
+
(tag: string): VNodeElem;
|
|
21
|
+
(tag: string, attrs: VNodeElemAttributes, ...args: VNodeArray): VNodeElem;
|
|
22
|
+
(tag: string, ...args: VNodeArray): VNodeElem;
|
|
23
|
+
};
|
|
24
|
+
export declare const compDef: (componentDefinition: VNodeCompDefinition) => VNodeCompDefinition;
|
|
25
|
+
export declare const comp: (componentDefinition: VNodeCompDefinition, attrs?: VNodeCompAttributes) => VNodeComp;
|
|
26
|
+
export declare const node: (el: Element) => VNodeNode;
|
|
27
|
+
export declare const bind: (el: Element, vNodeAnyOrArray: VNodeAny | VNodeArray) => VNodeComp;
|
|
28
|
+
export { ns };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export declare const VNODE_ELEM = 1;
|
|
2
|
+
export declare const VNODE_TEXT = 2;
|
|
3
|
+
export declare const VNODE_COMP = 3;
|
|
4
|
+
export declare const VNODE_NODE = 4;
|
|
5
|
+
export declare const VNODE_COMPDEF = 5;
|
|
6
|
+
export interface VNodeElemAttributes {
|
|
7
|
+
class?: string | string[];
|
|
8
|
+
style?: string | Record<string, string | number | null | undefined>;
|
|
9
|
+
$key?: string | number;
|
|
10
|
+
$diff?: number;
|
|
11
|
+
$remove?: boolean;
|
|
12
|
+
is?: string;
|
|
13
|
+
xmlns?: string;
|
|
14
|
+
[property: string]: any;
|
|
15
|
+
}
|
|
16
|
+
export interface VNodeCompAttributes {
|
|
17
|
+
$key?: string | number;
|
|
18
|
+
$remove?: boolean;
|
|
19
|
+
to?: string | Element;
|
|
20
|
+
[property: string]: any;
|
|
21
|
+
}
|
|
22
|
+
export type VNodeAttrs = VNodeElemAttributes | VNodeCompAttributes;
|
|
23
|
+
export interface VNodeNodeAttributes {
|
|
24
|
+
[property: string]: any;
|
|
25
|
+
}
|
|
26
|
+
export interface VNodeCompDefinition<A extends VNodeCompAttributes = VNodeCompAttributes> {
|
|
27
|
+
$tag?: number;
|
|
28
|
+
init?: (vNode: VNodeComp<A>) => void;
|
|
29
|
+
draw: (vNode: VNodeComp<A>, oldChildren?: VNodeFlatArray) => VNodeAnyOrArray;
|
|
30
|
+
drawn?: (vNode: VNodeComp<A>, isFirstDraw?: boolean) => void;
|
|
31
|
+
remove?: (vNode: VNodeComp<A>) => Promise<void> | void;
|
|
32
|
+
removed?: (vNode: VNodeComp<A>) => void;
|
|
33
|
+
$memo?: boolean | ((newAttrs: A, oldAttrs: A) => boolean);
|
|
34
|
+
}
|
|
35
|
+
interface VNodeBase {
|
|
36
|
+
$h?: boolean;
|
|
37
|
+
up: VNodeContainer | null;
|
|
38
|
+
nodes: VNodeFlatArray | null;
|
|
39
|
+
el: any;
|
|
40
|
+
}
|
|
41
|
+
export interface VNodeElem extends VNodeBase {
|
|
42
|
+
is: typeof VNODE_ELEM;
|
|
43
|
+
$tag: string;
|
|
44
|
+
$def: null;
|
|
45
|
+
attrs: VNodeElemAttributes;
|
|
46
|
+
nodes: VNodeFlatArray;
|
|
47
|
+
}
|
|
48
|
+
export interface VNodeText extends VNodeBase {
|
|
49
|
+
is: typeof VNODE_TEXT;
|
|
50
|
+
$tag: string;
|
|
51
|
+
$def: null;
|
|
52
|
+
attrs: null;
|
|
53
|
+
nodes: null;
|
|
54
|
+
}
|
|
55
|
+
export interface VNodeComp<A extends VNodeCompAttributes = VNodeCompAttributes> extends VNodeBase {
|
|
56
|
+
is: typeof VNODE_COMP;
|
|
57
|
+
$tag: number;
|
|
58
|
+
$def: VNodeCompDefinition;
|
|
59
|
+
attrs: A;
|
|
60
|
+
nodes: VNodeFlatArray;
|
|
61
|
+
provide?: Record<string, any>;
|
|
62
|
+
}
|
|
63
|
+
export interface VNodeNode extends VNodeBase {
|
|
64
|
+
is: typeof VNODE_NODE;
|
|
65
|
+
$tag: string;
|
|
66
|
+
$def: null;
|
|
67
|
+
attrs: null;
|
|
68
|
+
nodes: null;
|
|
69
|
+
}
|
|
70
|
+
export type VNode = VNodeElem | VNodeText | VNodeComp | VNodeNode;
|
|
71
|
+
export type VNodeAny = VNode;
|
|
72
|
+
export type VNodeContainer = VNodeComp | VNodeElem;
|
|
73
|
+
export type VNodeDrawable = VNodeComp;
|
|
74
|
+
export type VNodeHTML = VNodeElem;
|
|
75
|
+
export type VNodeDom = Node | Node[];
|
|
76
|
+
export type VNodeChild = VNodeAny | string | number | boolean | null | undefined;
|
|
77
|
+
export type VNodeArray = Array<VNodeChild | VNodeArray>;
|
|
78
|
+
export type VNodeAnyOrArray = VNodeChild | VNodeArray;
|
|
79
|
+
export interface VNodeFlatArray extends Array<VNodeAny> {
|
|
80
|
+
$keys?: Map<any, any> | null;
|
|
81
|
+
}
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/*! Zenith Helpers v1.0.0-beta.14 | MIT License | © 2022-2025 Daniel Barber */
|
|
2
|
+
import { node, draw } from './zenith.js';
|
|
3
|
+
|
|
4
|
+
const keep = () => true;
|
|
5
|
+
|
|
6
|
+
const VNODE_ELEM = 1;
|
|
7
|
+
const VNODE_TEXT = 2;
|
|
8
|
+
const VNODE_COMP = 3;
|
|
9
|
+
const VNODE_NODE = 4;
|
|
10
|
+
const VNODE_COMPDEF = 5;
|
|
11
|
+
|
|
12
|
+
const is = {
|
|
13
|
+
elem: VNODE_ELEM,
|
|
14
|
+
text: VNODE_TEXT,
|
|
15
|
+
comp: VNODE_COMP,
|
|
16
|
+
node: VNODE_NODE,
|
|
17
|
+
compDef: VNODE_COMPDEF
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const cls = (...args) => args.filter(Boolean).join(" ");
|
|
21
|
+
|
|
22
|
+
const slot = (content, attrs) => {
|
|
23
|
+
if (content) {
|
|
24
|
+
if (typeof content === "function") return content(attrs);
|
|
25
|
+
return content;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const html = (value) => {
|
|
30
|
+
const container = document.createElement("div");
|
|
31
|
+
container.innerHTML = value + "";
|
|
32
|
+
const nodes = [...container.childNodes].map((child) => node(child));
|
|
33
|
+
return nodes.length === 1 ? nodes[0] : nodes;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const closest = (vNode, predicate) => {
|
|
37
|
+
for (let current = vNode.up; current; current = current.up) {
|
|
38
|
+
if (predicate(current)) return current;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const inject = (vNode, ...keys) => {
|
|
43
|
+
const result = {};
|
|
44
|
+
const needed = new Set(keys);
|
|
45
|
+
for (let current = vNode.up; current && needed.size > 0; current = current.up) {
|
|
46
|
+
if (current.is === VNODE_COMP && current.provide) {
|
|
47
|
+
for (const key of needed) {
|
|
48
|
+
if (current.provide[key] !== void 0) {
|
|
49
|
+
result[key] = current.provide[key];
|
|
50
|
+
needed.delete(key);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const signal = (val) => {
|
|
59
|
+
let _val = val;
|
|
60
|
+
const _fns = /* @__PURE__ */ new Set();
|
|
61
|
+
const notify = () => {
|
|
62
|
+
for (const fn of _fns) fn(_val);
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
get val() {
|
|
66
|
+
return _val;
|
|
67
|
+
},
|
|
68
|
+
set val(newVal) {
|
|
69
|
+
if (_val !== newVal) {
|
|
70
|
+
_val = newVal;
|
|
71
|
+
notify();
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
sub(fn) {
|
|
75
|
+
_fns.add(fn);
|
|
76
|
+
return () => _fns.delete(fn);
|
|
77
|
+
},
|
|
78
|
+
notify
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const derived = (fn, deps) => {
|
|
83
|
+
const computed = signal(fn());
|
|
84
|
+
const cleanups = deps.map((dep) => dep.sub(() => {
|
|
85
|
+
computed.val = fn();
|
|
86
|
+
}));
|
|
87
|
+
computed.destroy = () => {
|
|
88
|
+
for (const cleanup of cleanups) cleanup();
|
|
89
|
+
cleanups.length = 0;
|
|
90
|
+
};
|
|
91
|
+
return computed;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const router = (routes, options = {}) => {
|
|
95
|
+
const isHistory = options.mode === "history";
|
|
96
|
+
const eventType = isHistory ? "popstate" : "hashchange";
|
|
97
|
+
const getPath = () => isHistory ? location.pathname : location.hash.slice(1) || "/";
|
|
98
|
+
const compiled = [];
|
|
99
|
+
const matchRoute = (path) => {
|
|
100
|
+
for (const route of compiled) {
|
|
101
|
+
const match = path.match(route.regex);
|
|
102
|
+
if (match) {
|
|
103
|
+
const params = {};
|
|
104
|
+
for (let i = 0; i < route.keys.length; i++) {
|
|
105
|
+
params[route.keys[i]] = match[i + 1];
|
|
106
|
+
}
|
|
107
|
+
return { component: route.component, params };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { component: routes["*"] || null, params: {} };
|
|
111
|
+
};
|
|
112
|
+
for (const pattern in routes) {
|
|
113
|
+
const keys = [];
|
|
114
|
+
const regexPattern = pattern.replace(/:[^/]+/g, (match) => {
|
|
115
|
+
keys.push(match.slice(1));
|
|
116
|
+
return "([^/]+)";
|
|
117
|
+
}).replace(/\*/g, "(.*)");
|
|
118
|
+
compiled.push({
|
|
119
|
+
regex: new RegExp(`^${regexPattern}$`),
|
|
120
|
+
keys,
|
|
121
|
+
component: routes[pattern]
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
init: (vNode) => {
|
|
126
|
+
const node = vNode;
|
|
127
|
+
const matched = matchRoute(getPath());
|
|
128
|
+
node.route = matched.component;
|
|
129
|
+
node.params = matched.params;
|
|
130
|
+
node.onroute = () => {
|
|
131
|
+
const matched2 = matchRoute(getPath());
|
|
132
|
+
node.route = matched2.component;
|
|
133
|
+
node.params = matched2.params;
|
|
134
|
+
draw(node, true);
|
|
135
|
+
};
|
|
136
|
+
window.addEventListener(eventType, node.onroute);
|
|
137
|
+
},
|
|
138
|
+
draw: (vNode) => {
|
|
139
|
+
var _a;
|
|
140
|
+
const node = vNode;
|
|
141
|
+
return (_a = node.route) == null ? void 0 : _a.call(node, node.params);
|
|
142
|
+
},
|
|
143
|
+
removed: (vNode) => {
|
|
144
|
+
const node = vNode;
|
|
145
|
+
if (node.onroute) {
|
|
146
|
+
window.removeEventListener(eventType, node.onroute);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const Portal = {
|
|
153
|
+
to: (vNode) => typeof vNode.attrs.to === "string" ? document.querySelector(vNode.attrs.to) : vNode.attrs.to,
|
|
154
|
+
draw: (vNode) => vNode.attrs.children
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export { Portal, closest, cls, derived, html, inject, is, keep, router, signal, slot };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! Zenith Helpers v1.0.0-beta.14 | MIT License | © 2022-2025 Daniel Barber */
|
|
2
|
+
!function(e){"use strict";const signal=e=>{let t=e;const o=new Set,n=()=>{for(const e of o)e(t)};return{get val(){return t},set val(e){t!==e&&(t=e,n())},sub:e=>(o.add(e),()=>o.delete(e)),notify:n}},t={keep:()=>!0,is:{elem:1,text:2,comp:3,node:4,compDef:5},cls:(...e)=>e.filter(Boolean).join(" "),slot:(e,t)=>{if(e)return"function"==typeof e?e(t):e},html:t=>{const o=document.createElement("div");o.innerHTML=t+"";const n=[...o.childNodes].map(t=>e.node(t));return 1===n.length?n[0]:n},closest:(e,t)=>{for(let o=e.up;o;o=o.up)if(t(o))return o},inject:(e,...t)=>{const o={},n=new Set(t);for(let t=e.up;t&&n.size>0;t=t.up)if(3===t.is&&t.provide)for(const e of n)void 0!==t.provide[e]&&(o[e]=t.provide[e],n.delete(e));return o},signal:signal,derived:(e,t)=>{const o=signal(e()),n=t.map(t=>t.sub(()=>{o.val=e()}));return o.destroy=()=>{for(const e of n)e();n.length=0},o},router:(t,o={})=>{const n="history"===o.mode,r=n?"popstate":"hashchange",s=()=>n?location.pathname:location.hash.slice(1)||"/",c=[],a=e=>{for(const t of c){const o=e.match(t.regex);if(o){const e={};for(let n=0;t.keys.length>n;n++)e[t.keys[n]]=o[n+1];return{component:t.component,params:e}}}return{component:t["*"]||null,params:{}}};for(const e in t){const o=[],n=e.replace(/:[^/]+/g,e=>(o.push(e.slice(1)),"([^/]+)")).replace(/\*/g,"(.*)");c.push({regex:RegExp(`^${n}$`),keys:o,component:t[e]})}return{init:t=>{const o=t,n=a(s());o.route=n.component,o.params=n.params,o.onroute=()=>{const t=a(s());o.route=t.component,o.params=t.params,e.draw(o,!0)},window.addEventListener(r,o.onroute)},draw:e=>{var t;return null==(t=e.route)?void 0:t.call(e,e.params)},removed:e=>{e.onroute&&window.removeEventListener(r,e.onroute)}}},Portal:{to:e=>"string"==typeof e.attrs.to?document.querySelector(e.attrs.to):e.attrs.to,draw:e=>e.attrs.children}};window.zh=t}(z);
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! Zenith Helpers v1.0.0-beta.14 | MIT License | © 2022-2025 Daniel Barber */
|
|
2
|
+
import{node as e,draw as t}from"./zenith.js";const keep=()=>!0,is={elem:1,text:2,comp:3,node:4,compDef:5},cls=(...e)=>e.filter(Boolean).join(" "),slot=(e,t)=>{if(e)return"function"==typeof e?e(t):e},html=t=>{const o=document.createElement("div");o.innerHTML=t+"";const n=[...o.childNodes].map(t=>e(t));return 1===n.length?n[0]:n},closest=(e,t)=>{for(let o=e.up;o;o=o.up)if(t(o))return o},inject=(e,...t)=>{const o={},n=new Set(t);for(let t=e.up;t&&n.size>0;t=t.up)if(3===t.is&&t.provide)for(const e of n)void 0!==t.provide[e]&&(o[e]=t.provide[e],n.delete(e));return o},signal=e=>{let t=e;const o=new Set,n=()=>{for(const e of o)e(t)};return{get val(){return t},set val(e){t!==e&&(t=e,n())},sub:e=>(o.add(e),()=>o.delete(e)),notify:n}},derived=(e,t)=>{const o=signal(e()),n=t.map(t=>t.sub(()=>{o.val=e()}));return o.destroy=()=>{for(const e of n)e();n.length=0},o},router=(e,o={})=>{const n="history"===o.mode,r=n?"popstate":"hashchange",s=()=>n?location.pathname:location.hash.slice(1)||"/",a=[],c=t=>{for(const e of a){const o=t.match(e.regex);if(o){const t={};for(let n=0;e.keys.length>n;n++)t[e.keys[n]]=o[n+1];return{component:e.component,params:t}}}return{component:e["*"]||null,params:{}}};for(const t in e){const o=[],n=t.replace(/:[^/]+/g,e=>(o.push(e.slice(1)),"([^/]+)")).replace(/\*/g,"(.*)");a.push({regex:RegExp(`^${n}$`),keys:o,component:e[t]})}return{init:e=>{const o=e,n=c(s());o.route=n.component,o.params=n.params,o.onroute=()=>{const e=c(s());o.route=e.component,o.params=e.params,t(o,!0)},window.addEventListener(r,o.onroute)},draw:e=>{var t;return null==(t=e.route)?void 0:t.call(e,e.params)},removed:e=>{e.onroute&&window.removeEventListener(r,e.onroute)}}},Portal={to:e=>"string"==typeof e.attrs.to?document.querySelector(e.attrs.to):e.attrs.to,draw:e=>e.attrs.children};export{Portal,closest,cls,derived,html,inject,is,keep,router,signal,slot};
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/*! Zenith v1.0.0-beta.14 | MIT License | © 2022-2025 Daniel Barber */
|
|
2
|
+
const VNODE_ELEM = 1;
|
|
3
|
+
const VNODE_TEXT = 2;
|
|
4
|
+
const VNODE_COMP = 3;
|
|
5
|
+
const VNODE_NODE = 4;
|
|
6
|
+
const VNODE_COMPDEF = 5;
|
|
7
|
+
|
|
8
|
+
const boundNodes = /* @__PURE__ */ new Map();
|
|
9
|
+
const redrawQueue = /* @__PURE__ */ new Set();
|
|
10
|
+
const ns = {
|
|
11
|
+
math: "http://www.w3.org/1998/Math/MathML",
|
|
12
|
+
svg: "http://www.w3.org/2000/svg"
|
|
13
|
+
};
|
|
14
|
+
const FLAG_IS_DEFINED = 1;
|
|
15
|
+
const FLAG_IS_STALE = 2;
|
|
16
|
+
const FLAG_IS_REMOVING = 4;
|
|
17
|
+
const FLAG_HAS_LIFECYCLE = 8;
|
|
18
|
+
const FLAG_HAS_REMOVE = 16;
|
|
19
|
+
const FLAG_HAS_REMOVED = 32;
|
|
20
|
+
const FLAG_REMOVE_CALLED = 64;
|
|
21
|
+
const DIFF_NONE = 1;
|
|
22
|
+
const DIFF_SELF = 2;
|
|
23
|
+
const DIFF_NODES = 4;
|
|
24
|
+
const DIFF_ALL = DIFF_SELF | DIFF_NODES;
|
|
25
|
+
const diff = {
|
|
26
|
+
none: DIFF_NONE,
|
|
27
|
+
self: DIFF_SELF,
|
|
28
|
+
nodes: DIFF_NODES,
|
|
29
|
+
all: DIFF_ALL
|
|
30
|
+
};
|
|
31
|
+
const ALWAYS_EMPTY_ARRAY = Object.freeze([]) ;
|
|
32
|
+
const ALWAYS_EMPTY_OBJECT = Object.freeze({}) ;
|
|
33
|
+
const ALWAYS_EMPTY_MAP = /* @__PURE__ */ new Map();
|
|
34
|
+
const sync = () => {
|
|
35
|
+
let remaining = redrawQueue.size;
|
|
36
|
+
let targetEl;
|
|
37
|
+
if (remaining) {
|
|
38
|
+
const active = document.activeElement;
|
|
39
|
+
for (const redrawNode of redrawQueue) {
|
|
40
|
+
targetEl = boundNodes.has(redrawNode.el) ? redrawNode.el : redrawNode.attrs && redrawNode.attrs.to || redrawNode.el && redrawNode.el.parentNode || redrawNode.up && redrawNode.up.el;
|
|
41
|
+
redrawQueue.delete(redrawNode);
|
|
42
|
+
if (targetEl) {
|
|
43
|
+
_redrawComponent(redrawNode, targetEl);
|
|
44
|
+
}
|
|
45
|
+
if (!--remaining) break;
|
|
46
|
+
}
|
|
47
|
+
if (redrawQueue.size) Promise.resolve().then(sync);
|
|
48
|
+
if (active && active !== document.activeElement && typeof active.focus === "function") {
|
|
49
|
+
active.focus();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const draw = (vNode, surgical) => {
|
|
54
|
+
if (surgical) {
|
|
55
|
+
if (vNode.is === VNODE_COMP) {
|
|
56
|
+
vNode.$tag |= FLAG_IS_STALE;
|
|
57
|
+
}
|
|
58
|
+
redrawQueue.add(vNode);
|
|
59
|
+
} else {
|
|
60
|
+
let current;
|
|
61
|
+
for (current = vNode; current.up; current = current.up) ;
|
|
62
|
+
redrawQueue.add(current);
|
|
63
|
+
for (current = vNode; current.up; current = current.up) {
|
|
64
|
+
if (current.is === VNODE_COMP) {
|
|
65
|
+
if (current.$tag & FLAG_IS_STALE) break;
|
|
66
|
+
current.$tag |= FLAG_IS_STALE;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (redrawQueue.size === 1) {
|
|
71
|
+
Promise.resolve().then(sync);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const _flattenReusableArray = [];
|
|
75
|
+
const _flatten = (nodes, offset = 0, arr = []) => {
|
|
76
|
+
let node2;
|
|
77
|
+
if (!(nodes && typeof nodes === "object" && nodes.length != null)) {
|
|
78
|
+
_flattenReusableArray[0] = nodes;
|
|
79
|
+
nodes = _flattenReusableArray;
|
|
80
|
+
}
|
|
81
|
+
for (offset; offset < nodes.length; offset++) {
|
|
82
|
+
node2 = nodes[offset];
|
|
83
|
+
if (Array.isArray(node2)) {
|
|
84
|
+
_flatten(node2, 0, arr);
|
|
85
|
+
} else if (node2 != null) {
|
|
86
|
+
if (node2.is) {
|
|
87
|
+
if (arr.$keys === void 0) {
|
|
88
|
+
arr.$keys = node2.attrs && "$key" in node2.attrs ? ALWAYS_EMPTY_MAP : null;
|
|
89
|
+
} else if (!arr.$keys !== !(node2.attrs && "$key" in node2.attrs)) {
|
|
90
|
+
throw new Error("Children have mixed keys..." );
|
|
91
|
+
}
|
|
92
|
+
arr.push(node2);
|
|
93
|
+
} else if (node2 !== true && node2 !== false) {
|
|
94
|
+
if (arr.$keys === void 0) {
|
|
95
|
+
arr.$keys = null;
|
|
96
|
+
} else if (arr.$keys) {
|
|
97
|
+
throw new Error(`Children have mixed keys. Wrap strings in elem('span', {$key}, "My keyed string.")` );
|
|
98
|
+
}
|
|
99
|
+
arr.push({
|
|
100
|
+
is: VNODE_TEXT,
|
|
101
|
+
$tag: node2 + "",
|
|
102
|
+
$def: null,
|
|
103
|
+
attrs: null,
|
|
104
|
+
nodes: null,
|
|
105
|
+
up: null,
|
|
106
|
+
el: null
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return arr;
|
|
112
|
+
};
|
|
113
|
+
const _lastRoot = (vNode) => {
|
|
114
|
+
if (vNode.is === VNODE_COMP && !vNode.attrs.to) {
|
|
115
|
+
for (let i = vNode.nodes.length - 1; i >= 0; i--) {
|
|
116
|
+
const root = _lastRoot(vNode.nodes[i]);
|
|
117
|
+
if (root) return root;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return vNode.el;
|
|
122
|
+
};
|
|
123
|
+
const _place = (vNode, parentEl, next) => {
|
|
124
|
+
if (vNode.is === VNODE_COMP && !vNode.attrs.to) {
|
|
125
|
+
for (let i = vNode.nodes.length - 1; i >= 0; i--) {
|
|
126
|
+
next = _place(vNode.nodes[i], parentEl, next);
|
|
127
|
+
}
|
|
128
|
+
} else if (vNode.el) {
|
|
129
|
+
if (vNode.el.nextSibling !== next) parentEl.insertBefore(vNode.el, next);
|
|
130
|
+
next = vNode.el;
|
|
131
|
+
}
|
|
132
|
+
return next;
|
|
133
|
+
};
|
|
134
|
+
const _updateNode = (parentNode, parentEl, newNode, oldNode, xmlns, skipMemo) => {
|
|
135
|
+
if (oldNode.el) {
|
|
136
|
+
const newNodeType = newNode.is;
|
|
137
|
+
if (newNodeType === oldNode.is) {
|
|
138
|
+
if (newNodeType === VNODE_ELEM) {
|
|
139
|
+
newNode.up = parentNode;
|
|
140
|
+
if (newNode.$tag === oldNode.$tag) {
|
|
141
|
+
newNode.el = oldNode.el;
|
|
142
|
+
newNode.$h = oldNode.$h;
|
|
143
|
+
const diff2 = newNode.attrs.$diff || DIFF_ALL;
|
|
144
|
+
if (diff2 & DIFF_SELF) {
|
|
145
|
+
_setElAttributes(newNode, oldNode.attrs);
|
|
146
|
+
}
|
|
147
|
+
if (diff2 & DIFF_NODES) {
|
|
148
|
+
_updateNodes(newNode, newNode.el, newNode.nodes, oldNode.nodes, newNode.attrs.xmlns || ns[newNode.$tag] || xmlns);
|
|
149
|
+
} else {
|
|
150
|
+
newNode.nodes = oldNode.nodes;
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
_create(parentNode, parentEl, newNode, xmlns);
|
|
154
|
+
parentEl.insertBefore(newNode.el, oldNode.el);
|
|
155
|
+
_remove(oldNode);
|
|
156
|
+
}
|
|
157
|
+
} else if (newNodeType === VNODE_TEXT) {
|
|
158
|
+
newNode.el = oldNode.el;
|
|
159
|
+
if (newNode.$tag !== oldNode.$tag) newNode.el.textContent = newNode.$tag;
|
|
160
|
+
} else if (newNodeType === VNODE_COMP) {
|
|
161
|
+
if (oldNode.$tag & FLAG_IS_REMOVING) {
|
|
162
|
+
oldNode.$tag &= ~FLAG_IS_REMOVING;
|
|
163
|
+
}
|
|
164
|
+
newNode.up = parentNode;
|
|
165
|
+
if (newNode.$def === oldNode.$def) {
|
|
166
|
+
newNode.el = oldNode.el;
|
|
167
|
+
newNode.$h = oldNode.$h;
|
|
168
|
+
newNode.nodes = oldNode.nodes;
|
|
169
|
+
if (skipMemo || oldNode.$tag & FLAG_IS_STALE || !newNode.$def.$memo || (newNode.$def.$memo === true ? newNode.attrs !== oldNode.attrs : !newNode.$def.$memo(newNode.attrs, oldNode.attrs))) {
|
|
170
|
+
newNode.$tag &= ~FLAG_IS_STALE;
|
|
171
|
+
_redrawComponent(newNode, parentEl, xmlns);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
_create(parentNode, parentEl, newNode, xmlns);
|
|
175
|
+
_remove(oldNode);
|
|
176
|
+
}
|
|
177
|
+
} else if (newNodeType === VNODE_NODE) {
|
|
178
|
+
if (newNode.el !== oldNode.el) {
|
|
179
|
+
parentEl.insertBefore(newNode.el, null);
|
|
180
|
+
oldNode.el.remove();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
_create(parentNode, parentEl, newNode, xmlns);
|
|
185
|
+
_remove(oldNode);
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
_create(parentNode, parentEl, newNode, xmlns);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const _updateNodes = (parentNode, parentEl, newNodes, oldNodes, xmlns, depth = 0) => {
|
|
192
|
+
const newNodesLength = newNodes.length;
|
|
193
|
+
const oldNodesLength = oldNodes.length;
|
|
194
|
+
if (newNodesLength) {
|
|
195
|
+
let oldNode;
|
|
196
|
+
let newNode;
|
|
197
|
+
let i = 0;
|
|
198
|
+
if (oldNodesLength) {
|
|
199
|
+
if (newNodes.$keys && oldNodes.$keys) {
|
|
200
|
+
while (i < newNodesLength && i < oldNodesLength && newNodes[i].attrs.$key === oldNodes[i].attrs.$key) {
|
|
201
|
+
_updateNode(parentNode, parentEl, newNodes[i], oldNodes[i], xmlns);
|
|
202
|
+
i++;
|
|
203
|
+
}
|
|
204
|
+
if (i < newNodesLength || i < oldNodesLength) {
|
|
205
|
+
const oldByKey = /* @__PURE__ */ new Map();
|
|
206
|
+
for (let k = i; k < oldNodesLength; k++) {
|
|
207
|
+
oldByKey.set(oldNodes[k].attrs.$key, oldNodes[k]);
|
|
208
|
+
}
|
|
209
|
+
const last = _lastRoot(oldNodes[oldNodesLength - 1]);
|
|
210
|
+
let next = last ? last.nextSibling : null;
|
|
211
|
+
for (let k = newNodesLength - 1; k >= i; k--) {
|
|
212
|
+
newNode = newNodes[k];
|
|
213
|
+
oldNode = oldByKey.get(newNode.attrs.$key);
|
|
214
|
+
if (oldNode) {
|
|
215
|
+
oldByKey.delete(newNode.attrs.$key);
|
|
216
|
+
if (newNode.is === VNODE_COMP && newNode.attrs.$remove) {
|
|
217
|
+
if (oldNode.$tag & FLAG_IS_REMOVING || !_remove(oldNode)) {
|
|
218
|
+
oldNode.up = parentNode;
|
|
219
|
+
newNodes[k] = oldNode;
|
|
220
|
+
} else {
|
|
221
|
+
newNodes.splice(k, 1);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
_updateNode(parentNode, parentEl, newNode, oldNode, xmlns);
|
|
226
|
+
}
|
|
227
|
+
} else if (!newNode.attrs.$remove) {
|
|
228
|
+
_create(parentNode, parentEl, newNode, xmlns);
|
|
229
|
+
} else {
|
|
230
|
+
newNodes.splice(k, 1);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
next = _place(newNodes[k], parentEl, next);
|
|
234
|
+
}
|
|
235
|
+
for (const oldNode2 of oldByKey.values()) _remove(oldNode2);
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
for (let i2 = newNodesLength; i2 < oldNodesLength; i2++) {
|
|
239
|
+
_remove(oldNodes[i2]);
|
|
240
|
+
}
|
|
241
|
+
for (let i2 = 0; i2 < newNodesLength; i2++) {
|
|
242
|
+
oldNode = oldNodes[i2];
|
|
243
|
+
newNode = newNodes[i2];
|
|
244
|
+
if (oldNode && oldNode.el) {
|
|
245
|
+
_updateNode(parentNode, parentEl, newNode, oldNode, xmlns);
|
|
246
|
+
} else {
|
|
247
|
+
_create(parentNode, parentEl, newNode, xmlns);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
for (; i < newNodesLength; i++) {
|
|
253
|
+
_create(parentNode, parentEl, newNodes[i], xmlns);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
if (parentNode.is === VNODE_ELEM) {
|
|
258
|
+
parentEl.textContent = "";
|
|
259
|
+
_unmount(oldNodes);
|
|
260
|
+
} else {
|
|
261
|
+
for (let i = 0; i < oldNodesLength; i++) _remove(oldNodes[i]);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
parentNode.nodes = newNodes;
|
|
265
|
+
};
|
|
266
|
+
const _setElAttributes = (newNode, oldAttrs = ALWAYS_EMPTY_OBJECT) => {
|
|
267
|
+
let attr;
|
|
268
|
+
let valueType;
|
|
269
|
+
const el = newNode.el;
|
|
270
|
+
const newAttrs = newNode.attrs;
|
|
271
|
+
if (newNode.$tag === "input" && newAttrs.type) {
|
|
272
|
+
el.setAttribute("type", newAttrs.type);
|
|
273
|
+
}
|
|
274
|
+
for (attr in newAttrs) {
|
|
275
|
+
const value = newAttrs[attr];
|
|
276
|
+
if (attr[0] === "o" && attr[1] === "n") {
|
|
277
|
+
if (value !== oldAttrs[attr]) {
|
|
278
|
+
el[attr] = typeof value === "function" ? (e) => {
|
|
279
|
+
if (value.call(e.currentTarget, e) === false) {
|
|
280
|
+
e.preventDefault();
|
|
281
|
+
e.stopPropagation();
|
|
282
|
+
}
|
|
283
|
+
if (e.draw !== false) {
|
|
284
|
+
draw(newNode);
|
|
285
|
+
}
|
|
286
|
+
} : null;
|
|
287
|
+
}
|
|
288
|
+
} else if (attr[0] !== "$" && (attr !== "type" || newNode.$tag !== "input") && attr !== "is" && attr !== "xmlns") {
|
|
289
|
+
valueType = typeof value;
|
|
290
|
+
if (valueType === "string" || valueType === "number") {
|
|
291
|
+
if (attr === "value") {
|
|
292
|
+
if (value !== oldAttrs[attr]) el.value = value;
|
|
293
|
+
} else if (value !== oldAttrs[attr]) {
|
|
294
|
+
el.setAttribute(attr, value + "");
|
|
295
|
+
}
|
|
296
|
+
} else if (value === true) {
|
|
297
|
+
if (oldAttrs[attr] !== true) el.setAttribute(attr, attr);
|
|
298
|
+
} else if (!value) {
|
|
299
|
+
if (oldAttrs[attr]) el.removeAttribute(attr);
|
|
300
|
+
} else if (attr === "style") {
|
|
301
|
+
if (typeof oldAttrs.style === "string") {
|
|
302
|
+
el.removeAttribute("style");
|
|
303
|
+
} else {
|
|
304
|
+
for (const oldAttr in oldAttrs.style || ALWAYS_EMPTY_OBJECT) {
|
|
305
|
+
if (!(oldAttr in value)) {
|
|
306
|
+
el.style[oldAttr] = "";
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
Object.assign(el.style, value);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
for (attr in oldAttrs) {
|
|
315
|
+
if (!(attr in newAttrs)) {
|
|
316
|
+
if (attr[0] === "o" && attr[1] === "n") {
|
|
317
|
+
el[attr] = null;
|
|
318
|
+
} else {
|
|
319
|
+
el.removeAttribute(attr);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
const _create = (parentNode, parentEl, vNode, xmlns) => {
|
|
325
|
+
if (vNode.is === VNODE_ELEM) {
|
|
326
|
+
vNode.up = parentNode;
|
|
327
|
+
xmlns = vNode.attrs && vNode.attrs.xmlns || ns[vNode.$tag] || xmlns;
|
|
328
|
+
vNode.el = xmlns ? document.createElementNS(xmlns, vNode.$tag, vNode.attrs.is ? { is: vNode.attrs.is } : null) : document.createElement(vNode.$tag, vNode.attrs.is ? { is: vNode.attrs.is } : null);
|
|
329
|
+
_setElAttributes(vNode);
|
|
330
|
+
for (let i = 0; i < vNode.nodes.length; i++) {
|
|
331
|
+
_create(vNode, vNode.el, vNode.nodes[i], xmlns);
|
|
332
|
+
}
|
|
333
|
+
parentEl.insertBefore(vNode.el, null);
|
|
334
|
+
} else if (vNode.is === VNODE_TEXT) {
|
|
335
|
+
vNode = vNode;
|
|
336
|
+
vNode.el = document.createTextNode(vNode.$tag);
|
|
337
|
+
parentEl.insertBefore(vNode.el, null);
|
|
338
|
+
} else if (vNode.is === VNODE_COMP) {
|
|
339
|
+
vNode.up = parentNode;
|
|
340
|
+
if (vNode.$def.init) vNode.$def.init(vNode);
|
|
341
|
+
vNode.nodes = _flatten(vNode.$def.draw(vNode));
|
|
342
|
+
if (vNode.$tag & FLAG_HAS_LIFECYCLE) {
|
|
343
|
+
for (let current = vNode; current && !current.$h; current = current.up) {
|
|
344
|
+
current.$h = true;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (vNode.attrs.to) {
|
|
348
|
+
if (!(vNode.attrs.to instanceof Element)) {
|
|
349
|
+
throw new Error('For components that act as portals, "to" attribute must be an Element');
|
|
350
|
+
}
|
|
351
|
+
vNode.el = document.createComment("");
|
|
352
|
+
parentEl.insertBefore(vNode.el, null);
|
|
353
|
+
}
|
|
354
|
+
for (let i = 0; i < vNode.nodes.length; i++) {
|
|
355
|
+
_create(vNode, vNode.attrs.to || parentEl, vNode.nodes[i], xmlns);
|
|
356
|
+
}
|
|
357
|
+
if (!vNode.el) vNode.el = vNode.nodes[0] && vNode.nodes[0].el;
|
|
358
|
+
if (vNode.$def.drawn) vNode.$def.drawn(vNode, true);
|
|
359
|
+
} else if (vNode.is === VNODE_NODE) {
|
|
360
|
+
parentEl.insertBefore(vNode.el, null);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
const _redrawComponent = (vNode, parentEl, xmlns) => {
|
|
364
|
+
const oldChildren = vNode.nodes;
|
|
365
|
+
const newChildren = vNode.$def.draw(vNode, oldChildren);
|
|
366
|
+
vNode.$tag &= ~FLAG_IS_STALE;
|
|
367
|
+
if (newChildren !== oldChildren) {
|
|
368
|
+
_updateNodes(vNode, vNode.attrs.to || parentEl, _flatten(newChildren), oldChildren, xmlns);
|
|
369
|
+
if (!vNode.el) vNode.el = vNode.nodes[0] && vNode.nodes[0].el;
|
|
370
|
+
}
|
|
371
|
+
if (vNode.$def.drawn) vNode.$def.drawn(vNode);
|
|
372
|
+
};
|
|
373
|
+
const _unmountReusableArray = [];
|
|
374
|
+
const _unmount = (nodes) => {
|
|
375
|
+
if (!Array.isArray(nodes)) {
|
|
376
|
+
_unmountReusableArray[0] = nodes;
|
|
377
|
+
nodes = _unmountReusableArray;
|
|
378
|
+
}
|
|
379
|
+
nodes = nodes;
|
|
380
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
381
|
+
const node2 = nodes[i];
|
|
382
|
+
if (node2.is === VNODE_COMP) {
|
|
383
|
+
if (node2.$tag & FLAG_HAS_REMOVE || node2.$def.remove) {
|
|
384
|
+
if (!(node2.$tag & FLAG_REMOVE_CALLED)) {
|
|
385
|
+
node2.$def.remove(node2);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (node2.$tag & FLAG_HAS_REMOVED || node2.$def.removed) {
|
|
389
|
+
node2.$def.removed(node2);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (node2.$h && node2.nodes.length) {
|
|
393
|
+
_unmount(node2.nodes);
|
|
394
|
+
node2.nodes = null;
|
|
395
|
+
}
|
|
396
|
+
node2.attrs = node2.nodes = node2.$tag = node2.$def = node2.el = node2.up = null;
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const _detach = (vNode) => {
|
|
400
|
+
if (vNode.is === VNODE_COMP) {
|
|
401
|
+
for (let i = 0; i < vNode.nodes.length; i++) {
|
|
402
|
+
_detach(vNode.nodes[i]);
|
|
403
|
+
}
|
|
404
|
+
if (vNode.attrs.to && vNode.el) vNode.el.remove();
|
|
405
|
+
} else if (vNode.el) {
|
|
406
|
+
vNode.el.remove();
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
const _destroy = (vNode) => {
|
|
410
|
+
_detach(vNode);
|
|
411
|
+
_unmount(vNode);
|
|
412
|
+
};
|
|
413
|
+
const _remove = (vNode) => {
|
|
414
|
+
if (vNode.is === VNODE_COMP) {
|
|
415
|
+
if (vNode.$tag & FLAG_HAS_REMOVE || vNode.$def.remove) {
|
|
416
|
+
if (vNode.$tag & FLAG_IS_REMOVING) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
vNode.$tag |= FLAG_REMOVE_CALLED | FLAG_IS_REMOVING;
|
|
420
|
+
const deferred = vNode.$def.remove(vNode);
|
|
421
|
+
if (deferred && deferred.then) {
|
|
422
|
+
if (!("$key" in vNode.attrs)) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
"Component remove() returned a Promise but component has no $key attribute.\nDeferred removal requires a key to track the component during exit.\nAdd a $key and use $remove flag. Example:\ncomp(MyComponent, { $key: item.id, $remove: item.deleted })"
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
const deferredDestroy = () => {
|
|
428
|
+
if (vNode.$tag & FLAG_IS_REMOVING) {
|
|
429
|
+
const parent = vNode.up;
|
|
430
|
+
if (!parent) {
|
|
431
|
+
console.warn(
|
|
432
|
+
"Promise resolved but component was already removed.",
|
|
433
|
+
"This usually means the component was removed without using $remove attribute."
|
|
434
|
+
);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
if (parent) {
|
|
438
|
+
const i = parent.nodes.indexOf(vNode);
|
|
439
|
+
if (i !== -1) parent.nodes.splice(i, 1);
|
|
440
|
+
}
|
|
441
|
+
_destroy(vNode);
|
|
442
|
+
draw(parent);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
deferred.then(deferredDestroy, deferredDestroy);
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
_destroy(vNode);
|
|
451
|
+
return true;
|
|
452
|
+
};
|
|
453
|
+
const elem = function(tag) {
|
|
454
|
+
const first = arguments[1];
|
|
455
|
+
const hasAttrs = first && typeof first === "object" && !Array.isArray(first) && !("$tag" in first);
|
|
456
|
+
return {
|
|
457
|
+
is: VNODE_ELEM,
|
|
458
|
+
$tag: tag,
|
|
459
|
+
// string: element name
|
|
460
|
+
$def: null,
|
|
461
|
+
// always null for elem
|
|
462
|
+
attrs: hasAttrs ? Object.freeze(first) : ALWAYS_EMPTY_OBJECT,
|
|
463
|
+
nodes: arguments.length > (hasAttrs ? 2 : 1) ? _flatten(arguments, hasAttrs ? 2 : 1) : ALWAYS_EMPTY_ARRAY,
|
|
464
|
+
up: null,
|
|
465
|
+
el: null
|
|
466
|
+
};
|
|
467
|
+
};
|
|
468
|
+
const compDef = (componentDefinition) => {
|
|
469
|
+
{
|
|
470
|
+
if (!componentDefinition.draw) {
|
|
471
|
+
throw new Error("Component definition must have a draw function");
|
|
472
|
+
}
|
|
473
|
+
if (typeof componentDefinition.draw !== "function") {
|
|
474
|
+
throw new Error("draw must be a function");
|
|
475
|
+
}
|
|
476
|
+
if (componentDefinition.init && typeof componentDefinition.init !== "function") {
|
|
477
|
+
throw new Error("init must be a function");
|
|
478
|
+
}
|
|
479
|
+
if (componentDefinition.drawn && typeof componentDefinition.drawn !== "function") {
|
|
480
|
+
throw new Error("drawn must be a function");
|
|
481
|
+
}
|
|
482
|
+
if (componentDefinition.remove && typeof componentDefinition.remove !== "function") {
|
|
483
|
+
throw new Error("remove must be a function");
|
|
484
|
+
}
|
|
485
|
+
if (componentDefinition.removed && typeof componentDefinition.removed !== "function") {
|
|
486
|
+
throw new Error("removed must be a function");
|
|
487
|
+
}
|
|
488
|
+
if (componentDefinition.$memo !== void 0 && typeof componentDefinition.$memo !== "boolean" && typeof componentDefinition.$memo !== "function") {
|
|
489
|
+
throw new Error("$memo must be a boolean or function");
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return Object.freeze({
|
|
493
|
+
is: VNODE_COMPDEF,
|
|
494
|
+
$tag: FLAG_IS_DEFINED | (componentDefinition.remove ? FLAG_HAS_REMOVE : 0) | (componentDefinition.removed ? FLAG_HAS_REMOVED : 0) | (componentDefinition.remove || componentDefinition.removed ? FLAG_HAS_LIFECYCLE : 0),
|
|
495
|
+
...componentDefinition
|
|
496
|
+
});
|
|
497
|
+
};
|
|
498
|
+
const comp = (componentDefinition, attrs = ALWAYS_EMPTY_OBJECT) => {
|
|
499
|
+
return {
|
|
500
|
+
is: VNODE_COMP,
|
|
501
|
+
$tag: componentDefinition.$tag || 0,
|
|
502
|
+
// number: flags
|
|
503
|
+
$def: componentDefinition,
|
|
504
|
+
// object: component definition
|
|
505
|
+
attrs: attrs === ALWAYS_EMPTY_OBJECT ? attrs : Object.freeze(attrs) ,
|
|
506
|
+
nodes: ALWAYS_EMPTY_ARRAY,
|
|
507
|
+
up: null,
|
|
508
|
+
el: null
|
|
509
|
+
};
|
|
510
|
+
};
|
|
511
|
+
const node = (el) => {
|
|
512
|
+
return {
|
|
513
|
+
is: VNODE_NODE,
|
|
514
|
+
$tag: el.nodeName.toLowerCase(),
|
|
515
|
+
// string: element name
|
|
516
|
+
$def: null,
|
|
517
|
+
// always null for node
|
|
518
|
+
attrs: null,
|
|
519
|
+
nodes: null,
|
|
520
|
+
up: null,
|
|
521
|
+
el
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
const RootWrapper = compDef({
|
|
525
|
+
draw: (vNode) => vNode.attrs.$nodes
|
|
526
|
+
});
|
|
527
|
+
const bind = (el, vNodeAnyOrArray) => {
|
|
528
|
+
if (el == null) throw new Error("mount");
|
|
529
|
+
const rootComp = boundNodes.get(el) || comp(RootWrapper);
|
|
530
|
+
rootComp.attrs = { $nodes: vNodeAnyOrArray };
|
|
531
|
+
boundNodes.set(rootComp.el = el, rootComp);
|
|
532
|
+
draw(rootComp);
|
|
533
|
+
sync();
|
|
534
|
+
return rootComp;
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
export { bind, comp, compDef, diff, draw, elem, node, ns, sync };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! Zenith v1.0.0-beta.14 | MIT License | © 2022-2025 Daniel Barber */
|
|
2
|
+
const e=new Map,t=new Set,ns={math:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg"},s=[],l={},n=new Map,sync=()=>{let s,l=t.size;if(l){const n=document.activeElement;for(const n of t)if(s=e.has(n.el)?n.el:n.attrs&&n.attrs.to||n.el&&n.el.parentNode||n.up&&n.up.el,t.delete(n),s&&g(n,s),! --l)break;t.size&&Promise.resolve().then(sync),n&&n!==document.activeElement&&"function"==typeof n.focus&&n.focus()}},draw=(e,s)=>{if(s)3===e.is&&(e.$tag|=2),t.add(e);else{let s;for(s=e;s.up;s=s.up);for(t.add(s),s=e;s.up;s=s.up)if(3===s.is){if(2&s.$tag)break;s.$tag|=2}}1===t.size&&Promise.resolve().then(sync)},r=[],o=(e,t=0,s=[])=>{let l;for(e&&"object"==typeof e&&null!=e.length||(r[0]=e,e=r);e.length>t;t++)if(l=e[t],Array.isArray(l))o(l,0,s);else if(null!=l)if(l.is){if(void 0===s.$keys)s.$keys=l.attrs&&"$key"in l.attrs?n:null;else if(!s.$keys!=!(l.attrs&&"$key"in l.attrs))throw Error("$keys");s.push(l)}else if(!0!==l&&!1!==l){if(void 0===s.$keys)s.$keys=null;else if(s.$keys)throw Error("$keys");s.push({is:2,$tag:l+"",$def:null,attrs:null,nodes:null,up:null,el:null})}return s},i=e=>{if(3===e.is&&!e.attrs.to){for(let t=e.nodes.length-1;t>=0;t--){const s=i(e.nodes[t]);if(s)return s}return null}return e.el},a=(e,t,s)=>{if(3!==e.is||e.attrs.to)e.el&&(e.el.nextSibling!==s&&t.insertBefore(e.el,s),s=e.el);else for(let l=e.nodes.length-1;l>=0;l--)s=a(e.nodes[l],t,s);return s},f=(e,t,s,l,n,r)=>{if(l.el){const o=s.is;if(o===l.is)if(1===o)if(s.up=e,s.$tag===l.$tag){s.el=l.el,s.$h=l.$h;const e=s.attrs.$diff||6;2&e&&$(s,l.attrs),4&e?d(s,s.el,s.nodes,l.nodes,s.attrs.xmlns||ns[s.$tag]||n):s.nodes=l.nodes}else u(e,t,s,n),t.insertBefore(s.el,l.el),h(l);else 2===o?(s.el=l.el,s.$tag!==l.$tag&&(s.el.textContent=s.$tag)):3===o?(4&l.$tag&&(l.$tag&=-5),s.up=e,s.$def===l.$def?(s.el=l.el,s.$h=l.$h,s.nodes=l.nodes,(r||2&l.$tag||!s.$def.$memo||(!0===s.$def.$memo?s.attrs!==l.attrs:!s.$def.$memo(s.attrs,l.attrs)))&&(s.$tag&=-3,g(s,t,n))):(u(e,t,s,n),h(l))):4===o&&s.el!==l.el&&(t.insertBefore(s.el,null),l.el.remove());else u(e,t,s,n),h(l)}else u(e,t,s,n)},d=(e,t,s,l,n,r=0)=>{const o=s.length,d=l.length;if(o){let r,$,g=0;if(d)if(s.$keys&&l.$keys){for(;o>g&&d>g&&s[g].attrs.$key===l[g].attrs.$key;)f(e,t,s[g],l[g],n),g++;if(o>g||d>g){const c=new Map;for(let e=g;d>e;e++)c.set(l[e].attrs.$key,l[e]);const m=i(l[d-1]);let p=m?m.nextSibling:null;for(let l=o-1;l>=g;l--){if($=s[l],r=c.get($.attrs.$key),r)if(c.delete($.attrs.$key),3===$.is&&$.attrs.$remove){if(!(4&r.$tag)&&h(r)){s.splice(l,1);continue}r.up=e,s[l]=r}else f(e,t,$,r,n);else{if($.attrs.$remove){s.splice(l,1);continue}u(e,t,$,n)}p=a(s[l],t,p)}for(const e of c.values())h(e)}}else{for(let e=o;d>e;e++)h(l[e]);for(let i=0;o>i;i++)r=l[i],$=s[i],r&&r.el?f(e,t,$,r,n):u(e,t,$,n)}else for(;o>g;g++)u(e,t,s[g],n)}else if(1===e.is)t.textContent="",m(l);else for(let e=0;d>e;e++)h(l[e]);e.nodes=s},$=(e,t=l)=>{let s,n;const r=e.el,o=e.attrs;for(s in"input"===e.$tag&&o.type&&r.setAttribute("type",o.type),o){const i=o[s];if("o"===s[0]&&"n"===s[1])i!==t[s]&&(r[s]="function"==typeof i?t=>{!1===i.call(t.currentTarget,t)&&(t.preventDefault(),t.stopPropagation()),!1!==t.draw&&draw(e)}:null);else if("$"!==s[0]&&("type"!==s||"input"!==e.$tag)&&"is"!==s&&"xmlns"!==s)if(n=typeof i,"string"===n||"number"===n)"value"===s?i!==t[s]&&(r.value=i):i!==t[s]&&r.setAttribute(s,i+"");else if(!0===i)!0!==t[s]&&r.setAttribute(s,s);else if(i){if("style"===s){if("string"==typeof t.style)r.removeAttribute("style");else for(const e in t.style||l)e in i||(r.style[e]="");Object.assign(r.style,i)}}else t[s]&&r.removeAttribute(s)}for(s in t)s in o||("o"===s[0]&&"n"===s[1]?r[s]=null:r.removeAttribute(s))},u=(e,t,s,l)=>{if(1===s.is){s.up=e,s.el=(l=s.attrs&&s.attrs.xmlns||ns[s.$tag]||l)?document.createElementNS(l,s.$tag,s.attrs.is?{is:s.attrs.is}:null):document.createElement(s.$tag,s.attrs.is?{is:s.attrs.is}:null),$(s);for(let e=0;s.nodes.length>e;e++)u(s,s.el,s.nodes[e],l);t.insertBefore(s.el,null)}else if(2===s.is)s.el=document.createTextNode(s.$tag),t.insertBefore(s.el,null);else if(3===s.is){if(s.up=e,s.$def.init&&s.$def.init(s),s.nodes=o(s.$def.draw(s)),8&s.$tag)for(let e=s;e&&!e.$h;e=e.up)e.$h=!0;s.attrs.to&&(s.el=document.createComment(""),t.insertBefore(s.el,null));for(let e=0;s.nodes.length>e;e++)u(s,s.attrs.to||t,s.nodes[e],l);s.el||(s.el=s.nodes[0]&&s.nodes[0].el),s.$def.drawn&&s.$def.drawn(s,!0)}else 4===s.is&&t.insertBefore(s.el,null)},g=(e,t,s)=>{const l=e.nodes,n=e.$def.draw(e,l);e.$tag&=-3,n!==l&&(d(e,e.attrs.to||t,o(n),l,s),e.el||(e.el=e.nodes[0]&&e.nodes[0].el)),e.$def.drawn&&e.$def.drawn(e)},c=[],m=e=>{Array.isArray(e)||(c[0]=e,e=c);for(let t=0;e.length>t;t++){const s=e[t];3===s.is&&((16&s.$tag||s.$def.remove)&&(64&s.$tag||s.$def.remove(s)),(32&s.$tag||s.$def.removed)&&s.$def.removed(s)),s.$h&&s.nodes.length&&(m(s.nodes),s.nodes=null),s.attrs=s.nodes=s.$tag=s.$def=s.el=s.up=null}},p=e=>{if(3===e.is){for(let t=0;e.nodes.length>t;t++)p(e.nodes[t]);e.attrs.to&&e.el&&e.el.remove()}else e.el&&e.el.remove()},y=e=>{p(e),m(e)},h=e=>{if(3===e.is&&(16&e.$tag||e.$def.remove)){if(4&e.$tag)return!1;e.$tag|=68;const t=e.$def.remove(e);if(t&&t.then){const s=()=>{if(4&e.$tag){const t=e.up;if(t){const s=t.nodes.indexOf(e);-1!==s&&t.nodes.splice(s,1)}y(e),draw(t)}};return t.then(s,s),!1}}return y(e),!0},compDef=e=>Object.freeze({is:5,$tag:1|(e.remove?16:0)|(e.removed?32:0)|(e.remove||e.removed?8:0),...e}),comp=(e,t=l)=>({is:3,$tag:e.$tag||0,$def:e,attrs:t,nodes:s,up:null,el:null}),v=compDef({draw:e=>e.attrs.$nodes});window.z={elem:function(e){const t=arguments[1],n=t&&"object"==typeof t&&!Array.isArray(t)&&!("$tag"in t);return{is:1,$tag:e,$def:null,attrs:n?t:l,nodes:arguments.length>(n?2:1)?o(arguments,n?2:1):s,up:null,el:null}},comp:comp,node:e=>({is:4,$tag:e.nodeName.toLowerCase(),$def:null,attrs:null,nodes:null,up:null,el:e}),compDef:compDef,bind:(t,s)=>{const l=e.get(t)||comp(v);return l.attrs={$nodes:s},e.set(l.el=t,l),draw(l),sync(),l},draw:draw,ns:ns,diff:{none:1,self:2,nodes:4,all:6}};
|
package/dist/zenith.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! Zenith v1.0.0-beta.14 | MIT License | © 2022-2025 Daniel Barber */
|
|
2
|
+
const e=new Map,t=new Set,ns={math:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg"},diff={none:1,self:2,nodes:4,all:6},s=[],l={},n=new Map,sync=()=>{let s,l=t.size;if(l){const n=document.activeElement;for(const n of t)if(s=e.has(n.el)?n.el:n.attrs&&n.attrs.to||n.el&&n.el.parentNode||n.up&&n.up.el,t.delete(n),s&&g(n,s),! --l)break;t.size&&Promise.resolve().then(sync),n&&n!==document.activeElement&&"function"==typeof n.focus&&n.focus()}},draw=(e,s)=>{if(s)3===e.is&&(e.$tag|=2),t.add(e);else{let s;for(s=e;s.up;s=s.up);for(t.add(s),s=e;s.up;s=s.up)if(3===s.is){if(2&s.$tag)break;s.$tag|=2}}1===t.size&&Promise.resolve().then(sync)},r=[],o=(e,t=0,s=[])=>{let l;for(e&&"object"==typeof e&&null!=e.length||(r[0]=e,e=r);e.length>t;t++)if(l=e[t],Array.isArray(l))o(l,0,s);else if(null!=l)if(l.is){if(void 0===s.$keys)s.$keys=l.attrs&&"$key"in l.attrs?n:null;else if(!s.$keys!=!(l.attrs&&"$key"in l.attrs))throw Error("$keys");s.push(l)}else if(!0!==l&&!1!==l){if(void 0===s.$keys)s.$keys=null;else if(s.$keys)throw Error("$keys");s.push({is:2,$tag:l+"",$def:null,attrs:null,nodes:null,up:null,el:null})}return s},i=e=>{if(3===e.is&&!e.attrs.to){for(let t=e.nodes.length-1;t>=0;t--){const s=i(e.nodes[t]);if(s)return s}return null}return e.el},a=(e,t,s)=>{if(3!==e.is||e.attrs.to)e.el&&(e.el.nextSibling!==s&&t.insertBefore(e.el,s),s=e.el);else for(let l=e.nodes.length-1;l>=0;l--)s=a(e.nodes[l],t,s);return s},f=(e,t,s,l,n,r)=>{if(l.el){const o=s.is;if(o===l.is)if(1===o)if(s.up=e,s.$tag===l.$tag){s.el=l.el,s.$h=l.$h;const e=s.attrs.$diff||6;2&e&&d(s,l.attrs),4&e?$(s,s.el,s.nodes,l.nodes,s.attrs.xmlns||ns[s.$tag]||n):s.nodes=l.nodes}else u(e,t,s,n),t.insertBefore(s.el,l.el),h(l);else 2===o?(s.el=l.el,s.$tag!==l.$tag&&(s.el.textContent=s.$tag)):3===o?(4&l.$tag&&(l.$tag&=-5),s.up=e,s.$def===l.$def?(s.el=l.el,s.$h=l.$h,s.nodes=l.nodes,(r||2&l.$tag||!s.$def.$memo||(!0===s.$def.$memo?s.attrs!==l.attrs:!s.$def.$memo(s.attrs,l.attrs)))&&(s.$tag&=-3,g(s,t,n))):(u(e,t,s,n),h(l))):4===o&&s.el!==l.el&&(t.insertBefore(s.el,null),l.el.remove());else u(e,t,s,n),h(l)}else u(e,t,s,n)},$=(e,t,s,l,n,r=0)=>{const o=s.length,$=l.length;if(o){let r,d,g=0;if($)if(s.$keys&&l.$keys){for(;o>g&&$>g&&s[g].attrs.$key===l[g].attrs.$key;)f(e,t,s[g],l[g],n),g++;if(o>g||$>g){const c=new Map;for(let e=g;$>e;e++)c.set(l[e].attrs.$key,l[e]);const p=i(l[$-1]);let m=p?p.nextSibling:null;for(let l=o-1;l>=g;l--){if(d=s[l],r=c.get(d.attrs.$key),r)if(c.delete(d.attrs.$key),3===d.is&&d.attrs.$remove){if(!(4&r.$tag)&&h(r)){s.splice(l,1);continue}r.up=e,s[l]=r}else f(e,t,d,r,n);else{if(d.attrs.$remove){s.splice(l,1);continue}u(e,t,d,n)}m=a(s[l],t,m)}for(const e of c.values())h(e)}}else{for(let e=o;$>e;e++)h(l[e]);for(let i=0;o>i;i++)r=l[i],d=s[i],r&&r.el?f(e,t,d,r,n):u(e,t,d,n)}else for(;o>g;g++)u(e,t,s[g],n)}else if(1===e.is)t.textContent="",p(l);else for(let e=0;$>e;e++)h(l[e]);e.nodes=s},d=(e,t=l)=>{let s,n;const r=e.el,o=e.attrs;for(s in"input"===e.$tag&&o.type&&r.setAttribute("type",o.type),o){const i=o[s];if("o"===s[0]&&"n"===s[1])i!==t[s]&&(r[s]="function"==typeof i?t=>{!1===i.call(t.currentTarget,t)&&(t.preventDefault(),t.stopPropagation()),!1!==t.draw&&draw(e)}:null);else if("$"!==s[0]&&("type"!==s||"input"!==e.$tag)&&"is"!==s&&"xmlns"!==s)if(n=typeof i,"string"===n||"number"===n)"value"===s?i!==t[s]&&(r.value=i):i!==t[s]&&r.setAttribute(s,i+"");else if(!0===i)!0!==t[s]&&r.setAttribute(s,s);else if(i){if("style"===s){if("string"==typeof t.style)r.removeAttribute("style");else for(const e in t.style||l)e in i||(r.style[e]="");Object.assign(r.style,i)}}else t[s]&&r.removeAttribute(s)}for(s in t)s in o||("o"===s[0]&&"n"===s[1]?r[s]=null:r.removeAttribute(s))},u=(e,t,s,l)=>{if(1===s.is){s.up=e,s.el=(l=s.attrs&&s.attrs.xmlns||ns[s.$tag]||l)?document.createElementNS(l,s.$tag,s.attrs.is?{is:s.attrs.is}:null):document.createElement(s.$tag,s.attrs.is?{is:s.attrs.is}:null),d(s);for(let e=0;s.nodes.length>e;e++)u(s,s.el,s.nodes[e],l);t.insertBefore(s.el,null)}else if(2===s.is)s.el=document.createTextNode(s.$tag),t.insertBefore(s.el,null);else if(3===s.is){if(s.up=e,s.$def.init&&s.$def.init(s),s.nodes=o(s.$def.draw(s)),8&s.$tag)for(let e=s;e&&!e.$h;e=e.up)e.$h=!0;s.attrs.to&&(s.el=document.createComment(""),t.insertBefore(s.el,null));for(let e=0;s.nodes.length>e;e++)u(s,s.attrs.to||t,s.nodes[e],l);s.el||(s.el=s.nodes[0]&&s.nodes[0].el),s.$def.drawn&&s.$def.drawn(s,!0)}else 4===s.is&&t.insertBefore(s.el,null)},g=(e,t,s)=>{const l=e.nodes,n=e.$def.draw(e,l);e.$tag&=-3,n!==l&&($(e,e.attrs.to||t,o(n),l,s),e.el||(e.el=e.nodes[0]&&e.nodes[0].el)),e.$def.drawn&&e.$def.drawn(e)},c=[],p=e=>{Array.isArray(e)||(c[0]=e,e=c);for(let t=0;e.length>t;t++){const s=e[t];3===s.is&&((16&s.$tag||s.$def.remove)&&(64&s.$tag||s.$def.remove(s)),(32&s.$tag||s.$def.removed)&&s.$def.removed(s)),s.$h&&s.nodes.length&&(p(s.nodes),s.nodes=null),s.attrs=s.nodes=s.$tag=s.$def=s.el=s.up=null}},m=e=>{if(3===e.is){for(let t=0;e.nodes.length>t;t++)m(e.nodes[t]);e.attrs.to&&e.el&&e.el.remove()}else e.el&&e.el.remove()},y=e=>{m(e),p(e)},h=e=>{if(3===e.is&&(16&e.$tag||e.$def.remove)){if(4&e.$tag)return!1;e.$tag|=68;const t=e.$def.remove(e);if(t&&t.then){const s=()=>{if(4&e.$tag){const t=e.up;if(t){const s=t.nodes.indexOf(e);-1!==s&&t.nodes.splice(s,1)}y(e),draw(t)}};return t.then(s,s),!1}}return y(e),!0},elem=function(e){const t=arguments[1],n=t&&"object"==typeof t&&!Array.isArray(t)&&!("$tag"in t);return{is:1,$tag:e,$def:null,attrs:n?t:l,nodes:arguments.length>(n?2:1)?o(arguments,n?2:1):s,up:null,el:null}},compDef=e=>Object.freeze({is:5,$tag:1|(e.remove?16:0)|(e.removed?32:0)|(e.remove||e.removed?8:0),...e}),comp=(e,t=l)=>({is:3,$tag:e.$tag||0,$def:e,attrs:t,nodes:s,up:null,el:null}),node=e=>({is:4,$tag:e.nodeName.toLowerCase(),$def:null,attrs:null,nodes:null,up:null,el:e}),v=compDef({draw:e=>e.attrs.$nodes}),bind=(t,s)=>{const l=e.get(t)||comp(v);return l.attrs={$nodes:s},e.set(l.el=t,l),draw(l),sync(),l};export{bind,comp,compDef,diff,draw,elem,node,ns,sync};
|
package/package.json
CHANGED
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
2
|
+
"name": "zenith",
|
|
3
|
+
"version": "1.0.0-beta.14",
|
|
4
|
+
"description": "Another front-end framework.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/zenith.js",
|
|
7
|
+
"module": "./dist/zenith.js",
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types/index.d.ts",
|
|
12
|
+
"default": "./dist/zenith.js"
|
|
13
|
+
},
|
|
14
|
+
"./helpers": {
|
|
15
|
+
"types": "./dist/types/helpers/index.d.ts",
|
|
16
|
+
"default": "./dist/zenith-helpers.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "rollup --config && tsc && node scripts/after-build.mjs",
|
|
25
|
+
"lint": "eslint . --ext .ts",
|
|
26
|
+
"test": "vitest --run --coverage --reporter verbose",
|
|
27
|
+
"test:ui": "vitest --ui",
|
|
28
|
+
"test:run": "vitest run"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/aleph1/zenith.git"
|
|
33
|
+
},
|
|
34
|
+
"author": "Daniel Barber / Aleph1",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/aleph1/Zenith/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/aleph1/Zenith#readme",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@rollup/plugin-replace": "^4.0.0",
|
|
42
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
43
|
+
"@typescript-eslint/eslint-plugin": "^8.71.2",
|
|
44
|
+
"@typescript-eslint/parser": "^8.71.2",
|
|
45
|
+
"@vitest/coverage-v8": "^4.0.14",
|
|
46
|
+
"@vitest/ui": "^4.0.14",
|
|
47
|
+
"brotli": "^1.3.3",
|
|
48
|
+
"eslint": "^8.57.1",
|
|
49
|
+
"jsdom": "^27.2.0",
|
|
50
|
+
"rollup": "^2.74.1",
|
|
51
|
+
"rollup-plugin-brotli": "^3.1.0",
|
|
52
|
+
"rollup-plugin-esbuild": "^6.2.1",
|
|
53
|
+
"typescript": "^5.9.3",
|
|
54
|
+
"vitest": "^4.0.14"
|
|
55
|
+
}
|
|
11
56
|
}
|
package/index.js
DELETED
package/readme.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Zenith is a WIP client side initialization module
|