@geektech/tsone 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -5
- package/dist/core/app.d.ts +6 -1
- package/dist/core/component/base.d.ts +18 -1
- package/dist/core/component/index.d.ts +2 -2
- package/dist/core/document.d.ts +32 -0
- package/dist/core/form.d.ts +24 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/model.d.ts +18 -0
- package/dist/core/reactive/types.d.ts +5 -0
- package/dist/core/reactive.d.ts +5 -2
- package/dist/core/renderer/types.d.ts +6 -1
- package/dist/core/renderer.d.ts +9 -4
- package/dist/core/vnode.d.ts +20 -16
- package/dist/{index-dgv88dz4.js → index-8wjswsye.js} +6 -2
- package/dist/index-8wjswsye.js.map +10 -0
- package/dist/index-vpx80nq5.js +32 -0
- package/dist/index-vpx80nq5.js.map +10 -0
- package/dist/{index-3j2jsdpc.js → index-ycmc7ga1.js} +413 -93
- package/dist/index-ycmc7ga1.js.map +21 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +214 -7
- package/dist/index.js.map +7 -5
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.js +6 -4
- package/dist/router/index.js.map +1 -1
- package/dist/style/StyleManager.d.ts +1 -0
- package/dist/style/index.d.ts +1 -0
- package/dist/style/index.js +6 -2
- package/dist/style/index.js.map +1 -1
- package/dist/style/sheet.d.ts +13 -0
- package/package.json +3 -13
- package/dist/index-3j2jsdpc.js.map +0 -20
- package/dist/index-dgv88dz4.js.map +0 -10
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
StyleManager
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-8wjswsye.js";
|
|
4
4
|
|
|
5
5
|
// lib/core/reactive/types.ts
|
|
6
6
|
var IS_REACTIVE = Symbol("is_reactive");
|
|
7
7
|
var IS_READONLY = Symbol("is_readonly");
|
|
8
|
+
var IS_REF = Symbol("is_ref");
|
|
8
9
|
var MUTATING_ARRAY_METHODS = [
|
|
9
10
|
"push",
|
|
10
11
|
"pop",
|
|
@@ -62,7 +63,7 @@ class ReactiveSystem {
|
|
|
62
63
|
}
|
|
63
64
|
this.track(target2, key);
|
|
64
65
|
const value = Reflect.get(target2, key);
|
|
65
|
-
if (value &&
|
|
66
|
+
if (isObject(value) && !hasReactiveFlag(value, IS_READONLY)) {
|
|
66
67
|
return this.reactive(value);
|
|
67
68
|
}
|
|
68
69
|
return value;
|
|
@@ -73,7 +74,7 @@ class ReactiveSystem {
|
|
|
73
74
|
return false;
|
|
74
75
|
}
|
|
75
76
|
const oldValue = Reflect.get(target2, key);
|
|
76
|
-
if (value &&
|
|
77
|
+
if (isObject(value) && !hasReactiveFlag(value, IS_REACTIVE) && !hasReactiveFlag(value, IS_READONLY)) {
|
|
77
78
|
value = this.reactive(value);
|
|
78
79
|
}
|
|
79
80
|
const result = Reflect.set(target2, key, value);
|
|
@@ -121,6 +122,9 @@ class ReactiveSystem {
|
|
|
121
122
|
return result;
|
|
122
123
|
};
|
|
123
124
|
}
|
|
125
|
+
if (isObject(value) && !hasReactiveFlag(value, IS_READONLY)) {
|
|
126
|
+
return this.reactive(value);
|
|
127
|
+
}
|
|
124
128
|
return value;
|
|
125
129
|
},
|
|
126
130
|
set: (target2, key, value) => {
|
|
@@ -129,7 +133,7 @@ class ReactiveSystem {
|
|
|
129
133
|
return false;
|
|
130
134
|
}
|
|
131
135
|
const oldValue = Reflect.get(target2, key);
|
|
132
|
-
if (value &&
|
|
136
|
+
if (isObject(value) && !hasReactiveFlag(value, IS_REACTIVE) && !hasReactiveFlag(value, IS_READONLY)) {
|
|
133
137
|
value = this.reactive(value);
|
|
134
138
|
}
|
|
135
139
|
const result = Reflect.set(target2, key, value);
|
|
@@ -171,11 +175,14 @@ class ReactiveSystem {
|
|
|
171
175
|
}
|
|
172
176
|
const proxy = new Proxy(target, {
|
|
173
177
|
get: (target2, key) => {
|
|
174
|
-
if (key === IS_REACTIVE
|
|
178
|
+
if (key === IS_REACTIVE) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (key === IS_READONLY) {
|
|
175
182
|
return true;
|
|
176
183
|
}
|
|
177
184
|
const value = Reflect.get(target2, key);
|
|
178
|
-
if (
|
|
185
|
+
if (isObject(value)) {
|
|
179
186
|
return this.readonly(value);
|
|
180
187
|
}
|
|
181
188
|
return value;
|
|
@@ -193,7 +200,7 @@ class ReactiveSystem {
|
|
|
193
200
|
return proxy;
|
|
194
201
|
}
|
|
195
202
|
effect(fn, options) {
|
|
196
|
-
const { lazy = false, scheduler } = options || {};
|
|
203
|
+
const { lazy = false, scheduler, throwOnError = false } = options || {};
|
|
197
204
|
const effectFn = () => {
|
|
198
205
|
if (!effectFn.active) {
|
|
199
206
|
return fn();
|
|
@@ -204,6 +211,9 @@ class ReactiveSystem {
|
|
|
204
211
|
this.activeEffect = effectFn;
|
|
205
212
|
return fn();
|
|
206
213
|
} catch (error) {
|
|
214
|
+
if (throwOnError) {
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
207
217
|
console.error("Effect error:", error);
|
|
208
218
|
return;
|
|
209
219
|
} finally {
|
|
@@ -310,6 +320,21 @@ function effect(fn, options) {
|
|
|
310
320
|
function computed(getter) {
|
|
311
321
|
return ReactiveSystem.getInstance().computed(getter);
|
|
312
322
|
}
|
|
323
|
+
function ref(value) {
|
|
324
|
+
const wrapper = { value };
|
|
325
|
+
Object.defineProperty(wrapper, IS_REF, {
|
|
326
|
+
configurable: false,
|
|
327
|
+
enumerable: false,
|
|
328
|
+
value: true
|
|
329
|
+
});
|
|
330
|
+
return reactive(wrapper);
|
|
331
|
+
}
|
|
332
|
+
function isRef(value) {
|
|
333
|
+
return isObject(value) && Boolean(Reflect.get(value, IS_REF));
|
|
334
|
+
}
|
|
335
|
+
function unref(value) {
|
|
336
|
+
return isRef(value) ? value.value : value;
|
|
337
|
+
}
|
|
313
338
|
function stop(effect2) {
|
|
314
339
|
ReactiveSystem.getInstance().stop(effect2);
|
|
315
340
|
}
|
|
@@ -354,6 +379,187 @@ function setStyleValue(style, property, value) {
|
|
|
354
379
|
style.setProperty(cssProperty, String(value));
|
|
355
380
|
}
|
|
356
381
|
|
|
382
|
+
// lib/core/model.ts
|
|
383
|
+
function pathSegments(path) {
|
|
384
|
+
const segments = path.split(".");
|
|
385
|
+
if (path.length === 0 || segments.some((segment) => segment.length === 0 || segment === "__proto__" || segment === "prototype" || segment === "constructor")) {
|
|
386
|
+
throw new Error(`Invalid model path "${path}"`);
|
|
387
|
+
}
|
|
388
|
+
return segments;
|
|
389
|
+
}
|
|
390
|
+
function isRecord(value) {
|
|
391
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
392
|
+
}
|
|
393
|
+
function hasOwn(value, key) {
|
|
394
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
395
|
+
}
|
|
396
|
+
function modelPath(binding) {
|
|
397
|
+
return typeof binding === "string" ? binding : binding.path;
|
|
398
|
+
}
|
|
399
|
+
function getModelValue(state, path) {
|
|
400
|
+
let value = state;
|
|
401
|
+
for (const segment of pathSegments(path)) {
|
|
402
|
+
if (!isRecord(value)) {
|
|
403
|
+
throw new Error(`Invalid model path "${path}"`);
|
|
404
|
+
}
|
|
405
|
+
if (!hasOwn(value, segment)) {
|
|
406
|
+
if (segment in value) {
|
|
407
|
+
throw new Error(`Invalid model path "${path}"`);
|
|
408
|
+
}
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
value = value[segment];
|
|
412
|
+
}
|
|
413
|
+
return value;
|
|
414
|
+
}
|
|
415
|
+
function setModelValue(state, path, value) {
|
|
416
|
+
const segments = pathSegments(path);
|
|
417
|
+
let target = state;
|
|
418
|
+
for (const segment of segments.slice(0, -1)) {
|
|
419
|
+
if (!hasOwn(target, segment)) {
|
|
420
|
+
if (segment in target) {
|
|
421
|
+
throw new Error(`Invalid model path "${path}"`);
|
|
422
|
+
}
|
|
423
|
+
target[segment] = {};
|
|
424
|
+
} else if (!isRecord(target[segment])) {
|
|
425
|
+
throw new Error(`Invalid model path "${path}"`);
|
|
426
|
+
}
|
|
427
|
+
const nextTarget = target[segment];
|
|
428
|
+
if (!isRecord(nextTarget)) {
|
|
429
|
+
throw new Error(`Invalid model path "${path}"`);
|
|
430
|
+
}
|
|
431
|
+
target = nextTarget;
|
|
432
|
+
}
|
|
433
|
+
const lastSegment = segments[segments.length - 1];
|
|
434
|
+
target[lastSegment] = value;
|
|
435
|
+
}
|
|
436
|
+
function displayValue(binding, value) {
|
|
437
|
+
if (typeof binding !== "string" && binding.format) {
|
|
438
|
+
return binding.format(value);
|
|
439
|
+
}
|
|
440
|
+
return value === undefined || value === null ? "" : String(value);
|
|
441
|
+
}
|
|
442
|
+
function toModelValue(binding, value) {
|
|
443
|
+
if (typeof binding !== "string" && binding.parse) {
|
|
444
|
+
return binding.parse(value);
|
|
445
|
+
}
|
|
446
|
+
return value;
|
|
447
|
+
}
|
|
448
|
+
function syncControl(element, binding, value) {
|
|
449
|
+
if (element instanceof HTMLInputElement) {
|
|
450
|
+
if (element.type === "checkbox") {
|
|
451
|
+
element.checked = Array.isArray(value) ? value.some((item) => String(item) === element.value) : Boolean(value);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (element.type === "radio") {
|
|
455
|
+
element.checked = value === element.value;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
element.value = displayValue(binding, value);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (element instanceof HTMLTextAreaElement) {
|
|
462
|
+
element.value = displayValue(binding, value);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (element instanceof HTMLSelectElement) {
|
|
466
|
+
if (element.multiple) {
|
|
467
|
+
const selected = Array.isArray(value) ? new Set(value.map(String)) : new Set;
|
|
468
|
+
for (let index = 0;index < element.options.length; index += 1) {
|
|
469
|
+
const option = element.options.item(index);
|
|
470
|
+
if (!option) {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
option.selected = selected.has(option.value);
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
element.value = displayValue(binding, value);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function controlValue(element, currentValue) {
|
|
481
|
+
if (element instanceof HTMLInputElement) {
|
|
482
|
+
if (element.type === "checkbox") {
|
|
483
|
+
if (Array.isArray(currentValue)) {
|
|
484
|
+
const values = currentValue.filter((value) => String(value) !== element.value);
|
|
485
|
+
return element.checked ? [...values, element.value] : values;
|
|
486
|
+
}
|
|
487
|
+
return element.checked;
|
|
488
|
+
}
|
|
489
|
+
if (element.type === "radio") {
|
|
490
|
+
return element.checked ? element.value : currentValue;
|
|
491
|
+
}
|
|
492
|
+
return element.value;
|
|
493
|
+
}
|
|
494
|
+
if (element instanceof HTMLTextAreaElement) {
|
|
495
|
+
return element.value;
|
|
496
|
+
}
|
|
497
|
+
if (element instanceof HTMLSelectElement) {
|
|
498
|
+
if (!element.multiple) {
|
|
499
|
+
return element.value;
|
|
500
|
+
}
|
|
501
|
+
const values = [];
|
|
502
|
+
for (let index = 0;index < element.selectedOptions.length; index += 1) {
|
|
503
|
+
const option = element.selectedOptions.item(index);
|
|
504
|
+
if (option) {
|
|
505
|
+
values.push(option.value);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return values;
|
|
509
|
+
}
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
class ModelBindingController {
|
|
514
|
+
bindings = new WeakMap;
|
|
515
|
+
bind(element, binding, state) {
|
|
516
|
+
if (!this.isSupportedControl(element)) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const existing = this.bindings.get(element);
|
|
520
|
+
if (existing && this.sameBinding(existing, binding)) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
this.cleanup(element);
|
|
524
|
+
const path = modelPath(binding);
|
|
525
|
+
const sync = () => syncControl(element, binding, getModelValue(state, path));
|
|
526
|
+
const eventName = element instanceof HTMLTextAreaElement || element instanceof HTMLInputElement && !["checkbox", "radio"].includes(element.type) ? "input" : "change";
|
|
527
|
+
const listener = () => {
|
|
528
|
+
const currentValue = getModelValue(state, path);
|
|
529
|
+
setModelValue(state, path, toModelValue(binding, controlValue(element, currentValue)));
|
|
530
|
+
};
|
|
531
|
+
element.addEventListener(eventName, listener);
|
|
532
|
+
const effectRef = effect(sync);
|
|
533
|
+
this.bindings.set(element, {
|
|
534
|
+
binding,
|
|
535
|
+
path,
|
|
536
|
+
parse: typeof binding === "string" ? undefined : binding.parse,
|
|
537
|
+
format: typeof binding === "string" ? undefined : binding.format,
|
|
538
|
+
eventName,
|
|
539
|
+
listener,
|
|
540
|
+
effect: effectRef
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
cleanup(element) {
|
|
544
|
+
const existing = this.bindings.get(element);
|
|
545
|
+
if (!existing) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
element.removeEventListener(existing.eventName, existing.listener);
|
|
549
|
+
stop(existing.effect);
|
|
550
|
+
this.bindings.delete(element);
|
|
551
|
+
}
|
|
552
|
+
isSupportedControl(element) {
|
|
553
|
+
return element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement;
|
|
554
|
+
}
|
|
555
|
+
sameBinding(record, binding) {
|
|
556
|
+
if (typeof record.binding === "string" || typeof binding === "string") {
|
|
557
|
+
return record.binding === binding;
|
|
558
|
+
}
|
|
559
|
+
return record.path === binding.path && record.parse === binding.parse && record.format === binding.format;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
357
563
|
// lib/core/vnode.ts
|
|
358
564
|
function isComponentNode(vnode) {
|
|
359
565
|
return typeof vnode === "object" && vnode !== null && "component" in vnode;
|
|
@@ -374,6 +580,17 @@ function h(tag, props, children, listeners, key, directions) {
|
|
|
374
580
|
directions
|
|
375
581
|
};
|
|
376
582
|
}
|
|
583
|
+
function createElementFactory(tag) {
|
|
584
|
+
return (options = {}) => ({
|
|
585
|
+
tag,
|
|
586
|
+
...options
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
var Div = createElementFactory("div");
|
|
590
|
+
var Span = createElementFactory("span");
|
|
591
|
+
var P = createElementFactory("p");
|
|
592
|
+
var Button = createElementFactory("button");
|
|
593
|
+
var Input = createElementFactory("input");
|
|
377
594
|
function createComponent(componentClass, props, children, key, directions) {
|
|
378
595
|
return {
|
|
379
596
|
component: componentClass,
|
|
@@ -391,6 +608,15 @@ function slot(name, key, directions) {
|
|
|
391
608
|
directions
|
|
392
609
|
};
|
|
393
610
|
}
|
|
611
|
+
function each(items, render, key) {
|
|
612
|
+
return items.map((item, index) => {
|
|
613
|
+
const vnode = render(item, index);
|
|
614
|
+
if (typeof vnode === "string") {
|
|
615
|
+
throw new Error("each render callback must return a VNode");
|
|
616
|
+
}
|
|
617
|
+
return { ...vnode, key: key(item, index) };
|
|
618
|
+
});
|
|
619
|
+
}
|
|
394
620
|
|
|
395
621
|
// lib/core/renderer.ts
|
|
396
622
|
class RendererContext {
|
|
@@ -449,44 +675,62 @@ class TextRenderStrategy {
|
|
|
449
675
|
|
|
450
676
|
class ComponentRenderStrategy {
|
|
451
677
|
instances = new WeakMap;
|
|
678
|
+
instanceNodes = new Map;
|
|
679
|
+
emitterUnsubscribers = new WeakMap;
|
|
452
680
|
matches(vnode) {
|
|
453
681
|
return typeof vnode === "object" && vnode !== null && isComponentNode(vnode);
|
|
454
682
|
}
|
|
455
683
|
mount(vnode, context) {
|
|
684
|
+
if (vnode.directions?.if === false) {
|
|
685
|
+
return document.createComment("if");
|
|
686
|
+
}
|
|
456
687
|
const ComponentClass = vnode.component;
|
|
457
688
|
const instance = new ComponentClass(this.createProps(vnode));
|
|
458
689
|
if (context.appContext && instance.setAppContext) {
|
|
459
690
|
instance.setAppContext(context.appContext);
|
|
460
691
|
}
|
|
461
|
-
|
|
462
|
-
Object.entries(vnode.emitters).forEach(([eventName, listener]) => {
|
|
463
|
-
instance.on(eventName, listener);
|
|
464
|
-
});
|
|
465
|
-
}
|
|
692
|
+
this.syncEmitters(instance, vnode.emitters ?? {});
|
|
466
693
|
context.registerChild(instance);
|
|
467
694
|
const node = instance.mountToNode();
|
|
468
|
-
this.
|
|
695
|
+
this.trackInstanceNode(instance, node);
|
|
696
|
+
instance.setElementChangeListener?.((previousNode, nextNode) => {
|
|
697
|
+
this.trackInstanceNode(instance, previousNode);
|
|
698
|
+
this.trackInstanceNode(instance, nextNode);
|
|
699
|
+
});
|
|
469
700
|
return node;
|
|
470
701
|
}
|
|
471
702
|
patch(oldVNode, newVNode, currentNode, context) {
|
|
703
|
+
if (currentNode.nodeType === Node.COMMENT_NODE) {
|
|
704
|
+
const nextNode2 = this.mount(newVNode, context);
|
|
705
|
+
currentNode.parentNode?.replaceChild(nextNode2, currentNode);
|
|
706
|
+
return nextNode2;
|
|
707
|
+
}
|
|
708
|
+
if (newVNode.directions?.if === false) {
|
|
709
|
+
const nextNode2 = document.createComment("if");
|
|
710
|
+
currentNode.parentNode?.replaceChild(nextNode2, currentNode);
|
|
711
|
+
this.unmount(oldVNode, currentNode, context);
|
|
712
|
+
return nextNode2;
|
|
713
|
+
}
|
|
472
714
|
const instance = this.instances.get(currentNode);
|
|
473
715
|
if (instance && oldVNode.component === newVNode.component) {
|
|
716
|
+
this.syncEmitters(instance, newVNode.emitters ?? {});
|
|
474
717
|
instance.setProps(this.createProps(newVNode));
|
|
475
|
-
instance.update();
|
|
476
718
|
const nextNode2 = instance.getElement() ?? currentNode;
|
|
477
|
-
this.
|
|
719
|
+
this.trackInstanceNode(instance, nextNode2);
|
|
478
720
|
return nextNode2;
|
|
479
721
|
}
|
|
480
722
|
const nextNode = this.mount(newVNode, context);
|
|
481
723
|
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
482
|
-
this.unmount(oldVNode, currentNode);
|
|
724
|
+
this.unmount(oldVNode, currentNode, context);
|
|
483
725
|
return nextNode;
|
|
484
726
|
}
|
|
485
|
-
unmount(_vnode, currentNode) {
|
|
727
|
+
unmount(_vnode, currentNode, context) {
|
|
486
728
|
const instance = this.instances.get(currentNode);
|
|
487
729
|
if (instance) {
|
|
730
|
+
this.clearEmitters(instance);
|
|
488
731
|
instance.unmount();
|
|
489
|
-
this.
|
|
732
|
+
this.clearInstanceNodes(instance);
|
|
733
|
+
context.unregisterChild(instance);
|
|
490
734
|
}
|
|
491
735
|
}
|
|
492
736
|
createProps(vnode) {
|
|
@@ -495,6 +739,44 @@ class ComponentRenderStrategy {
|
|
|
495
739
|
children: vnode.children ?? []
|
|
496
740
|
};
|
|
497
741
|
}
|
|
742
|
+
syncEmitters(instance, emitters) {
|
|
743
|
+
const current = this.emitterUnsubscribers.get(instance) ?? new Map;
|
|
744
|
+
current.forEach(({ listener: currentListener, unsubscribe }, eventName) => {
|
|
745
|
+
const listener = emitters[eventName];
|
|
746
|
+
if (!listener || listener !== currentListener) {
|
|
747
|
+
unsubscribe();
|
|
748
|
+
current.delete(eventName);
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
Object.entries(emitters).forEach(([eventName, listener]) => {
|
|
752
|
+
if (current.get(eventName)?.listener === listener) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
current.set(eventName, {
|
|
756
|
+
listener,
|
|
757
|
+
unsubscribe: instance.on(eventName, listener)
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
this.emitterUnsubscribers.set(instance, current);
|
|
761
|
+
}
|
|
762
|
+
clearEmitters(instance) {
|
|
763
|
+
this.emitterUnsubscribers.get(instance)?.forEach(({ unsubscribe }) => {
|
|
764
|
+
unsubscribe();
|
|
765
|
+
});
|
|
766
|
+
this.emitterUnsubscribers.delete(instance);
|
|
767
|
+
}
|
|
768
|
+
trackInstanceNode(instance, node) {
|
|
769
|
+
this.instances.set(node, instance);
|
|
770
|
+
const nodes = this.instanceNodes.get(instance) ?? new Set;
|
|
771
|
+
nodes.add(node);
|
|
772
|
+
this.instanceNodes.set(instance, nodes);
|
|
773
|
+
}
|
|
774
|
+
clearInstanceNodes(instance) {
|
|
775
|
+
this.instanceNodes.get(instance)?.forEach((node) => {
|
|
776
|
+
this.instances.delete(node);
|
|
777
|
+
});
|
|
778
|
+
this.instanceNodes.delete(instance);
|
|
779
|
+
}
|
|
498
780
|
}
|
|
499
781
|
|
|
500
782
|
class SlotRenderStrategy {
|
|
@@ -503,12 +785,26 @@ class SlotRenderStrategy {
|
|
|
503
785
|
return typeof vnode === "object" && vnode !== null && isSlotProvider(vnode);
|
|
504
786
|
}
|
|
505
787
|
mount(vnode, context) {
|
|
788
|
+
if (vnode.directions?.if === false) {
|
|
789
|
+
return document.createComment("if");
|
|
790
|
+
}
|
|
506
791
|
const slotContainer = document.createElement("div");
|
|
507
792
|
slotContainer.setAttribute("data-slot", vnode.props.name);
|
|
508
793
|
this.mountSlotChildren(slotContainer, this.resolveChildren(vnode, context), context);
|
|
509
794
|
return slotContainer;
|
|
510
795
|
}
|
|
511
796
|
patch(oldVNode, newVNode, currentNode, context) {
|
|
797
|
+
if (currentNode.nodeType === Node.COMMENT_NODE) {
|
|
798
|
+
const nextNode = this.mount(newVNode, context);
|
|
799
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
800
|
+
return nextNode;
|
|
801
|
+
}
|
|
802
|
+
if (newVNode.directions?.if === false) {
|
|
803
|
+
const nextNode = document.createComment("if");
|
|
804
|
+
currentNode.parentNode?.replaceChild(nextNode, currentNode);
|
|
805
|
+
this.unmount(oldVNode, currentNode, context);
|
|
806
|
+
return nextNode;
|
|
807
|
+
}
|
|
512
808
|
if (currentNode instanceof HTMLElement) {
|
|
513
809
|
currentNode.setAttribute("data-slot", newVNode.props.name);
|
|
514
810
|
this.replaceSlotChildren(currentNode, oldVNode, newVNode, context);
|
|
@@ -550,7 +846,7 @@ class SlotRenderStrategy {
|
|
|
550
846
|
class ElementRenderStrategy {
|
|
551
847
|
listeners = new WeakMap;
|
|
552
848
|
effects = new WeakMap;
|
|
553
|
-
modelBindings = new
|
|
849
|
+
modelBindings = new ModelBindingController;
|
|
554
850
|
matches(vnode) {
|
|
555
851
|
return typeof vnode === "object" && vnode !== null && isHTMLNode(vnode);
|
|
556
852
|
}
|
|
@@ -560,11 +856,11 @@ class ElementRenderStrategy {
|
|
|
560
856
|
}
|
|
561
857
|
const element = document.createElement(vnode.tag);
|
|
562
858
|
this.applyProps(element, {}, vnode.props ?? {}, context);
|
|
563
|
-
this.applyDirections(element, undefined, vnode.directions, context);
|
|
564
859
|
this.updateListeners(element, {}, this.collectListeners(vnode));
|
|
565
860
|
(vnode.children ?? []).forEach((child) => {
|
|
566
861
|
element.appendChild(context.renderer.mount(child, context));
|
|
567
862
|
});
|
|
863
|
+
this.applyDirections(element, undefined, vnode.directions, context);
|
|
568
864
|
return element;
|
|
569
865
|
}
|
|
570
866
|
patch(oldVNode, newVNode, currentNode, context) {
|
|
@@ -584,9 +880,9 @@ class ElementRenderStrategy {
|
|
|
584
880
|
return nextNode;
|
|
585
881
|
}
|
|
586
882
|
this.applyProps(currentNode, oldVNode.props ?? {}, newVNode.props ?? {}, context);
|
|
587
|
-
this.applyDirections(currentNode, oldVNode.directions, newVNode.directions, context);
|
|
588
883
|
this.updateListeners(currentNode, this.collectListeners(oldVNode), this.collectListeners(newVNode));
|
|
589
884
|
this.updateChildren(currentNode, oldVNode.children ?? [], newVNode.children ?? [], context);
|
|
885
|
+
this.applyDirections(currentNode, oldVNode.directions, newVNode.directions, context);
|
|
590
886
|
return currentNode;
|
|
591
887
|
}
|
|
592
888
|
unmount(vnode, currentNode, context) {
|
|
@@ -599,7 +895,7 @@ class ElementRenderStrategy {
|
|
|
599
895
|
currentNode.removeEventListener(eventName, listener);
|
|
600
896
|
});
|
|
601
897
|
this.listeners.delete(currentNode);
|
|
602
|
-
this.modelBindings.
|
|
898
|
+
this.modelBindings.cleanup(currentNode);
|
|
603
899
|
(vnode.children ?? []).forEach((child, index) => {
|
|
604
900
|
const childNode = currentNode.childNodes[index];
|
|
605
901
|
if (childNode) {
|
|
@@ -656,12 +952,16 @@ class ElementRenderStrategy {
|
|
|
656
952
|
} else if (oldDirections && "show" in oldDirections) {
|
|
657
953
|
element.style.display = "";
|
|
658
954
|
}
|
|
659
|
-
if (newDirections?.model) {
|
|
660
|
-
this.
|
|
955
|
+
if (!newDirections?.model) {
|
|
956
|
+
this.modelBindings.cleanup(element);
|
|
957
|
+
return;
|
|
661
958
|
}
|
|
959
|
+
this.modelBindings.bind(element, newDirections.model, context.templateEngine.state);
|
|
662
960
|
}
|
|
663
961
|
updateChildren(element, oldChildren, newChildren, context) {
|
|
664
|
-
|
|
962
|
+
this.assertNoDuplicateKeys(oldChildren);
|
|
963
|
+
this.assertNoDuplicateKeys(newChildren);
|
|
964
|
+
if (this.hasOnlyKeyedChildren(oldChildren, newChildren)) {
|
|
665
965
|
this.updateKeyedChildren(element, oldChildren, newChildren, context);
|
|
666
966
|
return;
|
|
667
967
|
}
|
|
@@ -730,8 +1030,21 @@ class ElementRenderStrategy {
|
|
|
730
1030
|
}
|
|
731
1031
|
});
|
|
732
1032
|
}
|
|
733
|
-
|
|
734
|
-
return [...oldChildren, ...newChildren].
|
|
1033
|
+
hasOnlyKeyedChildren(oldChildren, newChildren) {
|
|
1034
|
+
return [...oldChildren, ...newChildren].every((child) => this.getVNodeKey(child) !== undefined);
|
|
1035
|
+
}
|
|
1036
|
+
assertNoDuplicateKeys(children) {
|
|
1037
|
+
const keys = new Set;
|
|
1038
|
+
children.forEach((child) => {
|
|
1039
|
+
const key = this.getVNodeKey(child);
|
|
1040
|
+
if (key === undefined) {
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
if (keys.has(key)) {
|
|
1044
|
+
throw new Error(`Duplicate key "${key}"`);
|
|
1045
|
+
}
|
|
1046
|
+
keys.add(key);
|
|
1047
|
+
});
|
|
735
1048
|
}
|
|
736
1049
|
getVNodeKey(vnode) {
|
|
737
1050
|
if (typeof vnode === "string") {
|
|
@@ -780,55 +1093,6 @@ class ElementRenderStrategy {
|
|
|
780
1093
|
});
|
|
781
1094
|
this.trackEffect(element, effectRef);
|
|
782
1095
|
}
|
|
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
1096
|
trackEffect(element, effectRef) {
|
|
833
1097
|
const effects = this.effects.get(element) ?? new Set;
|
|
834
1098
|
effects.add(effectRef);
|
|
@@ -889,8 +1153,8 @@ class TemplateEngine {
|
|
|
889
1153
|
if (key) {
|
|
890
1154
|
keys.add(key);
|
|
891
1155
|
const value = this.getValueFromState(key);
|
|
892
|
-
const
|
|
893
|
-
result = result.replace(match[0],
|
|
1156
|
+
const displayValue2 = value === undefined || value === null ? "" : String(value);
|
|
1157
|
+
result = result.replace(match[0], displayValue2);
|
|
894
1158
|
}
|
|
895
1159
|
});
|
|
896
1160
|
return result;
|
|
@@ -910,7 +1174,7 @@ class TemplateEngine {
|
|
|
910
1174
|
}
|
|
911
1175
|
clearBindings() {
|
|
912
1176
|
this.bindings.forEach((binding) => {
|
|
913
|
-
binding.effect
|
|
1177
|
+
stop(binding.effect);
|
|
914
1178
|
});
|
|
915
1179
|
this.bindings = [];
|
|
916
1180
|
}
|
|
@@ -953,8 +1217,11 @@ class Component {
|
|
|
953
1217
|
templateEngine;
|
|
954
1218
|
childComponents = new Set;
|
|
955
1219
|
eventListeners = {};
|
|
1220
|
+
providers = new Map;
|
|
956
1221
|
updateEffect;
|
|
957
1222
|
appContext = null;
|
|
1223
|
+
parentComponent = null;
|
|
1224
|
+
elementChangeListener = null;
|
|
958
1225
|
styleManager;
|
|
959
1226
|
state;
|
|
960
1227
|
mounted = false;
|
|
@@ -969,7 +1236,7 @@ class Component {
|
|
|
969
1236
|
if (this.mounted) {
|
|
970
1237
|
this.update();
|
|
971
1238
|
}
|
|
972
|
-
});
|
|
1239
|
+
}, { throwOnError: true });
|
|
973
1240
|
}
|
|
974
1241
|
mount(container) {
|
|
975
1242
|
if (!container || !(container instanceof HTMLElement)) {
|
|
@@ -997,15 +1264,15 @@ class Component {
|
|
|
997
1264
|
if (!this.el || !this.vnode) {
|
|
998
1265
|
return;
|
|
999
1266
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
this.
|
|
1006
|
-
} catch (error) {
|
|
1007
|
-
console.error("组件更新错误:", error);
|
|
1267
|
+
this.beforeUpdate();
|
|
1268
|
+
const newVNode = this.render();
|
|
1269
|
+
const previousElement = this.el;
|
|
1270
|
+
this.el = this.renderer.patch(this.vnode, newVNode, this.el, this.createRenderContext());
|
|
1271
|
+
if (previousElement !== this.el) {
|
|
1272
|
+
this.elementChangeListener?.(previousElement, this.el);
|
|
1008
1273
|
}
|
|
1274
|
+
this.vnode = newVNode;
|
|
1275
|
+
this.onUpdated();
|
|
1009
1276
|
}
|
|
1010
1277
|
unmount() {
|
|
1011
1278
|
if (!this.mounted) {
|
|
@@ -1016,8 +1283,15 @@ class Component {
|
|
|
1016
1283
|
this.renderer.unmount(this.vnode, this.el, this.createRenderContext());
|
|
1017
1284
|
}
|
|
1018
1285
|
this.childComponents.clear();
|
|
1286
|
+
Object.keys(this.eventListeners).forEach((eventName) => {
|
|
1287
|
+
this.eventListeners[eventName].clear();
|
|
1288
|
+
delete this.eventListeners[eventName];
|
|
1289
|
+
});
|
|
1290
|
+
this.providers.clear();
|
|
1291
|
+
this.parentComponent = null;
|
|
1292
|
+
this.elementChangeListener = null;
|
|
1019
1293
|
this.templateEngine.clearBindings();
|
|
1020
|
-
this.styleManager.
|
|
1294
|
+
this.styleManager.destroy();
|
|
1021
1295
|
stop(this.updateEffect);
|
|
1022
1296
|
if (this.el?.parentNode) {
|
|
1023
1297
|
this.el.parentNode.removeChild(this.el);
|
|
@@ -1045,6 +1319,28 @@ class Component {
|
|
|
1045
1319
|
child.setAppContext?.(context);
|
|
1046
1320
|
});
|
|
1047
1321
|
}
|
|
1322
|
+
setParentComponent(parent) {
|
|
1323
|
+
this.parentComponent = parent;
|
|
1324
|
+
}
|
|
1325
|
+
setElementChangeListener(listener) {
|
|
1326
|
+
this.elementChangeListener = listener;
|
|
1327
|
+
}
|
|
1328
|
+
provide(key, value) {
|
|
1329
|
+
this.providers.set(key, value);
|
|
1330
|
+
}
|
|
1331
|
+
inject(key, fallback) {
|
|
1332
|
+
const result = this.resolveInjection(key);
|
|
1333
|
+
return result.found ? result.value : fallback;
|
|
1334
|
+
}
|
|
1335
|
+
resolveInjection(key) {
|
|
1336
|
+
if (this.providers.has(key)) {
|
|
1337
|
+
return { found: true, value: this.providers.get(key) };
|
|
1338
|
+
}
|
|
1339
|
+
if (this.parentComponent?.resolveInjection) {
|
|
1340
|
+
return this.parentComponent.resolveInjection(key);
|
|
1341
|
+
}
|
|
1342
|
+
return this.resolveAppInjection(key);
|
|
1343
|
+
}
|
|
1048
1344
|
getElement() {
|
|
1049
1345
|
return this.el;
|
|
1050
1346
|
}
|
|
@@ -1070,6 +1366,7 @@ class Component {
|
|
|
1070
1366
|
this.eventListeners[eventName] = new Set;
|
|
1071
1367
|
}
|
|
1072
1368
|
this.eventListeners[eventName].add(listener);
|
|
1369
|
+
return () => this.off(eventName, listener);
|
|
1073
1370
|
}
|
|
1074
1371
|
off(eventName, listener) {
|
|
1075
1372
|
this.eventListeners[eventName]?.delete(listener);
|
|
@@ -1082,7 +1379,12 @@ class Component {
|
|
|
1082
1379
|
slots: this.collectSlots(),
|
|
1083
1380
|
registerChild: (component) => {
|
|
1084
1381
|
this.childComponents.add(component);
|
|
1382
|
+
component.setParentComponent?.(this);
|
|
1085
1383
|
component.setAppContext?.(this.appContext);
|
|
1384
|
+
},
|
|
1385
|
+
unregisterChild: (component) => {
|
|
1386
|
+
this.childComponents.delete(component);
|
|
1387
|
+
component.setParentComponent?.(null);
|
|
1086
1388
|
}
|
|
1087
1389
|
};
|
|
1088
1390
|
}
|
|
@@ -1125,6 +1427,13 @@ class Component {
|
|
|
1125
1427
|
const globalApp = globalThis.__APP__;
|
|
1126
1428
|
return this.getRouterFrom(globalApp);
|
|
1127
1429
|
}
|
|
1430
|
+
resolveAppInjection(key) {
|
|
1431
|
+
if (!this.appContext || typeof this.appContext !== "object") {
|
|
1432
|
+
return { found: false, value: undefined };
|
|
1433
|
+
}
|
|
1434
|
+
const app = this.appContext.app;
|
|
1435
|
+
return app?.resolveInjection?.(key) ?? { found: false, value: undefined };
|
|
1436
|
+
}
|
|
1128
1437
|
trackReactiveValue(value, seen) {
|
|
1129
1438
|
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
1130
1439
|
return;
|
|
@@ -1147,6 +1456,12 @@ function setRouter(r) {
|
|
|
1147
1456
|
}
|
|
1148
1457
|
router = r;
|
|
1149
1458
|
}
|
|
1459
|
+
function useRouter() {
|
|
1460
|
+
if (!router) {
|
|
1461
|
+
throw new Error("Router is not initialized. Please make sure you have installed the router plugin.");
|
|
1462
|
+
}
|
|
1463
|
+
return router;
|
|
1464
|
+
}
|
|
1150
1465
|
|
|
1151
1466
|
// lib/router/matcher.ts
|
|
1152
1467
|
function normalizePath(path) {
|
|
@@ -1207,13 +1522,18 @@ function createRouterHref(path, mode, base) {
|
|
|
1207
1522
|
function getBrowserLocation(mode, base) {
|
|
1208
1523
|
let path;
|
|
1209
1524
|
let fullPath;
|
|
1525
|
+
let queryString;
|
|
1210
1526
|
if (mode === "history") {
|
|
1211
1527
|
fullPath = window.location.pathname + window.location.search;
|
|
1212
1528
|
path = window.location.pathname;
|
|
1529
|
+
queryString = window.location.search;
|
|
1213
1530
|
} else {
|
|
1214
1531
|
const hash = window.location.hash;
|
|
1215
1532
|
fullPath = hash || "#/";
|
|
1216
1533
|
path = fullPath.startsWith("#") ? fullPath.slice(1) : fullPath;
|
|
1534
|
+
const queryStart = path.indexOf("?");
|
|
1535
|
+
queryString = queryStart >= 0 ? path.slice(queryStart + 1) : "";
|
|
1536
|
+
path = queryStart >= 0 ? path.slice(0, queryStart) : path;
|
|
1217
1537
|
}
|
|
1218
1538
|
if (path.startsWith(base) && base !== "/" && path !== "/") {
|
|
1219
1539
|
path = path.slice(base.length);
|
|
@@ -1222,7 +1542,7 @@ function getBrowserLocation(mode, base) {
|
|
|
1222
1542
|
return {
|
|
1223
1543
|
path,
|
|
1224
1544
|
fullPath,
|
|
1225
|
-
query: parseQuery(
|
|
1545
|
+
query: parseQuery(queryString),
|
|
1226
1546
|
params: {}
|
|
1227
1547
|
};
|
|
1228
1548
|
}
|
|
@@ -1492,7 +1812,7 @@ function createRouter(options) {
|
|
|
1492
1812
|
return new Router(options);
|
|
1493
1813
|
}
|
|
1494
1814
|
|
|
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 };
|
|
1815
|
+
export { ReactiveSystem, reactive, readonly, effect, computed, ref, isRef, unref, stop, isReactive, isReadonly, TemplateEngine, modelPath, getModelValue, setModelValue, ModelBindingController, isComponentNode, isHTMLNode, isSlotProvider, h, Div, Span, P, Button, Input, createComponent, slot, each, RendererContext, TextRenderStrategy, ComponentRenderStrategy, SlotRenderStrategy, ElementRenderStrategy, Component, useRouter, Router, RouterLink, RouterView, createRouter };
|
|
1496
1816
|
|
|
1497
|
-
//# debugId=
|
|
1498
|
-
//# sourceMappingURL=index-
|
|
1817
|
+
//# debugId=EA20D7B3780F196864756E2164756E21
|
|
1818
|
+
//# sourceMappingURL=index-ycmc7ga1.js.map
|