valyrian.js 7.2.10 → 7.2.12
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/dist/dataset/index.d.ts +2 -2
- package/dist/dataset/index.d.ts.map +1 -1
- package/dist/dataset/index.js +21 -21
- package/dist/dataset/index.js.map +2 -2
- package/dist/dataset/index.mjs +22 -22
- package/dist/dataset/index.mjs.map +2 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +17 -32
- package/dist/hooks/index.js.map +3 -3
- package/dist/hooks/index.mjs +17 -32
- package/dist/hooks/index.mjs.map +3 -3
- package/dist/index.d.ts +49 -53
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +378 -326
- package/dist/index.js.map +3 -3
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.mjs +378 -326
- package/dist/index.mjs.map +3 -3
- package/dist/node/index.js +115 -88
- package/dist/node/index.js.map +2 -2
- package/dist/node/index.mjs +115 -88
- package/dist/node/index.mjs.map +2 -2
- package/dist/node/utils/tree-adapter.d.ts +5 -0
- package/dist/node/utils/tree-adapter.d.ts.map +1 -1
- package/dist/proxy-signal/index.js +10 -10
- package/dist/proxy-signal/index.js.map +2 -2
- package/dist/proxy-signal/index.mjs +10 -10
- package/dist/proxy-signal/index.mjs.map +2 -2
- package/dist/request/index.js +16 -16
- package/dist/request/index.js.map +2 -2
- package/dist/request/index.mjs +16 -16
- package/dist/request/index.mjs.map +2 -2
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +21 -20
- package/dist/router/index.js.map +2 -2
- package/dist/router/index.mjs +21 -20
- package/dist/router/index.mjs.map +2 -2
- package/dist/signal/index.d.ts +7 -18
- package/dist/signal/index.d.ts.map +1 -1
- package/dist/signal/index.js +29 -48
- package/dist/signal/index.js.map +3 -3
- package/dist/signal/index.mjs +31 -50
- package/dist/signal/index.mjs.map +3 -3
- package/dist/store/index.js +2 -2
- package/dist/store/index.js.map +2 -2
- package/dist/store/index.mjs +2 -2
- package/dist/store/index.mjs.map +2 -2
- package/lib/dataset/index.ts +25 -25
- package/lib/hooks/index.ts +25 -54
- package/lib/index.ts +465 -715
- package/lib/node/index.ts +2 -2
- package/lib/node/utils/icons.ts +5 -5
- package/lib/node/utils/inline.ts +17 -17
- package/lib/node/utils/sw.ts +3 -3
- package/lib/node/utils/tree-adapter.ts +95 -62
- package/lib/proxy-signal/index.ts +10 -10
- package/lib/request/index.ts +16 -16
- package/lib/router/index.ts +21 -20
- package/lib/signal/index.ts +56 -131
- package/lib/store/index.ts +2 -2
- package/package.json +10 -3
- package/lib/index.d.ts +0 -0
- package/lib/interfaces.ts.bak +0 -141
package/lib/index.ts
CHANGED
|
@@ -1,369 +1,181 @@
|
|
|
1
|
-
/* eslint-disable no-use-before-define */
|
|
2
|
-
/* eslint-disable indent */
|
|
3
1
|
/* eslint-disable sonarjs/cognitive-complexity */
|
|
4
|
-
|
|
2
|
+
declare global {
|
|
3
|
+
// eslint-disable-next-line vars-on-top, no-var, no-unused-vars
|
|
4
|
+
var document: Document;
|
|
5
|
+
// eslint-disable-next-line no-unused-vars
|
|
6
|
+
namespace JSX {
|
|
7
|
+
// eslint-disable-next-line no-unused-vars, no-use-before-define
|
|
8
|
+
interface IntrinsicElements extends DefaultRecord {}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
5
11
|
|
|
6
12
|
interface DefaultRecord extends Record<string | number | symbol, any> {}
|
|
7
13
|
|
|
8
|
-
// The VnodeProperties interface represents properties that can be passed to a virtual node.
|
|
9
14
|
export interface VnodeProperties extends DefaultRecord {
|
|
10
|
-
// A unique key for the virtual node, which can be a string or a number.
|
|
11
|
-
// This is useful for optimizing updates in a list of nodes.
|
|
12
15
|
key?: string | number;
|
|
13
|
-
// A state object that is associated with the virtual node.
|
|
14
|
-
state?: any;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
// The DomElement interface extends the Element interface with an index signature.
|
|
18
|
-
// This allows for any additional properties to be added to DOM elements.
|
|
19
18
|
export interface DomElement extends Element, DefaultRecord {}
|
|
20
19
|
|
|
21
|
-
// The VnodeInterface represents a virtual node. It has a number of optional fields,
|
|
22
|
-
// including a tag, props, children, and a DOM element.
|
|
23
|
-
export interface VnodeInterface extends DefaultRecord {
|
|
24
|
-
// The constructor for the virtual node. It takes a tag, props, and children as arguments.
|
|
25
|
-
// The tag can be a string, a component, or a POJO component.
|
|
26
|
-
// eslint-disable-next-line no-unused-vars
|
|
27
|
-
new (tag: string | Component | POJOComponent, props: VnodeProperties, children: Children): VnodeInterface;
|
|
28
|
-
// The tag for the virtual node. It can be a string, a component, or a POJO component.
|
|
29
|
-
tag: string | Component | POJOComponent;
|
|
30
|
-
// The props for the virtual node.
|
|
31
|
-
props: VnodeProperties;
|
|
32
|
-
// The children for the virtual node.
|
|
33
|
-
children: Children;
|
|
34
|
-
// A boolean indicating whether the virtual node is an SVG element.
|
|
35
|
-
isSVG?: boolean;
|
|
36
|
-
// The DOM element that corresponds to the virtual node.
|
|
37
|
-
dom?: DomElement;
|
|
38
|
-
// A boolean indicating whether the virtual node has been processed in the keyed diffing algorithm.
|
|
39
|
-
processed?: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// The VnodeWithDom interface represents a virtual node that has a DOM element associated with it.
|
|
43
|
-
export interface VnodeWithDom extends VnodeInterface {
|
|
44
|
-
dom: DomElement;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// The Component interface represents a function that returns a virtual node or a list of virtual nodes.
|
|
48
|
-
// It can also have additional properties.
|
|
49
20
|
export interface Component extends DefaultRecord {
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
// eslint-disable-next-line no-unused-vars
|
|
53
|
-
(props?: VnodeProperties | null, ...children: any[]): VnodeInterface | Children | any;
|
|
21
|
+
// eslint-disable-next-line no-unused-vars, no-use-before-define
|
|
22
|
+
(props: VnodeProperties, children: any[]): Vnode | any;
|
|
54
23
|
}
|
|
55
24
|
|
|
56
|
-
// The POJOComponent interface represents a "plain old JavaScript object" (POJO) component.
|
|
57
|
-
// It has a view function that returns a virtual node or a list of virtual nodes,
|
|
58
|
-
// as well as optional props and children.
|
|
59
|
-
// It can be used also to identify class instance components.
|
|
60
25
|
export interface POJOComponent extends DefaultRecord {
|
|
61
|
-
// The view function that returns a virtual node or a list of virtual nodes.
|
|
62
26
|
view: Component;
|
|
63
|
-
// The props for the component.
|
|
64
|
-
props?: VnodeProperties | null;
|
|
65
|
-
// The children for the component.
|
|
66
|
-
children?: any[];
|
|
67
27
|
}
|
|
68
28
|
|
|
69
|
-
|
|
70
|
-
// It has props and children, just like a regular virtual node.
|
|
71
|
-
export interface VnodeComponentInterface extends VnodeInterface {
|
|
72
|
-
tag: Component | POJOComponent;
|
|
73
|
-
props: VnodeProperties;
|
|
74
|
-
children: Children;
|
|
75
|
-
}
|
|
29
|
+
export type ValyrianComponent = Component | POJOComponent;
|
|
76
30
|
|
|
77
|
-
//
|
|
78
|
-
export interface
|
|
79
|
-
|
|
80
|
-
// The Directive interface represents a function that can be applied to a virtual node.
|
|
81
|
-
// It receives the value, virtual node, and old virtual node as arguments, and can return a boolean value.
|
|
82
|
-
// If only the virtual node is passed, it means its the on create phase for the v-node.
|
|
83
|
-
// If the old virtual node is also passed, it means its the on update phase for the v-node.
|
|
84
|
-
export interface Directive {
|
|
85
|
-
// eslint-disable-next-line no-unused-vars
|
|
86
|
-
(value: any, vnode: VnodeWithDom, oldVnode?: VnodeWithDom): void | boolean;
|
|
31
|
+
// eslint-disable-next-line no-use-before-define
|
|
32
|
+
export interface VnodeComponentInterface extends Vnode {
|
|
33
|
+
tag: ValyrianComponent;
|
|
87
34
|
}
|
|
88
35
|
|
|
89
|
-
//
|
|
90
|
-
export interface
|
|
91
|
-
|
|
92
|
-
// The ReservedProps interface is a mapping of reserved prop names to the value `true`.
|
|
93
|
-
// These prop names cannot be used as custom prop names.
|
|
94
|
-
export interface ReservedProps extends Record<string, true> {}
|
|
95
|
-
|
|
96
|
-
// The Current interface represents the current component and virtual node that are being processed.
|
|
97
|
-
export interface Current {
|
|
98
|
-
// The current component. It can be a component, a POJO component, or null.
|
|
99
|
-
component: Component | POJOComponent | null;
|
|
100
|
-
// The current virtual node. It must have a DOM element associated with it.
|
|
101
|
-
vnode: VnodeWithDom | null;
|
|
102
|
-
// The old virtual node. It must have a DOM element associated with it.
|
|
103
|
-
oldVnode?: VnodeWithDom | null;
|
|
104
|
-
// The current event. It can be an event or null.
|
|
105
|
-
event: Event | null;
|
|
106
|
-
}
|
|
36
|
+
// eslint-disable-next-line no-use-before-define
|
|
37
|
+
export interface Children extends Array<Vnode | VnodeComponentInterface | ValyrianComponent | any> {}
|
|
107
38
|
|
|
108
|
-
|
|
109
|
-
// It takes a tag or component, props, and children as arguments, and returns a virtual node.
|
|
110
|
-
export interface V {
|
|
111
|
-
// eslint-disable-next-line no-unused-vars, no-use-before-define
|
|
112
|
-
(tagOrComponent: string | Component | POJOComponent, props: VnodeProperties | null, ...children: Children):
|
|
113
|
-
| VnodeInterface
|
|
114
|
-
| VnodeComponentInterface;
|
|
39
|
+
export interface Directive {
|
|
115
40
|
// eslint-disable-next-line no-unused-vars, no-use-before-define
|
|
116
|
-
|
|
41
|
+
(value: any, vnode: VnodeWithDom, oldProps: VnodeProperties | null): void | boolean;
|
|
117
42
|
}
|
|
118
43
|
|
|
119
|
-
|
|
120
|
-
const textTag = "#text";
|
|
44
|
+
export const isNodeJs = Boolean(typeof process !== "undefined" && process.versions && process.versions.node);
|
|
121
45
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
46
|
+
export class Vnode {
|
|
47
|
+
constructor(
|
|
48
|
+
// eslint-disable-next-line no-unused-vars
|
|
49
|
+
public tag: string | Component | POJOComponent,
|
|
50
|
+
// eslint-disable-next-line no-unused-vars
|
|
51
|
+
public props: null | VnodeProperties,
|
|
52
|
+
// eslint-disable-next-line no-unused-vars
|
|
53
|
+
public children: Children,
|
|
54
|
+
// eslint-disable-next-line no-unused-vars
|
|
55
|
+
public dom?: DomElement,
|
|
56
|
+
// eslint-disable-next-line no-unused-vars
|
|
57
|
+
public isSVG?: boolean
|
|
58
|
+
) {}
|
|
130
59
|
}
|
|
131
60
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
export const Vnode = function Vnode(this: VnodeInterface, tag: string, props: VnodeProperties, children: Children) {
|
|
137
|
-
// 'this' refers to the current instance of 'Vnode'.
|
|
138
|
-
this.tag = tag;
|
|
139
|
-
this.props = props;
|
|
140
|
-
this.children = children;
|
|
141
|
-
} as unknown as VnodeInterface;
|
|
142
|
-
|
|
143
|
-
// 'isComponent' is a function that returns true if the given 'component' is a valid component and false otherwise.
|
|
144
|
-
// A component is either a function or an object with a 'view' function.
|
|
145
|
-
export function isComponent(component: unknown): component is Component {
|
|
146
|
-
return Boolean(
|
|
147
|
-
component && (typeof component === "function" || (typeof component === "object" && "view" in component))
|
|
148
|
-
);
|
|
61
|
+
export interface VnodeWithDom extends Vnode {
|
|
62
|
+
tag: string;
|
|
63
|
+
dom: DomElement;
|
|
64
|
+
props: VnodeProperties;
|
|
149
65
|
}
|
|
150
66
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return object instanceof Vnode;
|
|
155
|
-
};
|
|
67
|
+
export const isComponent = (component: unknown): component is Component =>
|
|
68
|
+
Boolean(typeof component === "function" || (component && typeof component === "object" && "view" in component));
|
|
69
|
+
export const isVnode = (object?: unknown): object is Vnode => object instanceof Vnode;
|
|
156
70
|
|
|
157
|
-
|
|
158
|
-
// It returns false otherwise.
|
|
159
|
-
export const isVnodeComponent = (object?: unknown | VnodeComponentInterface): object is VnodeComponentInterface => {
|
|
160
|
-
// Check if 'object' is a 'Vnode' instance and its 'tag' property is a valid component.
|
|
71
|
+
export const isVnodeComponent = (object?: unknown): object is VnodeComponentInterface => {
|
|
161
72
|
return isVnode(object) && isComponent(object.tag);
|
|
162
73
|
};
|
|
163
74
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
75
|
+
export function v(tagOrComponent: string | Component, props: VnodeProperties, ...children: Children) {
|
|
76
|
+
return new Vnode(tagOrComponent, props, children);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
v.fragment = (_: VnodeProperties, ...children: Children) => children;
|
|
80
|
+
|
|
81
|
+
export function domToVnode(dom: any): VnodeWithDom | void {
|
|
169
82
|
if (dom.nodeType === 3) {
|
|
170
|
-
|
|
171
|
-
vnode.dom = dom;
|
|
172
|
-
return vnode as VnodeWithDom;
|
|
83
|
+
return dom.nodeValue;
|
|
173
84
|
}
|
|
174
85
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
86
|
+
if (dom.nodeType === 1) {
|
|
87
|
+
const tag = dom.nodeName.toLowerCase();
|
|
88
|
+
const props = {} as VnodeProperties;
|
|
89
|
+
const children = [] as Children;
|
|
90
|
+
|
|
91
|
+
for (let i = 0, l = dom.childNodes.length; i < l; i++) {
|
|
92
|
+
const childDom = dom.childNodes[i];
|
|
93
|
+
if (childDom.nodeType === 3) {
|
|
94
|
+
children.push(childDom.nodeValue);
|
|
95
|
+
} else if (childDom.nodeType === 1) {
|
|
96
|
+
const childVnode = domToVnode(childDom);
|
|
97
|
+
children.push(childVnode);
|
|
98
|
+
}
|
|
183
99
|
}
|
|
184
|
-
}
|
|
185
100
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
props[attr.nodeName] = attr.nodeValue;
|
|
192
|
-
}
|
|
101
|
+
const attributes = dom.attributes;
|
|
102
|
+
for (let i = 0, l = attributes.length; i < l; i++) {
|
|
103
|
+
const attr = attributes[i];
|
|
104
|
+
props[attr.nodeName] = attr.nodeValue;
|
|
105
|
+
}
|
|
193
106
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// Set the 'dom' property of the 'Vnode' instance to the DOM node.
|
|
197
|
-
let vnode = new Vnode(dom.tagName.toLowerCase(), props, children);
|
|
198
|
-
vnode.dom = dom;
|
|
199
|
-
return vnode as VnodeWithDom;
|
|
107
|
+
return new Vnode(tag, props, children, dom, tag === "svg") as VnodeWithDom;
|
|
108
|
+
}
|
|
200
109
|
}
|
|
201
110
|
|
|
202
|
-
// This function takes in an HTML string and creates a virtual node representation of it
|
|
203
|
-
// using the `domToVnode` function. It does this by creating a new `div` element, setting
|
|
204
|
-
// its `innerHTML` to the provided HTML string, and then using `map` to iterate over the
|
|
205
|
-
// `childNodes` of the `div` element, passing each one to `domToVnode` to create a virtual
|
|
206
|
-
// node representation of it. The resulting array of virtual nodes is then returned.
|
|
207
111
|
export function trust(htmlString: string) {
|
|
208
|
-
|
|
112
|
+
const div = document.createElement("div");
|
|
209
113
|
div.innerHTML = htmlString.trim();
|
|
210
|
-
|
|
211
|
-
return [].map.call(div.childNodes, (item) => domToVnode(item));
|
|
114
|
+
return Array.from(div.childNodes).map(domToVnode);
|
|
212
115
|
}
|
|
213
116
|
|
|
214
|
-
/* ========================================================================== */
|
|
215
|
-
/* Main Component implementation */
|
|
216
|
-
/* ========================================================================== */
|
|
217
|
-
|
|
218
|
-
// These variables are used to store the main component, the main virtual node, and whether
|
|
219
|
-
// the main component is currently mounted.
|
|
220
117
|
let mainComponent: VnodeComponentInterface | null = null;
|
|
221
118
|
let mainVnode: VnodeWithDom | null = null;
|
|
222
119
|
let isMounted = false;
|
|
223
120
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
component: null,
|
|
229
|
-
event: null
|
|
121
|
+
export const current = {
|
|
122
|
+
vnode: null as Vnode | null,
|
|
123
|
+
component: null as ValyrianComponent | null,
|
|
124
|
+
event: null as Event | null
|
|
230
125
|
};
|
|
231
126
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
"v-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
"v-
|
|
242
|
-
"v-
|
|
243
|
-
"v-
|
|
244
|
-
"v-
|
|
245
|
-
"v-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
const
|
|
258
|
-
const onUpdateSet: Set<Function> = new Set();
|
|
259
|
-
const onUnmountSet: Set<Function> = new Set();
|
|
260
|
-
|
|
261
|
-
// These functions allow users to register callbacks for the corresponding lifecycle events.
|
|
262
|
-
export function onMount(callback: Function) {
|
|
263
|
-
if (!isMounted) {
|
|
264
|
-
onMountSet.add(callback);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export function onUpdate(callback: Function) {
|
|
269
|
-
onUpdateSet.add(callback);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export function onCleanup(callback: Function) {
|
|
273
|
-
onCleanupSet.add(callback);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
export function onUnmount(callback: Function) {
|
|
277
|
-
if (!isMounted) {
|
|
278
|
-
onUnmountSet.add(callback);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// This function is used to call all the callbacks in a given set.
|
|
283
|
-
function callSet(set: Set<Function>) {
|
|
284
|
-
for (let callback of set) {
|
|
127
|
+
export const reservedProps = new Set<string>([
|
|
128
|
+
"key",
|
|
129
|
+
"state",
|
|
130
|
+
"v-keep",
|
|
131
|
+
"v-text",
|
|
132
|
+
"v-if",
|
|
133
|
+
"v-for",
|
|
134
|
+
"v-show",
|
|
135
|
+
"v-class",
|
|
136
|
+
"v-html",
|
|
137
|
+
"v-model",
|
|
138
|
+
"v-create",
|
|
139
|
+
"v-update",
|
|
140
|
+
"v-cleanup"
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
const onCleanupSet = new Set<Function>();
|
|
144
|
+
const onMountSet = new Set<Function>();
|
|
145
|
+
const onUpdateSet = new Set<Function>();
|
|
146
|
+
const onUnmountSet = new Set<Function>();
|
|
147
|
+
export const onMount = (callback: Function) => !isMounted && onMountSet.add(callback);
|
|
148
|
+
export const onUpdate = (callback: Function) => onUpdateSet.add(callback);
|
|
149
|
+
export const onCleanup = (callback: Function) => onCleanupSet.add(callback);
|
|
150
|
+
export const onUnmount = (callback: Function) => !isMounted && onUnmountSet.add(callback);
|
|
151
|
+
const callSet = (set: Set<Function>) => {
|
|
152
|
+
for (const callback of set) {
|
|
285
153
|
callback();
|
|
286
154
|
}
|
|
287
|
-
|
|
288
155
|
set.clear();
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/* Event listener ----------------------------------------------------------- */
|
|
292
|
-
|
|
293
|
-
// This object stores the names of event listeners that have been added
|
|
294
|
-
const eventListenerNames: Record<string, true> = {};
|
|
295
|
-
|
|
296
|
-
// This function is called when an event occurs
|
|
297
|
-
function eventListener(e: Event) {
|
|
298
|
-
// Set the current event to the event that occurred so that it can be prevented if necessary
|
|
299
|
-
current.event = e;
|
|
300
|
-
|
|
301
|
-
// Convert the target of the event to a DOM element
|
|
302
|
-
let dom = e.target as DomElement;
|
|
303
|
-
|
|
304
|
-
// Create the name of the event listener by adding "v-on" to the event type
|
|
305
|
-
let name = `v-on${e.type}`;
|
|
306
|
-
|
|
307
|
-
// Keep going up the DOM tree until we find an element with an event listener
|
|
308
|
-
// matching the event type
|
|
309
|
-
while (dom) {
|
|
310
|
-
if (dom[name]) {
|
|
311
|
-
// Call the event listener function
|
|
312
|
-
dom[name](e, dom);
|
|
156
|
+
};
|
|
313
157
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
158
|
+
const handleVIf = (shouldRender: boolean): Directive => {
|
|
159
|
+
return (value, vnode) => {
|
|
160
|
+
const bool = shouldRender !== Boolean(value);
|
|
161
|
+
if (bool) {
|
|
162
|
+
const parentNode = vnode.dom?.parentNode;
|
|
163
|
+
if (parentNode) {
|
|
164
|
+
const newdom = document.createTextNode("");
|
|
165
|
+
parentNode.replaceChild(newdom, vnode.dom);
|
|
317
166
|
}
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
dom = dom.parentNode as DomElement;
|
|
321
|
-
}
|
|
322
167
|
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/* Directives --------------------------------------------------------------- */
|
|
327
|
-
|
|
328
|
-
// This function creates a directive that hides an element based on a condition
|
|
329
|
-
let hideDirective = (test: boolean) => (bool: boolean, vnode: VnodeInterface, oldnode?: VnodeInterface) => {
|
|
330
|
-
// If test is true, use the value of bool. Otherwise, use the opposite of bool.
|
|
331
|
-
let value = test ? bool : !bool;
|
|
332
|
-
|
|
333
|
-
// If the value is true, hide the element by replacing it with a text node
|
|
334
|
-
if (value) {
|
|
335
|
-
let newdom = document.createTextNode("");
|
|
336
|
-
if (oldnode && oldnode.dom && oldnode.dom.parentNode) {
|
|
337
|
-
oldnode.dom.parentNode.replaceChild(newdom, oldnode.dom);
|
|
168
|
+
return false;
|
|
338
169
|
}
|
|
339
|
-
|
|
340
|
-
vnode.children = [];
|
|
341
|
-
vnode.props = {};
|
|
342
|
-
vnode.dom = newdom as unknown as DomElement;
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
170
|
+
};
|
|
345
171
|
};
|
|
346
172
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
"v-if": hideDirective(false),
|
|
173
|
+
export const directives: Record<string, Directive> = {
|
|
174
|
+
"v-if": handleVIf(true),
|
|
175
|
+
"v-unless": handleVIf(false),
|
|
351
176
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
// The "v-for" directive creates a loop and applies a callback function to each item in the loop
|
|
356
|
-
"v-for": (set: unknown[], vnode: VnodeWithDom) => {
|
|
357
|
-
let newChildren: VnodeInterface[] = [];
|
|
358
|
-
let callback = vnode.children[0];
|
|
359
|
-
for (let i = 0, l = set.length; i < l; i++) {
|
|
360
|
-
newChildren.push(callback(set[i], i));
|
|
361
|
-
}
|
|
362
|
-
vnode.children = newChildren;
|
|
363
|
-
},
|
|
364
|
-
|
|
365
|
-
// The "v-show" directive shows or hides an element by setting the "display" style property
|
|
366
|
-
"v-show": (bool: boolean, vnode: VnodeWithDom) => {
|
|
177
|
+
"v-show": (value, vnode) => {
|
|
178
|
+
const bool = Boolean(value);
|
|
367
179
|
(
|
|
368
180
|
vnode.dom as unknown as {
|
|
369
181
|
style: { display: string };
|
|
@@ -371,23 +183,14 @@ export const directives: Directives = {
|
|
|
371
183
|
).style.display = bool ? "" : "none";
|
|
372
184
|
},
|
|
373
185
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
// Loop through all the class names in the classes object
|
|
377
|
-
for (let name in classes) {
|
|
378
|
-
// Add or remove the class name from the element's class list based on the value in the classes object
|
|
379
|
-
(vnode.dom as DomElement).classList.toggle(name, classes[name]);
|
|
380
|
-
}
|
|
381
|
-
},
|
|
382
|
-
|
|
383
|
-
// The "v-html" directive sets the inner HTML of an element to the given HTML string
|
|
384
|
-
"v-html": (html: string, vnode: VnodeWithDom) => {
|
|
385
|
-
// Set the children of the vnode to a trusted version of the HTML string
|
|
386
|
-
vnode.children = [trust(html)];
|
|
186
|
+
"v-html": (value, vnode) => {
|
|
187
|
+
vnode.children = [trust(value as string)];
|
|
387
188
|
},
|
|
388
189
|
|
|
389
190
|
// The "v-model" directive binds the value of an input element to a model property
|
|
390
|
-
"v-model": (
|
|
191
|
+
"v-model": (val, vnode) => {
|
|
192
|
+
// eslint-disable-next-line prefer-const
|
|
193
|
+
let [model, property, event]: any[] = val as any[];
|
|
391
194
|
let value;
|
|
392
195
|
// This function updates the model property when the input element's value changes
|
|
393
196
|
let handler = (e: Event) => (model[property] = (e.target as DomElement & Record<string, any>).value);
|
|
@@ -400,8 +203,8 @@ export const directives: Directives = {
|
|
|
400
203
|
if (Array.isArray(model[property])) {
|
|
401
204
|
// If the model property is an array, add or remove the value from the array when the checkbox is checked or unchecked
|
|
402
205
|
handler = (e: Event) => {
|
|
403
|
-
|
|
404
|
-
|
|
206
|
+
const val = (e.target as DomElement & Record<string, any>).value;
|
|
207
|
+
const idx = model[property].indexOf(val);
|
|
405
208
|
if (idx === -1) {
|
|
406
209
|
model[property].push(val);
|
|
407
210
|
} else {
|
|
@@ -448,10 +251,10 @@ export const directives: Directives = {
|
|
|
448
251
|
if (vnode.props.multiple) {
|
|
449
252
|
// If the select element allows multiple selections, update the model property with an array of selected values
|
|
450
253
|
handler = (e: Event & Record<string, any>) => {
|
|
451
|
-
|
|
254
|
+
const val = (e.target as DomElement & Record<string, any>).value;
|
|
452
255
|
if (e.ctrlKey) {
|
|
453
256
|
// If the Ctrl key is pressed, add or remove the value from the array
|
|
454
|
-
|
|
257
|
+
const idx = model[property].indexOf(val);
|
|
455
258
|
if (idx === -1) {
|
|
456
259
|
model[property].push(val);
|
|
457
260
|
} else {
|
|
@@ -464,17 +267,17 @@ export const directives: Directives = {
|
|
|
464
267
|
}
|
|
465
268
|
};
|
|
466
269
|
// Set the "selected" attribute on the options based on whether they are in the model property array
|
|
467
|
-
vnode.children.forEach((child:
|
|
270
|
+
vnode.children.forEach((child: VnodeWithDom) => {
|
|
468
271
|
if (child.tag === "option") {
|
|
469
|
-
|
|
272
|
+
const value = "value" in child.props ? child.props.value : child.children.join("").trim();
|
|
470
273
|
child.props.selected = model[property].indexOf(value) !== -1;
|
|
471
274
|
}
|
|
472
275
|
});
|
|
473
276
|
} else {
|
|
474
277
|
// If the select element does not allow multiple selections, set the "selected" attribute on the options based on the value of the model property
|
|
475
|
-
vnode.children.forEach((child:
|
|
278
|
+
vnode.children.forEach((child: VnodeWithDom) => {
|
|
476
279
|
if (child.tag === "option") {
|
|
477
|
-
|
|
280
|
+
const value = "value" in child.props ? child.props.value : child.children.join("").trim();
|
|
478
281
|
child.props.selected = value === model[property];
|
|
479
282
|
}
|
|
480
283
|
});
|
|
@@ -487,7 +290,7 @@ export const directives: Directives = {
|
|
|
487
290
|
}
|
|
488
291
|
|
|
489
292
|
// We assume that the prev handler if any will not be changed by the user across patchs
|
|
490
|
-
|
|
293
|
+
const prevHandler = vnode.props[event];
|
|
491
294
|
|
|
492
295
|
// Set the event handler on the element
|
|
493
296
|
// eslint-disable-next-line no-use-before-define
|
|
@@ -501,524 +304,471 @@ export const directives: Directives = {
|
|
|
501
304
|
prevHandler(e);
|
|
502
305
|
}
|
|
503
306
|
},
|
|
504
|
-
vnode
|
|
505
|
-
oldVnode
|
|
307
|
+
vnode
|
|
506
308
|
);
|
|
507
309
|
},
|
|
508
310
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
// eslint-disable-next-line no-unused-vars
|
|
513
|
-
"v-create": (callback: (vnode: VnodeWithDom) => void, vnode: VnodeWithDom, oldVnode?: VnodeWithDom) => {
|
|
514
|
-
// If this is not an update, call the callback function with the new virtual node
|
|
515
|
-
if (!oldVnode) {
|
|
516
|
-
let cleanup = callback(vnode);
|
|
311
|
+
"v-create": (callback, vnode, oldProps) => {
|
|
312
|
+
if (!oldProps) {
|
|
313
|
+
const cleanup = callback(vnode);
|
|
517
314
|
|
|
518
|
-
// If the callback function returns a function, call it when the update is gonna be cleaned up
|
|
519
315
|
if (typeof cleanup === "function") {
|
|
520
316
|
onCleanup(cleanup);
|
|
521
317
|
}
|
|
522
318
|
}
|
|
523
319
|
},
|
|
524
320
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
// eslint-disable-next-line no-unused-vars
|
|
530
|
-
callback: (vnode: VnodeWithDom, oldVnode: VnodeWithDom) => void,
|
|
531
|
-
vnode: VnodeWithDom,
|
|
532
|
-
oldVnode?: VnodeWithDom
|
|
533
|
-
) => {
|
|
534
|
-
// If this is an update, call the callback function with the new and old virtual nodes
|
|
535
|
-
if (oldVnode) {
|
|
536
|
-
let cleanup = callback(vnode, oldVnode);
|
|
537
|
-
|
|
538
|
-
// If the callback function returns a function, call it when the update is gonna be cleaned up
|
|
321
|
+
"v-update": (callback, vnode, oldProps) => {
|
|
322
|
+
if (oldProps) {
|
|
323
|
+
const cleanup = callback(vnode, oldProps);
|
|
324
|
+
|
|
539
325
|
if (typeof cleanup === "function") {
|
|
540
326
|
onCleanup(cleanup);
|
|
541
327
|
}
|
|
542
328
|
}
|
|
543
329
|
},
|
|
544
330
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
331
|
+
"v-cleanup": (callback, vnode) => {
|
|
332
|
+
onCleanup(() => callback(vnode));
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
"v-class": (value, vnode) => {
|
|
336
|
+
if (typeof value === "string") {
|
|
337
|
+
vnode.dom.className = value;
|
|
338
|
+
} else if (Array.isArray(value)) {
|
|
339
|
+
vnode.dom.className = value.join(" ");
|
|
340
|
+
} else if (typeof value === "object") {
|
|
341
|
+
const classList = vnode.dom.classList;
|
|
342
|
+
for (const name in value) {
|
|
343
|
+
const val = typeof value[name] === "function" ? (value[name] as Function)() : value[name];
|
|
344
|
+
classList.toggle(name, val);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
// Frequent used properties
|
|
350
|
+
class(value, vnode) {
|
|
351
|
+
if (vnode.dom.className !== value) {
|
|
352
|
+
vnode.dom.className = value;
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
className(value, vnode) {
|
|
357
|
+
directives.class(value, vnode, null);
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
id: (value, vnode) => {
|
|
361
|
+
vnode.dom.id = value;
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
style: (value, vnode) => {
|
|
365
|
+
if (typeof value === "string") {
|
|
366
|
+
vnode.dom.style = value;
|
|
367
|
+
} else if (typeof value === "object") {
|
|
368
|
+
vnode.dom.style = "";
|
|
369
|
+
const domStyle = vnode.dom.style;
|
|
370
|
+
for (const name in value) {
|
|
371
|
+
domStyle[name] = value[name];
|
|
372
|
+
}
|
|
373
|
+
}
|
|
556
374
|
}
|
|
557
375
|
};
|
|
558
|
-
|
|
559
|
-
// preceded by "v-". Also add the name to the global reservedProps object.
|
|
376
|
+
|
|
560
377
|
export function directive(name: string, directive: Directive) {
|
|
561
|
-
|
|
378
|
+
const directiveName = `v-${name}`;
|
|
562
379
|
directives[directiveName] = directive;
|
|
563
|
-
reservedProps
|
|
380
|
+
reservedProps.add(directiveName);
|
|
564
381
|
}
|
|
565
382
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
383
|
+
const eventListenerNames = new Set<string>();
|
|
384
|
+
|
|
385
|
+
function eventListener(e: Event) {
|
|
386
|
+
current.event = e;
|
|
387
|
+
let dom = e.target as DomElement;
|
|
388
|
+
const name = `on${e.type}`;
|
|
389
|
+
|
|
390
|
+
while (dom) {
|
|
391
|
+
const oldProps = dom.props;
|
|
392
|
+
if (oldProps && oldProps[name]) {
|
|
393
|
+
oldProps[name](e, dom);
|
|
394
|
+
|
|
395
|
+
if (!e.defaultPrevented) {
|
|
396
|
+
// eslint-disable-next-line no-use-before-define
|
|
397
|
+
update();
|
|
398
|
+
}
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
dom = dom.parentNode as DomElement;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
current.event = null;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function sharedSetAttribute(name: string, value: any, newVnode: VnodeWithDom): void | boolean {
|
|
408
|
+
const newVnodeDom = newVnode.dom;
|
|
574
409
|
if (typeof value === "function") {
|
|
575
|
-
|
|
576
|
-
if (name in eventListenerNames === false) {
|
|
410
|
+
if (!eventListenerNames.has(name)) {
|
|
577
411
|
(mainVnode as VnodeWithDom).dom.addEventListener(name.slice(2), eventListener);
|
|
578
|
-
eventListenerNames
|
|
412
|
+
eventListenerNames.add(name);
|
|
579
413
|
}
|
|
580
|
-
newVnode.dom[`v-${name}`] = value;
|
|
581
414
|
return;
|
|
582
415
|
}
|
|
583
416
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (name in newVnode.dom && newVnode.isSVG === false) {
|
|
587
|
-
// eslint-disable-next-line eqeqeq
|
|
588
|
-
if (newVnode.dom[name] != value) {
|
|
589
|
-
newVnode.dom[name] = value;
|
|
590
|
-
}
|
|
417
|
+
if (name in newVnodeDom) {
|
|
418
|
+
newVnodeDom[name] = value;
|
|
591
419
|
return;
|
|
592
420
|
}
|
|
593
421
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
newVnode.dom.removeAttribute(name);
|
|
599
|
-
} else {
|
|
600
|
-
newVnode.dom.setAttribute(name, value);
|
|
601
|
-
}
|
|
422
|
+
if (value === false) {
|
|
423
|
+
newVnodeDom.removeAttribute(name);
|
|
424
|
+
} else {
|
|
425
|
+
newVnodeDom.setAttribute(name, value);
|
|
602
426
|
}
|
|
603
427
|
}
|
|
604
428
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
return;
|
|
429
|
+
export function setAttribute(name: string, value: any, newVnode: VnodeWithDom): void {
|
|
430
|
+
if (!reservedProps.has(name)) {
|
|
431
|
+
newVnode.props[name] = value;
|
|
432
|
+
sharedSetAttribute(name, value, newVnode);
|
|
610
433
|
}
|
|
611
|
-
newVnode.props[name] = value;
|
|
612
|
-
sharedSetAttribute(name, value, newVnode as VnodeWithDom, oldVnode);
|
|
613
434
|
}
|
|
614
435
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
if (name in newVnode.dom && newVnode.isSVG === false) {
|
|
630
|
-
newVnode.dom[name] = null;
|
|
631
|
-
} else {
|
|
632
|
-
newVnode.dom.removeAttribute(name);
|
|
633
|
-
}
|
|
436
|
+
function removeAttributes(vnode: VnodeWithDom, oldProps: VnodeProperties | null): void {
|
|
437
|
+
if (!oldProps) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const vnodeDom = vnode.dom;
|
|
442
|
+
const vnodeProps = vnode.props;
|
|
443
|
+
|
|
444
|
+
for (const name in oldProps) {
|
|
445
|
+
if (name in vnodeProps === false && !eventListenerNames.has(name) && !reservedProps.has(name)) {
|
|
446
|
+
if (name in vnodeDom) {
|
|
447
|
+
vnodeDom[name] = null;
|
|
448
|
+
} else {
|
|
449
|
+
vnodeDom.removeAttribute(name);
|
|
634
450
|
}
|
|
635
451
|
}
|
|
636
452
|
}
|
|
453
|
+
}
|
|
637
454
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
for (
|
|
641
|
-
if (name
|
|
642
|
-
|
|
643
|
-
// and the two virtual DOM nodes as arguments. If the directive returns false,
|
|
644
|
-
// exit the loop.
|
|
645
|
-
if (name in directives && directives[name](newVnode.props[name], newVnode, oldVnode) === false) {
|
|
455
|
+
function addProperties(vnode: VnodeWithDom, oldProps: VnodeProperties | null) {
|
|
456
|
+
const vnodeProps = vnode.props;
|
|
457
|
+
for (const name in vnodeProps) {
|
|
458
|
+
if (directives[name]) {
|
|
459
|
+
if (directives[name](vnodeProps[name], vnode, oldProps) === false) {
|
|
646
460
|
break;
|
|
647
461
|
}
|
|
648
462
|
continue;
|
|
649
463
|
}
|
|
650
|
-
|
|
464
|
+
|
|
465
|
+
if (reservedProps.has(name)) {
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
sharedSetAttribute(name, vnodeProps[name], vnode);
|
|
651
470
|
}
|
|
652
471
|
}
|
|
653
472
|
|
|
654
|
-
|
|
473
|
+
export function updateAttributes(newVnode: VnodeWithDom, oldProps: VnodeProperties | null): void {
|
|
474
|
+
removeAttributes(newVnode, oldProps);
|
|
475
|
+
addProperties(newVnode, oldProps);
|
|
476
|
+
}
|
|
655
477
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
478
|
+
export function createElement(tag: string, isSVG: boolean): DomElement {
|
|
479
|
+
return isSVG
|
|
480
|
+
? document.createElementNS("http://www.w3.org/2000/svg", tag)
|
|
481
|
+
: (document.createElement(tag) as DomElement);
|
|
482
|
+
}
|
|
663
483
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
let
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
// Get the lengths of the new and old trees
|
|
675
|
-
let newTreeLength = newTree.length;
|
|
676
|
-
|
|
677
|
-
// Create an object that maps keys to indices in the old tree
|
|
678
|
-
let oldKeyedList: Record<string, number> = {};
|
|
679
|
-
for (let i = 0; i < oldTreeLength; i++) {
|
|
680
|
-
oldKeyedList[oldTree[i].props.key] = i;
|
|
484
|
+
function flatTree(newVnode: VnodeWithDom, children: Children) {
|
|
485
|
+
current.vnode = newVnode;
|
|
486
|
+
let i = 0;
|
|
487
|
+
|
|
488
|
+
while (i < children.length) {
|
|
489
|
+
const newChild = children[i];
|
|
490
|
+
|
|
491
|
+
if (newChild == null) {
|
|
492
|
+
children.splice(i, 1);
|
|
493
|
+
continue;
|
|
681
494
|
}
|
|
682
495
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
newKeyedList[newTree[i].props.key] = i;
|
|
496
|
+
if (Array.isArray(newChild)) {
|
|
497
|
+
children.splice(i, 1, ...newChild);
|
|
498
|
+
continue;
|
|
687
499
|
}
|
|
688
500
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
// If the old child exists, update the DOM element of the new child to match the old child's DOM element
|
|
698
|
-
if (oldChild) {
|
|
699
|
-
newChild.dom = oldChild.dom;
|
|
700
|
-
// If the new and old children have the same "v-keep" attribute value, update the children of the new child to match the old child's children
|
|
701
|
-
if ("v-keep" in newChild.props && newChild.props["v-keep"] === oldChild.props["v-keep"]) {
|
|
702
|
-
newChild.children = oldChild.children;
|
|
703
|
-
// Set the shouldPatch flag to false
|
|
704
|
-
shouldPatch = false;
|
|
705
|
-
} else {
|
|
706
|
-
updateAttributes(newChild, oldChild);
|
|
707
|
-
}
|
|
501
|
+
if (newChild instanceof Vnode) {
|
|
502
|
+
if (newChild.props === null) {
|
|
503
|
+
newChild.props = {};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (typeof newChild.tag !== "string") {
|
|
507
|
+
const component = newChild.tag;
|
|
708
508
|
|
|
709
|
-
|
|
509
|
+
current.component = newChild.tag;
|
|
510
|
+
children[i] = ("view" in component ? component.view : component).bind(component)(
|
|
511
|
+
newChild.props,
|
|
512
|
+
newChild.children
|
|
513
|
+
);
|
|
514
|
+
continue;
|
|
710
515
|
} else {
|
|
711
|
-
newChild.
|
|
712
|
-
updateAttributes(newChild);
|
|
516
|
+
newChild.isSVG = newVnode.isSVG || newChild.tag === "svg";
|
|
713
517
|
}
|
|
518
|
+
}
|
|
714
519
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
newVnode.dom.appendChild(newChild.dom);
|
|
520
|
+
i++;
|
|
521
|
+
}
|
|
718
522
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
newVnode.dom.replaceChild(newChild.dom, newVnode.dom.childNodes[i]);
|
|
722
|
-
}
|
|
523
|
+
return children;
|
|
524
|
+
}
|
|
723
525
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
526
|
+
function handleVFor(newVnode: VnodeWithDom) {
|
|
527
|
+
if ("v-for" in newVnode.props) {
|
|
528
|
+
const set = newVnode.props["v-for"];
|
|
529
|
+
const children = [];
|
|
530
|
+
const callback = newVnode.children[0];
|
|
531
|
+
children.length = set.length;
|
|
727
532
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
// If the i-th child of the old tree does not have a corresponding key in the new tree, remove its DOM element from the parent DOM element
|
|
731
|
-
if (!newKeyedList[oldTree[i].props.key]) {
|
|
732
|
-
oldTree[i].dom.parentNode && oldTree[i].dom.parentNode.removeChild(oldTree[i].dom);
|
|
733
|
-
}
|
|
533
|
+
for (let i = 0, l = set.length; i < l; i++) {
|
|
534
|
+
children[i] = callback(set[i], i);
|
|
734
535
|
}
|
|
536
|
+
|
|
537
|
+
return children;
|
|
538
|
+
}
|
|
539
|
+
return [...newVnode.children];
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function createNewElement(newChild: VnodeWithDom, newVnode: VnodeWithDom, oldChild: DomElement | null) {
|
|
543
|
+
const dom = createElement(newChild.tag, newChild.isSVG as boolean);
|
|
544
|
+
if (oldChild) {
|
|
545
|
+
newVnode.dom.replaceChild(dom, oldChild);
|
|
546
|
+
} else {
|
|
547
|
+
newVnode.dom.appendChild(dom);
|
|
548
|
+
}
|
|
549
|
+
newChild.dom = dom;
|
|
550
|
+
addProperties(newChild, null);
|
|
551
|
+
newChild.dom.props = newChild.props;
|
|
552
|
+
if ("v-text" in newChild.props) {
|
|
553
|
+
newChild.dom.textContent = newChild.props["v-text"];
|
|
735
554
|
return;
|
|
736
555
|
}
|
|
737
556
|
|
|
738
|
-
|
|
739
|
-
if (
|
|
740
|
-
|
|
557
|
+
const children = flatTree(newChild, handleVFor(newChild));
|
|
558
|
+
if (children.length === 0) {
|
|
559
|
+
newChild.dom.textContent = "";
|
|
741
560
|
return;
|
|
742
561
|
}
|
|
743
562
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
563
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
564
|
+
if (children[i] instanceof Vnode === false) {
|
|
565
|
+
newChild.dom.appendChild(document.createTextNode(children[i]));
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
createNewElement(children[i], newChild, null);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
747
571
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
572
|
+
function patchKeyed(newVnode: VnodeWithDom, children: Children) {
|
|
573
|
+
const oldTree = [...Array.from(newVnode.dom.childNodes)] as unknown as DomElement[];
|
|
574
|
+
const childNodes = newVnode.dom.childNodes;
|
|
575
|
+
const oldKeyedList: Record<string, number> = {};
|
|
576
|
+
const newKeyedList: Record<string, number> = {};
|
|
753
577
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
current.component = newChild.tag;
|
|
760
|
-
// Replace the new child with the result of calling its view or bind method, passing in the props and children as arguments
|
|
761
|
-
newTree.splice(
|
|
762
|
-
i--,
|
|
763
|
-
1,
|
|
764
|
-
("view" in newChild.tag ? newChild.tag.view.bind(newChild.tag) : newChild.tag.bind(newChild.tag))(
|
|
765
|
-
newChild.props,
|
|
766
|
-
...newChild.children
|
|
767
|
-
)
|
|
768
|
-
);
|
|
769
|
-
}
|
|
578
|
+
for (let i = 0, l = oldTree.length; i < l; i++) {
|
|
579
|
+
const oldProps = oldTree[i].props;
|
|
580
|
+
if (oldProps) {
|
|
581
|
+
oldKeyedList[oldProps.key as string] = i;
|
|
582
|
+
}
|
|
770
583
|
|
|
771
|
-
|
|
584
|
+
if (i < children.length && children[i] instanceof Vnode) {
|
|
585
|
+
newKeyedList[children[i].props.key as string] = i;
|
|
772
586
|
}
|
|
587
|
+
}
|
|
773
588
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
589
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
590
|
+
const newChild = children[i];
|
|
591
|
+
const oldChild = oldTree[oldKeyedList[newChild.props.key as string]];
|
|
592
|
+
|
|
593
|
+
if (!oldChild) {
|
|
594
|
+
createNewElement(newChild, newVnode, childNodes[i] as DomElement | null);
|
|
777
595
|
continue;
|
|
778
596
|
}
|
|
779
597
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
598
|
+
newChild.dom = oldChild;
|
|
599
|
+
const currentChild = childNodes[i];
|
|
600
|
+
if (!currentChild) {
|
|
601
|
+
newVnode.dom.appendChild(oldChild);
|
|
602
|
+
} else if (currentChild !== oldChild) {
|
|
603
|
+
newVnode.dom.replaceChild(oldChild, currentChild);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if ("v-keep" in newChild.props === false || oldChild.props["v-keep"] !== newChild.props["v-keep"]) {
|
|
607
|
+
updateAttributes(newChild as VnodeWithDom, oldChild.props);
|
|
608
|
+
oldChild.props = newChild.props;
|
|
609
|
+
|
|
610
|
+
if ("v-text" in newChild.props) {
|
|
611
|
+
// eslint-disable-next-line eqeqeq
|
|
612
|
+
if (oldChild.textContent != newChild.props["v-text"]) {
|
|
613
|
+
oldChild.textContent = newChild.props["v-text"];
|
|
614
|
+
}
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
// eslint-disable-next-line no-use-before-define
|
|
618
|
+
patch(newChild as VnodeWithDom);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
for (let i = children.length, l = childNodes.length; i < l; i++) {
|
|
623
|
+
childNodes[i]?.remove();
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// eslint-disable-next-line complexity
|
|
628
|
+
export function patch(newVnode: VnodeWithDom): void {
|
|
629
|
+
const children = flatTree(newVnode, handleVFor(newVnode));
|
|
630
|
+
|
|
631
|
+
const dom = newVnode.dom;
|
|
632
|
+
|
|
633
|
+
if (children.length === 0) {
|
|
634
|
+
if (dom.childNodes.length) {
|
|
635
|
+
dom.textContent = "";
|
|
784
636
|
}
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
785
639
|
|
|
786
|
-
|
|
787
|
-
|
|
640
|
+
const oldDomChildren = dom.childNodes as unknown as DomElement[];
|
|
641
|
+
const oldChildrenLength = oldDomChildren.length;
|
|
642
|
+
if (oldChildrenLength > 0) {
|
|
643
|
+
const firstOldProps = oldDomChildren[0].props;
|
|
644
|
+
const firstVnode = children[0] as VnodeWithDom;
|
|
645
|
+
if (firstOldProps && firstVnode instanceof Vnode && "key" in firstVnode.props && "key" in firstOldProps) {
|
|
646
|
+
patchKeyed(newVnode, children);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
788
649
|
}
|
|
789
650
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
let
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
// If no old child exists at the same index
|
|
796
|
-
if (i >= oldTreeLength) {
|
|
797
|
-
// Create a new text node for the new child
|
|
798
|
-
newChild.dom = document.createTextNode(newChild.children[0]);
|
|
799
|
-
// Append the new text node to the dom
|
|
800
|
-
newVnode.dom.appendChild(newChild.dom);
|
|
651
|
+
const childrenLength = children.length;
|
|
652
|
+
if (oldChildrenLength === 0) {
|
|
653
|
+
for (let i = 0; i < childrenLength; i++) {
|
|
654
|
+
if (children[i] instanceof Vnode === false) {
|
|
655
|
+
dom.appendChild(document.createTextNode(children[i]));
|
|
801
656
|
continue;
|
|
802
657
|
}
|
|
658
|
+
createNewElement(children[i], newVnode, null);
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
803
662
|
|
|
804
|
-
|
|
805
|
-
|
|
663
|
+
for (let i = 0; i < childrenLength; i++) {
|
|
664
|
+
const oldChild = oldDomChildren[i];
|
|
665
|
+
const newChild = children[i];
|
|
666
|
+
|
|
667
|
+
if (!oldChild) {
|
|
668
|
+
createNewElement(newChild, newVnode, null);
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
806
671
|
|
|
807
|
-
|
|
808
|
-
if (oldChild.
|
|
809
|
-
|
|
810
|
-
newChild.dom = document.createTextNode(newChild.children[0]);
|
|
811
|
-
// Replace the old child in the dom with the new text node
|
|
812
|
-
newVnode.dom.replaceChild(newChild.dom, oldChild.dom);
|
|
672
|
+
if (newChild instanceof Vnode === false) {
|
|
673
|
+
if (oldChild.nodeType !== 3) {
|
|
674
|
+
newVnode.dom.replaceChild(document.createTextNode(newChild), oldChild);
|
|
813
675
|
continue;
|
|
814
676
|
}
|
|
815
677
|
|
|
816
|
-
// If the old child is a text node
|
|
817
|
-
// Set the dom property of the text Vnode to the dom property of the old child
|
|
818
|
-
newChild.dom = oldChild.dom;
|
|
819
|
-
// If the text content of the old child is different from the new child, update the text content of the old child
|
|
820
678
|
// eslint-disable-next-line eqeqeq
|
|
821
|
-
if (
|
|
822
|
-
oldChild.
|
|
679
|
+
if (oldChild.nodeValue != newChild) {
|
|
680
|
+
oldChild.nodeValue = newChild;
|
|
823
681
|
}
|
|
824
682
|
continue;
|
|
825
683
|
}
|
|
826
684
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
// Append the new child to the dom
|
|
838
|
-
newVnode.dom.appendChild(newChild.dom);
|
|
839
|
-
// Recursively patch the new child
|
|
840
|
-
patch(newChild as VnodeWithDom);
|
|
841
|
-
continue;
|
|
685
|
+
if ("v-keep" in newChild.props) {
|
|
686
|
+
if (oldChild.props && oldChild.props["v-keep"] === newChild.props["v-keep"]) {
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const nextOldChild = oldDomChildren[i + 1];
|
|
691
|
+
if (nextOldChild && nextOldChild.props && nextOldChild.props["v-keep"] === newChild.props["v-keep"]) {
|
|
692
|
+
oldChild.remove();
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
842
695
|
}
|
|
843
696
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
// If the tag of the new child is different from the tag of the old child
|
|
848
|
-
if (newChild.tag !== oldChild.tag) {
|
|
849
|
-
// Create a new dom element for the new child
|
|
850
|
-
newChild.dom = createDomElement(newChild.tag as string, newChild.isSVG);
|
|
851
|
-
// Update the attributes of the new child
|
|
852
|
-
updateAttributes(newChild as VnodeWithDom);
|
|
853
|
-
// Replace the old child in the dom with the new child
|
|
854
|
-
newVnode.dom.replaceChild(newChild.dom, oldChild.dom);
|
|
855
|
-
// Recursively patch the new child
|
|
856
|
-
patch(newChild as VnodeWithDom);
|
|
697
|
+
if (newChild.tag !== oldChild.nodeName.toLowerCase()) {
|
|
698
|
+
createNewElement(newChild, newVnode, oldChild);
|
|
857
699
|
continue;
|
|
858
700
|
}
|
|
859
701
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
newChild.
|
|
702
|
+
newChild.dom = oldChild;
|
|
703
|
+
updateAttributes(newChild as VnodeWithDom, oldChild.props || null);
|
|
704
|
+
oldChild.props = newChild.props;
|
|
705
|
+
if ("v-text" in newChild.props) {
|
|
706
|
+
// eslint-disable-next-line eqeqeq
|
|
707
|
+
if (newChild.dom.textContent != newChild.props["v-text"]) {
|
|
708
|
+
newChild.dom.textContent = newChild.props["v-text"];
|
|
709
|
+
}
|
|
866
710
|
continue;
|
|
867
711
|
}
|
|
868
|
-
|
|
869
|
-
// Update the attributes of the new child based on the old child
|
|
870
|
-
updateAttributes(newChild as VnodeWithDom, oldChild);
|
|
871
|
-
// Recursively patch the new and old children
|
|
872
|
-
patch(newChild as VnodeWithDom, oldChild);
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
// Remove any old children that are no longer present in the new tree
|
|
876
|
-
for (let i = newTree.length; i < oldTreeLength; i++) {
|
|
877
|
-
newVnode.dom.removeChild(oldTree[i].dom);
|
|
712
|
+
patch(newChild);
|
|
878
713
|
}
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
// Update the main Vnode
|
|
882
|
-
export function update(): void | string {
|
|
883
|
-
// If the main Vnode exists
|
|
884
|
-
if (mainVnode) {
|
|
885
|
-
// Call any cleanup functions that are registered with the onCleanupSet set
|
|
886
|
-
callSet(onCleanupSet);
|
|
887
|
-
// Store a reference to the old main Vnode
|
|
888
|
-
let oldMainVnode = mainVnode;
|
|
889
|
-
// Create a new main Vnode with the main component as its only child
|
|
890
|
-
mainVnode = new Vnode(oldMainVnode.tag, oldMainVnode.props, [mainComponent]) as VnodeWithDom;
|
|
891
|
-
mainVnode.dom = oldMainVnode.dom;
|
|
892
|
-
mainVnode.isSVG = oldMainVnode.isSVG;
|
|
893
|
-
|
|
894
|
-
// Recursively patch the new and old main Vnodes
|
|
895
|
-
patch(mainVnode, oldMainVnode);
|
|
896
714
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
// Set the isMounted flag to true
|
|
901
|
-
isMounted = true;
|
|
902
|
-
|
|
903
|
-
// Reset the current vnode, oldVnode, and component properties
|
|
904
|
-
current.vnode = null;
|
|
905
|
-
current.oldVnode = null;
|
|
906
|
-
current.component = null;
|
|
907
|
-
|
|
908
|
-
// If the code is running in a Node.js environment, return the inner HTML of the main Vnode's dom element
|
|
909
|
-
if (isNodeJs) {
|
|
910
|
-
return mainVnode.dom.innerHTML;
|
|
911
|
-
}
|
|
715
|
+
for (let i = childrenLength, l = oldDomChildren.length; i < l; i++) {
|
|
716
|
+
oldDomChildren[i]?.remove();
|
|
912
717
|
}
|
|
913
718
|
}
|
|
914
719
|
|
|
915
|
-
|
|
916
|
-
// It is assumed that a first mount has already occurred, so,
|
|
917
|
-
// the oldVnode is not null and the dom property of the oldVnode is not null
|
|
918
|
-
// You need to set the dom property of the newVnode to the dom property of the oldVnode
|
|
919
|
-
// The same with the isSVG property
|
|
920
|
-
// Prefer this function over patch to allow for cleanup, onUpdate and onMount sets to be called
|
|
921
|
-
export function updateVnode(vnode: VnodeWithDom, oldVnode: VnodeWithDom): string | void {
|
|
922
|
-
// Call any cleanup functions that are registered with the onCleanupSet set
|
|
720
|
+
export function updateVnode(vnode: VnodeWithDom): string | void {
|
|
923
721
|
callSet(onCleanupSet);
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
patch(vnode, oldVnode);
|
|
927
|
-
|
|
928
|
-
// Set the oldVnode's tag, props, children, dom, and isSVG properties to the newVnode's tag, props, children, dom, and isSVG properties
|
|
929
|
-
// This is necessary to allow for the oldVnode to be used as the newVnode in the next update with the normal update function
|
|
930
|
-
oldVnode.tag = vnode.tag;
|
|
931
|
-
oldVnode.props = { ...vnode.props };
|
|
932
|
-
oldVnode.children = [...vnode.children];
|
|
933
|
-
oldVnode.dom = vnode.dom;
|
|
934
|
-
oldVnode.isSVG = vnode.isSVG;
|
|
935
|
-
|
|
936
|
-
// Call any update or mount functions that are registered with the onUpdateSet or onMountSet set
|
|
722
|
+
vnode.props = vnode.props || {};
|
|
723
|
+
patch(vnode);
|
|
937
724
|
callSet(isMounted ? onUpdateSet : onMountSet);
|
|
938
|
-
|
|
939
|
-
// Set the isMounted flag to true
|
|
940
725
|
isMounted = true;
|
|
941
|
-
|
|
942
|
-
// Reset the current vnode, oldVnode, and component properties
|
|
943
726
|
current.vnode = null;
|
|
944
|
-
current.oldVnode = null;
|
|
945
727
|
current.component = null;
|
|
946
|
-
|
|
947
728
|
if (isNodeJs) {
|
|
948
729
|
return vnode.dom.innerHTML;
|
|
949
730
|
}
|
|
950
731
|
}
|
|
951
732
|
|
|
952
|
-
|
|
733
|
+
export function update(): void | string {
|
|
734
|
+
if (mainVnode) {
|
|
735
|
+
mainVnode.children = [mainComponent];
|
|
736
|
+
return updateVnode(mainVnode);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
953
740
|
export function unmount() {
|
|
954
|
-
// If the main Vnode exists
|
|
955
741
|
if (mainVnode) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
// Update the main Vnode
|
|
959
|
-
let result = update();
|
|
960
|
-
// Call any unmount functions that are registered with the onUnmountSet set
|
|
742
|
+
mainComponent = v(() => null, {}) as VnodeComponentInterface;
|
|
743
|
+
const result = update();
|
|
961
744
|
callSet(onUnmountSet);
|
|
962
|
-
|
|
963
|
-
// Remove any event listeners that were added to the main Vnode's dom element
|
|
964
|
-
for (let name in eventListenerNames) {
|
|
745
|
+
for (const name in eventListenerNames) {
|
|
965
746
|
mainVnode.dom.removeEventListener(name.slice(2).toLowerCase(), eventListener);
|
|
966
747
|
Reflect.deleteProperty(eventListenerNames, name);
|
|
967
748
|
}
|
|
968
749
|
|
|
969
|
-
// Reset the main component and main Vnode
|
|
970
750
|
mainComponent = null;
|
|
971
751
|
mainVnode = null;
|
|
972
|
-
// Set the isMounted flag to false
|
|
973
752
|
isMounted = false;
|
|
974
|
-
// Reset the current vnode, oldVnode, and component properties
|
|
975
753
|
current.vnode = null;
|
|
976
|
-
current.oldVnode = null;
|
|
977
754
|
current.component = null;
|
|
978
|
-
|
|
755
|
+
current.event = null;
|
|
979
756
|
return result;
|
|
980
757
|
}
|
|
981
758
|
}
|
|
982
|
-
|
|
759
|
+
|
|
983
760
|
export function mount(dom: string | DomElement, component: any) {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
// Check if the 'component' argument is a Vnode component or a regular component.
|
|
994
|
-
// If it's a regular component, create a new Vnode component using the 'component' argument as the tag.
|
|
995
|
-
// If it's not a component at all, create a new Vnode component with the 'component' argument as the rendering function.
|
|
996
|
-
let vnodeComponent = isVnodeComponent(component)
|
|
997
|
-
? component
|
|
998
|
-
: isComponent(component)
|
|
999
|
-
? new Vnode(component, {}, [])
|
|
1000
|
-
: new Vnode(() => component, {}, []);
|
|
1001
|
-
|
|
1002
|
-
// If a main component already exists and it's not the same as the current 'vnodeComponent', unmount it.
|
|
1003
|
-
if (mainComponent && mainComponent.tag !== vnodeComponent.tag) {
|
|
1004
|
-
unmount();
|
|
761
|
+
const container =
|
|
762
|
+
typeof dom === "string" ? (isNodeJs ? createElement(dom, dom === "svg") : document.querySelector(dom)) : dom;
|
|
763
|
+
|
|
764
|
+
if (isComponent(component)) {
|
|
765
|
+
mainComponent = new Vnode(component, {}, []) as VnodeComponentInterface;
|
|
766
|
+
} else if (isVnodeComponent(component)) {
|
|
767
|
+
mainComponent = component;
|
|
768
|
+
} else {
|
|
769
|
+
mainComponent = new Vnode(() => component, {}, []) as VnodeComponentInterface;
|
|
1005
770
|
}
|
|
1006
771
|
|
|
1007
|
-
|
|
1008
|
-
mainComponent = vnodeComponent as VnodeComponentInterface;
|
|
1009
|
-
// Convert the container element to a Vnode.
|
|
1010
|
-
mainVnode = domToVnode(container);
|
|
1011
|
-
// Update the DOM with the new component.
|
|
772
|
+
mainVnode = domToVnode(container) as VnodeWithDom;
|
|
1012
773
|
return update();
|
|
1013
774
|
}
|
|
1014
|
-
|
|
1015
|
-
// This is a utility function for creating Vnode objects.
|
|
1016
|
-
// It takes in a tag or component, and optional props and children arguments.
|
|
1017
|
-
export const v: V = (tagOrComponent, props = {}, ...children) => {
|
|
1018
|
-
// Return a new Vnode object using the given arguments.
|
|
1019
|
-
return new Vnode(tagOrComponent, props || {}, children);
|
|
1020
|
-
};
|
|
1021
|
-
|
|
1022
|
-
// This utility function creates a fragment Vnode.
|
|
1023
|
-
// It takes in a placeholder and the children arguments, returns only the children.
|
|
1024
|
-
v.fragment = (_: VnodeProperties, ...children: Children) => children;
|