@geektech/tsone 0.0.1
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 +176 -0
- package/dist/core/app.d.ts +92 -0
- package/dist/core/component/base.d.ts +53 -0
- package/dist/core/component/index.d.ts +4 -0
- package/dist/core/component.d.ts +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/reactive/types.d.ts +19 -0
- package/dist/core/reactive.d.ts +31 -0
- package/dist/core/renderer/props.d.ts +9 -0
- package/dist/core/renderer/types.d.ts +31 -0
- package/dist/core/renderer.d.ts +57 -0
- package/dist/core/template.d.ts +50 -0
- package/dist/core/vnode.d.ts +64 -0
- package/dist/index-3j2jsdpc.js +1498 -0
- package/dist/index-3j2jsdpc.js.map +20 -0
- package/dist/index-dgv88dz4.js +59 -0
- package/dist/index-dgv88dz4.js.map +10 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +248 -0
- package/dist/index.js.map +11 -0
- package/dist/router/history.d.ts +5 -0
- package/dist/router/index.d.ts +88 -0
- package/dist/router/index.js +16 -0
- package/dist/router/index.js.map +9 -0
- package/dist/router/instance.d.ts +26 -0
- package/dist/router/matcher.d.ts +7 -0
- package/dist/style/StyleManager.d.ts +16 -0
- package/dist/style/class-style.d.ts +0 -0
- package/dist/style/id-style.d.ts +0 -0
- package/dist/style/index.d.ts +1 -0
- package/dist/style/index.js +9 -0
- package/dist/style/index.js.map +9 -0
- package/dist/style/style.d.ts +0 -0
- package/package.json +66 -0
|
@@ -0,0 +1,1498 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StyleManager
|
|
3
|
+
} from "./index-dgv88dz4.js";
|
|
4
|
+
|
|
5
|
+
// lib/core/reactive/types.ts
|
|
6
|
+
var IS_REACTIVE = Symbol("is_reactive");
|
|
7
|
+
var IS_READONLY = Symbol("is_readonly");
|
|
8
|
+
var MUTATING_ARRAY_METHODS = [
|
|
9
|
+
"push",
|
|
10
|
+
"pop",
|
|
11
|
+
"shift",
|
|
12
|
+
"unshift",
|
|
13
|
+
"splice",
|
|
14
|
+
"sort",
|
|
15
|
+
"reverse"
|
|
16
|
+
];
|
|
17
|
+
function hasReactiveFlag(value, flag) {
|
|
18
|
+
return Boolean(Reflect.get(value, flag));
|
|
19
|
+
}
|
|
20
|
+
function isObject(value) {
|
|
21
|
+
return value !== null && typeof value === "object";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// lib/core/reactive.ts
|
|
25
|
+
var effectId = 0;
|
|
26
|
+
|
|
27
|
+
class ReactiveSystem {
|
|
28
|
+
static instance;
|
|
29
|
+
activeEffect = null;
|
|
30
|
+
effectStack = [];
|
|
31
|
+
targetMap = new WeakMap;
|
|
32
|
+
reactiveMap = new WeakMap;
|
|
33
|
+
readonlyMap = new WeakMap;
|
|
34
|
+
constructor() {}
|
|
35
|
+
static getInstance() {
|
|
36
|
+
if (!ReactiveSystem.instance) {
|
|
37
|
+
ReactiveSystem.instance = new ReactiveSystem;
|
|
38
|
+
}
|
|
39
|
+
return ReactiveSystem.instance;
|
|
40
|
+
}
|
|
41
|
+
reactive(target) {
|
|
42
|
+
if (!isObject(target)) {
|
|
43
|
+
console.warn("reactive: target must be an object");
|
|
44
|
+
return target;
|
|
45
|
+
}
|
|
46
|
+
if (isReactive(target)) {
|
|
47
|
+
return target;
|
|
48
|
+
}
|
|
49
|
+
if (this.reactiveMap.has(target)) {
|
|
50
|
+
return this.reactiveMap.get(target);
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(target)) {
|
|
53
|
+
return this.createReactiveArray(target);
|
|
54
|
+
}
|
|
55
|
+
const proxy = new Proxy(target, {
|
|
56
|
+
get: (target2, key) => {
|
|
57
|
+
if (key === IS_REACTIVE) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (key === IS_READONLY) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
this.track(target2, key);
|
|
64
|
+
const value = Reflect.get(target2, key);
|
|
65
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
66
|
+
return this.reactive(value);
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
},
|
|
70
|
+
set: (target2, key, value) => {
|
|
71
|
+
if (hasReactiveFlag(target2, IS_READONLY)) {
|
|
72
|
+
console.warn(`Cannot set property ${String(key)} on readonly object`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const oldValue = Reflect.get(target2, key);
|
|
76
|
+
if (value && typeof value === "object" && !Array.isArray(value) && !hasReactiveFlag(value, IS_REACTIVE)) {
|
|
77
|
+
value = this.reactive(value);
|
|
78
|
+
}
|
|
79
|
+
const result = Reflect.set(target2, key, value);
|
|
80
|
+
if (oldValue !== value) {
|
|
81
|
+
this.trigger(target2, key);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
},
|
|
85
|
+
deleteProperty: (target2, key) => {
|
|
86
|
+
if (hasReactiveFlag(target2, IS_READONLY)) {
|
|
87
|
+
console.warn(`Cannot delete property ${String(key)} on readonly object`);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const hadKey = key in target2;
|
|
91
|
+
const result = Reflect.deleteProperty(target2, key);
|
|
92
|
+
if (hadKey) {
|
|
93
|
+
this.trigger(target2, key);
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
this.reactiveMap.set(target, proxy);
|
|
99
|
+
return proxy;
|
|
100
|
+
}
|
|
101
|
+
createReactiveArray(target) {
|
|
102
|
+
if (this.reactiveMap.has(target)) {
|
|
103
|
+
return this.reactiveMap.get(target);
|
|
104
|
+
}
|
|
105
|
+
const proxy = new Proxy(target, {
|
|
106
|
+
get: (target2, key) => {
|
|
107
|
+
if (key === IS_REACTIVE) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
if (key === IS_READONLY) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
this.track(target2, key);
|
|
114
|
+
const value = Reflect.get(target2, key);
|
|
115
|
+
if (typeof key === "string" && MUTATING_ARRAY_METHODS.includes(key)) {
|
|
116
|
+
return (...args) => {
|
|
117
|
+
const arrayMethod = value;
|
|
118
|
+
const result = arrayMethod.apply(target2, args);
|
|
119
|
+
this.trigger(target2, "length");
|
|
120
|
+
this.trigger(target2, key);
|
|
121
|
+
return result;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
},
|
|
126
|
+
set: (target2, key, value) => {
|
|
127
|
+
if (hasReactiveFlag(target2, IS_READONLY)) {
|
|
128
|
+
console.warn(`Cannot set property ${String(key)} on readonly object`);
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
const oldValue = Reflect.get(target2, key);
|
|
132
|
+
if (value && typeof value === "object" && !Array.isArray(value) && !hasReactiveFlag(value, IS_REACTIVE)) {
|
|
133
|
+
value = this.reactive(value);
|
|
134
|
+
}
|
|
135
|
+
const result = Reflect.set(target2, key, value);
|
|
136
|
+
if (oldValue !== value) {
|
|
137
|
+
this.trigger(target2, key);
|
|
138
|
+
if (typeof key === "string" && !isNaN(Number(key))) {
|
|
139
|
+
this.trigger(target2, "length");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
},
|
|
144
|
+
deleteProperty: (target2, key) => {
|
|
145
|
+
if (hasReactiveFlag(target2, IS_READONLY)) {
|
|
146
|
+
console.warn(`Cannot delete property ${String(key)} on readonly object`);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const hadKey = key in target2;
|
|
150
|
+
const result = Reflect.deleteProperty(target2, key);
|
|
151
|
+
if (hadKey) {
|
|
152
|
+
this.trigger(target2, key);
|
|
153
|
+
this.trigger(target2, "length");
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
this.reactiveMap.set(target, proxy);
|
|
159
|
+
return proxy;
|
|
160
|
+
}
|
|
161
|
+
readonly(target) {
|
|
162
|
+
if (!isObject(target)) {
|
|
163
|
+
console.warn("readonly: target must be an object");
|
|
164
|
+
return target;
|
|
165
|
+
}
|
|
166
|
+
if (hasReactiveFlag(target, IS_READONLY)) {
|
|
167
|
+
return target;
|
|
168
|
+
}
|
|
169
|
+
if (this.readonlyMap.has(target)) {
|
|
170
|
+
return this.readonlyMap.get(target);
|
|
171
|
+
}
|
|
172
|
+
const proxy = new Proxy(target, {
|
|
173
|
+
get: (target2, key) => {
|
|
174
|
+
if (key === IS_REACTIVE || key === IS_READONLY) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
const value = Reflect.get(target2, key);
|
|
178
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
179
|
+
return this.readonly(value);
|
|
180
|
+
}
|
|
181
|
+
return value;
|
|
182
|
+
},
|
|
183
|
+
set: () => {
|
|
184
|
+
console.warn("Cannot set property on readonly object");
|
|
185
|
+
return false;
|
|
186
|
+
},
|
|
187
|
+
deleteProperty: () => {
|
|
188
|
+
console.warn("Cannot delete property on readonly object");
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
this.readonlyMap.set(target, proxy);
|
|
193
|
+
return proxy;
|
|
194
|
+
}
|
|
195
|
+
effect(fn, options) {
|
|
196
|
+
const { lazy = false, scheduler } = options || {};
|
|
197
|
+
const effectFn = () => {
|
|
198
|
+
if (!effectFn.active) {
|
|
199
|
+
return fn();
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
this.cleanup(effectFn);
|
|
203
|
+
this.effectStack.push(effectFn);
|
|
204
|
+
this.activeEffect = effectFn;
|
|
205
|
+
return fn();
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error("Effect error:", error);
|
|
208
|
+
return;
|
|
209
|
+
} finally {
|
|
210
|
+
this.effectStack.pop();
|
|
211
|
+
this.activeEffect = this.effectStack[this.effectStack.length - 1] ?? null;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
effectFn.id = effectId++;
|
|
215
|
+
effectFn.deps = [];
|
|
216
|
+
effectFn.active = true;
|
|
217
|
+
effectFn.scheduler = scheduler;
|
|
218
|
+
if (!lazy) {
|
|
219
|
+
effectFn();
|
|
220
|
+
}
|
|
221
|
+
return effectFn;
|
|
222
|
+
}
|
|
223
|
+
computed(getter) {
|
|
224
|
+
let dirty = true;
|
|
225
|
+
let value;
|
|
226
|
+
const computedTarget = {};
|
|
227
|
+
const trackComputedValue = () => {
|
|
228
|
+
this.track(computedTarget, "value");
|
|
229
|
+
};
|
|
230
|
+
const runner = this.effect(() => {
|
|
231
|
+
value = getter();
|
|
232
|
+
dirty = false;
|
|
233
|
+
}, {
|
|
234
|
+
lazy: true,
|
|
235
|
+
scheduler: () => {
|
|
236
|
+
if (!dirty) {
|
|
237
|
+
dirty = true;
|
|
238
|
+
this.trigger(computedTarget, "value");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
get value() {
|
|
244
|
+
if (dirty) {
|
|
245
|
+
runner();
|
|
246
|
+
}
|
|
247
|
+
trackComputedValue();
|
|
248
|
+
return value;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
cleanup(effect) {
|
|
253
|
+
effect.deps.forEach((dep) => {
|
|
254
|
+
dep.delete(effect);
|
|
255
|
+
});
|
|
256
|
+
effect.deps.length = 0;
|
|
257
|
+
}
|
|
258
|
+
track(target, key) {
|
|
259
|
+
if (!this.activeEffect || !this.activeEffect.active)
|
|
260
|
+
return;
|
|
261
|
+
let depsMap = this.targetMap.get(target);
|
|
262
|
+
if (!depsMap) {
|
|
263
|
+
depsMap = new Map;
|
|
264
|
+
this.targetMap.set(target, depsMap);
|
|
265
|
+
}
|
|
266
|
+
let dep = depsMap.get(key);
|
|
267
|
+
if (!dep) {
|
|
268
|
+
dep = new Set;
|
|
269
|
+
depsMap.set(key, dep);
|
|
270
|
+
}
|
|
271
|
+
if (!dep.has(this.activeEffect)) {
|
|
272
|
+
dep.add(this.activeEffect);
|
|
273
|
+
this.activeEffect.deps.push(dep);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
trigger(target, key) {
|
|
277
|
+
const depsMap = this.targetMap.get(target);
|
|
278
|
+
if (!depsMap)
|
|
279
|
+
return;
|
|
280
|
+
const dep = depsMap.get(key);
|
|
281
|
+
if (!dep)
|
|
282
|
+
return;
|
|
283
|
+
const effects = new Set(dep);
|
|
284
|
+
effects.forEach((effect) => {
|
|
285
|
+
if (effect.active) {
|
|
286
|
+
if (effect.scheduler) {
|
|
287
|
+
effect.scheduler(effect);
|
|
288
|
+
} else {
|
|
289
|
+
effect();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
stop(effect) {
|
|
295
|
+
if (effect.active) {
|
|
296
|
+
this.cleanup(effect);
|
|
297
|
+
effect.active = false;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function reactive(target) {
|
|
302
|
+
return ReactiveSystem.getInstance().reactive(target);
|
|
303
|
+
}
|
|
304
|
+
function readonly(target) {
|
|
305
|
+
return ReactiveSystem.getInstance().readonly(target);
|
|
306
|
+
}
|
|
307
|
+
function effect(fn, options) {
|
|
308
|
+
return ReactiveSystem.getInstance().effect(fn, options);
|
|
309
|
+
}
|
|
310
|
+
function computed(getter) {
|
|
311
|
+
return ReactiveSystem.getInstance().computed(getter);
|
|
312
|
+
}
|
|
313
|
+
function stop(effect2) {
|
|
314
|
+
ReactiveSystem.getInstance().stop(effect2);
|
|
315
|
+
}
|
|
316
|
+
function isReactive(value) {
|
|
317
|
+
return isObject(value) && hasReactiveFlag(value, IS_REACTIVE);
|
|
318
|
+
}
|
|
319
|
+
function isReadonly(value) {
|
|
320
|
+
return isObject(value) && hasReactiveFlag(value, IS_READONLY);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// lib/core/renderer/props.ts
|
|
324
|
+
function isEventProp(key) {
|
|
325
|
+
return /^on[A-Z]/.test(key) || /^on[a-z]/.test(key);
|
|
326
|
+
}
|
|
327
|
+
function eventNameFromProp(key) {
|
|
328
|
+
return key.slice(2).toLowerCase();
|
|
329
|
+
}
|
|
330
|
+
function parseEventName(event) {
|
|
331
|
+
const [eventName, ...modifiers] = event.split(".");
|
|
332
|
+
return { eventName, modifiers: new Set(modifiers) };
|
|
333
|
+
}
|
|
334
|
+
function wrapEventHandler(handler, modifiers) {
|
|
335
|
+
const eventHandler = (event) => {
|
|
336
|
+
if (modifiers.has("stop")) {
|
|
337
|
+
event.stopPropagation();
|
|
338
|
+
}
|
|
339
|
+
if (modifiers.has("prevent")) {
|
|
340
|
+
event.preventDefault();
|
|
341
|
+
}
|
|
342
|
+
if (modifiers.has("self") && event.currentTarget !== event.target) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (modifiers.has("once")) {
|
|
346
|
+
event.currentTarget.removeEventListener(event.type, eventHandler);
|
|
347
|
+
}
|
|
348
|
+
handler(event);
|
|
349
|
+
};
|
|
350
|
+
return eventHandler;
|
|
351
|
+
}
|
|
352
|
+
function setStyleValue(style, property, value) {
|
|
353
|
+
const cssProperty = property.includes("-") ? property : property.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
354
|
+
style.setProperty(cssProperty, String(value));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// lib/core/vnode.ts
|
|
358
|
+
function isComponentNode(vnode) {
|
|
359
|
+
return typeof vnode === "object" && vnode !== null && "component" in vnode;
|
|
360
|
+
}
|
|
361
|
+
function isHTMLNode(vnode) {
|
|
362
|
+
return typeof vnode === "object" && vnode !== null && "tag" in vnode && vnode.tag !== "slot";
|
|
363
|
+
}
|
|
364
|
+
function isSlotProvider(vnode) {
|
|
365
|
+
return typeof vnode === "object" && vnode !== null && "tag" in vnode && vnode.tag === "slot";
|
|
366
|
+
}
|
|
367
|
+
function h(tag, props, children, listeners, key, directions) {
|
|
368
|
+
return {
|
|
369
|
+
tag,
|
|
370
|
+
props,
|
|
371
|
+
children,
|
|
372
|
+
listeners,
|
|
373
|
+
key,
|
|
374
|
+
directions
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function createComponent(componentClass, props, children, key, directions) {
|
|
378
|
+
return {
|
|
379
|
+
component: componentClass,
|
|
380
|
+
props,
|
|
381
|
+
children,
|
|
382
|
+
key,
|
|
383
|
+
directions
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function slot(name, key, directions) {
|
|
387
|
+
return {
|
|
388
|
+
tag: "slot",
|
|
389
|
+
props: { name },
|
|
390
|
+
key,
|
|
391
|
+
directions
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// lib/core/renderer.ts
|
|
396
|
+
class RendererContext {
|
|
397
|
+
strategies;
|
|
398
|
+
constructor() {
|
|
399
|
+
this.strategies = [
|
|
400
|
+
new TextRenderStrategy,
|
|
401
|
+
new ComponentRenderStrategy,
|
|
402
|
+
new SlotRenderStrategy,
|
|
403
|
+
new ElementRenderStrategy
|
|
404
|
+
];
|
|
405
|
+
}
|
|
406
|
+
mount(vnode, context) {
|
|
407
|
+
return this.findStrategy(vnode).mount(vnode, context);
|
|
408
|
+
}
|
|
409
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
410
|
+
const oldStrategy = this.findStrategy(oldVNode);
|
|
411
|
+
const newStrategy = this.findStrategy(newVNode);
|
|
412
|
+
if (oldStrategy !== newStrategy) {
|
|
413
|
+
const nextNode = newStrategy.mount(newVNode, context);
|
|
414
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
415
|
+
oldStrategy.unmount(oldVNode, currentNode, context);
|
|
416
|
+
return nextNode;
|
|
417
|
+
}
|
|
418
|
+
return oldStrategy.patch(oldVNode, newVNode, currentNode, context);
|
|
419
|
+
}
|
|
420
|
+
unmount(vnode, currentNode, context) {
|
|
421
|
+
this.findStrategy(vnode).unmount(vnode, currentNode, context);
|
|
422
|
+
}
|
|
423
|
+
findStrategy(vnode) {
|
|
424
|
+
const strategy = this.strategies.find((item) => item.matches(vnode));
|
|
425
|
+
if (!strategy) {
|
|
426
|
+
throw new Error("No render strategy found for vnode");
|
|
427
|
+
}
|
|
428
|
+
return strategy;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
class TextRenderStrategy {
|
|
433
|
+
matches(vnode) {
|
|
434
|
+
return typeof vnode === "string";
|
|
435
|
+
}
|
|
436
|
+
mount(vnode, context) {
|
|
437
|
+
return context.templateEngine.parseTemplate(vnode);
|
|
438
|
+
}
|
|
439
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
440
|
+
if (oldVNode === newVNode) {
|
|
441
|
+
return currentNode;
|
|
442
|
+
}
|
|
443
|
+
const nextNode = this.mount(newVNode, context);
|
|
444
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
445
|
+
return nextNode;
|
|
446
|
+
}
|
|
447
|
+
unmount() {}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
class ComponentRenderStrategy {
|
|
451
|
+
instances = new WeakMap;
|
|
452
|
+
matches(vnode) {
|
|
453
|
+
return typeof vnode === "object" && vnode !== null && isComponentNode(vnode);
|
|
454
|
+
}
|
|
455
|
+
mount(vnode, context) {
|
|
456
|
+
const ComponentClass = vnode.component;
|
|
457
|
+
const instance = new ComponentClass(this.createProps(vnode));
|
|
458
|
+
if (context.appContext && instance.setAppContext) {
|
|
459
|
+
instance.setAppContext(context.appContext);
|
|
460
|
+
}
|
|
461
|
+
if (vnode.emitters) {
|
|
462
|
+
Object.entries(vnode.emitters).forEach(([eventName, listener]) => {
|
|
463
|
+
instance.on(eventName, listener);
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
context.registerChild(instance);
|
|
467
|
+
const node = instance.mountToNode();
|
|
468
|
+
this.instances.set(node, instance);
|
|
469
|
+
return node;
|
|
470
|
+
}
|
|
471
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
472
|
+
const instance = this.instances.get(currentNode);
|
|
473
|
+
if (instance && oldVNode.component === newVNode.component) {
|
|
474
|
+
instance.setProps(this.createProps(newVNode));
|
|
475
|
+
instance.update();
|
|
476
|
+
const nextNode2 = instance.getElement() ?? currentNode;
|
|
477
|
+
this.instances.set(nextNode2, instance);
|
|
478
|
+
return nextNode2;
|
|
479
|
+
}
|
|
480
|
+
const nextNode = this.mount(newVNode, context);
|
|
481
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
482
|
+
this.unmount(oldVNode, currentNode);
|
|
483
|
+
return nextNode;
|
|
484
|
+
}
|
|
485
|
+
unmount(_vnode, currentNode) {
|
|
486
|
+
const instance = this.instances.get(currentNode);
|
|
487
|
+
if (instance) {
|
|
488
|
+
instance.unmount();
|
|
489
|
+
this.instances.delete(currentNode);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
createProps(vnode) {
|
|
493
|
+
return {
|
|
494
|
+
...vnode.props ?? {},
|
|
495
|
+
children: vnode.children ?? []
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
class SlotRenderStrategy {
|
|
501
|
+
renderedChildren = new WeakMap;
|
|
502
|
+
matches(vnode) {
|
|
503
|
+
return typeof vnode === "object" && vnode !== null && isSlotProvider(vnode);
|
|
504
|
+
}
|
|
505
|
+
mount(vnode, context) {
|
|
506
|
+
const slotContainer = document.createElement("div");
|
|
507
|
+
slotContainer.setAttribute("data-slot", vnode.props.name);
|
|
508
|
+
this.mountSlotChildren(slotContainer, this.resolveChildren(vnode, context), context);
|
|
509
|
+
return slotContainer;
|
|
510
|
+
}
|
|
511
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
512
|
+
if (currentNode instanceof HTMLElement) {
|
|
513
|
+
currentNode.setAttribute("data-slot", newVNode.props.name);
|
|
514
|
+
this.replaceSlotChildren(currentNode, oldVNode, newVNode, context);
|
|
515
|
+
}
|
|
516
|
+
return currentNode;
|
|
517
|
+
}
|
|
518
|
+
unmount(_vnode, currentNode, context) {
|
|
519
|
+
if (!(currentNode instanceof HTMLElement)) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
this.unmountSlotChildren(currentNode, context);
|
|
523
|
+
this.renderedChildren.delete(currentNode);
|
|
524
|
+
}
|
|
525
|
+
resolveChildren(vnode, context) {
|
|
526
|
+
return context.slots[vnode.props.name] ?? vnode.children ?? [];
|
|
527
|
+
}
|
|
528
|
+
replaceSlotChildren(element, _oldVNode, newVNode, context) {
|
|
529
|
+
this.unmountSlotChildren(element, context);
|
|
530
|
+
element.textContent = "";
|
|
531
|
+
this.mountSlotChildren(element, this.resolveChildren(newVNode, context), context);
|
|
532
|
+
}
|
|
533
|
+
mountSlotChildren(element, children, context) {
|
|
534
|
+
children.forEach((child) => {
|
|
535
|
+
element.appendChild(context.renderer.mount(child, context));
|
|
536
|
+
});
|
|
537
|
+
this.renderedChildren.set(element, children);
|
|
538
|
+
}
|
|
539
|
+
unmountSlotChildren(element, context) {
|
|
540
|
+
const children = this.renderedChildren.get(element) ?? [];
|
|
541
|
+
children.forEach((child, index) => {
|
|
542
|
+
const childNode = element.childNodes[index];
|
|
543
|
+
if (childNode) {
|
|
544
|
+
context.renderer.unmount(child, childNode, context);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
class ElementRenderStrategy {
|
|
551
|
+
listeners = new WeakMap;
|
|
552
|
+
effects = new WeakMap;
|
|
553
|
+
modelBindings = new WeakMap;
|
|
554
|
+
matches(vnode) {
|
|
555
|
+
return typeof vnode === "object" && vnode !== null && isHTMLNode(vnode);
|
|
556
|
+
}
|
|
557
|
+
mount(vnode, context) {
|
|
558
|
+
if (vnode.directions?.if === false) {
|
|
559
|
+
return document.createComment("if");
|
|
560
|
+
}
|
|
561
|
+
const element = document.createElement(vnode.tag);
|
|
562
|
+
this.applyProps(element, {}, vnode.props ?? {}, context);
|
|
563
|
+
this.applyDirections(element, undefined, vnode.directions, context);
|
|
564
|
+
this.updateListeners(element, {}, this.collectListeners(vnode));
|
|
565
|
+
(vnode.children ?? []).forEach((child) => {
|
|
566
|
+
element.appendChild(context.renderer.mount(child, context));
|
|
567
|
+
});
|
|
568
|
+
return element;
|
|
569
|
+
}
|
|
570
|
+
patch(oldVNode, newVNode, currentNode, context) {
|
|
571
|
+
if (oldVNode.tag !== newVNode.tag || currentNode.nodeType === Node.COMMENT_NODE) {
|
|
572
|
+
const nextNode = this.mount(newVNode, context);
|
|
573
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
574
|
+
this.unmount(oldVNode, currentNode, context);
|
|
575
|
+
return nextNode;
|
|
576
|
+
}
|
|
577
|
+
if (!(currentNode instanceof HTMLElement)) {
|
|
578
|
+
return currentNode;
|
|
579
|
+
}
|
|
580
|
+
if (newVNode.directions?.if === false) {
|
|
581
|
+
const nextNode = document.createComment("if");
|
|
582
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
583
|
+
this.unmount(oldVNode, currentNode, context);
|
|
584
|
+
return nextNode;
|
|
585
|
+
}
|
|
586
|
+
this.applyProps(currentNode, oldVNode.props ?? {}, newVNode.props ?? {}, context);
|
|
587
|
+
this.applyDirections(currentNode, oldVNode.directions, newVNode.directions, context);
|
|
588
|
+
this.updateListeners(currentNode, this.collectListeners(oldVNode), this.collectListeners(newVNode));
|
|
589
|
+
this.updateChildren(currentNode, oldVNode.children ?? [], newVNode.children ?? [], context);
|
|
590
|
+
return currentNode;
|
|
591
|
+
}
|
|
592
|
+
unmount(vnode, currentNode, context) {
|
|
593
|
+
if (!(currentNode instanceof HTMLElement)) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
this.effects.get(currentNode)?.forEach((item) => stop(item));
|
|
597
|
+
this.effects.delete(currentNode);
|
|
598
|
+
this.listeners.get(currentNode)?.forEach(({ eventName, listener }) => {
|
|
599
|
+
currentNode.removeEventListener(eventName, listener);
|
|
600
|
+
});
|
|
601
|
+
this.listeners.delete(currentNode);
|
|
602
|
+
this.modelBindings.delete(currentNode);
|
|
603
|
+
(vnode.children ?? []).forEach((child, index) => {
|
|
604
|
+
const childNode = currentNode.childNodes[index];
|
|
605
|
+
if (childNode) {
|
|
606
|
+
context.renderer.unmount(child, childNode, context);
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
applyProps(element, oldProps, newProps, context) {
|
|
611
|
+
Object.keys(oldProps).forEach((key) => {
|
|
612
|
+
if (isEventProp(key) || key in newProps) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (key === "className" || key === "class") {
|
|
616
|
+
element.removeAttribute("class");
|
|
617
|
+
} else if (key === "style") {
|
|
618
|
+
element.removeAttribute("style");
|
|
619
|
+
} else {
|
|
620
|
+
element.removeAttribute(key);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
Object.entries(newProps).forEach(([key, value]) => {
|
|
624
|
+
if (isEventProp(key)) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (key === "className" || key === "class") {
|
|
628
|
+
element.className = String(value ?? "");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (key === "style" && typeof value === "object" && value !== null) {
|
|
632
|
+
element.removeAttribute("style");
|
|
633
|
+
Object.entries(value).forEach(([cssKey, cssValue]) => {
|
|
634
|
+
setStyleValue(element.style, cssKey, cssValue);
|
|
635
|
+
});
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
if (value === false || value === undefined || value === null) {
|
|
639
|
+
element.removeAttribute(key);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (value === true) {
|
|
643
|
+
element.setAttribute(key, "");
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (typeof value === "string" && context.templateEngine.hasExpressions(value)) {
|
|
647
|
+
this.setupReactiveAttribute(element, key, value, context);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
element.setAttribute(key, String(value));
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
applyDirections(element, oldDirections, newDirections, context) {
|
|
654
|
+
if (newDirections && "show" in newDirections) {
|
|
655
|
+
element.style.display = newDirections.show ? "" : "none";
|
|
656
|
+
} else if (oldDirections && "show" in oldDirections) {
|
|
657
|
+
element.style.display = "";
|
|
658
|
+
}
|
|
659
|
+
if (newDirections?.model) {
|
|
660
|
+
this.setupTwoWayBinding(element, newDirections.model, context);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
updateChildren(element, oldChildren, newChildren, context) {
|
|
664
|
+
if (this.hasKeyedChildren(oldChildren, newChildren)) {
|
|
665
|
+
this.updateKeyedChildren(element, oldChildren, newChildren, context);
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const sharedLength = Math.min(oldChildren.length, newChildren.length);
|
|
669
|
+
for (let index = 0;index < sharedLength; index += 1) {
|
|
670
|
+
const childNode = element.childNodes[index];
|
|
671
|
+
if (!childNode) {
|
|
672
|
+
element.appendChild(context.renderer.mount(newChildren[index], context));
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
context.renderer.patch(oldChildren[index], newChildren[index], childNode, context);
|
|
676
|
+
}
|
|
677
|
+
for (let index = sharedLength;index < newChildren.length; index += 1) {
|
|
678
|
+
element.appendChild(context.renderer.mount(newChildren[index], context));
|
|
679
|
+
}
|
|
680
|
+
for (let index = oldChildren.length - 1;index >= newChildren.length; index -= 1) {
|
|
681
|
+
const childNode = element.childNodes[index];
|
|
682
|
+
if (childNode) {
|
|
683
|
+
context.renderer.unmount(oldChildren[index], childNode, context);
|
|
684
|
+
if (childNode.parentNode === element) {
|
|
685
|
+
element.removeChild(childNode);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
updateKeyedChildren(element, oldChildren, newChildren, context) {
|
|
691
|
+
const oldEntries = oldChildren.map((vnode, index) => ({
|
|
692
|
+
vnode,
|
|
693
|
+
node: element.childNodes[index],
|
|
694
|
+
index
|
|
695
|
+
}));
|
|
696
|
+
const keyedOldEntries = new Map;
|
|
697
|
+
const usedOldIndexes = new Set;
|
|
698
|
+
oldEntries.forEach((entry) => {
|
|
699
|
+
const key = this.getVNodeKey(entry.vnode);
|
|
700
|
+
if (key !== undefined && entry.node) {
|
|
701
|
+
keyedOldEntries.set(key, {
|
|
702
|
+
vnode: entry.vnode,
|
|
703
|
+
node: entry.node,
|
|
704
|
+
index: entry.index
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
newChildren.forEach((newChild, newIndex) => {
|
|
709
|
+
const key = this.getVNodeKey(newChild);
|
|
710
|
+
const oldEntry = key === undefined ? undefined : keyedOldEntries.get(key);
|
|
711
|
+
let nextNode;
|
|
712
|
+
if (oldEntry) {
|
|
713
|
+
nextNode = context.renderer.patch(oldEntry.vnode, newChild, oldEntry.node, context);
|
|
714
|
+
usedOldIndexes.add(oldEntry.index);
|
|
715
|
+
} else {
|
|
716
|
+
nextNode = context.renderer.mount(newChild, context);
|
|
717
|
+
}
|
|
718
|
+
const referenceNode = element.childNodes[newIndex] ?? null;
|
|
719
|
+
if (nextNode !== referenceNode) {
|
|
720
|
+
element.insertBefore(nextNode, referenceNode);
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
oldEntries.forEach((entry) => {
|
|
724
|
+
if (!entry.node || usedOldIndexes.has(entry.index)) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
context.renderer.unmount(entry.vnode, entry.node, context);
|
|
728
|
+
if (entry.node.parentNode === element) {
|
|
729
|
+
element.removeChild(entry.node);
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
hasKeyedChildren(oldChildren, newChildren) {
|
|
734
|
+
return [...oldChildren, ...newChildren].some((child) => this.getVNodeKey(child) !== undefined);
|
|
735
|
+
}
|
|
736
|
+
getVNodeKey(vnode) {
|
|
737
|
+
if (typeof vnode === "string") {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
return vnode.key;
|
|
741
|
+
}
|
|
742
|
+
collectListeners(vnode) {
|
|
743
|
+
const listeners = {};
|
|
744
|
+
Object.entries(vnode.props ?? {}).forEach(([key, value]) => {
|
|
745
|
+
if (isEventProp(key) && typeof value === "function") {
|
|
746
|
+
listeners[eventNameFromProp(key)] = value;
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
return {
|
|
750
|
+
...listeners,
|
|
751
|
+
...vnode.listeners ?? {}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
updateListeners(element, oldListeners, newListeners) {
|
|
755
|
+
const store = this.listeners.get(element) ?? new Map;
|
|
756
|
+
const oldKeys = new Set(Object.keys(oldListeners));
|
|
757
|
+
const newKeys = new Set(Object.keys(newListeners));
|
|
758
|
+
oldKeys.forEach((event) => {
|
|
759
|
+
if (!newKeys.has(event) || oldListeners[event] !== newListeners[event]) {
|
|
760
|
+
const stored = store.get(event);
|
|
761
|
+
if (stored) {
|
|
762
|
+
element.removeEventListener(stored.eventName, stored.listener);
|
|
763
|
+
store.delete(event);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
newKeys.forEach((event) => {
|
|
768
|
+
if (!oldKeys.has(event) || oldListeners[event] !== newListeners[event]) {
|
|
769
|
+
const { eventName, modifiers } = parseEventName(event);
|
|
770
|
+
const listener = wrapEventHandler(newListeners[event], modifiers);
|
|
771
|
+
element.addEventListener(eventName, listener);
|
|
772
|
+
store.set(event, { eventName, listener });
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
this.listeners.set(element, store);
|
|
776
|
+
}
|
|
777
|
+
setupReactiveAttribute(element, attrName, attrValue, context) {
|
|
778
|
+
const effectRef = effect(() => {
|
|
779
|
+
element.setAttribute(attrName, context.templateEngine.evaluateTemplateValue(attrValue));
|
|
780
|
+
});
|
|
781
|
+
this.trackEffect(element, effectRef);
|
|
782
|
+
}
|
|
783
|
+
setupTwoWayBinding(element, modelKey, context) {
|
|
784
|
+
if (!(element instanceof HTMLInputElement) && !(element instanceof HTMLTextAreaElement) && !(element instanceof HTMLSelectElement)) {
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (this.modelBindings.get(element) === modelKey) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
this.modelBindings.set(element, modelKey);
|
|
791
|
+
const getValue = () => {
|
|
792
|
+
const value = this.getStateValue(context, modelKey);
|
|
793
|
+
return value === undefined || value === null ? "" : String(value);
|
|
794
|
+
};
|
|
795
|
+
const setValue = (value) => {
|
|
796
|
+
const keys = modelKey.split(".");
|
|
797
|
+
let target = context.templateEngine.state;
|
|
798
|
+
for (let index = 0;index < keys.length - 1; index += 1) {
|
|
799
|
+
const key = keys[index];
|
|
800
|
+
if (!target[key] || typeof target[key] !== "object") {
|
|
801
|
+
target[key] = {};
|
|
802
|
+
}
|
|
803
|
+
target = target[key];
|
|
804
|
+
}
|
|
805
|
+
target[keys[keys.length - 1]] = value;
|
|
806
|
+
};
|
|
807
|
+
element.value = getValue();
|
|
808
|
+
const eventName = element instanceof HTMLSelectElement ? "change" : "input";
|
|
809
|
+
const inputListener = () => {
|
|
810
|
+
setValue(element.value);
|
|
811
|
+
};
|
|
812
|
+
element.addEventListener(eventName, inputListener);
|
|
813
|
+
const store = this.listeners.get(element) ?? new Map;
|
|
814
|
+
store.set(`model:${modelKey}`, { eventName, listener: inputListener });
|
|
815
|
+
this.listeners.set(element, store);
|
|
816
|
+
const effectRef = effect(() => {
|
|
817
|
+
const nextValue = getValue();
|
|
818
|
+
if (element.value !== nextValue) {
|
|
819
|
+
element.value = nextValue;
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
this.trackEffect(element, effectRef);
|
|
823
|
+
}
|
|
824
|
+
getStateValue(context, modelKey) {
|
|
825
|
+
return modelKey.split(".").reduce((value, key) => {
|
|
826
|
+
if (!value || typeof value !== "object") {
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
return value[key];
|
|
830
|
+
}, context.templateEngine.state);
|
|
831
|
+
}
|
|
832
|
+
trackEffect(element, effectRef) {
|
|
833
|
+
const effects = this.effects.get(element) ?? new Set;
|
|
834
|
+
effects.add(effectRef);
|
|
835
|
+
this.effects.set(element, effects);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// lib/core/template.ts
|
|
840
|
+
class TemplateEngine {
|
|
841
|
+
state;
|
|
842
|
+
bindings = [];
|
|
843
|
+
templateRegex = /{{(.*?)}}/g;
|
|
844
|
+
constructor(state) {
|
|
845
|
+
this.state = state;
|
|
846
|
+
if (!state || typeof state !== "object") {
|
|
847
|
+
throw new Error("TemplateEngine requires a valid state object");
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
parseTemplate(text) {
|
|
851
|
+
if (typeof text !== "string") {
|
|
852
|
+
text = String(text);
|
|
853
|
+
}
|
|
854
|
+
const textNode = document.createTextNode("");
|
|
855
|
+
this.templateRegex.lastIndex = 0;
|
|
856
|
+
const matches = Array.from(text.matchAll(this.templateRegex));
|
|
857
|
+
if (matches && matches.length > 0) {
|
|
858
|
+
this.setupReactiveBindings(textNode, text, matches);
|
|
859
|
+
} else {
|
|
860
|
+
textNode.textContent = text;
|
|
861
|
+
}
|
|
862
|
+
return textNode;
|
|
863
|
+
}
|
|
864
|
+
setupReactiveBindings(node, originalText, matches) {
|
|
865
|
+
const keys = new Set;
|
|
866
|
+
const initialText = this.evaluateTemplate(originalText, matches, keys);
|
|
867
|
+
node.textContent = initialText;
|
|
868
|
+
const effectFn = effect(() => {
|
|
869
|
+
try {
|
|
870
|
+
const updatedText = this.evaluateTemplate(originalText, matches, keys);
|
|
871
|
+
if (node.textContent !== updatedText) {
|
|
872
|
+
node.textContent = updatedText;
|
|
873
|
+
}
|
|
874
|
+
} catch (error) {
|
|
875
|
+
console.error("Template update error:", error);
|
|
876
|
+
node.textContent = `Error: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
this.bindings.push({
|
|
880
|
+
node,
|
|
881
|
+
originalText,
|
|
882
|
+
effect: effectFn
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
evaluateTemplate(text, matches, keys) {
|
|
886
|
+
let result = text;
|
|
887
|
+
matches.forEach((match) => {
|
|
888
|
+
const key = match[1]?.trim();
|
|
889
|
+
if (key) {
|
|
890
|
+
keys.add(key);
|
|
891
|
+
const value = this.getValueFromState(key);
|
|
892
|
+
const displayValue = value === undefined || value === null ? "" : String(value);
|
|
893
|
+
result = result.replace(match[0], displayValue);
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
return result;
|
|
897
|
+
}
|
|
898
|
+
getValueFromState(keyPath) {
|
|
899
|
+
if (!keyPath)
|
|
900
|
+
return;
|
|
901
|
+
const keys = keyPath.split(".");
|
|
902
|
+
let value = this.state;
|
|
903
|
+
for (const key of keys) {
|
|
904
|
+
if (!value || typeof value !== "object") {
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
value = value[key];
|
|
908
|
+
}
|
|
909
|
+
return value;
|
|
910
|
+
}
|
|
911
|
+
clearBindings() {
|
|
912
|
+
this.bindings.forEach((binding) => {
|
|
913
|
+
binding.effect.active = false;
|
|
914
|
+
});
|
|
915
|
+
this.bindings = [];
|
|
916
|
+
}
|
|
917
|
+
getBindingCount() {
|
|
918
|
+
return this.bindings.length;
|
|
919
|
+
}
|
|
920
|
+
hasExpressions(text) {
|
|
921
|
+
this.templateRegex.lastIndex = 0;
|
|
922
|
+
return this.templateRegex.test(text);
|
|
923
|
+
}
|
|
924
|
+
extractKeys(text) {
|
|
925
|
+
const keys = [];
|
|
926
|
+
let match;
|
|
927
|
+
const regex = new RegExp(this.templateRegex, "g");
|
|
928
|
+
while ((match = regex.exec(text)) !== null) {
|
|
929
|
+
const key = match[1]?.trim();
|
|
930
|
+
if (key) {
|
|
931
|
+
keys.push(key);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return keys;
|
|
935
|
+
}
|
|
936
|
+
evaluateTemplateValue(text) {
|
|
937
|
+
this.templateRegex.lastIndex = 0;
|
|
938
|
+
const matches = Array.from(text.matchAll(this.templateRegex));
|
|
939
|
+
if (!matches || matches.length === 0) {
|
|
940
|
+
return text;
|
|
941
|
+
}
|
|
942
|
+
const keys = new Set;
|
|
943
|
+
return this.evaluateTemplate(text, matches, keys);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// lib/core/component/base.ts
|
|
948
|
+
class Component {
|
|
949
|
+
props;
|
|
950
|
+
vnode = null;
|
|
951
|
+
el = null;
|
|
952
|
+
renderer = new RendererContext;
|
|
953
|
+
templateEngine;
|
|
954
|
+
childComponents = new Set;
|
|
955
|
+
eventListeners = {};
|
|
956
|
+
updateEffect;
|
|
957
|
+
appContext = null;
|
|
958
|
+
styleManager;
|
|
959
|
+
state;
|
|
960
|
+
mounted = false;
|
|
961
|
+
constructor(props = {}) {
|
|
962
|
+
this.props = props;
|
|
963
|
+
this.styleManager = new StyleManager;
|
|
964
|
+
this.state = reactive(this.initState() ?? {});
|
|
965
|
+
this.templateEngine = new TemplateEngine(this.state);
|
|
966
|
+
this.initStyles();
|
|
967
|
+
this.updateEffect = effect(() => {
|
|
968
|
+
this.trackStateProperties();
|
|
969
|
+
if (this.mounted) {
|
|
970
|
+
this.update();
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
mount(container) {
|
|
975
|
+
if (!container || !(container instanceof HTMLElement)) {
|
|
976
|
+
throw new Error("Invalid container element");
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
container.appendChild(this.mountToNode());
|
|
980
|
+
} catch (error) {
|
|
981
|
+
console.error("组件渲染错误:", error);
|
|
982
|
+
throw error;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
mountToNode() {
|
|
986
|
+
if (this.mounted && this.el) {
|
|
987
|
+
return this.el;
|
|
988
|
+
}
|
|
989
|
+
this.beforeMount();
|
|
990
|
+
this.vnode = this.render();
|
|
991
|
+
this.el = this.renderer.mount(this.vnode, this.createRenderContext());
|
|
992
|
+
this.mounted = true;
|
|
993
|
+
this.onMounted();
|
|
994
|
+
return this.el;
|
|
995
|
+
}
|
|
996
|
+
update() {
|
|
997
|
+
if (!this.el || !this.vnode) {
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
try {
|
|
1001
|
+
this.beforeUpdate();
|
|
1002
|
+
const newVNode = this.render();
|
|
1003
|
+
this.el = this.renderer.patch(this.vnode, newVNode, this.el, this.createRenderContext());
|
|
1004
|
+
this.vnode = newVNode;
|
|
1005
|
+
this.onUpdated();
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
console.error("组件更新错误:", error);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
unmount() {
|
|
1011
|
+
if (!this.mounted) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
this.beforeUnmount();
|
|
1015
|
+
if (this.vnode && this.el) {
|
|
1016
|
+
this.renderer.unmount(this.vnode, this.el, this.createRenderContext());
|
|
1017
|
+
}
|
|
1018
|
+
this.childComponents.clear();
|
|
1019
|
+
this.templateEngine.clearBindings();
|
|
1020
|
+
this.styleManager.clearStyles();
|
|
1021
|
+
stop(this.updateEffect);
|
|
1022
|
+
if (this.el?.parentNode) {
|
|
1023
|
+
this.el.parentNode.removeChild(this.el);
|
|
1024
|
+
}
|
|
1025
|
+
this.el = null;
|
|
1026
|
+
this.vnode = null;
|
|
1027
|
+
this.mounted = false;
|
|
1028
|
+
this.onUnmounted();
|
|
1029
|
+
}
|
|
1030
|
+
setProps(props) {
|
|
1031
|
+
this.props = {
|
|
1032
|
+
...this.props,
|
|
1033
|
+
...props
|
|
1034
|
+
};
|
|
1035
|
+
if (this.mounted) {
|
|
1036
|
+
this.update();
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
setState(state) {
|
|
1040
|
+
Object.assign(this.state, state);
|
|
1041
|
+
}
|
|
1042
|
+
setAppContext(context) {
|
|
1043
|
+
this.appContext = context;
|
|
1044
|
+
this.childComponents.forEach((child) => {
|
|
1045
|
+
child.setAppContext?.(context);
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
getElement() {
|
|
1049
|
+
return this.el;
|
|
1050
|
+
}
|
|
1051
|
+
beforeMount() {}
|
|
1052
|
+
onMounted() {}
|
|
1053
|
+
beforeUpdate() {}
|
|
1054
|
+
onUpdated() {}
|
|
1055
|
+
beforeUnmount() {}
|
|
1056
|
+
onUnmounted() {}
|
|
1057
|
+
getContext() {
|
|
1058
|
+
return this.appContext;
|
|
1059
|
+
}
|
|
1060
|
+
get router() {
|
|
1061
|
+
return this.getRouterFrom(this.appContext) ?? this.getRouterFromGlobalApp();
|
|
1062
|
+
}
|
|
1063
|
+
emit(eventName, ...args) {
|
|
1064
|
+
this.eventListeners[eventName]?.forEach((listener) => {
|
|
1065
|
+
listener(...args);
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
on(eventName, listener) {
|
|
1069
|
+
if (!this.eventListeners[eventName]) {
|
|
1070
|
+
this.eventListeners[eventName] = new Set;
|
|
1071
|
+
}
|
|
1072
|
+
this.eventListeners[eventName].add(listener);
|
|
1073
|
+
}
|
|
1074
|
+
off(eventName, listener) {
|
|
1075
|
+
this.eventListeners[eventName]?.delete(listener);
|
|
1076
|
+
}
|
|
1077
|
+
createRenderContext() {
|
|
1078
|
+
return {
|
|
1079
|
+
appContext: this.appContext,
|
|
1080
|
+
templateEngine: this.templateEngine,
|
|
1081
|
+
renderer: this.renderer,
|
|
1082
|
+
slots: this.collectSlots(),
|
|
1083
|
+
registerChild: (component) => {
|
|
1084
|
+
this.childComponents.add(component);
|
|
1085
|
+
component.setAppContext?.(this.appContext);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
collectSlots() {
|
|
1090
|
+
const slots = { default: [] };
|
|
1091
|
+
const children = this.props.children ?? [];
|
|
1092
|
+
children.forEach((child) => {
|
|
1093
|
+
const slotName = this.getSlotName(child);
|
|
1094
|
+
if (!slots[slotName]) {
|
|
1095
|
+
slots[slotName] = [];
|
|
1096
|
+
}
|
|
1097
|
+
slots[slotName].push(this.normalizeSlotChild(child));
|
|
1098
|
+
});
|
|
1099
|
+
return slots;
|
|
1100
|
+
}
|
|
1101
|
+
getSlotName(child) {
|
|
1102
|
+
if (typeof child === "string") {
|
|
1103
|
+
return "default";
|
|
1104
|
+
}
|
|
1105
|
+
return "slot" in child && typeof child.slot === "string" ? child.slot : "default";
|
|
1106
|
+
}
|
|
1107
|
+
normalizeSlotChild(child) {
|
|
1108
|
+
if (typeof child === "string" || !("slot" in child)) {
|
|
1109
|
+
return child;
|
|
1110
|
+
}
|
|
1111
|
+
const clone = { ...child };
|
|
1112
|
+
delete clone.slot;
|
|
1113
|
+
return clone;
|
|
1114
|
+
}
|
|
1115
|
+
trackStateProperties() {
|
|
1116
|
+
this.trackReactiveValue(this.state, new Set);
|
|
1117
|
+
}
|
|
1118
|
+
getRouterFrom(value) {
|
|
1119
|
+
if (!value || typeof value !== "object" || !("router" in value)) {
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
return value.router;
|
|
1123
|
+
}
|
|
1124
|
+
getRouterFromGlobalApp() {
|
|
1125
|
+
const globalApp = globalThis.__APP__;
|
|
1126
|
+
return this.getRouterFrom(globalApp);
|
|
1127
|
+
}
|
|
1128
|
+
trackReactiveValue(value, seen) {
|
|
1129
|
+
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
seen.add(value);
|
|
1133
|
+
if (Array.isArray(value)) {
|
|
1134
|
+
value.length;
|
|
1135
|
+
}
|
|
1136
|
+
Object.keys(value).forEach((key) => {
|
|
1137
|
+
const child = value[key];
|
|
1138
|
+
this.trackReactiveValue(child, seen);
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
// lib/router/instance.ts
|
|
1143
|
+
var router = null;
|
|
1144
|
+
function setRouter(r) {
|
|
1145
|
+
if (!r || !(r instanceof Router)) {
|
|
1146
|
+
throw new Error("Invalid router instance");
|
|
1147
|
+
}
|
|
1148
|
+
router = r;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// lib/router/matcher.ts
|
|
1152
|
+
function normalizePath(path) {
|
|
1153
|
+
if (!path.startsWith("/")) {
|
|
1154
|
+
return `/${path}`;
|
|
1155
|
+
}
|
|
1156
|
+
return path || "/";
|
|
1157
|
+
}
|
|
1158
|
+
function matchRoute(routes, path) {
|
|
1159
|
+
const normalizedPath = normalizePath(path.split("?")[0]);
|
|
1160
|
+
for (const route of routes) {
|
|
1161
|
+
const params = matchRoutePath(route.path, normalizedPath);
|
|
1162
|
+
if (params) {
|
|
1163
|
+
return { route, params };
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const fallback = routes.find((route) => route.path === "/");
|
|
1167
|
+
return fallback ? { route: fallback, params: {} } : null;
|
|
1168
|
+
}
|
|
1169
|
+
function matchRoutePath(routePath, currentPath) {
|
|
1170
|
+
const routeSegments = getPathSegments(routePath);
|
|
1171
|
+
const currentSegments = getPathSegments(currentPath);
|
|
1172
|
+
if (routeSegments.length !== currentSegments.length) {
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
const params = {};
|
|
1176
|
+
for (let index = 0;index < routeSegments.length; index += 1) {
|
|
1177
|
+
const routeSegment = routeSegments[index];
|
|
1178
|
+
const currentSegment = currentSegments[index];
|
|
1179
|
+
if (routeSegment.startsWith(":")) {
|
|
1180
|
+
const paramName = routeSegment.slice(1);
|
|
1181
|
+
if (!paramName) {
|
|
1182
|
+
return null;
|
|
1183
|
+
}
|
|
1184
|
+
params[decodeURIComponent(paramName)] = decodeURIComponent(currentSegment);
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
if (routeSegment !== currentSegment) {
|
|
1188
|
+
return null;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
return params;
|
|
1192
|
+
}
|
|
1193
|
+
function getPathSegments(path) {
|
|
1194
|
+
const normalizedPath = normalizePath(path);
|
|
1195
|
+
if (normalizedPath === "/") {
|
|
1196
|
+
return [];
|
|
1197
|
+
}
|
|
1198
|
+
return normalizedPath.split("/").filter(Boolean);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// lib/router/history.ts
|
|
1202
|
+
function createRouterHref(path, mode, base) {
|
|
1203
|
+
const normalizedPath = normalizePath(path);
|
|
1204
|
+
const fullPath = base === "/" ? normalizedPath : base + normalizedPath;
|
|
1205
|
+
return mode === "hash" ? `#${fullPath}` : fullPath;
|
|
1206
|
+
}
|
|
1207
|
+
function getBrowserLocation(mode, base) {
|
|
1208
|
+
let path;
|
|
1209
|
+
let fullPath;
|
|
1210
|
+
if (mode === "history") {
|
|
1211
|
+
fullPath = window.location.pathname + window.location.search;
|
|
1212
|
+
path = window.location.pathname;
|
|
1213
|
+
} else {
|
|
1214
|
+
const hash = window.location.hash;
|
|
1215
|
+
fullPath = hash || "#/";
|
|
1216
|
+
path = fullPath.startsWith("#") ? fullPath.slice(1) : fullPath;
|
|
1217
|
+
}
|
|
1218
|
+
if (path.startsWith(base) && base !== "/" && path !== "/") {
|
|
1219
|
+
path = path.slice(base.length);
|
|
1220
|
+
}
|
|
1221
|
+
path = normalizePath(path);
|
|
1222
|
+
return {
|
|
1223
|
+
path,
|
|
1224
|
+
fullPath,
|
|
1225
|
+
query: parseQuery(mode === "hash" ? path.split("?")[1] ?? "" : window.location.search),
|
|
1226
|
+
params: {}
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
function navigateBrowser(path, replace, mode, base) {
|
|
1230
|
+
const normalizedPath = normalizePath(path);
|
|
1231
|
+
const fullPath = base === "/" ? normalizedPath : base + normalizedPath;
|
|
1232
|
+
if (mode === "history") {
|
|
1233
|
+
if (replace) {
|
|
1234
|
+
window.history.replaceState({}, "", fullPath);
|
|
1235
|
+
} else {
|
|
1236
|
+
window.history.pushState({}, "", fullPath);
|
|
1237
|
+
}
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
if (replace) {
|
|
1241
|
+
const href = window.location.href.split("#")[0];
|
|
1242
|
+
window.location.replace(`${href}#${fullPath}`);
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
window.location.hash = fullPath;
|
|
1246
|
+
}
|
|
1247
|
+
function parseQuery(queryString) {
|
|
1248
|
+
const query = {};
|
|
1249
|
+
const normalizedQuery = queryString.startsWith("?") ? queryString.slice(1) : queryString;
|
|
1250
|
+
if (!normalizedQuery) {
|
|
1251
|
+
return query;
|
|
1252
|
+
}
|
|
1253
|
+
normalizedQuery.split("&").forEach((param) => {
|
|
1254
|
+
const [key, value] = param.split("=");
|
|
1255
|
+
if (key) {
|
|
1256
|
+
query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : "";
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
return query;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// lib/router/index.ts
|
|
1263
|
+
class Router {
|
|
1264
|
+
currentRoute = null;
|
|
1265
|
+
currentLocation = null;
|
|
1266
|
+
routes = [];
|
|
1267
|
+
app = null;
|
|
1268
|
+
mode;
|
|
1269
|
+
base;
|
|
1270
|
+
routeChangeListeners = [];
|
|
1271
|
+
removeWindowListener;
|
|
1272
|
+
constructor(options) {
|
|
1273
|
+
const resolvedOptions = Array.isArray(options) ? { routes: options } : options;
|
|
1274
|
+
this.routes = resolvedOptions.routes || [];
|
|
1275
|
+
this.mode = resolvedOptions.mode || "history";
|
|
1276
|
+
this.base = resolvedOptions.base || "/";
|
|
1277
|
+
this.validateRoutes();
|
|
1278
|
+
this.initEvents();
|
|
1279
|
+
this.resolveCurrentRoute();
|
|
1280
|
+
}
|
|
1281
|
+
install(app) {
|
|
1282
|
+
this.app = app;
|
|
1283
|
+
app.router = this;
|
|
1284
|
+
setRouter(this);
|
|
1285
|
+
const context = app.getContext();
|
|
1286
|
+
context.router = this;
|
|
1287
|
+
this.resolveCurrentRoute();
|
|
1288
|
+
}
|
|
1289
|
+
push(path) {
|
|
1290
|
+
this.navigate(path, false);
|
|
1291
|
+
}
|
|
1292
|
+
replace(path) {
|
|
1293
|
+
this.navigate(path, true);
|
|
1294
|
+
}
|
|
1295
|
+
forward() {
|
|
1296
|
+
window.history.forward();
|
|
1297
|
+
}
|
|
1298
|
+
back() {
|
|
1299
|
+
window.history.back();
|
|
1300
|
+
}
|
|
1301
|
+
go(delta) {
|
|
1302
|
+
window.history.go(delta);
|
|
1303
|
+
}
|
|
1304
|
+
getCurrentRoute() {
|
|
1305
|
+
if (!this.currentLocation) {
|
|
1306
|
+
this.resolveCurrentRoute();
|
|
1307
|
+
}
|
|
1308
|
+
return this.currentLocation;
|
|
1309
|
+
}
|
|
1310
|
+
getCurrentRouteRecord() {
|
|
1311
|
+
if (!this.currentRoute) {
|
|
1312
|
+
this.resolveCurrentRoute();
|
|
1313
|
+
}
|
|
1314
|
+
return this.currentRoute;
|
|
1315
|
+
}
|
|
1316
|
+
onRouteChange(listener) {
|
|
1317
|
+
this.routeChangeListeners.push(listener);
|
|
1318
|
+
return () => {
|
|
1319
|
+
const index = this.routeChangeListeners.indexOf(listener);
|
|
1320
|
+
if (index > -1) {
|
|
1321
|
+
this.routeChangeListeners.splice(index, 1);
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
getRoutes() {
|
|
1326
|
+
return [...this.routes];
|
|
1327
|
+
}
|
|
1328
|
+
addRoute(route) {
|
|
1329
|
+
if (this.routes.some((item) => item.path === route.path)) {
|
|
1330
|
+
throw new Error(`Route already exists: ${route.path}`);
|
|
1331
|
+
}
|
|
1332
|
+
this.routes.push(route);
|
|
1333
|
+
const location = this.getCurrentLocation();
|
|
1334
|
+
if (location.path === route.path) {
|
|
1335
|
+
this.handleRouteChange();
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
createHref(path) {
|
|
1339
|
+
return createRouterHref(path, this.mode, this.base);
|
|
1340
|
+
}
|
|
1341
|
+
destroy() {
|
|
1342
|
+
this.removeWindowListener?.();
|
|
1343
|
+
this.removeWindowListener = undefined;
|
|
1344
|
+
if (this.app?.router === this) {
|
|
1345
|
+
this.app.router = undefined;
|
|
1346
|
+
}
|
|
1347
|
+
this.app = null;
|
|
1348
|
+
}
|
|
1349
|
+
navigate(path, replace) {
|
|
1350
|
+
if (!path || typeof path !== "string") {
|
|
1351
|
+
throw new Error("Path must be a non-empty string");
|
|
1352
|
+
}
|
|
1353
|
+
navigateBrowser(path, replace, this.mode, this.base);
|
|
1354
|
+
this.handleRouteChange();
|
|
1355
|
+
}
|
|
1356
|
+
validateRoutes() {
|
|
1357
|
+
if (!Array.isArray(this.routes)) {
|
|
1358
|
+
throw new Error("Router routes must be an array");
|
|
1359
|
+
}
|
|
1360
|
+
const paths = new Set;
|
|
1361
|
+
this.routes.forEach((route) => {
|
|
1362
|
+
if (paths.has(route.path)) {
|
|
1363
|
+
throw new Error(`Duplicate route path: ${route.path}`);
|
|
1364
|
+
}
|
|
1365
|
+
paths.add(route.path);
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
initEvents() {
|
|
1369
|
+
if (typeof window === "undefined") {
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const eventName = this.mode === "history" ? "popstate" : "hashchange";
|
|
1373
|
+
const listener = () => {
|
|
1374
|
+
this.handleRouteChange();
|
|
1375
|
+
};
|
|
1376
|
+
window.addEventListener(eventName, listener);
|
|
1377
|
+
this.removeWindowListener = () => {
|
|
1378
|
+
window.removeEventListener(eventName, listener);
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
handleRouteChange() {
|
|
1382
|
+
const fromLocation = this.currentLocation;
|
|
1383
|
+
const nextLocation = this.resolveCurrentRoute();
|
|
1384
|
+
if (!this.isSameLocation(fromLocation, nextLocation)) {
|
|
1385
|
+
this.triggerRouteChangeListeners(nextLocation, fromLocation);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
resolveCurrentRoute() {
|
|
1389
|
+
const location = this.getCurrentLocation();
|
|
1390
|
+
const match = matchRoute(this.routes, location.path);
|
|
1391
|
+
const route = match?.route ?? null;
|
|
1392
|
+
this.currentRoute = route;
|
|
1393
|
+
this.currentLocation = {
|
|
1394
|
+
...location,
|
|
1395
|
+
params: match?.params ?? {},
|
|
1396
|
+
name: route?.name,
|
|
1397
|
+
meta: route?.meta
|
|
1398
|
+
};
|
|
1399
|
+
return this.currentLocation;
|
|
1400
|
+
}
|
|
1401
|
+
getCurrentLocation() {
|
|
1402
|
+
return getBrowserLocation(this.mode, this.base);
|
|
1403
|
+
}
|
|
1404
|
+
isSameLocation(from, to) {
|
|
1405
|
+
return from?.fullPath === to?.fullPath && from?.name === to?.name;
|
|
1406
|
+
}
|
|
1407
|
+
triggerRouteChangeListeners(to, from) {
|
|
1408
|
+
this.routeChangeListeners.forEach((listener) => {
|
|
1409
|
+
try {
|
|
1410
|
+
listener(to, from);
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
console.error("Route change listener error:", error);
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
class RouterLink extends Component {
|
|
1419
|
+
unsubscribe;
|
|
1420
|
+
initState() {
|
|
1421
|
+
const router2 = this.router;
|
|
1422
|
+
return {
|
|
1423
|
+
currentPath: router2?.getCurrentRoute()?.path ?? window.location.pathname
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
initStyles() {}
|
|
1427
|
+
onMounted() {
|
|
1428
|
+
const router2 = this.router;
|
|
1429
|
+
this.unsubscribe = router2?.onRouteChange((to) => {
|
|
1430
|
+
this.state.currentPath = to.path;
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
onUnmounted() {
|
|
1434
|
+
this.unsubscribe?.();
|
|
1435
|
+
}
|
|
1436
|
+
render() {
|
|
1437
|
+
const router2 = this.router;
|
|
1438
|
+
const activeClass = this.props.activeClass ?? "active";
|
|
1439
|
+
const isActive = this.state.currentPath === this.props.to;
|
|
1440
|
+
const className = [this.props.className, isActive ? activeClass : undefined].filter(Boolean).join(" ");
|
|
1441
|
+
return {
|
|
1442
|
+
tag: "a",
|
|
1443
|
+
props: {
|
|
1444
|
+
href: router2?.createHref(this.props.to) ?? this.props.to,
|
|
1445
|
+
className
|
|
1446
|
+
},
|
|
1447
|
+
listeners: {
|
|
1448
|
+
click: (event) => {
|
|
1449
|
+
event.preventDefault();
|
|
1450
|
+
if (this.props.replace) {
|
|
1451
|
+
router2?.replace(this.props.to);
|
|
1452
|
+
} else {
|
|
1453
|
+
router2?.push(this.props.to);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
},
|
|
1457
|
+
children: this.props.children && this.props.children.length > 0 ? this.props.children : [this.props.to]
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
class RouterView extends Component {
|
|
1463
|
+
unsubscribe;
|
|
1464
|
+
initState() {
|
|
1465
|
+
const router2 = this.router;
|
|
1466
|
+
return {
|
|
1467
|
+
route: router2?.getCurrentRoute() ?? null,
|
|
1468
|
+
record: router2?.getCurrentRouteRecord() ?? null
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
initStyles() {}
|
|
1472
|
+
onMounted() {
|
|
1473
|
+
const router2 = this.router;
|
|
1474
|
+
this.unsubscribe = router2?.onRouteChange((to) => {
|
|
1475
|
+
this.state.route = to;
|
|
1476
|
+
this.state.record = router2.getCurrentRouteRecord();
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
onUnmounted() {
|
|
1480
|
+
this.unsubscribe?.();
|
|
1481
|
+
}
|
|
1482
|
+
render() {
|
|
1483
|
+
const routeRecord = this.state.record ?? this.router?.getCurrentRouteRecord();
|
|
1484
|
+
return {
|
|
1485
|
+
tag: "div",
|
|
1486
|
+
props: { "data-router-view": "" },
|
|
1487
|
+
children: routeRecord ? [{ component: routeRecord.component }] : []
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
function createRouter(options) {
|
|
1492
|
+
return new Router(options);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
export { ReactiveSystem, reactive, readonly, effect, computed, stop, isReactive, isReadonly, TemplateEngine, isComponentNode, isHTMLNode, isSlotProvider, h, createComponent, slot, RendererContext, TextRenderStrategy, ComponentRenderStrategy, SlotRenderStrategy, ElementRenderStrategy, Component, Router, RouterLink, RouterView, createRouter };
|
|
1496
|
+
|
|
1497
|
+
//# debugId=97822D95A36A4F3E64756E2164756E21
|
|
1498
|
+
//# sourceMappingURL=index-3j2jsdpc.js.map
|