@viewfly/core 0.0.1-alpha.0

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.
@@ -0,0 +1,1188 @@
1
+ import 'reflect-metadata';
2
+ import { ReflectiveInjector, normalizeProvider, NullInjector, Injectable } from '@tanbo/di';
3
+ export * from '@tanbo/di';
4
+ import { Subject, Subscription, microTask } from '@tanbo/stream';
5
+
6
+ class NativeRenderer {
7
+ }
8
+
9
+ /******************************************************************************
10
+ Copyright (c) Microsoft Corporation.
11
+
12
+ Permission to use, copy, modify, and/or distribute this software for any
13
+ purpose with or without fee is hereby granted.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
16
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
17
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
18
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
19
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
20
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21
+ PERFORMANCE OF THIS SOFTWARE.
22
+ ***************************************************************************** */
23
+ /* global Reflect, Promise */
24
+
25
+
26
+ function __decorate(decorators, target, key, desc) {
27
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
28
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
29
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
30
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
31
+ }
32
+
33
+ function __metadata(metadataKey, metadataValue) {
34
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
35
+ }
36
+
37
+ function Fragment(props) {
38
+ return () => new JSXFragment(props);
39
+ }
40
+ class JSXFragment {
41
+ constructor(props) {
42
+ this.props = props;
43
+ }
44
+ }
45
+ function jsx(setup, config) {
46
+ if (typeof setup === 'string') {
47
+ return new JSXElement(setup, config);
48
+ }
49
+ return function (context) {
50
+ return new Component(context, setup, config);
51
+ };
52
+ }
53
+ function jsxs(setup, config) {
54
+ if (typeof setup === 'string') {
55
+ return new JSXElement(setup, config);
56
+ }
57
+ return function (context) {
58
+ return new Component(context, setup, config);
59
+ };
60
+ }
61
+ class JSXText {
62
+ constructor(text) {
63
+ this.text = text;
64
+ }
65
+ }
66
+ function flatChildren(jsxNodes) {
67
+ const children = [];
68
+ for (const node of jsxNodes) {
69
+ if (node instanceof JSXElement || typeof node === 'function') {
70
+ children.push(node);
71
+ }
72
+ else if (typeof node === 'string' && node.length) {
73
+ children.push(new JSXText(node));
74
+ }
75
+ else if (node !== null && typeof node !== 'undefined') {
76
+ children.push(new JSXText(String(node)));
77
+ }
78
+ }
79
+ return children;
80
+ }
81
+ class Props {
82
+ constructor(props) {
83
+ this.attrs = new Map();
84
+ this.styles = new Map();
85
+ this.classes = new Set();
86
+ this.listeners = {};
87
+ this.children = [];
88
+ if (!props) {
89
+ return;
90
+ }
91
+ Object.keys(props).forEach(key => {
92
+ if (key === 'children') {
93
+ if (props.children !== null && typeof props.children !== 'undefined') {
94
+ if (Array.isArray(props.children)) {
95
+ this.children = flatChildren(props.children);
96
+ }
97
+ else {
98
+ this.children = flatChildren([props.children]);
99
+ }
100
+ }
101
+ return;
102
+ }
103
+ if (key === 'class') {
104
+ this.classes = new Set(Props.classToArray(props[key]));
105
+ return;
106
+ }
107
+ if (key === 'style') {
108
+ const style = props.style || '';
109
+ if (typeof style === 'string') {
110
+ style.split(';').map(s => s.split(':')).forEach(v => {
111
+ if (!v[0] || !v[1]) {
112
+ return;
113
+ }
114
+ this.styles.set(v[0].trim(), v[1].trim());
115
+ });
116
+ }
117
+ else if (typeof style === 'object') {
118
+ Object.keys(style).forEach(key => {
119
+ this.styles.set(key, style[key]);
120
+ });
121
+ }
122
+ return;
123
+ }
124
+ if (/^on[A-Z]/.test(key)) {
125
+ const listener = props[key];
126
+ if (typeof listener === 'function') {
127
+ this.listeners[key.replace(/^on/, '').toLowerCase()] = listener;
128
+ }
129
+ else {
130
+ this.attrs.set(key, listener);
131
+ }
132
+ return;
133
+ }
134
+ this.attrs.set(key, props[key]);
135
+ });
136
+ }
137
+ static classToArray(config) {
138
+ const classes = [];
139
+ if (!config) {
140
+ return classes;
141
+ }
142
+ if (typeof config === 'string') {
143
+ const items = config.match(/\S+/g);
144
+ return items || classes;
145
+ }
146
+ else if (Array.isArray(config)) {
147
+ for (const i of config) {
148
+ classes.push(...Props.classToArray(i));
149
+ }
150
+ }
151
+ else if (typeof config === 'object') {
152
+ if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
153
+ classes.push(config.toString());
154
+ return classes;
155
+ }
156
+ for (const key in config) {
157
+ if ({}.hasOwnProperty.call(config, key) && config[key]) {
158
+ classes.push(key);
159
+ }
160
+ }
161
+ }
162
+ return classes;
163
+ }
164
+ }
165
+ class JSXElement {
166
+ constructor(name, config) {
167
+ this.name = name;
168
+ this.config = config;
169
+ this.props = new Props(config);
170
+ }
171
+ }
172
+
173
+ function makeError(name) {
174
+ return function viewflyError(message) {
175
+ const error = new Error(message);
176
+ error.name = `[ViewflyError: ${name}]`;
177
+ error.stack = error.stack.replace(/\n.*?(?=\n)/, '');
178
+ return error;
179
+ };
180
+ }
181
+
182
+ const componentStack = [];
183
+ const componentErrorFn = makeError('component');
184
+ function getComponentContext(need = true) {
185
+ const current = componentStack[componentStack.length - 1];
186
+ if (!current && need) {
187
+ throw componentErrorFn('cannot be called outside the component!');
188
+ }
189
+ return current;
190
+ }
191
+ /**
192
+ * Viewfly 组件管理类,用于管理组件的生命周期,上下文等
193
+ */
194
+ class Component extends ReflectiveInjector {
195
+ get dirty() {
196
+ return this._dirty;
197
+ }
198
+ get changed() {
199
+ return this._changed;
200
+ }
201
+ constructor(context, setup, config) {
202
+ super(context, []);
203
+ this.setup = setup;
204
+ this.config = config;
205
+ this.destroyCallbacks = [];
206
+ this.mountCallbacks = [];
207
+ this.propsChangedCallbacks = [];
208
+ this.updatedCallbacks = [];
209
+ this._dirty = true;
210
+ this._changed = true;
211
+ this.updatedDestroyCallbacks = [];
212
+ this.propsChangedDestroyCallbacks = [];
213
+ this.isFirstRending = true;
214
+ this.props = new Props(config);
215
+ this.parentComponent = this.parentInjector;
216
+ }
217
+ addProvide(providers) {
218
+ providers = Array.isArray(providers) ? providers : [providers];
219
+ providers.forEach(p => {
220
+ this.normalizedProviders.push(normalizeProvider(p));
221
+ });
222
+ }
223
+ init() {
224
+ componentStack.push(this);
225
+ const self = this;
226
+ const props = new Proxy({}, {
227
+ get(_, key) {
228
+ if (self.config) {
229
+ return self.config[key];
230
+ }
231
+ },
232
+ set() {
233
+ throw componentErrorFn('component props is readonly!');
234
+ }
235
+ });
236
+ const render = this.setup(props);
237
+ const template = render();
238
+ componentStack.pop();
239
+ return {
240
+ template,
241
+ render: () => {
242
+ componentStack.push(this);
243
+ const template = render();
244
+ componentStack.pop();
245
+ return template;
246
+ }
247
+ };
248
+ }
249
+ markAsDirtied() {
250
+ this._dirty = true;
251
+ this.markAsChanged();
252
+ }
253
+ markAsChanged() {
254
+ this._changed = true;
255
+ this.parentComponent.markAsChanged();
256
+ }
257
+ rendered() {
258
+ const is = this.isFirstRending;
259
+ this.isFirstRending = false;
260
+ this._dirty = this._changed = false;
261
+ if (is) {
262
+ this.invokeUpdatedHooks();
263
+ this.invokeMountHooks();
264
+ }
265
+ else {
266
+ this.invokeUpdatedHooks();
267
+ }
268
+ }
269
+ invokePropsChangedHooks(newProps) {
270
+ const oldProps = this.config;
271
+ this.config = newProps;
272
+ this.propsChangedDestroyCallbacks.forEach(fn => {
273
+ fn();
274
+ });
275
+ this.propsChangedDestroyCallbacks = [];
276
+ for (const fn of this.propsChangedCallbacks) {
277
+ const destroyFn = fn(newProps, oldProps);
278
+ if (typeof destroyFn === 'function') {
279
+ this.propsChangedDestroyCallbacks.push(destroyFn);
280
+ }
281
+ }
282
+ }
283
+ destroy() {
284
+ this.updatedDestroyCallbacks.forEach(fn => {
285
+ fn();
286
+ });
287
+ this.updatedDestroyCallbacks = [];
288
+ this.propsChangedDestroyCallbacks.forEach(fn => {
289
+ fn();
290
+ });
291
+ this.propsChangedDestroyCallbacks = [];
292
+ for (const fn of this.destroyCallbacks) {
293
+ fn();
294
+ }
295
+ this.updatedCallbacks = [];
296
+ this.mountCallbacks = [];
297
+ this.updatedCallbacks = [];
298
+ }
299
+ invokeMountHooks() {
300
+ for (const fn of this.mountCallbacks) {
301
+ const destroyFn = fn();
302
+ if (typeof destroyFn === 'function') {
303
+ this.destroyCallbacks.push(destroyFn);
304
+ }
305
+ }
306
+ }
307
+ invokeUpdatedHooks() {
308
+ this.updatedDestroyCallbacks.forEach(fn => {
309
+ fn();
310
+ });
311
+ this.updatedDestroyCallbacks = [];
312
+ for (const fn of this.updatedCallbacks) {
313
+ const destroyFn = fn();
314
+ if (typeof destroyFn === 'function') {
315
+ this.updatedDestroyCallbacks.push(destroyFn);
316
+ }
317
+ }
318
+ }
319
+ }
320
+ /**
321
+ * 当组件第一次渲染完成时触发
322
+ * @param callback
323
+ * ```tsx
324
+ * function App() {
325
+ * onMount(() => {
326
+ * console.log('App mounted!')
327
+ * })
328
+ * return () => <div>...</div>
329
+ * }
330
+ * ```
331
+ */
332
+ function onMount(callback) {
333
+ const component = getComponentContext();
334
+ component.mountCallbacks.push(callback);
335
+ }
336
+ /**
337
+ * 当组件视图更新后调用
338
+ * @param callback
339
+ * ```tsx
340
+ * function App() {
341
+ * onUpdated(() => {
342
+ * console.log('App updated!')
343
+ * return () => {
344
+ * console.log('destroy prev update!')
345
+ * }
346
+ * })
347
+ * return () => <div>...</div>
348
+ * }
349
+ * ```
350
+ */
351
+ function onUpdated(callback) {
352
+ const component = getComponentContext();
353
+ component.updatedCallbacks.push(callback);
354
+ return () => {
355
+ const index = component.updatedCallbacks.indexOf(callback);
356
+ if (index > -1) {
357
+ component.updatedCallbacks.splice(index, 1);
358
+ }
359
+ };
360
+ }
361
+ /**
362
+ * 当组件 props 更新地调用
363
+ * @param callback
364
+ * @example
365
+ * ```tsx
366
+ * function YourComponent(props) {
367
+ * onPropsChanged((currentProps, prevProps) => {
368
+ * console.log(currentProps, prevProps)
369
+ *
370
+ * return () => {
371
+ * console.log('destroy prev changed!')
372
+ * }
373
+ * })
374
+ * return () => {
375
+ * return <div>xxx</div>
376
+ * }
377
+ * }
378
+ * ```
379
+ */
380
+ function onPropsChanged(callback) {
381
+ const component = getComponentContext();
382
+ component.propsChangedCallbacks.push(callback);
383
+ return () => {
384
+ const index = component.propsChangedCallbacks.indexOf(callback);
385
+ if (index > -1) {
386
+ component.propsChangedCallbacks.splice(index, 1);
387
+ }
388
+ };
389
+ }
390
+ /**
391
+ * 当组件销毁时调用回调函数
392
+ * @param callback
393
+ */
394
+ function onDestroy(callback) {
395
+ const component = getComponentContext();
396
+ component.destroyCallbacks.push(callback);
397
+ }
398
+ class Ref {
399
+ // private prevValue: T | null = null
400
+ constructor(callback, component) {
401
+ this.callback = callback;
402
+ this.component = component;
403
+ this.unListenFn = null;
404
+ component.destroyCallbacks.push(() => {
405
+ this.unListen();
406
+ });
407
+ }
408
+ update(value) {
409
+ // if (value === this.prevValue) {
410
+ // return
411
+ // }
412
+ // this.prevValue = value
413
+ this.unListen();
414
+ this.unListenFn = this.callback(value) || null;
415
+ }
416
+ unListen() {
417
+ if (typeof this.unListenFn === 'function') {
418
+ this.unListenFn();
419
+ this.unListenFn = null;
420
+ }
421
+ }
422
+ }
423
+ /**
424
+ * 用于节点渲染完成时获取 DOM 节点
425
+ * @param callback 获取 DOM 节点的回调函数
426
+ * @example
427
+ * ```tsx
428
+ * function App() {
429
+ * const ref = useRef(node => {
430
+ * function fn() {
431
+ * // do something...
432
+ * }
433
+ * node.addEventListener('click', fn)
434
+ * return () => {
435
+ * node.removeEventListener('click', fn)
436
+ * }
437
+ * })
438
+ * return () => {
439
+ * return <div ref={ref}>xxx</div>
440
+ * }
441
+ * }
442
+ * ```
443
+ */
444
+ function useRef(callback) {
445
+ const component = getComponentContext();
446
+ return new Ref(callback, component);
447
+ }
448
+ const depsKey = Symbol('deps');
449
+ /**
450
+ * 组件状态管理器
451
+ * @param state 初始状态
452
+ * @example
453
+ * ```tsx
454
+ * function App() {
455
+ * // 初始化状态
456
+ * const state = useSignal(1)
457
+ *
458
+ * return () => {
459
+ * <div>
460
+ * <div>当前值为:{state()}</div>
461
+ * <div>
462
+ * <button type="button" onClick={() => {
463
+ * // 当点击时更新状态
464
+ * state.set(state() + 1)
465
+ * }
466
+ * }>updateState</button>
467
+ * </div>
468
+ * </div>
469
+ * }
470
+ * }
471
+ */
472
+ function useSignal(state) {
473
+ const usedComponents = new Set();
474
+ function stateManager() {
475
+ const component = getComponentContext(false);
476
+ if (component && !usedComponents.has(component)) {
477
+ usedComponents.add(component);
478
+ component.destroyCallbacks.push(() => {
479
+ usedComponents.delete(component);
480
+ });
481
+ }
482
+ return state;
483
+ }
484
+ stateManager.set = function (newState) {
485
+ if (typeof newState === 'function') {
486
+ newState = newState(state);
487
+ }
488
+ else if (newState === state) {
489
+ return;
490
+ }
491
+ state = newState;
492
+ for (const component of usedComponents) {
493
+ component.markAsDirtied();
494
+ }
495
+ for (const fn of stateManager[depsKey]) {
496
+ fn();
497
+ }
498
+ };
499
+ stateManager[depsKey] = new Set();
500
+ return stateManager;
501
+ }
502
+ /**
503
+ * 监听状态变化,当任意一个状态发生变更时,触发回调。
504
+ * useEffect 会返回一个取消监听的函数,调用此函数,可以取消监听。
505
+ * 当在组件中调用时,组件销毁时会自动取消监听。
506
+ * @param deps 依赖的状态 Signal,可以是一个 Signal,只可以一个数包含 Signal 的数组
507
+ * @param effect 状态变更后的回调函数
508
+ */
509
+ function useEffect(deps, effect) {
510
+ const signals = Array.isArray(deps) ? deps : [deps];
511
+ let prevCleanup;
512
+ function effectCallback() {
513
+ if (typeof prevCleanup === 'function') {
514
+ prevCleanup();
515
+ }
516
+ prevCleanup = effect();
517
+ }
518
+ for (const dep of signals) {
519
+ dep[depsKey].add(effectCallback);
520
+ }
521
+ const component = getComponentContext(false);
522
+ let isClean = false;
523
+ const destroyFn = () => {
524
+ if (isClean) {
525
+ return;
526
+ }
527
+ isClean = true;
528
+ if (component) {
529
+ const index = component.destroyCallbacks.indexOf(destroyFn);
530
+ component.destroyCallbacks.splice(index, 1);
531
+ }
532
+ for (const dep of signals) {
533
+ dep[depsKey].delete(effectCallback);
534
+ }
535
+ };
536
+ if (component) {
537
+ component.destroyCallbacks.push(destroyFn);
538
+ }
539
+ return destroyFn;
540
+ }
541
+ /**
542
+ * 通过 IoC 容器当前组件提供上下文共享数据的方法
543
+ * @param provider
544
+ */
545
+ function provide(provider) {
546
+ const component = getComponentContext();
547
+ component.addProvide(provider);
548
+ return component;
549
+ }
550
+ /**
551
+ * 通过组件上下文获取 IoC 容器内数据的勾子方法
552
+ */
553
+ function inject(token, notFoundValue, flags) {
554
+ const component = getComponentContext();
555
+ return component.parentInjector.get(token, notFoundValue, flags);
556
+ }
557
+
558
+ /**
559
+ * Viewfly 根组件,用于实现组件状态更新事件通知
560
+ */
561
+ class RootComponent extends Component {
562
+ constructor(factory) {
563
+ super(new NullInjector(), factory, new Props(null));
564
+ this.changeEmitter = new Subject();
565
+ }
566
+ markAsChanged() {
567
+ this._changed = true;
568
+ this.changeEmitter.next();
569
+ }
570
+ }
571
+
572
+ const refKey = 'ref';
573
+ function getObjectChanges(target, source) {
574
+ const changes = {
575
+ remove: [],
576
+ add: []
577
+ };
578
+ // if (!target) {
579
+ // if (source) {
580
+ // Object.keys(source).forEach(key => {
581
+ // changes.remove.push([key, source[key]])
582
+ // })
583
+ // }
584
+ // return changes
585
+ // }
586
+ //
587
+ // if (!source) {
588
+ // Object.keys(target).forEach(key => {
589
+ // changes.add.push([key, target[key]])
590
+ // })
591
+ // return changes
592
+ // }
593
+ Object.keys(target).forEach(key => {
594
+ const leftValue = target[key];
595
+ if (!Reflect.has(source, key)) {
596
+ changes.add.push([key, leftValue]);
597
+ return;
598
+ }
599
+ const rightValue = source[key];
600
+ if (leftValue === rightValue) {
601
+ return;
602
+ }
603
+ changes.add.push([key, leftValue]);
604
+ changes.remove.push([key, rightValue]);
605
+ });
606
+ Object.keys(source).forEach(key => {
607
+ if (!Reflect.has(target, key)) {
608
+ changes.remove.push([key, source[key]]);
609
+ }
610
+ });
611
+ return changes;
612
+ }
613
+ function getMapChanges(target, source) {
614
+ const changes = {
615
+ remove: [],
616
+ set: []
617
+ };
618
+ // if (!target) {
619
+ // if (source) {
620
+ // source.forEach((value, key) => {
621
+ // changes.remove.push([key, value])
622
+ // })
623
+ // }
624
+ // return changes
625
+ // }
626
+ // if (!source) {
627
+ // target.forEach((value, key) => {
628
+ // changes.set.push([key, value])
629
+ // })
630
+ // return changes
631
+ // }
632
+ target.forEach((value, key) => {
633
+ const rightValue = source.get(key);
634
+ if (value === rightValue) {
635
+ return;
636
+ }
637
+ changes.set.push([key, value]);
638
+ });
639
+ source.forEach((value, key) => {
640
+ if (key === refKey && value instanceof Ref) {
641
+ const newValue = target.get(key);
642
+ if (value !== newValue) {
643
+ changes.remove.push([key, value]);
644
+ }
645
+ return;
646
+ }
647
+ if (!target.has(key)) {
648
+ changes.remove.push([key, value]);
649
+ }
650
+ });
651
+ return changes;
652
+ }
653
+ function getSetChanges(target, source) {
654
+ const changes = {
655
+ add: [],
656
+ remove: []
657
+ };
658
+ // if (!target) {
659
+ // if (source) {
660
+ // source.forEach(i => {
661
+ // changes.remove.push(i)
662
+ // })
663
+ // }
664
+ // return changes
665
+ // }
666
+ //
667
+ // if (!source) {
668
+ // target.forEach(i => {
669
+ // changes.add.push(i)
670
+ // })
671
+ // return changes
672
+ // }
673
+ target.forEach(i => {
674
+ if (!source.has(i)) {
675
+ changes.add.push(i);
676
+ }
677
+ });
678
+ source.forEach(i => {
679
+ if (!target.has(i)) {
680
+ changes.remove.push(i);
681
+ }
682
+ });
683
+ return changes;
684
+ }
685
+ const compareText = '0'.repeat(8);
686
+ function getNodeChanges(newVNode, oldVNode) {
687
+ const newProps = newVNode.props;
688
+ const oldProps = oldVNode.props;
689
+ const styleChanges = getMapChanges(newProps.styles, oldProps.styles);
690
+ const attrChanges = getMapChanges(newProps.attrs, oldProps.attrs);
691
+ const classesChanges = getSetChanges(newProps.classes, oldProps.classes);
692
+ const listenerChanges = getObjectChanges(newProps.listeners, oldProps.listeners);
693
+ return {
694
+ styleChanges,
695
+ attrChanges,
696
+ classesChanges,
697
+ listenerChanges,
698
+ isChanged: [
699
+ attrChanges.set.length,
700
+ attrChanges.remove.length,
701
+ styleChanges.set.length,
702
+ styleChanges.remove.length,
703
+ classesChanges.add.length,
704
+ classesChanges.remove.length,
705
+ listenerChanges.add.length,
706
+ listenerChanges.remove.length
707
+ ].join('') !== compareText
708
+ };
709
+ }
710
+
711
+ class RootComponentRef {
712
+ }
713
+ class Atom {
714
+ constructor(jsxNode, parent) {
715
+ this.jsxNode = jsxNode;
716
+ this.parent = parent;
717
+ this.nativeNode = null;
718
+ this.child = null;
719
+ this.sibling = null;
720
+ }
721
+ }
722
+ let Renderer = class Renderer {
723
+ constructor(nativeRenderer, rootComponentRef) {
724
+ this.nativeRenderer = nativeRenderer;
725
+ this.rootComponentRef = rootComponentRef;
726
+ this.componentAtomCaches = new WeakMap();
727
+ }
728
+ render() {
729
+ const { component, host } = this.rootComponentRef;
730
+ const chain = new Atom(component, null);
731
+ const children = this.buildView(chain);
732
+ children.forEach(child => {
733
+ this.nativeRenderer.appendChild(host, child);
734
+ });
735
+ }
736
+ refresh() {
737
+ const { component, host } = this.rootComponentRef;
738
+ this.reconcile(component, {
739
+ host,
740
+ isParent: true
741
+ });
742
+ }
743
+ reconcile(component, context) {
744
+ if (component.dirty) {
745
+ this.applyChanges(component, context);
746
+ }
747
+ else if (component.changed) {
748
+ const atom = this.componentAtomCaches.get(component).atom.child;
749
+ this.reconcileElement(atom, context);
750
+ }
751
+ }
752
+ reconcileElement(atom, context) {
753
+ while (atom) {
754
+ if (atom.jsxNode instanceof Component) {
755
+ this.reconcile(atom.jsxNode, context);
756
+ atom = atom.sibling;
757
+ continue;
758
+ }
759
+ if (atom.jsxNode instanceof JSXElement) {
760
+ this.reconcileElement(atom.child, {
761
+ host: atom.nativeNode,
762
+ isParent: true
763
+ });
764
+ context.host = atom.nativeNode;
765
+ context.isParent = false;
766
+ }
767
+ atom = atom.sibling;
768
+ }
769
+ }
770
+ applyChanges(component, context) {
771
+ const { atom, render } = this.componentAtomCaches.get(component);
772
+ const diffAtom = atom.child;
773
+ const template = render();
774
+ if (template) {
775
+ this.linkTemplate(template, component, atom);
776
+ }
777
+ else {
778
+ atom.child = null;
779
+ }
780
+ this.diff(atom.child, diffAtom, context);
781
+ component.rendered();
782
+ }
783
+ diff(start, diffAtom, context) {
784
+ const oldChildren = [];
785
+ while (diffAtom) {
786
+ oldChildren.push(diffAtom);
787
+ diffAtom = diffAtom.sibling;
788
+ }
789
+ const commits = [];
790
+ const addReuseCommit = (start, reusedAtom) => {
791
+ commits.push(() => {
792
+ const isComponent = start.jsxNode instanceof Component;
793
+ if (!isComponent) {
794
+ const host = context.host;
795
+ if (context.isParent) {
796
+ this.nativeRenderer.prependChild(host, start.nativeNode);
797
+ }
798
+ else {
799
+ this.nativeRenderer.insertAfter(start.nativeNode, host);
800
+ }
801
+ context.host = start.nativeNode;
802
+ context.isParent = false;
803
+ }
804
+ if (start.child) {
805
+ const childContext = start.jsxNode instanceof JSXElement ? {
806
+ host: start.nativeNode,
807
+ isParent: true
808
+ } : context;
809
+ this.diff(start.child, reusedAtom.child, childContext);
810
+ }
811
+ else if (reusedAtom.child) {
812
+ this.cleanView(reusedAtom.child, false);
813
+ }
814
+ if (isComponent) {
815
+ start.jsxNode.rendered();
816
+ }
817
+ });
818
+ };
819
+ const addCreateCommit = (start) => {
820
+ commits.push(() => {
821
+ const children = this.createViewByAtom(start);
822
+ children.forEach(child => {
823
+ if (context.isParent) {
824
+ this.nativeRenderer.prependChild(context.host, child);
825
+ }
826
+ else {
827
+ this.nativeRenderer.insertAfter(child, context.host);
828
+ }
829
+ context.host = child;
830
+ context.isParent = false;
831
+ });
832
+ });
833
+ };
834
+ while (start && !start.nativeNode) {
835
+ const reusedAtom = this.reuseAndUpdate(start, oldChildren);
836
+ if (reusedAtom) {
837
+ addReuseCommit(start, reusedAtom);
838
+ }
839
+ else {
840
+ addCreateCommit(start);
841
+ }
842
+ start = start.sibling;
843
+ }
844
+ for (const atom of oldChildren) {
845
+ this.cleanView(atom, false);
846
+ }
847
+ for (const commit of commits) {
848
+ commit();
849
+ }
850
+ }
851
+ cleanView(atom, isClean) {
852
+ if (atom.nativeNode) {
853
+ if (!isClean) {
854
+ this.nativeRenderer.remove(atom.nativeNode);
855
+ isClean = true;
856
+ }
857
+ if (atom.jsxNode instanceof JSXElement) {
858
+ const ref = atom.jsxNode.props.attrs.get(refKey);
859
+ if (ref instanceof Ref) {
860
+ ref.unListen();
861
+ }
862
+ }
863
+ }
864
+ let child = atom.child;
865
+ while (child) {
866
+ this.cleanView(child, isClean);
867
+ child = child.sibling;
868
+ }
869
+ if (atom.jsxNode instanceof Component) {
870
+ atom.jsxNode.destroy();
871
+ }
872
+ }
873
+ reuseAndUpdate(start, oldChildren) {
874
+ for (let i = 0; i < oldChildren.length; i++) {
875
+ const diffAtom = oldChildren[i];
876
+ if (start.jsxNode instanceof JSXElement) {
877
+ if (diffAtom.jsxNode instanceof JSXElement && start.jsxNode.name === diffAtom.jsxNode.name) {
878
+ const nativeNode = diffAtom.nativeNode;
879
+ start.nativeNode = nativeNode;
880
+ this.updateNativeNodeProperties(start.jsxNode, diffAtom.jsxNode, nativeNode);
881
+ oldChildren.splice(i, 1);
882
+ return diffAtom;
883
+ }
884
+ }
885
+ else if (start.jsxNode instanceof JSXText) {
886
+ if (diffAtom.jsxNode instanceof JSXText) {
887
+ const nativeNode = diffAtom.nativeNode;
888
+ if (start.jsxNode.text !== diffAtom.jsxNode.text) {
889
+ this.nativeRenderer.syncTextContent(nativeNode, start.jsxNode.text);
890
+ }
891
+ start.nativeNode = nativeNode;
892
+ oldChildren.splice(i, 1);
893
+ return diffAtom;
894
+ }
895
+ }
896
+ else if (diffAtom.jsxNode instanceof Component) {
897
+ if (start.jsxNode.setup === diffAtom.jsxNode.setup) {
898
+ const { isChanged } = getNodeChanges(start.jsxNode, diffAtom.jsxNode);
899
+ if (isChanged) {
900
+ diffAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.config);
901
+ }
902
+ start.jsxNode = diffAtom.jsxNode;
903
+ const { render } = this.componentAtomCaches.get(start.jsxNode);
904
+ const template = render();
905
+ if (template) {
906
+ this.linkTemplate(template, start.jsxNode, start);
907
+ }
908
+ this.componentAtomCaches.set(start.jsxNode, {
909
+ render,
910
+ atom: start
911
+ });
912
+ oldChildren.splice(i, 1);
913
+ return diffAtom;
914
+ }
915
+ }
916
+ }
917
+ return null;
918
+ }
919
+ createViewByAtom(atom) {
920
+ if (atom.jsxNode instanceof JSXElement) {
921
+ const nativeNode = this.createElement(atom.jsxNode);
922
+ atom.nativeNode = nativeNode;
923
+ if (atom.child) {
924
+ const children = this.buildView(atom.child);
925
+ for (const child of children) {
926
+ this.nativeRenderer.appendChild(nativeNode, child);
927
+ }
928
+ }
929
+ return [nativeNode];
930
+ }
931
+ else if (atom.jsxNode instanceof JSXText) {
932
+ const nativeNode = this.createTextNode(atom.jsxNode);
933
+ atom.nativeNode = nativeNode;
934
+ return [nativeNode];
935
+ }
936
+ const { template, render } = atom.jsxNode.init();
937
+ this.componentAtomCaches.set(atom.jsxNode, {
938
+ atom,
939
+ render
940
+ });
941
+ if (template) {
942
+ this.linkTemplate(template, atom.jsxNode, atom);
943
+ }
944
+ if (atom.child) {
945
+ return this.buildView(atom.child);
946
+ }
947
+ return [];
948
+ }
949
+ buildView(chain) {
950
+ const context = [];
951
+ const children = [];
952
+ function getContext() {
953
+ return context[context.length - 1];
954
+ }
955
+ let atom = chain;
956
+ const stopAtom = chain.parent;
957
+ wrap: while (atom) {
958
+ if (atom.jsxNode instanceof Component) {
959
+ this.componentRender(atom.jsxNode, atom);
960
+ if (atom.child) {
961
+ atom = atom.child;
962
+ continue;
963
+ }
964
+ atom.jsxNode.rendered();
965
+ }
966
+ else {
967
+ const host = getContext();
968
+ const nativeNode = atom.jsxNode instanceof JSXElement ? this.createElement(atom.jsxNode) : this.createTextNode(atom.jsxNode);
969
+ atom.nativeNode = nativeNode;
970
+ if (host) {
971
+ this.nativeRenderer.appendChild(host, nativeNode);
972
+ }
973
+ else {
974
+ children.push(nativeNode);
975
+ }
976
+ if (atom.child) {
977
+ context.push(nativeNode);
978
+ atom = atom.child;
979
+ continue;
980
+ }
981
+ }
982
+ while (atom) {
983
+ if (atom.sibling) {
984
+ atom = atom.sibling;
985
+ break;
986
+ }
987
+ atom = atom.parent;
988
+ const isComponent = (atom === null || atom === void 0 ? void 0 : atom.jsxNode) instanceof Component;
989
+ if (isComponent) {
990
+ atom.jsxNode.rendered();
991
+ }
992
+ if (atom === stopAtom) {
993
+ break wrap;
994
+ }
995
+ if (isComponent) {
996
+ continue;
997
+ }
998
+ context.pop();
999
+ }
1000
+ }
1001
+ return children;
1002
+ }
1003
+ componentRender(component, parent) {
1004
+ const { template, render } = component.init();
1005
+ if (template) {
1006
+ this.linkTemplate(template, component, parent);
1007
+ }
1008
+ this.componentAtomCaches.set(component, {
1009
+ render,
1010
+ atom: parent
1011
+ });
1012
+ return parent;
1013
+ }
1014
+ createChainByComponentFactory(context, factory, parent) {
1015
+ const component = factory(context);
1016
+ if (component.setup === Fragment) {
1017
+ return this.createChainByChildren(component, component.props.children, parent);
1018
+ }
1019
+ return new Atom(component, parent);
1020
+ }
1021
+ createChain(context, template, parent) {
1022
+ if (template instanceof JSXElement) {
1023
+ return this.createChainByJSXElement(context, template, parent);
1024
+ }
1025
+ if (template instanceof JSXText) {
1026
+ return this.createChainByJSXText(template, parent);
1027
+ }
1028
+ return this.createChainByComponentFactory(context, template, parent);
1029
+ }
1030
+ createChainByJSXElement(context, element, parent) {
1031
+ const atom = new Atom(element, parent);
1032
+ const children = this.createChainByChildren(context, element.props.children, atom);
1033
+ this.link(atom, children);
1034
+ return atom;
1035
+ }
1036
+ createChainByJSXText(node, parent) {
1037
+ return new Atom(node, parent);
1038
+ }
1039
+ createChainByChildren(context, children, parent) {
1040
+ const atoms = [];
1041
+ for (const item of children) {
1042
+ const child = this.createChain(context, item, parent);
1043
+ if (Array.isArray(child)) {
1044
+ atoms.push(...child);
1045
+ }
1046
+ else {
1047
+ atoms.push(child);
1048
+ }
1049
+ }
1050
+ return atoms;
1051
+ }
1052
+ linkTemplate(template, component, parent) {
1053
+ if (template) {
1054
+ const child = this.createChain(component, template, parent);
1055
+ this.link(parent, Array.isArray(child) ? child : [child]);
1056
+ }
1057
+ }
1058
+ link(parent, children) {
1059
+ for (let i = 1; i < children.length; i++) {
1060
+ const prev = children[i - 1];
1061
+ prev.sibling = children[i];
1062
+ }
1063
+ parent.child = children[0] || null;
1064
+ }
1065
+ createElement(vNode) {
1066
+ const nativeNode = this.nativeRenderer.createElement(vNode.name);
1067
+ const props = vNode.props;
1068
+ if (props) {
1069
+ props.attrs.forEach((value, key) => {
1070
+ if (key === refKey && value instanceof Ref) {
1071
+ value.update(nativeNode);
1072
+ return;
1073
+ }
1074
+ this.nativeRenderer.setProperty(nativeNode, key, value);
1075
+ });
1076
+ props.styles.forEach((value, key) => {
1077
+ this.nativeRenderer.setStyle(nativeNode, key, value);
1078
+ });
1079
+ props.classes.forEach(k => this.nativeRenderer.addClass(nativeNode, k));
1080
+ Object.keys(props.listeners).forEach(type => {
1081
+ this.nativeRenderer.listen(nativeNode, type, props.listeners[type]);
1082
+ });
1083
+ }
1084
+ return nativeNode;
1085
+ }
1086
+ createTextNode(child) {
1087
+ return this.nativeRenderer.createTextNode(child.text);
1088
+ }
1089
+ updateNativeNodeProperties(newVNode, oldVNode, nativeNode) {
1090
+ const { styleChanges, attrChanges, classesChanges, listenerChanges, isChanged } = getNodeChanges(newVNode, oldVNode);
1091
+ if (!isChanged) {
1092
+ return;
1093
+ }
1094
+ styleChanges.remove.forEach(i => this.nativeRenderer.removeStyle(nativeNode, i[0]));
1095
+ styleChanges.set.forEach(i => this.nativeRenderer.setStyle(nativeNode, i[0], i[1]));
1096
+ attrChanges.remove.forEach(([key, value]) => {
1097
+ if (key === refKey && value instanceof Ref) {
1098
+ value.unListen();
1099
+ return;
1100
+ }
1101
+ this.nativeRenderer.removeProperty(nativeNode, key);
1102
+ });
1103
+ attrChanges.set.forEach(([key, value]) => {
1104
+ if (key === refKey && value instanceof Ref) {
1105
+ value.update(nativeNode);
1106
+ return;
1107
+ }
1108
+ this.nativeRenderer.setProperty(nativeNode, key, value);
1109
+ });
1110
+ classesChanges.remove.forEach(i => this.nativeRenderer.removeClass(nativeNode, i));
1111
+ classesChanges.add.forEach(i => this.nativeRenderer.addClass(nativeNode, i));
1112
+ listenerChanges.remove.forEach(i => {
1113
+ this.nativeRenderer.unListen(nativeNode, i[0], i[1]);
1114
+ });
1115
+ listenerChanges.add.forEach(i => {
1116
+ this.nativeRenderer.listen(nativeNode, i[0], i[1]);
1117
+ });
1118
+ }
1119
+ };
1120
+ Renderer = __decorate([
1121
+ Injectable(),
1122
+ __metadata("design:paramtypes", [NativeRenderer,
1123
+ RootComponentRef])
1124
+ ], Renderer);
1125
+
1126
+ const viewflyErrorFn = makeError('Viewfly');
1127
+ /**
1128
+ * Viewfly 核心类,用于启动一个 Viewfly 应用
1129
+ */
1130
+ class Viewfly extends ReflectiveInjector {
1131
+ constructor(config) {
1132
+ super(new NullInjector(), [
1133
+ ...(config.providers || []),
1134
+ Renderer,
1135
+ {
1136
+ provide: RootComponentRef,
1137
+ useFactory: () => {
1138
+ return {
1139
+ host: config.host,
1140
+ component: this.rootComponent
1141
+ };
1142
+ }
1143
+ },
1144
+ {
1145
+ provide: NativeRenderer,
1146
+ useFactory() {
1147
+ throw viewflyErrorFn('You must implement the `NativeRenderer` interface to start Viewfly!');
1148
+ }
1149
+ }
1150
+ ]);
1151
+ this.config = config;
1152
+ this.destroyed = false;
1153
+ this.subscription = new Subscription();
1154
+ this.rootComponent = this.createRootComponent(config.root);
1155
+ }
1156
+ /**
1157
+ * 启动 Viewfly
1158
+ */
1159
+ start() {
1160
+ const renderer = this.get(Renderer);
1161
+ renderer.render();
1162
+ if (this.config.autoUpdate === false) {
1163
+ return;
1164
+ }
1165
+ this.subscription.add(this.rootComponent.changeEmitter.pipe(microTask()).subscribe(() => {
1166
+ renderer.refresh();
1167
+ }));
1168
+ }
1169
+ /**
1170
+ * 销毁 Viewfly 实例
1171
+ */
1172
+ destroy() {
1173
+ const renderer = this.get(Renderer);
1174
+ this.destroyed = true;
1175
+ this.rootComponent.markAsDirtied();
1176
+ this.subscription.unsubscribe();
1177
+ renderer.refresh();
1178
+ }
1179
+ createRootComponent(rootNode) {
1180
+ return new RootComponent(() => {
1181
+ return () => {
1182
+ return this.destroyed ? null : rootNode;
1183
+ };
1184
+ });
1185
+ }
1186
+ }
1187
+
1188
+ export { Component, Fragment, JSXElement, JSXFragment, JSXText, NativeRenderer, Props, Ref, Renderer, RootComponent, RootComponentRef, Viewfly, inject, jsx, jsxs, onDestroy, onMount, onPropsChanged, onUpdated, provide, useEffect, useRef, useSignal };