glass-easel 0.1.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.
- package/README.md +40 -0
- package/dist/glass_easel.all.d.ts +1 -0
- package/dist/glass_easel.all.js +2 -0
- package/dist/glass_easel.all.js.map +1 -0
- package/dist/glass_easel.domlike.global.d.ts +1 -0
- package/dist/glass_easel.domlike.global.js +2 -0
- package/dist/glass_easel.domlike.global.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/src/backend/backend_protocol.d.ts +119 -0
- package/dist/types/src/backend/backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/composed_backend_protocol.d.ts +90 -0
- package/dist/types/src/backend/composed_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/domlike_backend_protocol.d.ts +76 -0
- package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/mode.d.ts +46 -0
- package/dist/types/src/backend/mode.d.ts.map +1 -0
- package/dist/types/src/backend/suggested_backend_protocol.d.ts +30 -0
- package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/behavior.d.ts +428 -0
- package/dist/types/src/behavior.d.ts.map +1 -0
- package/dist/types/src/class_list.d.ts +79 -0
- package/dist/types/src/class_list.d.ts.map +1 -0
- package/dist/types/src/component.d.ts +291 -0
- package/dist/types/src/component.d.ts.map +1 -0
- package/dist/types/src/component_params.d.ts +239 -0
- package/dist/types/src/component_params.d.ts.map +1 -0
- package/dist/types/src/component_space.d.ts +164 -0
- package/dist/types/src/component_space.d.ts.map +1 -0
- package/dist/types/src/data_path.d.ts +5 -0
- package/dist/types/src/data_path.d.ts.map +1 -0
- package/dist/types/src/data_proxy.d.ts +107 -0
- package/dist/types/src/data_proxy.d.ts.map +1 -0
- package/dist/types/src/data_utils.d.ts +3 -0
- package/dist/types/src/data_utils.d.ts.map +1 -0
- package/dist/types/src/element.d.ts +275 -0
- package/dist/types/src/element.d.ts.map +1 -0
- package/dist/types/src/element_iterator.d.ts +43 -0
- package/dist/types/src/element_iterator.d.ts.map +1 -0
- package/dist/types/src/event.d.ts +104 -0
- package/dist/types/src/event.d.ts.map +1 -0
- package/dist/types/src/external_shadow_tree.d.ts +20 -0
- package/dist/types/src/external_shadow_tree.d.ts.map +1 -0
- package/dist/types/src/func_arr.d.ts +39 -0
- package/dist/types/src/func_arr.d.ts.map +1 -0
- package/dist/types/src/global_options.d.ts +111 -0
- package/dist/types/src/global_options.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +43 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/mutation_observer.d.ts +79 -0
- package/dist/types/src/mutation_observer.d.ts.map +1 -0
- package/dist/types/src/native_node.d.ts +8 -0
- package/dist/types/src/native_node.d.ts.map +1 -0
- package/dist/types/src/node.d.ts +49 -0
- package/dist/types/src/node.d.ts.map +1 -0
- package/dist/types/src/relation.d.ts +47 -0
- package/dist/types/src/relation.d.ts.map +1 -0
- package/dist/types/src/render.d.ts +3 -0
- package/dist/types/src/render.d.ts.map +1 -0
- package/dist/types/src/selector.d.ts +32 -0
- package/dist/types/src/selector.d.ts.map +1 -0
- package/dist/types/src/shadow_root.d.ts +136 -0
- package/dist/types/src/shadow_root.d.ts.map +1 -0
- package/dist/types/src/template_engine.d.ts +18 -0
- package/dist/types/src/template_engine.d.ts.map +1 -0
- package/dist/types/src/text_node.d.ts +32 -0
- package/dist/types/src/text_node.d.ts.map +1 -0
- package/dist/types/src/tmpl/index.d.ts +18 -0
- package/dist/types/src/tmpl/index.d.ts.map +1 -0
- package/dist/types/src/tmpl/native_rendering.d.ts +45 -0
- package/dist/types/src/tmpl/native_rendering.d.ts.map +1 -0
- package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +80 -0
- package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +1 -0
- package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +50 -0
- package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +1 -0
- package/dist/types/src/tmpl/range_list_diff.d.ts +19 -0
- package/dist/types/src/tmpl/range_list_diff.d.ts.map +1 -0
- package/dist/types/src/trait_behaviors.d.ts +38 -0
- package/dist/types/src/trait_behaviors.d.ts.map +1 -0
- package/dist/types/src/virtual_node.d.ts +10 -0
- package/dist/types/src/virtual_node.d.ts.map +1 -0
- package/dist/types/tests/backend/domlike.test.d.ts +2 -0
- package/dist/types/tests/backend/domlike.test.d.ts.map +1 -0
- package/dist/types/tests/base/env.d.ts +29 -0
- package/dist/types/tests/base/env.d.ts.map +1 -0
- package/dist/types/tests/base/match.d.ts +9 -0
- package/dist/types/tests/base/match.d.ts.map +1 -0
- package/dist/types/tests/core/backend.test.d.ts +2 -0
- package/dist/types/tests/core/backend.test.d.ts.map +1 -0
- package/dist/types/tests/core/behavior.test.d.ts +2 -0
- package/dist/types/tests/core/behavior.test.d.ts.map +1 -0
- package/dist/types/tests/core/component_space.test.d.ts +2 -0
- package/dist/types/tests/core/component_space.test.d.ts.map +1 -0
- package/dist/types/tests/core/data_update.test.d.ts +2 -0
- package/dist/types/tests/core/data_update.test.d.ts.map +1 -0
- package/dist/types/tests/core/misc.test.d.ts +2 -0
- package/dist/types/tests/core/misc.test.d.ts.map +1 -0
- package/dist/types/tests/core/placeholder.test.d.ts +2 -0
- package/dist/types/tests/core/placeholder.test.d.ts.map +1 -0
- package/dist/types/tests/core/slot.test.d.ts +2 -0
- package/dist/types/tests/core/slot.test.d.ts.map +1 -0
- package/dist/types/tests/core/trait_behaviors.test.d.ts +2 -0
- package/dist/types/tests/core/trait_behaviors.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/binding_map.test.d.ts +2 -0
- package/dist/types/tests/tmpl/binding_map.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/event.test.d.ts +2 -0
- package/dist/types/tests/tmpl/event.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/expression.test.d.ts +2 -0
- package/dist/types/tests/tmpl/expression.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/lvalue.test.d.ts +2 -0
- package/dist/types/tests/tmpl/lvalue.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/native_rendering.test.d.ts +2 -0
- package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/structure.test.d.ts +2 -0
- package/dist/types/tests/tmpl/structure.test.d.ts.map +1 -0
- package/dist/types/tests/types/chaining.test.d.ts +2 -0
- package/dist/types/tests/types/chaining.test.d.ts.map +1 -0
- package/dist/types/tests/types/createElement.test.d.ts +2 -0
- package/dist/types/tests/types/createElement.test.d.ts.map +1 -0
- package/dist/types/tests/types/definition.test.d.ts +2 -0
- package/dist/types/tests/types/definition.test.d.ts.map +1 -0
- package/guide/zh_CN/advanced/binding_map_update.md +32 -0
- package/guide/zh_CN/advanced/build_args.md +28 -0
- package/guide/zh_CN/advanced/component_filter.md +70 -0
- package/guide/zh_CN/advanced/component_space.md +124 -0
- package/guide/zh_CN/advanced/custom_backend.md +53 -0
- package/guide/zh_CN/advanced/error_listener.md +32 -0
- package/guide/zh_CN/advanced/external_component.md +73 -0
- package/guide/zh_CN/advanced/template_engine.md +61 -0
- package/guide/zh_CN/appendix/backend_protocol.md +501 -0
- package/guide/zh_CN/appendix/list_diff_algorithm.md +406 -0
- package/guide/zh_CN/basic/beginning.md +94 -0
- package/guide/zh_CN/basic/component.md +156 -0
- package/guide/zh_CN/basic/event.md +169 -0
- package/guide/zh_CN/basic/lifetime.md +66 -0
- package/guide/zh_CN/basic/method.md +62 -0
- package/guide/zh_CN/basic/template.md +135 -0
- package/guide/zh_CN/data_management/advanced_update.md +170 -0
- package/guide/zh_CN/data_management/data_deep_copy.md +157 -0
- package/guide/zh_CN/data_management/data_observer.md +154 -0
- package/guide/zh_CN/data_management/property_early_init.md +31 -0
- package/guide/zh_CN/data_management/pure_data_pattern.md +21 -0
- package/guide/zh_CN/index.md +93 -0
- package/guide/zh_CN/interaction/behavior.md +52 -0
- package/guide/zh_CN/interaction/component_path.md +37 -0
- package/guide/zh_CN/interaction/generic.md +73 -0
- package/guide/zh_CN/interaction/placeholder.md +40 -0
- package/guide/zh_CN/interaction/relation.md +151 -0
- package/guide/zh_CN/interaction/slot.md +137 -0
- package/guide/zh_CN/interaction/template_import.md +94 -0
- package/guide/zh_CN/interaction/trait_behavior.md +117 -0
- package/guide/zh_CN/styling/external_class.md +46 -0
- package/guide/zh_CN/styling/style_isolation.md +54 -0
- package/guide/zh_CN/styling/virtual_host.md +52 -0
- package/guide/zh_CN/tree/element_iterator.md +54 -0
- package/guide/zh_CN/tree/mutation_observer.md +52 -0
- package/guide/zh_CN/tree/node_tree.md +142 -0
- package/guide/zh_CN/tree/node_tree_modification.md +78 -0
- package/guide/zh_CN/tree/selector.md +66 -0
- package/jest.config.js +6 -0
- package/jest.dts.config.js +9 -0
- package/jest.unit.config.js +14 -0
- package/package.json +28 -0
- package/src/backend/backend_protocol.ts +313 -0
- package/src/backend/composed_backend_protocol.ts +252 -0
- package/src/backend/domlike_backend_protocol.ts +370 -0
- package/src/backend/mode.ts +51 -0
- package/src/backend/suggested_backend_protocol.ts +83 -0
- package/src/behavior.ts +1655 -0
- package/src/bootstrap_dom_dev.js +22 -0
- package/src/class_list.ts +376 -0
- package/src/component.ts +1309 -0
- package/src/component_params.ts +461 -0
- package/src/component_space.ts +547 -0
- package/src/data_path.ts +225 -0
- package/src/data_proxy.ts +670 -0
- package/src/data_utils.ts +50 -0
- package/src/element.ts +1966 -0
- package/src/element_iterator.ts +158 -0
- package/src/event.ts +401 -0
- package/src/external_shadow_tree.ts +27 -0
- package/src/func_arr.ts +198 -0
- package/src/global_options.ts +242 -0
- package/src/index.ts +187 -0
- package/src/mutation_observer.ts +252 -0
- package/src/native_node.ts +74 -0
- package/src/node.ts +174 -0
- package/src/relation.ts +380 -0
- package/src/render.ts +25 -0
- package/src/selector.ts +218 -0
- package/src/shadow_root.ts +766 -0
- package/src/template_engine.ts +45 -0
- package/src/text_node.ts +149 -0
- package/src/tmpl/index.ts +199 -0
- package/src/tmpl/native_rendering.ts +175 -0
- package/src/tmpl/proc_gen_wrapper.ts +954 -0
- package/src/tmpl/proc_gen_wrapper_dom.ts +230 -0
- package/src/tmpl/range_list_diff.ts +443 -0
- package/src/trait_behaviors.ts +51 -0
- package/src/virtual_node.ts +51 -0
- package/tests/backend/domlike.test.ts +254 -0
- package/tests/base/env.ts +78 -0
- package/tests/base/match.ts +185 -0
- package/tests/core/backend.test.ts +144 -0
- package/tests/core/behavior.test.ts +546 -0
- package/tests/core/component_space.test.ts +212 -0
- package/tests/core/data_update.test.ts +461 -0
- package/tests/core/misc.test.ts +339 -0
- package/tests/core/placeholder.test.ts +180 -0
- package/tests/core/slot.test.ts +1495 -0
- package/tests/core/trait_behaviors.test.ts +153 -0
- package/tests/legacy/README.md +3 -0
- package/tests/legacy/behavior.test.js +293 -0
- package/tests/legacy/component.test.js +1247 -0
- package/tests/legacy/data_path.test.js +149 -0
- package/tests/legacy/data_proxy.test.js +759 -0
- package/tests/legacy/element_iterator.test.js +148 -0
- package/tests/legacy/event.test.js +849 -0
- package/tests/legacy/external.test.js +510 -0
- package/tests/legacy/extra_info.test.js +109 -0
- package/tests/legacy/generics.test.js +176 -0
- package/tests/legacy/mutation_observer.test.js +210 -0
- package/tests/legacy/relation.test.js +517 -0
- package/tests/legacy/selector.test.js +263 -0
- package/tests/legacy/slot.test.js +915 -0
- package/tests/legacy/virtual.test.js +394 -0
- package/tests/tmpl/binding_map.test.ts +208 -0
- package/tests/tmpl/event.test.ts +206 -0
- package/tests/tmpl/expression.test.ts +429 -0
- package/tests/tmpl/lvalue.test.ts +160 -0
- package/tests/tmpl/native_rendering.test.ts +155 -0
- package/tests/tmpl/structure.test.ts +998 -0
- package/tests/types/chaining.test.ts +614 -0
- package/tests/types/createElement.test.ts +82 -0
- package/tests/types/definition.test.ts +442 -0
- package/tsconfig.json +11 -0
- package/webpack.config.js +270 -0
package/src/behavior.ts
ADDED
|
@@ -0,0 +1,1655 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ComponentParams,
|
|
5
|
+
DataList,
|
|
6
|
+
PropertyList,
|
|
7
|
+
MethodList,
|
|
8
|
+
ComponentInstance,
|
|
9
|
+
PropertyListItem,
|
|
10
|
+
ComponentMethod,
|
|
11
|
+
RelationParams,
|
|
12
|
+
PropertyType,
|
|
13
|
+
TraitRelationParams,
|
|
14
|
+
DataWithPropertyValues,
|
|
15
|
+
PropertyOption,
|
|
16
|
+
PropertyTypeToValueType,
|
|
17
|
+
TaggedMethod,
|
|
18
|
+
UnTaggedMethod,
|
|
19
|
+
ChainingFilterFunc,
|
|
20
|
+
ChainingFilterType,
|
|
21
|
+
SetDataSetter,
|
|
22
|
+
DeepReadonly,
|
|
23
|
+
Empty,
|
|
24
|
+
NewFieldList,
|
|
25
|
+
ObserverDataPathStrings,
|
|
26
|
+
GetFromObserverPathString,
|
|
27
|
+
IsNever,
|
|
28
|
+
} from './component_params'
|
|
29
|
+
import {
|
|
30
|
+
FuncArr,
|
|
31
|
+
triggerWarning,
|
|
32
|
+
} from './func_arr'
|
|
33
|
+
import {
|
|
34
|
+
ComponentOptions,
|
|
35
|
+
getDefaultComponentSpace,
|
|
36
|
+
} from './global_options'
|
|
37
|
+
import {
|
|
38
|
+
MultiPaths,
|
|
39
|
+
parseMultiPaths,
|
|
40
|
+
} from './data_path'
|
|
41
|
+
import {
|
|
42
|
+
DataValue,
|
|
43
|
+
DataGroupObserverTree,
|
|
44
|
+
} from './data_proxy'
|
|
45
|
+
import {
|
|
46
|
+
ComponentDefinition,
|
|
47
|
+
GeneralComponentDefinition,
|
|
48
|
+
LifetimeFuncs,
|
|
49
|
+
Lifetimes,
|
|
50
|
+
PageLifetimeFuncs,
|
|
51
|
+
} from './component'
|
|
52
|
+
import {
|
|
53
|
+
RelationDefinition,
|
|
54
|
+
RelationType,
|
|
55
|
+
RelationListener,
|
|
56
|
+
RelationFailedListener,
|
|
57
|
+
} from './relation'
|
|
58
|
+
import {
|
|
59
|
+
normalizeUrl,
|
|
60
|
+
ComponentSpace,
|
|
61
|
+
ComponentWaitingList,
|
|
62
|
+
} from './component_space'
|
|
63
|
+
import { TraitBehavior } from './trait_behaviors'
|
|
64
|
+
import { simpleDeepCopy } from './data_utils'
|
|
65
|
+
import { EventListener } from './event'
|
|
66
|
+
|
|
67
|
+
export const enum NormalizedPropertyType {
|
|
68
|
+
Invalid,
|
|
69
|
+
Any,
|
|
70
|
+
String,
|
|
71
|
+
Number,
|
|
72
|
+
Boolean,
|
|
73
|
+
Object,
|
|
74
|
+
Array,
|
|
75
|
+
Function,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type PropertyDefinition = {
|
|
79
|
+
type: NormalizedPropertyType
|
|
80
|
+
optionalTypes: NormalizedPropertyType[] | null
|
|
81
|
+
value: unknown | undefined
|
|
82
|
+
default: (() => unknown) | undefined
|
|
83
|
+
observer: ((newValue: unknown, oldValue: unknown) => void) | null
|
|
84
|
+
reflectIdPrefix: boolean
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type ComponentDefinitionWithPlaceholder = {
|
|
88
|
+
final: GeneralComponentDefinition | null,
|
|
89
|
+
placeholder: string | null,
|
|
90
|
+
waiting: ComponentWaitingList | null,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type ResolveBehaviorBuilder<B, TChainingFilter extends ChainingFilterType> =
|
|
94
|
+
IsNever<TChainingFilter> extends false
|
|
95
|
+
? TChainingFilter extends ChainingFilterType
|
|
96
|
+
? Omit<B, TChainingFilter['remove']> & TChainingFilter['add']
|
|
97
|
+
: B
|
|
98
|
+
: B
|
|
99
|
+
|
|
100
|
+
const shallowMerge = (dest: { [key: string]: unknown }, src: { [key: string]: unknown }) => {
|
|
101
|
+
const keys = Object.keys(src)
|
|
102
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
103
|
+
const key = keys[i]!
|
|
104
|
+
if (Object.prototype.hasOwnProperty.call(dest, key)) {
|
|
105
|
+
if (key[0] === '_') {
|
|
106
|
+
triggerWarning(`data field "${key}" from different behaviors is overriding or merging.`)
|
|
107
|
+
}
|
|
108
|
+
if (
|
|
109
|
+
typeof dest[key] === 'object'
|
|
110
|
+
&& typeof src[key] === 'object'
|
|
111
|
+
&& src[key] !== null
|
|
112
|
+
&& !Array.isArray(src[key])
|
|
113
|
+
) {
|
|
114
|
+
if (Array.isArray(dest[key])) {
|
|
115
|
+
dest[key] = (dest[key] as DataValue[]).slice()
|
|
116
|
+
} else {
|
|
117
|
+
const oldDest = dest[key] as { [key: string]: DataValue }
|
|
118
|
+
const newDest = {} as { [key: string]: DataValue }
|
|
119
|
+
const subKeys = Object.keys(oldDest)
|
|
120
|
+
for (let i = 0; i < subKeys.length; i += 1) {
|
|
121
|
+
const subKey = subKeys[i]!
|
|
122
|
+
newDest[subKey] = oldDest[subKey]
|
|
123
|
+
}
|
|
124
|
+
dest[key] = newDest
|
|
125
|
+
}
|
|
126
|
+
shallowMerge(
|
|
127
|
+
dest[key] as { [key: string]: unknown },
|
|
128
|
+
src[key] as { [key: string]: unknown },
|
|
129
|
+
)
|
|
130
|
+
} else {
|
|
131
|
+
dest[key] = src[key]
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
dest[key] = src[key]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const normalizePropertyTypeShortHand = (
|
|
140
|
+
propDef: unknown,
|
|
141
|
+
): PropertyDefinition | null => {
|
|
142
|
+
if (propDef === String) {
|
|
143
|
+
return {
|
|
144
|
+
type: NormalizedPropertyType.String,
|
|
145
|
+
optionalTypes: null,
|
|
146
|
+
value: '',
|
|
147
|
+
default: undefined,
|
|
148
|
+
observer: null,
|
|
149
|
+
reflectIdPrefix: false,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (propDef === Number) {
|
|
153
|
+
return {
|
|
154
|
+
type: NormalizedPropertyType.Number,
|
|
155
|
+
optionalTypes: null,
|
|
156
|
+
value: 0,
|
|
157
|
+
default: undefined,
|
|
158
|
+
observer: null,
|
|
159
|
+
reflectIdPrefix: false,
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (propDef === Boolean) {
|
|
163
|
+
return {
|
|
164
|
+
type: NormalizedPropertyType.Boolean,
|
|
165
|
+
optionalTypes: null,
|
|
166
|
+
value: false,
|
|
167
|
+
default: undefined,
|
|
168
|
+
observer: null,
|
|
169
|
+
reflectIdPrefix: false,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (propDef === Object) {
|
|
173
|
+
return {
|
|
174
|
+
type: NormalizedPropertyType.Object,
|
|
175
|
+
optionalTypes: null,
|
|
176
|
+
value: null,
|
|
177
|
+
default: undefined,
|
|
178
|
+
observer: null,
|
|
179
|
+
reflectIdPrefix: false,
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (propDef === Array) {
|
|
183
|
+
return {
|
|
184
|
+
type: NormalizedPropertyType.Array,
|
|
185
|
+
optionalTypes: null,
|
|
186
|
+
value: [],
|
|
187
|
+
default: undefined,
|
|
188
|
+
observer: null,
|
|
189
|
+
reflectIdPrefix: false,
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (propDef === Function) {
|
|
193
|
+
return {
|
|
194
|
+
type: NormalizedPropertyType.Function,
|
|
195
|
+
optionalTypes: null,
|
|
196
|
+
value() { /* empty */ },
|
|
197
|
+
default: undefined,
|
|
198
|
+
observer: null,
|
|
199
|
+
reflectIdPrefix: false,
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (propDef === null || propDef === undefined) {
|
|
203
|
+
return {
|
|
204
|
+
type: NormalizedPropertyType.Any,
|
|
205
|
+
optionalTypes: null,
|
|
206
|
+
value: null,
|
|
207
|
+
default: undefined,
|
|
208
|
+
observer: null,
|
|
209
|
+
reflectIdPrefix: false,
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return null
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const normalizePropertyType = (t: unknown): NormalizedPropertyType => {
|
|
216
|
+
if (t === String) {
|
|
217
|
+
return NormalizedPropertyType.String
|
|
218
|
+
}
|
|
219
|
+
if (t === Number) {
|
|
220
|
+
return NormalizedPropertyType.Number
|
|
221
|
+
}
|
|
222
|
+
if (t === Boolean) {
|
|
223
|
+
return NormalizedPropertyType.Boolean
|
|
224
|
+
}
|
|
225
|
+
if (t === Object) {
|
|
226
|
+
return NormalizedPropertyType.Object
|
|
227
|
+
}
|
|
228
|
+
if (t === Array) {
|
|
229
|
+
return NormalizedPropertyType.Array
|
|
230
|
+
}
|
|
231
|
+
if (t === Function) {
|
|
232
|
+
return NormalizedPropertyType.Function
|
|
233
|
+
}
|
|
234
|
+
if (t === null || t === undefined) {
|
|
235
|
+
return NormalizedPropertyType.Any
|
|
236
|
+
}
|
|
237
|
+
return NormalizedPropertyType.Invalid
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export const convertValueToType = (
|
|
241
|
+
value: unknown,
|
|
242
|
+
propName: string,
|
|
243
|
+
prop: PropertyDefinition,
|
|
244
|
+
): unknown => {
|
|
245
|
+
const type = prop.type
|
|
246
|
+
const defaultFn = prop.default
|
|
247
|
+
// try match optional types
|
|
248
|
+
const optionalTypes = prop.optionalTypes
|
|
249
|
+
if (optionalTypes) {
|
|
250
|
+
for (let i = 0; i < optionalTypes.length; i += 1) {
|
|
251
|
+
if (matchTypeWithValue(optionalTypes[i]!, value)) {
|
|
252
|
+
return value
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// for string
|
|
257
|
+
if (type === NormalizedPropertyType.String) {
|
|
258
|
+
if (value === null || value === undefined) {
|
|
259
|
+
triggerWarning(`property "${propName}" received type-incompatible value: expected <String> but get null value. Used default value instead.`)
|
|
260
|
+
return defaultFn === undefined ? '' : defaultFn()
|
|
261
|
+
}
|
|
262
|
+
if (typeof value === 'object') {
|
|
263
|
+
triggerWarning(`property "${propName}" received type-incompatible value: expected <String> but got object-typed value. Force converted.`)
|
|
264
|
+
}
|
|
265
|
+
return String(value)
|
|
266
|
+
}
|
|
267
|
+
// for number
|
|
268
|
+
if (type === NormalizedPropertyType.Number) {
|
|
269
|
+
// eslint-disable-next-line no-restricted-globals
|
|
270
|
+
if (isFinite(value as number)) return Number(value)
|
|
271
|
+
if (typeof value === 'number') {
|
|
272
|
+
triggerWarning(`property "${propName}" received type-incompatible value: expected <Number> but got NaN or Infinity. Used default value instead.`)
|
|
273
|
+
} else {
|
|
274
|
+
triggerWarning(`property "${propName}" received type-incompatible value: expected <Number> but got non-number value. Used default value instead.`)
|
|
275
|
+
}
|
|
276
|
+
return defaultFn === undefined ? 0 : defaultFn()
|
|
277
|
+
}
|
|
278
|
+
// for boolean
|
|
279
|
+
if (type === NormalizedPropertyType.Boolean) {
|
|
280
|
+
return !!value
|
|
281
|
+
}
|
|
282
|
+
// for array
|
|
283
|
+
if (type === NormalizedPropertyType.Array) {
|
|
284
|
+
if (Array.isArray(value)) return value as unknown
|
|
285
|
+
triggerWarning(`property "${propName}" received type-incompatible value: expected <Array> but got non-array value. Used default value instead.`)
|
|
286
|
+
return defaultFn === undefined ? [] : defaultFn()
|
|
287
|
+
}
|
|
288
|
+
// for object
|
|
289
|
+
if (type === NormalizedPropertyType.Object) {
|
|
290
|
+
if (typeof value === 'object') return value
|
|
291
|
+
triggerWarning(`property "${propName}" received type-incompatible value: expected <Object> but got non-object value. Used default value instead.`)
|
|
292
|
+
return defaultFn === undefined ? null : defaultFn()
|
|
293
|
+
}
|
|
294
|
+
// for function
|
|
295
|
+
if (type === NormalizedPropertyType.Function) {
|
|
296
|
+
if (typeof value === 'function') return value
|
|
297
|
+
triggerWarning(`property "${propName}" received type-incompatible value: expected <Function> but got non-function value. Used default value instead.`)
|
|
298
|
+
// eslint-disable-next-line func-names
|
|
299
|
+
return defaultFn === undefined ? function () { /* empty */ } : defaultFn()
|
|
300
|
+
}
|
|
301
|
+
// for any-typed, just return the value and avoid undefined
|
|
302
|
+
if (value === undefined) return defaultFn === undefined ? null : defaultFn()
|
|
303
|
+
return value
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export const matchTypeWithValue = (type: NormalizedPropertyType, value: any) => {
|
|
307
|
+
if (type === NormalizedPropertyType.String) {
|
|
308
|
+
if (typeof value !== 'string') return false
|
|
309
|
+
} else if (type === NormalizedPropertyType.Number) {
|
|
310
|
+
if (!Number.isFinite(value)) return false
|
|
311
|
+
} else if (type === NormalizedPropertyType.Boolean) {
|
|
312
|
+
if (typeof value !== 'boolean') return false
|
|
313
|
+
} else if (type === NormalizedPropertyType.Object) {
|
|
314
|
+
if (typeof value !== 'object' || Array.isArray(value)) return false
|
|
315
|
+
} else if (type === NormalizedPropertyType.Array) {
|
|
316
|
+
if (typeof value !== 'object' || !Array.isArray(value)) return false
|
|
317
|
+
} else if (type === NormalizedPropertyType.Function) {
|
|
318
|
+
if (typeof value !== 'function') return false
|
|
319
|
+
} else if (value === undefined) {
|
|
320
|
+
return false
|
|
321
|
+
}
|
|
322
|
+
return true
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export const normalizeRelation = <TOut extends { [key: string]: any }>(
|
|
326
|
+
is: string,
|
|
327
|
+
key: string,
|
|
328
|
+
relation: RelationParams | TraitRelationParams<TOut>,
|
|
329
|
+
): RelationDefinition | null => {
|
|
330
|
+
const checkRelationFunc = (f: unknown): RelationListener | null => {
|
|
331
|
+
if (typeof f === 'function') {
|
|
332
|
+
return f as RelationListener
|
|
333
|
+
}
|
|
334
|
+
if (f !== undefined) {
|
|
335
|
+
triggerWarning(`the "${key}" relation listener is not a function (when preparing behavior "${is}").`)
|
|
336
|
+
}
|
|
337
|
+
return null
|
|
338
|
+
}
|
|
339
|
+
let type: RelationType
|
|
340
|
+
if (relation.type === 'parent') {
|
|
341
|
+
type = RelationType.ParentComponent
|
|
342
|
+
} else if (relation.type === 'child') {
|
|
343
|
+
type = RelationType.ChildComponent
|
|
344
|
+
} else if (relation.type === 'parent-common-node') {
|
|
345
|
+
type = RelationType.ParentNonVirtualNode
|
|
346
|
+
} else if (relation.type === 'child-common-node') {
|
|
347
|
+
type = RelationType.ChildNonVirtualNode
|
|
348
|
+
} else if (relation.type === 'ancestor') {
|
|
349
|
+
type = RelationType.Ancestor
|
|
350
|
+
} else if (relation.type === 'descendant') {
|
|
351
|
+
type = RelationType.Descendant
|
|
352
|
+
} else {
|
|
353
|
+
const type = relation.type as string
|
|
354
|
+
triggerWarning(`the "${key}" relation has an invalid relation type "${type}" (when preparing behavior "${is}").`)
|
|
355
|
+
return null
|
|
356
|
+
}
|
|
357
|
+
let target: string
|
|
358
|
+
| GeneralBehavior
|
|
359
|
+
| TraitBehavior<{ [key: string]: unknown }, { [key: string]: unknown }>
|
|
360
|
+
let domain: string | null = null
|
|
361
|
+
if (relation.target instanceof ComponentDefinition) {
|
|
362
|
+
target = relation.target.behavior as GeneralBehavior
|
|
363
|
+
} else if (relation.target instanceof Behavior || relation.target instanceof TraitBehavior) {
|
|
364
|
+
target = relation.target
|
|
365
|
+
} else {
|
|
366
|
+
const { domain: d, absPath } = normalizeUrl(relation.target || key, is)
|
|
367
|
+
target = absPath
|
|
368
|
+
domain = d
|
|
369
|
+
}
|
|
370
|
+
if (!target) {
|
|
371
|
+
triggerWarning(`the target of relation "${key}" is not a valid behavior or component (when preparing behavior "${is}").`)
|
|
372
|
+
return null
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
target,
|
|
376
|
+
domain,
|
|
377
|
+
type,
|
|
378
|
+
linked: checkRelationFunc(relation.linked),
|
|
379
|
+
linkChanged: checkRelationFunc(relation.linkChanged),
|
|
380
|
+
unlinked: checkRelationFunc(relation.unlinked),
|
|
381
|
+
linkFailed: checkRelationFunc(relation.linkFailed) as RelationFailedListener,
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export interface RelationHandler<TTarget, TOut> {
|
|
386
|
+
list(): TTarget[]
|
|
387
|
+
listAsTrait: TOut extends never
|
|
388
|
+
? undefined
|
|
389
|
+
: () => TOut[]
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export interface RelationInit {
|
|
393
|
+
(def: RelationParams): RelationHandler<any, never>
|
|
394
|
+
<TOut extends { [key: string]: any }>(
|
|
395
|
+
def: TraitRelationParams<TOut>,
|
|
396
|
+
): RelationHandler<unknown, TOut>
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export interface BuilderContext<
|
|
400
|
+
TPrevData extends DataList,
|
|
401
|
+
TProperty extends PropertyList,
|
|
402
|
+
TMethodCaller
|
|
403
|
+
> extends ThisType<TMethodCaller> {
|
|
404
|
+
self: TMethodCaller;
|
|
405
|
+
data: DeepReadonly<DataWithPropertyValues<TPrevData, TProperty>>;
|
|
406
|
+
setData: (newData: Partial<SetDataSetter<TPrevData>>) => void;
|
|
407
|
+
implement: <TIn extends { [x: string]: any }>(
|
|
408
|
+
traitBehavior: TraitBehavior<TIn, any>,
|
|
409
|
+
impl: TIn
|
|
410
|
+
) => void;
|
|
411
|
+
relation<TOut extends { [key: string]: any }>
|
|
412
|
+
(def: TraitRelationParams<TOut>): RelationHandler<any, TOut>
|
|
413
|
+
relation(def: RelationParams): RelationHandler<any, never>
|
|
414
|
+
observer<
|
|
415
|
+
P extends ObserverDataPathStrings<
|
|
416
|
+
DataWithPropertyValues<TPrevData, TProperty>
|
|
417
|
+
>,
|
|
418
|
+
V = DeepReadonly<
|
|
419
|
+
GetFromObserverPathString<DataWithPropertyValues<TPrevData, TProperty>, P>
|
|
420
|
+
>
|
|
421
|
+
>(
|
|
422
|
+
paths: P,
|
|
423
|
+
func: (newValue: V) => void,
|
|
424
|
+
): void;
|
|
425
|
+
observer<
|
|
426
|
+
P extends ObserverDataPathStrings<
|
|
427
|
+
DataWithPropertyValues<TPrevData, TProperty>
|
|
428
|
+
>[],
|
|
429
|
+
V = {
|
|
430
|
+
[K in keyof P]: DeepReadonly<
|
|
431
|
+
GetFromObserverPathString<DataWithPropertyValues<TPrevData, TProperty>, P[K]>
|
|
432
|
+
>
|
|
433
|
+
}
|
|
434
|
+
>(
|
|
435
|
+
paths: readonly [...P],
|
|
436
|
+
func: (...newValues: V extends any[] ? V : never) => void,
|
|
437
|
+
): void;
|
|
438
|
+
lifetime: <L extends keyof Lifetimes>(name: L, func: Lifetimes[L]) => void;
|
|
439
|
+
pageLifetime: (name: string, func: (...args: any[]) => void) => void;
|
|
440
|
+
method: <Fn extends ComponentMethod>(func: Fn) => TaggedMethod<Fn>;
|
|
441
|
+
listener: <T>(func: EventListener<T>) => TaggedMethod<EventListener<T>>;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export type GeneralBehaviorBuilder = BehaviorBuilder<
|
|
445
|
+
Record<string, any>,
|
|
446
|
+
Record<string, any>,
|
|
447
|
+
Record<string, any>,
|
|
448
|
+
Record<string, any>,
|
|
449
|
+
never,
|
|
450
|
+
never
|
|
451
|
+
>
|
|
452
|
+
|
|
453
|
+
export class BehaviorBuilder<
|
|
454
|
+
TPrevData extends DataList = Empty,
|
|
455
|
+
TData extends DataList = Empty,
|
|
456
|
+
TProperty extends PropertyList = Empty,
|
|
457
|
+
TMethod extends MethodList = Empty,
|
|
458
|
+
TChainingFilter extends ChainingFilterType = never,
|
|
459
|
+
TPendingChainingFilter extends ChainingFilterType = never,
|
|
460
|
+
> {
|
|
461
|
+
/** @internal */
|
|
462
|
+
_$ownerSpace: ComponentSpace
|
|
463
|
+
/** @internal */
|
|
464
|
+
_$is: string | undefined
|
|
465
|
+
/** @internal */
|
|
466
|
+
_$behaviors: (string | GeneralBehavior)[] = []
|
|
467
|
+
/** @internal */
|
|
468
|
+
_$chainingFilter?: (chain: GeneralBehaviorBuilder) => any
|
|
469
|
+
/** @internal */
|
|
470
|
+
_$options?: ComponentOptions
|
|
471
|
+
/** @internal */
|
|
472
|
+
_$traitBehaviors: { traitBehavior: TraitBehavior<any, any>, impl: any }[] = []
|
|
473
|
+
/** @internal */
|
|
474
|
+
_$template?: { [key: string]: unknown }
|
|
475
|
+
/** @internal */
|
|
476
|
+
_$using?: { [alias: string]: string | GeneralComponentDefinition }
|
|
477
|
+
/** @internal */
|
|
478
|
+
_$placeholders?: { [alias: string]: string }
|
|
479
|
+
/** @internal */
|
|
480
|
+
_$generics?: { [alias: string]: { default?: string | GeneralComponentDefinition } | true }
|
|
481
|
+
/** @internal */
|
|
482
|
+
_$externalClasses?: string[]
|
|
483
|
+
/** @internal */
|
|
484
|
+
_$staticData: { [field: string]: any } | undefined = undefined
|
|
485
|
+
/** @internal */
|
|
486
|
+
_$data: (() => { [field: string]: any })[] = []
|
|
487
|
+
/** @internal */
|
|
488
|
+
_$properties?: { name: string, def: PropertyListItem<PropertyType, unknown> }[]
|
|
489
|
+
/** @internal */
|
|
490
|
+
_$methods: { name: string, func: ComponentMethod }[] = []
|
|
491
|
+
/** @internal */
|
|
492
|
+
_$observers?: {
|
|
493
|
+
dataPaths: MultiPaths,
|
|
494
|
+
func: ComponentMethod | string,
|
|
495
|
+
once: boolean,
|
|
496
|
+
}[]
|
|
497
|
+
/** @internal */
|
|
498
|
+
_$lifetimes: { name: string, func: ComponentMethod, once: boolean }[] = []
|
|
499
|
+
/** @internal */
|
|
500
|
+
_$pageLifetimes?: { name: string, func: ComponentMethod, once: boolean }[] = []
|
|
501
|
+
/** @internal */
|
|
502
|
+
_$listeners?: {
|
|
503
|
+
[name: string]: ComponentMethod | string,
|
|
504
|
+
}
|
|
505
|
+
/** @internal */
|
|
506
|
+
_$relations?: { name: string, rel: RelationParams }[]
|
|
507
|
+
/** @internal */
|
|
508
|
+
_$init: ((this: any, ctx: any) => any)[] = []
|
|
509
|
+
/** @internal */
|
|
510
|
+
_$methodCallerInit?: (
|
|
511
|
+
this: ComponentInstance<TData, TProperty, TMethod>
|
|
512
|
+
) => any
|
|
513
|
+
|
|
514
|
+
/** @internal */
|
|
515
|
+
constructor(
|
|
516
|
+
is: string | undefined,
|
|
517
|
+
ownerSpace: ComponentSpace,
|
|
518
|
+
) {
|
|
519
|
+
this._$is = is
|
|
520
|
+
this._$ownerSpace = ownerSpace
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Set a front-most init function
|
|
525
|
+
*
|
|
526
|
+
* It should return the method caller (the `this` value for various callbacks)
|
|
527
|
+
* that will be used in future.
|
|
528
|
+
*/
|
|
529
|
+
methodCallerInit(
|
|
530
|
+
func: (this: ComponentInstance<TData, TProperty, TMethod>) => any,
|
|
531
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
532
|
+
this._$methodCallerInit = func
|
|
533
|
+
return this as any
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Add a behavior
|
|
538
|
+
*
|
|
539
|
+
* If the behavior contains a chaining filter, the chaining filter is called.
|
|
540
|
+
*/
|
|
541
|
+
behavior<
|
|
542
|
+
UData extends DataList,
|
|
543
|
+
UProperty extends PropertyList,
|
|
544
|
+
UMethod extends MethodList,
|
|
545
|
+
UChainingFilter extends ChainingFilterType
|
|
546
|
+
>(
|
|
547
|
+
behavior: Behavior<UData, UProperty, UMethod, UChainingFilter>,
|
|
548
|
+
): ResolveBehaviorBuilder<
|
|
549
|
+
BehaviorBuilder<
|
|
550
|
+
TPrevData,
|
|
551
|
+
TData & UData,
|
|
552
|
+
TProperty & UProperty,
|
|
553
|
+
TMethod & UMethod,
|
|
554
|
+
UChainingFilter,
|
|
555
|
+
TPendingChainingFilter
|
|
556
|
+
>,
|
|
557
|
+
UChainingFilter
|
|
558
|
+
> {
|
|
559
|
+
this._$behaviors.push(behavior as GeneralBehavior)
|
|
560
|
+
if (behavior._$chainingFilter) {
|
|
561
|
+
return behavior._$chainingFilter(this as any) as any
|
|
562
|
+
}
|
|
563
|
+
return this as any
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Set the chaining filter
|
|
568
|
+
*
|
|
569
|
+
* The chaining filter is the definition filter for chaining API.
|
|
570
|
+
* It SHOULD return another chainable object for further chaining.
|
|
571
|
+
*/
|
|
572
|
+
chainingFilter<
|
|
573
|
+
TAddedFields extends { [key: string]: any },
|
|
574
|
+
TRemovedFields extends string = never,
|
|
575
|
+
>(
|
|
576
|
+
func: ChainingFilterFunc<TAddedFields, TRemovedFields>,
|
|
577
|
+
): ResolveBehaviorBuilder<
|
|
578
|
+
BehaviorBuilder<TPrevData, TData, TProperty, TMethod, TChainingFilter, {
|
|
579
|
+
add: TAddedFields,
|
|
580
|
+
remove: TRemovedFields,
|
|
581
|
+
}>,
|
|
582
|
+
TChainingFilter
|
|
583
|
+
> {
|
|
584
|
+
this._$chainingFilter = func
|
|
585
|
+
return this as any
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Set component options
|
|
590
|
+
*
|
|
591
|
+
* The options will be merged with previous settings.
|
|
592
|
+
*/
|
|
593
|
+
options(options: ComponentOptions): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
594
|
+
const oldOptions = this._$options
|
|
595
|
+
if (oldOptions) {
|
|
596
|
+
this._$options = { ...oldOptions, ...options }
|
|
597
|
+
} else {
|
|
598
|
+
this._$options = options
|
|
599
|
+
}
|
|
600
|
+
return this as any
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Implement a trait behavior
|
|
605
|
+
*/
|
|
606
|
+
implement<TIn extends { [key: string]: any }>(
|
|
607
|
+
traitBehavior: TraitBehavior<TIn, any>,
|
|
608
|
+
impl: TIn,
|
|
609
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
610
|
+
this._$traitBehaviors.push({ traitBehavior, impl })
|
|
611
|
+
return this as any
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Set the compiled template object
|
|
616
|
+
*/
|
|
617
|
+
template(template: { [key: string]: unknown }): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
618
|
+
this._$template = template
|
|
619
|
+
return this as any
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Add other components to the component using list
|
|
624
|
+
*/
|
|
625
|
+
usingComponents<T extends Record<string, string | ComponentDefinition<any, any, any>>>(
|
|
626
|
+
list: T,
|
|
627
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
628
|
+
if (this._$using) this._$using = { ...this._$using, ...list }
|
|
629
|
+
else this._$using = list
|
|
630
|
+
return this as any
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Add some placeholders
|
|
635
|
+
*
|
|
636
|
+
* The alias SHOULD be in the using list, otherwise it will be ignored.
|
|
637
|
+
*/
|
|
638
|
+
placeholders(
|
|
639
|
+
list: Record<string, string>,
|
|
640
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
641
|
+
if (this._$placeholders) this._$placeholders = { ...this._$placeholders, ...list }
|
|
642
|
+
else this._$placeholders = list
|
|
643
|
+
return this as any
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Add other generics
|
|
648
|
+
*
|
|
649
|
+
* The alias SHOULD NOT be in the using list, otherwise it will be ignored.
|
|
650
|
+
*/
|
|
651
|
+
generics(
|
|
652
|
+
list: Record<string, { default?: string | GeneralComponentDefinition } | true>,
|
|
653
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
654
|
+
if (this._$generics) this._$generics = { ...this._$generics, ...list }
|
|
655
|
+
else this._$generics = list
|
|
656
|
+
return this as any
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Add external classes
|
|
661
|
+
*/
|
|
662
|
+
externalClasses(list: string[]): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
663
|
+
if (this._$externalClasses) this._$externalClasses = this._$externalClasses.concat(list)
|
|
664
|
+
else this._$externalClasses = list
|
|
665
|
+
return this as any
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Add some template data fields
|
|
670
|
+
*
|
|
671
|
+
* It does not support raw data, but a `gen` function which returns the new data fields.
|
|
672
|
+
* The `gen` function executes once during component creation.
|
|
673
|
+
*/
|
|
674
|
+
data<T extends DataList>(
|
|
675
|
+
gen: (() => NewFieldList<DataWithPropertyValues<TData, TProperty>, T>),
|
|
676
|
+
): ResolveBehaviorBuilder<
|
|
677
|
+
BehaviorBuilder<T, TData & T, TProperty, TMethod, TChainingFilter, TPendingChainingFilter>,
|
|
678
|
+
TChainingFilter
|
|
679
|
+
> {
|
|
680
|
+
this._$data.push(gen)
|
|
681
|
+
return this as any
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Set the static template data fields
|
|
686
|
+
*
|
|
687
|
+
* The data will be cloned once during component creation.
|
|
688
|
+
* If called multiple times, the static data will be overwritten but not merged!
|
|
689
|
+
* Usually, the `data()` method is preferred.
|
|
690
|
+
*/
|
|
691
|
+
staticData<T extends DataList>(
|
|
692
|
+
data: NewFieldList<DataWithPropertyValues<TData, TProperty>, T>,
|
|
693
|
+
): ResolveBehaviorBuilder<
|
|
694
|
+
BehaviorBuilder<T, TData & T, TProperty, TMethod, TChainingFilter, TPendingChainingFilter>,
|
|
695
|
+
TChainingFilter
|
|
696
|
+
> {
|
|
697
|
+
this._$staticData = data
|
|
698
|
+
return this as any
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Add a single property
|
|
703
|
+
*
|
|
704
|
+
* The property name should be different from other properties.
|
|
705
|
+
*/
|
|
706
|
+
property<
|
|
707
|
+
N extends string,
|
|
708
|
+
T extends PropertyType,
|
|
709
|
+
V extends PropertyTypeToValueType<T>,
|
|
710
|
+
>(
|
|
711
|
+
name: N,
|
|
712
|
+
def: N extends keyof (TData & TProperty) ? never : PropertyListItem<T, V>,
|
|
713
|
+
): ResolveBehaviorBuilder<
|
|
714
|
+
BehaviorBuilder<
|
|
715
|
+
TPrevData,
|
|
716
|
+
TData,
|
|
717
|
+
TProperty & Record<N, unknown extends V ? T : PropertyOption<T, V>>,
|
|
718
|
+
TMethod,
|
|
719
|
+
TChainingFilter,
|
|
720
|
+
TPendingChainingFilter
|
|
721
|
+
>,
|
|
722
|
+
TChainingFilter
|
|
723
|
+
> {
|
|
724
|
+
if (!this._$properties) this._$properties = []
|
|
725
|
+
this._$properties.push({ name, def: def as PropertyListItem<PropertyType, unknown> })
|
|
726
|
+
return this as any
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Add a single public method
|
|
731
|
+
*
|
|
732
|
+
* The public method can be used as an event handler, and can be visited in component instance.
|
|
733
|
+
*/
|
|
734
|
+
methods<
|
|
735
|
+
T extends MethodList,
|
|
736
|
+
>(
|
|
737
|
+
funcs: T & ThisType<ComponentInstance<TData, TProperty, TMethod & T>>,
|
|
738
|
+
): ResolveBehaviorBuilder<
|
|
739
|
+
BehaviorBuilder<
|
|
740
|
+
TPrevData,
|
|
741
|
+
TData,
|
|
742
|
+
TProperty,
|
|
743
|
+
TMethod & T,
|
|
744
|
+
TChainingFilter,
|
|
745
|
+
TPendingChainingFilter
|
|
746
|
+
>,
|
|
747
|
+
TChainingFilter
|
|
748
|
+
> {
|
|
749
|
+
const keys = Object.keys(funcs)
|
|
750
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
751
|
+
const name = keys[i]!
|
|
752
|
+
const func = funcs[name]!
|
|
753
|
+
this._$methods.push({ name, func })
|
|
754
|
+
}
|
|
755
|
+
return this as any
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Add a data observer
|
|
760
|
+
*/
|
|
761
|
+
observer<
|
|
762
|
+
P extends ObserverDataPathStrings<
|
|
763
|
+
DataWithPropertyValues<TPrevData, TProperty>
|
|
764
|
+
>,
|
|
765
|
+
V = DeepReadonly<
|
|
766
|
+
GetFromObserverPathString<DataWithPropertyValues<TPrevData, TProperty>, P>
|
|
767
|
+
>
|
|
768
|
+
>(
|
|
769
|
+
paths: P,
|
|
770
|
+
func: (this: ComponentInstance<TData, TProperty, TMethod>, newValue: V) => void,
|
|
771
|
+
once?: boolean,
|
|
772
|
+
): ResolveBehaviorBuilder<this, TChainingFilter>;
|
|
773
|
+
observer<
|
|
774
|
+
P extends ObserverDataPathStrings<
|
|
775
|
+
DataWithPropertyValues<TPrevData, TProperty>
|
|
776
|
+
>[],
|
|
777
|
+
V = {
|
|
778
|
+
[K in keyof P]: DeepReadonly<
|
|
779
|
+
GetFromObserverPathString<DataWithPropertyValues<TPrevData, TProperty>, P[K]>
|
|
780
|
+
>
|
|
781
|
+
}
|
|
782
|
+
>(
|
|
783
|
+
paths: readonly [...P],
|
|
784
|
+
func: (
|
|
785
|
+
this: ComponentInstance<TData, TProperty, TMethod>,
|
|
786
|
+
...newValues: V extends any[] ? V : never
|
|
787
|
+
) => void,
|
|
788
|
+
once?: boolean,
|
|
789
|
+
): ResolveBehaviorBuilder<this, TChainingFilter>;
|
|
790
|
+
observer(
|
|
791
|
+
paths: string | readonly string[],
|
|
792
|
+
func: (this: ComponentInstance<TData, TProperty, TMethod>, ...args: any[]) => any,
|
|
793
|
+
once = false,
|
|
794
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
795
|
+
if (!this._$observers) this._$observers = []
|
|
796
|
+
this._$observers.push({ dataPaths: parseMultiPaths(paths as string | string[]), func, once })
|
|
797
|
+
return this as any
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Add a lifetime callback
|
|
802
|
+
*/
|
|
803
|
+
lifetime<L extends keyof Lifetimes>(
|
|
804
|
+
name: L,
|
|
805
|
+
func: (
|
|
806
|
+
this: ComponentInstance<TData, TProperty, TMethod>,
|
|
807
|
+
...args: Parameters<Lifetimes[L]>
|
|
808
|
+
) => ReturnType<Lifetimes[L]>,
|
|
809
|
+
once = false,
|
|
810
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
811
|
+
this._$lifetimes.push({ name, func, once })
|
|
812
|
+
return this as any
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Add a page-lifetime callback
|
|
817
|
+
*/
|
|
818
|
+
pageLifetime(
|
|
819
|
+
name: string,
|
|
820
|
+
func: (this: ComponentInstance<TData, TProperty, TMethod>, ...args: any[]) => any,
|
|
821
|
+
once = false,
|
|
822
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
823
|
+
if (!this._$pageLifetimes) this._$pageLifetimes = []
|
|
824
|
+
this._$pageLifetimes.push({ name, func, once })
|
|
825
|
+
return this as any
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Add a relation
|
|
830
|
+
*/
|
|
831
|
+
relation(
|
|
832
|
+
name: string,
|
|
833
|
+
rel: RelationParams & ThisType<ComponentInstance<TData, TProperty, TMethod>>,
|
|
834
|
+
): ResolveBehaviorBuilder<this, TChainingFilter> {
|
|
835
|
+
if (!this._$relations) this._$relations = []
|
|
836
|
+
this._$relations.push({ name, rel })
|
|
837
|
+
return this as any
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Execute a function while component instance creation
|
|
842
|
+
*
|
|
843
|
+
* A `BuilderContext` is provided to tweak the component creation progress.
|
|
844
|
+
* The return value is used as the "export" value of the behavior,
|
|
845
|
+
* which can be imported by other behaviors.
|
|
846
|
+
*/
|
|
847
|
+
init<TExport extends Record<string, TaggedMethod<(...args: any[]) => any>> | void>(
|
|
848
|
+
func: (
|
|
849
|
+
this: ComponentInstance<TData, TProperty, TMethod>,
|
|
850
|
+
builderContext: BuilderContext<
|
|
851
|
+
TPrevData,
|
|
852
|
+
TProperty,
|
|
853
|
+
ComponentInstance<TData, TProperty, TMethod>
|
|
854
|
+
>,
|
|
855
|
+
) => TExport,
|
|
856
|
+
// eslint-disable-next-line function-paren-newline
|
|
857
|
+
): ResolveBehaviorBuilder<
|
|
858
|
+
BehaviorBuilder<
|
|
859
|
+
TPrevData,
|
|
860
|
+
TData,
|
|
861
|
+
TProperty,
|
|
862
|
+
TMethod & (TExport extends void ? Empty : {
|
|
863
|
+
[K in keyof TExport]: UnTaggedMethod<TExport[K]>
|
|
864
|
+
}),
|
|
865
|
+
TChainingFilter,
|
|
866
|
+
TPendingChainingFilter
|
|
867
|
+
>,
|
|
868
|
+
TChainingFilter
|
|
869
|
+
> {
|
|
870
|
+
this._$init.push(func)
|
|
871
|
+
return this as any
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Apply a classic-style definition
|
|
876
|
+
*/
|
|
877
|
+
definition<
|
|
878
|
+
TNewData extends DataList = Empty,
|
|
879
|
+
TNewProperty extends PropertyList = Empty,
|
|
880
|
+
TNewMethod extends MethodList = Empty,
|
|
881
|
+
>(
|
|
882
|
+
def:
|
|
883
|
+
ComponentParams<TNewData, TNewProperty, TNewMethod> &
|
|
884
|
+
ThisType<ComponentInstance<TData & TNewData, TProperty & TNewProperty, TMethod & TNewMethod>>,
|
|
885
|
+
): ResolveBehaviorBuilder<
|
|
886
|
+
BehaviorBuilder<
|
|
887
|
+
TPrevData,
|
|
888
|
+
TData & TNewData,
|
|
889
|
+
TProperty & TNewProperty,
|
|
890
|
+
TMethod & TNewMethod,
|
|
891
|
+
TChainingFilter,
|
|
892
|
+
TPendingChainingFilter
|
|
893
|
+
>,
|
|
894
|
+
TChainingFilter
|
|
895
|
+
> {
|
|
896
|
+
if (def.behaviors) this._$behaviors.push(...def.behaviors)
|
|
897
|
+
if (def.options) this.options(def.options)
|
|
898
|
+
if (def.template) this.template(def.template)
|
|
899
|
+
if (def.using) this.usingComponents(def.using)
|
|
900
|
+
if (def.placeholders) this.placeholders(def.placeholders)
|
|
901
|
+
if (def.generics) this.generics(def.generics)
|
|
902
|
+
if (def.externalClasses) this.externalClasses(def.externalClasses)
|
|
903
|
+
const rawData = def.data
|
|
904
|
+
if (rawData !== undefined) {
|
|
905
|
+
if (typeof rawData === 'function') {
|
|
906
|
+
this._$data.push(rawData as () => DataList)
|
|
907
|
+
} else {
|
|
908
|
+
this._$staticData = rawData
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
const rawProperties = def.properties
|
|
912
|
+
if (rawProperties !== undefined) {
|
|
913
|
+
if (!this._$properties) this._$properties = []
|
|
914
|
+
const keys = Object.keys(rawProperties)
|
|
915
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
916
|
+
const name = keys[i]!
|
|
917
|
+
const def = rawProperties[name]! as PropertyList
|
|
918
|
+
this._$properties.push({ name, def })
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const rawMethods = def.methods
|
|
922
|
+
if (rawMethods !== undefined) {
|
|
923
|
+
const keys = Object.keys(rawMethods)
|
|
924
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
925
|
+
const name = keys[i]!
|
|
926
|
+
const func = rawMethods[name]!
|
|
927
|
+
this._$methods.push({ name, func })
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const rawObservers = def.observers
|
|
931
|
+
if (rawObservers !== undefined) {
|
|
932
|
+
if (!this._$observers) this._$observers = []
|
|
933
|
+
if (Array.isArray(rawObservers)) {
|
|
934
|
+
for (let i = 0; i < rawObservers.length; i += 1) {
|
|
935
|
+
const { fields, observer } = rawObservers[i]!
|
|
936
|
+
this._$observers.push({ dataPaths: parseMultiPaths(fields ?? '**'), func: observer, once: false })
|
|
937
|
+
}
|
|
938
|
+
} else {
|
|
939
|
+
const keys = Object.keys(rawObservers)
|
|
940
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
941
|
+
const fields = keys[i]!
|
|
942
|
+
const observer = rawObservers[fields]!
|
|
943
|
+
this._$observers.push({ dataPaths: parseMultiPaths(fields), func: observer, once: false })
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
const rawLifetimes = def.lifetimes
|
|
948
|
+
if (rawLifetimes?.created === undefined && typeof def.created === 'function') {
|
|
949
|
+
this._$lifetimes.push({ name: 'created', func: def.created, once: true })
|
|
950
|
+
}
|
|
951
|
+
if (rawLifetimes?.attached === undefined && typeof def.attached === 'function') {
|
|
952
|
+
this._$lifetimes.push({ name: 'attached', func: def.attached, once: true })
|
|
953
|
+
}
|
|
954
|
+
if (rawLifetimes?.moved === undefined && typeof def.moved === 'function') {
|
|
955
|
+
this._$lifetimes.push({ name: 'moved', func: def.moved, once: true })
|
|
956
|
+
}
|
|
957
|
+
if (rawLifetimes?.detached === undefined && typeof def.detached === 'function') {
|
|
958
|
+
this._$lifetimes.push({ name: 'detached', func: def.detached, once: true })
|
|
959
|
+
}
|
|
960
|
+
if (rawLifetimes?.ready === undefined && typeof def.ready === 'function') {
|
|
961
|
+
this._$lifetimes.push({ name: 'ready', func: def.ready, once: true })
|
|
962
|
+
}
|
|
963
|
+
if (rawLifetimes) {
|
|
964
|
+
const keys = Object.keys(rawLifetimes)
|
|
965
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
966
|
+
const name = keys[i]!
|
|
967
|
+
const func = rawLifetimes[name]!
|
|
968
|
+
this._$lifetimes.push({ name, func, once: true })
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
const rawPageLifetimes = def.pageLifetimes
|
|
972
|
+
if (rawPageLifetimes) {
|
|
973
|
+
if (!this._$pageLifetimes) this._$pageLifetimes = []
|
|
974
|
+
const keys = Object.keys(rawPageLifetimes)
|
|
975
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
976
|
+
const name = keys[i]!
|
|
977
|
+
const func = rawPageLifetimes[name]!
|
|
978
|
+
this._$pageLifetimes.push({ name, func, once: true })
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
this._$listeners = def.listeners
|
|
982
|
+
const rawRelations = def.relations
|
|
983
|
+
if (rawRelations) {
|
|
984
|
+
if (!this._$relations) this._$relations = []
|
|
985
|
+
const keys = Object.keys(rawRelations)
|
|
986
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
987
|
+
const name = keys[i]!
|
|
988
|
+
const rel = rawRelations[name]!
|
|
989
|
+
this._$relations.push({ name, rel })
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return this as any
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Finish build, generate a behavior, and register it in the component space
|
|
997
|
+
*/
|
|
998
|
+
registerBehavior(): Behavior<TData, TProperty, TMethod, TPendingChainingFilter> {
|
|
999
|
+
const is = this._$is
|
|
1000
|
+
const behavior = new Behavior(this)
|
|
1001
|
+
if (is !== undefined) {
|
|
1002
|
+
this._$ownerSpace._$registerBehavior(is, behavior as unknown as GeneralBehavior)
|
|
1003
|
+
}
|
|
1004
|
+
return behavior as any
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Finish build, generate a component definition, and register it in the component space
|
|
1009
|
+
*/
|
|
1010
|
+
registerComponent(): ComponentDefinition<TData, TProperty, TMethod> {
|
|
1011
|
+
const is = this._$is
|
|
1012
|
+
const behavior = new Behavior(this)
|
|
1013
|
+
const compDef = new ComponentDefinition(behavior)
|
|
1014
|
+
if (is !== undefined) {
|
|
1015
|
+
this._$ownerSpace._$registerComponent(is, compDef as unknown as GeneralComponentDefinition)
|
|
1016
|
+
}
|
|
1017
|
+
return compDef
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Common mixin-like behavior
|
|
1023
|
+
*
|
|
1024
|
+
* Each component definition contains a single *root* behavior.
|
|
1025
|
+
* A behavior can mixin other behaviors.
|
|
1026
|
+
*/
|
|
1027
|
+
export class Behavior<
|
|
1028
|
+
TData extends DataList,
|
|
1029
|
+
TProperty extends PropertyList,
|
|
1030
|
+
TMethod extends MethodList,
|
|
1031
|
+
TChainingFilter extends ChainingFilterType,
|
|
1032
|
+
> {
|
|
1033
|
+
/** @internal */
|
|
1034
|
+
private _$unprepared: boolean
|
|
1035
|
+
is: string
|
|
1036
|
+
ownerSpace: ComponentSpace
|
|
1037
|
+
/** @internal */
|
|
1038
|
+
private _$builder: BehaviorBuilder<
|
|
1039
|
+
any,
|
|
1040
|
+
TData,
|
|
1041
|
+
TProperty,
|
|
1042
|
+
TMethod,
|
|
1043
|
+
any,
|
|
1044
|
+
TChainingFilter
|
|
1045
|
+
>
|
|
1046
|
+
/** @internal */
|
|
1047
|
+
private _$flatAncestors: Set<GeneralBehavior>
|
|
1048
|
+
/** @internal */
|
|
1049
|
+
_$chainingFilter?: (chain: GeneralBehaviorBuilder) => TChainingFilter
|
|
1050
|
+
/** @internal */
|
|
1051
|
+
_$options?: ComponentOptions
|
|
1052
|
+
/** @internal */
|
|
1053
|
+
_$traitBehaviors?: { traitBehavior: TraitBehavior<any, any>, impl: any }[]
|
|
1054
|
+
/** @internal */
|
|
1055
|
+
_$template?: { [key: string]: unknown }
|
|
1056
|
+
/** @internal */
|
|
1057
|
+
_$using: { [alias: string]: ComponentDefinitionWithPlaceholder }
|
|
1058
|
+
/** @internal */
|
|
1059
|
+
_$generics?: string[]
|
|
1060
|
+
/** @internal */
|
|
1061
|
+
_$genericDefaults?: { [alias: string]: null | GeneralComponentDefinition }
|
|
1062
|
+
/** @internal */
|
|
1063
|
+
_$externalClasses?: string[]
|
|
1064
|
+
/** @internal */
|
|
1065
|
+
_$staticData?: DataList
|
|
1066
|
+
/** @internal */
|
|
1067
|
+
_$data: (() => { [field: string]: any })[]
|
|
1068
|
+
/** @internal */
|
|
1069
|
+
_$propertyMap: { [name: string]: PropertyDefinition }
|
|
1070
|
+
/** @internal */
|
|
1071
|
+
_$methodMap: { [name: string]: ComponentMethod }
|
|
1072
|
+
/** @internal */
|
|
1073
|
+
_$observers: { dataPaths: MultiPaths, observer: ComponentMethod, once: boolean }[]
|
|
1074
|
+
/** @internal */
|
|
1075
|
+
_$lifetimes: { name: string, func: ComponentMethod, once: boolean }[]
|
|
1076
|
+
/** @internal */
|
|
1077
|
+
_$pageLifetimes?: { name: string, func: ComponentMethod, once: boolean }[]
|
|
1078
|
+
/** @internal */
|
|
1079
|
+
_$listeners?: { id: string, ev: string, listener: ComponentMethod }[]
|
|
1080
|
+
/** @internal */
|
|
1081
|
+
_$relationMap?: { [name: string]: RelationDefinition }
|
|
1082
|
+
/** @internal */
|
|
1083
|
+
_$init: ((this: any, ctx: any) => any)[]
|
|
1084
|
+
/** @internal */
|
|
1085
|
+
_$methodCallerInit?: (this: ComponentInstance<TData, TProperty, TMethod>) =>
|
|
1086
|
+
ComponentInstance<TData, TProperty, TMethod>
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Create a behavior with classic-style definition
|
|
1090
|
+
*/
|
|
1091
|
+
static create<
|
|
1092
|
+
TData extends DataList,
|
|
1093
|
+
TProperty extends PropertyList,
|
|
1094
|
+
TMethod extends MethodList,
|
|
1095
|
+
>(
|
|
1096
|
+
def: ComponentParams<
|
|
1097
|
+
TData,
|
|
1098
|
+
TProperty,
|
|
1099
|
+
TMethod
|
|
1100
|
+
> & ThisType<ComponentInstance<TData, TProperty, TMethod>>,
|
|
1101
|
+
ownerSpace?: ComponentSpace,
|
|
1102
|
+
) {
|
|
1103
|
+
return new BehaviorBuilder(def.is, ownerSpace || getDefaultComponentSpace())
|
|
1104
|
+
.definition(def)
|
|
1105
|
+
.registerBehavior()
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/** @internal */
|
|
1109
|
+
constructor(
|
|
1110
|
+
builder: BehaviorBuilder<
|
|
1111
|
+
any,
|
|
1112
|
+
TData,
|
|
1113
|
+
TProperty,
|
|
1114
|
+
TMethod,
|
|
1115
|
+
any,
|
|
1116
|
+
TChainingFilter
|
|
1117
|
+
>,
|
|
1118
|
+
) {
|
|
1119
|
+
this._$unprepared = true
|
|
1120
|
+
this.is = builder._$is || ''
|
|
1121
|
+
this.ownerSpace = builder._$ownerSpace
|
|
1122
|
+
this._$builder = builder
|
|
1123
|
+
this._$flatAncestors = new Set()
|
|
1124
|
+
this._$chainingFilter = builder._$chainingFilter
|
|
1125
|
+
this._$options = builder._$options
|
|
1126
|
+
this._$traitBehaviors = undefined
|
|
1127
|
+
this._$template = builder._$template
|
|
1128
|
+
this._$using = Object.create(null) as { [alias: string]: ComponentDefinitionWithPlaceholder }
|
|
1129
|
+
this._$generics = undefined
|
|
1130
|
+
this._$genericDefaults = undefined
|
|
1131
|
+
this._$externalClasses = builder._$externalClasses
|
|
1132
|
+
this._$staticData = undefined
|
|
1133
|
+
this._$data = []
|
|
1134
|
+
this._$propertyMap = Object.create(null) as
|
|
1135
|
+
{ [name: string]: PropertyDefinition }
|
|
1136
|
+
this._$methodMap = Object.create(null) as { [name: string]: ComponentMethod }
|
|
1137
|
+
this._$observers = []
|
|
1138
|
+
this._$lifetimes = []
|
|
1139
|
+
this._$pageLifetimes = undefined
|
|
1140
|
+
this._$listeners = undefined
|
|
1141
|
+
this._$relationMap = undefined
|
|
1142
|
+
this._$init = []
|
|
1143
|
+
this._$methodCallerInit = builder._$methodCallerInit
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
general(): GeneralBehavior {
|
|
1147
|
+
return this as unknown as GeneralBehavior
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* List all component dependencies (recursively)
|
|
1152
|
+
*
|
|
1153
|
+
* This method will prepare the underlying behavior.
|
|
1154
|
+
*/
|
|
1155
|
+
getComponentDependencies(
|
|
1156
|
+
genericTargets?: { [name: string]: GeneralComponentDefinition },
|
|
1157
|
+
): Set<GeneralComponentDefinition> {
|
|
1158
|
+
const ret: Set<GeneralComponentDefinition> = new Set()
|
|
1159
|
+
const rec = function (this: void, beh: GeneralBehavior) {
|
|
1160
|
+
if (beh._$unprepared) beh.prepare()
|
|
1161
|
+
const keys = Object.keys(beh._$using)
|
|
1162
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
1163
|
+
const k = keys[i]!
|
|
1164
|
+
const v = beh._$using[k]!
|
|
1165
|
+
const dep = v.final
|
|
1166
|
+
if (dep && !ret.has(dep)) {
|
|
1167
|
+
ret.add(dep)
|
|
1168
|
+
rec(dep.behavior as GeneralBehavior)
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
rec(this.general())
|
|
1173
|
+
if (genericTargets) {
|
|
1174
|
+
const list = Object.values(genericTargets)
|
|
1175
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
1176
|
+
const dep = list[i]!
|
|
1177
|
+
if (!ret.has(dep)) {
|
|
1178
|
+
ret.add(dep)
|
|
1179
|
+
rec(dep.behavior as GeneralBehavior)
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
return ret
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/** Same as `prepare` method in the prototype (for backward compatibility) */
|
|
1187
|
+
static prepare<
|
|
1188
|
+
TData extends DataList,
|
|
1189
|
+
TProperty extends PropertyList,
|
|
1190
|
+
TMethod extends MethodList,
|
|
1191
|
+
TChainingFilter extends ChainingFilterType = never,
|
|
1192
|
+
>(behavior: Behavior<TData, TProperty, TMethod, TChainingFilter>) {
|
|
1193
|
+
behavior.prepare()
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Execute the prepare phase (an optimization phase) of this behavior
|
|
1198
|
+
*
|
|
1199
|
+
* Every behavior needs this phase for better future performance.
|
|
1200
|
+
* However, this phase requires a little time for execution,
|
|
1201
|
+
* and requires its all dependent behaviors created.
|
|
1202
|
+
* If a dependent behavior is not prepared, then its prepare phase is also executed.
|
|
1203
|
+
*/
|
|
1204
|
+
prepare() {
|
|
1205
|
+
if (!this._$unprepared) return
|
|
1206
|
+
this._$unprepared = false
|
|
1207
|
+
const is = this.is
|
|
1208
|
+
const space = this.ownerSpace
|
|
1209
|
+
const builder = this._$builder
|
|
1210
|
+
|
|
1211
|
+
// mixin dependent behaviors
|
|
1212
|
+
const flatAncestors = this._$flatAncestors
|
|
1213
|
+
if (Array.isArray(builder._$behaviors)) {
|
|
1214
|
+
for (let i = 0; i < builder._$behaviors.length; i += 1) {
|
|
1215
|
+
const parentName = builder._$behaviors[i]!
|
|
1216
|
+
let parent: GeneralBehavior | null = null
|
|
1217
|
+
if (parentName instanceof Behavior) {
|
|
1218
|
+
parent = parentName
|
|
1219
|
+
} else {
|
|
1220
|
+
const parentNameStr = String(parentName)
|
|
1221
|
+
if (space) {
|
|
1222
|
+
parent = space.getBehaviorByUrl(parentNameStr, is)
|
|
1223
|
+
}
|
|
1224
|
+
if (!parent) {
|
|
1225
|
+
triggerWarning(`behavior "${parentNameStr}" is not found (when preparing behavior "${is}").`)
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
if (!parent) continue
|
|
1229
|
+
if (parent._$unprepared) Behavior.prepare(parent)
|
|
1230
|
+
|
|
1231
|
+
// merge trait behaviors
|
|
1232
|
+
const traitBehaviors = parent._$traitBehaviors
|
|
1233
|
+
if (traitBehaviors !== undefined) {
|
|
1234
|
+
if (!this._$traitBehaviors) this._$traitBehaviors = []
|
|
1235
|
+
this._$traitBehaviors.push(...traitBehaviors)
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// merge static data
|
|
1239
|
+
const staticData = parent._$staticData
|
|
1240
|
+
if (staticData) {
|
|
1241
|
+
if (!this._$staticData) this._$staticData = {}
|
|
1242
|
+
shallowMerge(this._$staticData, staticData)
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// merge methods
|
|
1246
|
+
Object.assign(this._$methodMap, parent._$methodMap)
|
|
1247
|
+
|
|
1248
|
+
// merge properties
|
|
1249
|
+
Object.assign(this._$propertyMap, parent._$propertyMap)
|
|
1250
|
+
|
|
1251
|
+
// merge dynamic data
|
|
1252
|
+
this._$data.push(...parent._$data)
|
|
1253
|
+
|
|
1254
|
+
// merge observers
|
|
1255
|
+
const observers = parent._$observers
|
|
1256
|
+
for (let i = 0; i < observers.length; i += 1) {
|
|
1257
|
+
const item = observers[i]!
|
|
1258
|
+
if (item.once) {
|
|
1259
|
+
if (this._$observers.indexOf(item as any) >= 0) continue
|
|
1260
|
+
}
|
|
1261
|
+
this._$observers.push(item as any)
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// merge lifetimes
|
|
1265
|
+
const lifetimes = parent._$lifetimes
|
|
1266
|
+
for (let i = 0; i < lifetimes.length; i += 1) {
|
|
1267
|
+
const item = lifetimes[i]!
|
|
1268
|
+
if (item.once) {
|
|
1269
|
+
if (this._$lifetimes.indexOf(item as any) >= 0) continue
|
|
1270
|
+
}
|
|
1271
|
+
this._$lifetimes.push(item as any)
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// merge page lifetimes
|
|
1275
|
+
const pageLifetimes = parent._$pageLifetimes
|
|
1276
|
+
if (pageLifetimes !== undefined) {
|
|
1277
|
+
if (this._$pageLifetimes) {
|
|
1278
|
+
for (let i = 0; i < pageLifetimes.length; i += 1) {
|
|
1279
|
+
const item = pageLifetimes[i]!
|
|
1280
|
+
if (item.once) {
|
|
1281
|
+
if (this._$pageLifetimes.indexOf(item as any) >= 0) continue
|
|
1282
|
+
}
|
|
1283
|
+
this._$pageLifetimes.push(item as any)
|
|
1284
|
+
}
|
|
1285
|
+
} else {
|
|
1286
|
+
this._$pageLifetimes = pageLifetimes.slice() as any[]
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// merge legacy listeners
|
|
1291
|
+
const listeners = parent._$listeners as any[] | undefined
|
|
1292
|
+
if (listeners !== undefined) {
|
|
1293
|
+
if (!this._$listeners) this._$listeners = []
|
|
1294
|
+
this._$listeners.push(...listeners)
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// merge relations
|
|
1298
|
+
const relations = parent._$relationMap
|
|
1299
|
+
if (relations !== undefined) {
|
|
1300
|
+
if (!this._$relationMap) {
|
|
1301
|
+
this._$relationMap = Object.create(null) as { [name: string]: RelationDefinition }
|
|
1302
|
+
}
|
|
1303
|
+
Object.assign(this._$relationMap, relations)
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// merge init
|
|
1307
|
+
this._$init.push(...parent._$init)
|
|
1308
|
+
|
|
1309
|
+
// construct flat ancestors
|
|
1310
|
+
parent._$flatAncestors.forEach((p) => {
|
|
1311
|
+
flatAncestors.add(p)
|
|
1312
|
+
})
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// init trait behaviors
|
|
1317
|
+
const traitBehaviors = builder._$traitBehaviors
|
|
1318
|
+
if (traitBehaviors !== undefined) {
|
|
1319
|
+
if (!this._$traitBehaviors) this._$traitBehaviors = []
|
|
1320
|
+
this._$traitBehaviors.push(...traitBehaviors)
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// init using
|
|
1324
|
+
if (typeof builder._$using === 'object' && builder._$using !== null) {
|
|
1325
|
+
const hasPlaceholders = typeof builder._$placeholders === 'object' && builder._$placeholders !== null
|
|
1326
|
+
const keys = Object.keys(builder._$using)
|
|
1327
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
1328
|
+
const k = keys[i]!
|
|
1329
|
+
const v = builder._$using[k]
|
|
1330
|
+
let placeholder = null
|
|
1331
|
+
if (hasPlaceholders) {
|
|
1332
|
+
placeholder = (
|
|
1333
|
+
builder._$placeholders as { [name: string]: string }
|
|
1334
|
+
)[k] ?? null
|
|
1335
|
+
}
|
|
1336
|
+
if (v instanceof ComponentDefinition) {
|
|
1337
|
+
this._$using[k] = {
|
|
1338
|
+
final: v,
|
|
1339
|
+
placeholder,
|
|
1340
|
+
waiting: null,
|
|
1341
|
+
}
|
|
1342
|
+
} else if (space) {
|
|
1343
|
+
const path = String(v)
|
|
1344
|
+
const final = space.getComponentByUrlWithoutDefault(path, is)
|
|
1345
|
+
if (final) {
|
|
1346
|
+
this._$using[k] = {
|
|
1347
|
+
final,
|
|
1348
|
+
placeholder: null,
|
|
1349
|
+
waiting: null,
|
|
1350
|
+
}
|
|
1351
|
+
} else if (placeholder !== null) {
|
|
1352
|
+
const p: ComponentDefinitionWithPlaceholder = {
|
|
1353
|
+
final: null,
|
|
1354
|
+
placeholder,
|
|
1355
|
+
waiting: null,
|
|
1356
|
+
}
|
|
1357
|
+
const wl = space._$componentWaitingList(path, is)
|
|
1358
|
+
if (wl) {
|
|
1359
|
+
wl.add((c) => {
|
|
1360
|
+
p.final = c
|
|
1361
|
+
p.placeholder = null
|
|
1362
|
+
p.waiting = null
|
|
1363
|
+
})
|
|
1364
|
+
p.waiting = wl
|
|
1365
|
+
}
|
|
1366
|
+
this._$using[k] = p
|
|
1367
|
+
} else {
|
|
1368
|
+
this._$using[k] = {
|
|
1369
|
+
final: space.getDefaultComponent(),
|
|
1370
|
+
placeholder: null,
|
|
1371
|
+
waiting: null,
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
} else {
|
|
1375
|
+
triggerWarning(`cannot find component "${String(v)}" (when preparing behavior "${is}").`)
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// init generics
|
|
1381
|
+
if (typeof builder._$generics === 'object' && builder._$generics !== null) {
|
|
1382
|
+
const generics = this._$generics = [] as string[]
|
|
1383
|
+
const genericDefaults = Object.create(null) as {
|
|
1384
|
+
[alias: string]: null | GeneralComponentDefinition
|
|
1385
|
+
}
|
|
1386
|
+
this._$genericDefaults = genericDefaults
|
|
1387
|
+
const genericKeys = Object.keys(builder._$generics)
|
|
1388
|
+
for (let i = 0; i < genericKeys.length; i += 1) {
|
|
1389
|
+
const k = genericKeys[i]!
|
|
1390
|
+
const genericDef = builder._$generics[k]
|
|
1391
|
+
let defaultComp: GeneralComponentDefinition | null = null
|
|
1392
|
+
const d = genericDef === true ? undefined : genericDef?.default
|
|
1393
|
+
if (d !== undefined) {
|
|
1394
|
+
if (d instanceof ComponentDefinition) {
|
|
1395
|
+
defaultComp = d
|
|
1396
|
+
} else if (space) {
|
|
1397
|
+
defaultComp = space.getComponentByUrl(String(d), is)
|
|
1398
|
+
} else {
|
|
1399
|
+
triggerWarning(`cannot define generic "${k}" without a default implementor (when preparing behavior "${is}").`)
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
generics.push(k)
|
|
1403
|
+
genericDefaults[k] = defaultComp
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// init static data
|
|
1408
|
+
const staticData = builder._$staticData
|
|
1409
|
+
if (staticData) {
|
|
1410
|
+
if (!this._$staticData) {
|
|
1411
|
+
this._$staticData = staticData
|
|
1412
|
+
} else {
|
|
1413
|
+
shallowMerge(this._$staticData, staticData)
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// init methods (methods should be initialized before others with function refs)
|
|
1418
|
+
const methods = builder._$methods
|
|
1419
|
+
for (let i = 0; i < methods.length; i += 1) {
|
|
1420
|
+
const { name, func } = methods[i]!
|
|
1421
|
+
this._$methodMap[name] = func
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// init properties
|
|
1425
|
+
const properties = builder._$properties
|
|
1426
|
+
if (properties !== undefined) {
|
|
1427
|
+
const initValueFuncs: { name: string, func: () => any }[] = []
|
|
1428
|
+
for (let i = 0; i < properties.length; i += 1) {
|
|
1429
|
+
const { name, def } = properties[i]!
|
|
1430
|
+
const shortHandDef = normalizePropertyTypeShortHand(def)
|
|
1431
|
+
let d: PropertyDefinition
|
|
1432
|
+
if (shortHandDef !== null) {
|
|
1433
|
+
d = shortHandDef
|
|
1434
|
+
} else {
|
|
1435
|
+
const propDef = def as PropertyOption<any, unknown>
|
|
1436
|
+
let type = normalizePropertyType(propDef.type)
|
|
1437
|
+
let optionalTypes: NormalizedPropertyType[] | null = null
|
|
1438
|
+
if (Array.isArray(propDef.optionalTypes)) {
|
|
1439
|
+
optionalTypes = propDef.optionalTypes.map(normalizePropertyType)
|
|
1440
|
+
if (optionalTypes.length > 0) {
|
|
1441
|
+
if (type === NormalizedPropertyType.Invalid || type === NormalizedPropertyType.Any) {
|
|
1442
|
+
type = optionalTypes[0]!
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
if (type === NormalizedPropertyType.Invalid) {
|
|
1447
|
+
triggerWarning(`the type of property "${name}" is illegal (when preparing behavior "${is}").`)
|
|
1448
|
+
}
|
|
1449
|
+
let value: DataValue = propDef.value
|
|
1450
|
+
if (propDef.value === undefined) {
|
|
1451
|
+
if (type === NormalizedPropertyType.String) value = ''
|
|
1452
|
+
else if (type === NormalizedPropertyType.Number) value = 0
|
|
1453
|
+
else if (type === NormalizedPropertyType.Boolean) value = false
|
|
1454
|
+
else if (type === NormalizedPropertyType.Array) value = []
|
|
1455
|
+
else value = null
|
|
1456
|
+
}
|
|
1457
|
+
let observer: ((newValue: any, oldValue: any) => void) | null
|
|
1458
|
+
if (typeof propDef.observer === 'function') {
|
|
1459
|
+
observer = propDef.observer
|
|
1460
|
+
} else if (propDef.observer) {
|
|
1461
|
+
observer = this._$methodMap[propDef.observer] || null
|
|
1462
|
+
} else {
|
|
1463
|
+
observer = null
|
|
1464
|
+
if (propDef.observer !== undefined) {
|
|
1465
|
+
triggerWarning(`the observer of property "${name}" is not a function (when preparing behavior "${is}").`)
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
const reflectIdPrefix = !!propDef.reflectIdPrefix
|
|
1469
|
+
d = {
|
|
1470
|
+
type,
|
|
1471
|
+
optionalTypes,
|
|
1472
|
+
value,
|
|
1473
|
+
default: propDef.default,
|
|
1474
|
+
observer,
|
|
1475
|
+
reflectIdPrefix,
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
this._$propertyMap[name] = d
|
|
1479
|
+
initValueFuncs.push({
|
|
1480
|
+
name,
|
|
1481
|
+
func: d.default === undefined ? () => simpleDeepCopy(d.value) : d.default,
|
|
1482
|
+
})
|
|
1483
|
+
}
|
|
1484
|
+
this._$data.push(() => {
|
|
1485
|
+
const ret: DataList = {}
|
|
1486
|
+
for (let i = 0; i < initValueFuncs.length; i += 1) {
|
|
1487
|
+
const { name, func } = initValueFuncs[i]!
|
|
1488
|
+
ret[name] = func()
|
|
1489
|
+
}
|
|
1490
|
+
return ret
|
|
1491
|
+
})
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// init dynamic data
|
|
1495
|
+
this._$data.push(...builder._$data)
|
|
1496
|
+
|
|
1497
|
+
// observers
|
|
1498
|
+
const observers = builder._$observers
|
|
1499
|
+
if (observers !== undefined) {
|
|
1500
|
+
for (let i = 0; i < observers.length; i += 1) {
|
|
1501
|
+
const { dataPaths, func, once } = observers[i]!
|
|
1502
|
+
const observer = typeof func === 'function' ? func : this._$methodMap[func]
|
|
1503
|
+
if (typeof observer === 'function') {
|
|
1504
|
+
this._$observers.push({ dataPaths, observer, once })
|
|
1505
|
+
} else {
|
|
1506
|
+
triggerWarning(`the "${String(observer)}" observer is not a function (when preparing behavior "${is}").`)
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// lifetimes
|
|
1512
|
+
const lifetimes = builder._$lifetimes
|
|
1513
|
+
this._$lifetimes.push(...lifetimes)
|
|
1514
|
+
|
|
1515
|
+
// page lifetimes
|
|
1516
|
+
const pageLifetimes = builder._$pageLifetimes
|
|
1517
|
+
if (pageLifetimes) {
|
|
1518
|
+
if (!this._$pageLifetimes) this._$pageLifetimes = []
|
|
1519
|
+
this._$pageLifetimes.push(...pageLifetimes)
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
// listeners
|
|
1523
|
+
const listeners = builder._$listeners
|
|
1524
|
+
if (listeners !== undefined) {
|
|
1525
|
+
const keys = Object.keys(listeners)
|
|
1526
|
+
if (keys.length > 0) {
|
|
1527
|
+
this._$listeners = []
|
|
1528
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
1529
|
+
const k = keys[i]!
|
|
1530
|
+
const v = listeners[k]!
|
|
1531
|
+
const listener = typeof v === 'function' ? v : this._$methodMap[v]
|
|
1532
|
+
if (listener) {
|
|
1533
|
+
const dot = k.indexOf('.')
|
|
1534
|
+
let id: string
|
|
1535
|
+
let ev: string
|
|
1536
|
+
if (dot >= 0) {
|
|
1537
|
+
id = k.slice(0, dot)
|
|
1538
|
+
ev = k.slice(dot + 1)
|
|
1539
|
+
} else {
|
|
1540
|
+
id = ''
|
|
1541
|
+
ev = k
|
|
1542
|
+
}
|
|
1543
|
+
this._$listeners.push({ id, ev, listener })
|
|
1544
|
+
} else {
|
|
1545
|
+
triggerWarning(`the "${k}" listener is not a function or a method name (when preparing behavior "${is}").`)
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// relations
|
|
1552
|
+
const relations = builder._$relations
|
|
1553
|
+
if (relations !== undefined) {
|
|
1554
|
+
if (!this._$relationMap) {
|
|
1555
|
+
this._$relationMap = Object.create(null) as { [name: string]: RelationDefinition }
|
|
1556
|
+
}
|
|
1557
|
+
for (let i = 0; i < relations.length; i += 1) {
|
|
1558
|
+
const { name: key, rel: relation } = relations[i]!
|
|
1559
|
+
if (relation === undefined || relation === null) continue
|
|
1560
|
+
const rel = normalizeRelation(is, key, relation)
|
|
1561
|
+
if (rel) this._$relationMap[key] = rel
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// init funcs
|
|
1566
|
+
this._$init.push(...builder._$init)
|
|
1567
|
+
|
|
1568
|
+
flatAncestors.add(this as unknown as GeneralBehavior)
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
/**
|
|
1572
|
+
* Get the template content
|
|
1573
|
+
*
|
|
1574
|
+
* This method is usually used by the template engine.
|
|
1575
|
+
*/
|
|
1576
|
+
getTemplate() {
|
|
1577
|
+
return this._$template
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/** Check whether the `other` behavior is a dependent behavior of this behavior */
|
|
1581
|
+
hasBehavior(other: string | GeneralBehavior): boolean {
|
|
1582
|
+
if (this._$unprepared) this.prepare()
|
|
1583
|
+
if (other instanceof Behavior) {
|
|
1584
|
+
return this._$flatAncestors.has(other)
|
|
1585
|
+
}
|
|
1586
|
+
if (this.ownerSpace) {
|
|
1587
|
+
const b = this.ownerSpace.getBehaviorByUrl(other, this.is)
|
|
1588
|
+
if (!b) {
|
|
1589
|
+
return false
|
|
1590
|
+
}
|
|
1591
|
+
return this._$flatAncestors.has(b)
|
|
1592
|
+
}
|
|
1593
|
+
return false
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
/**
|
|
1597
|
+
* Get the methods
|
|
1598
|
+
*
|
|
1599
|
+
* Only valid after `prepare` .
|
|
1600
|
+
*/
|
|
1601
|
+
getMethods(): TMethod {
|
|
1602
|
+
return this._$methodMap as TMethod
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
/** @internal */
|
|
1606
|
+
_$generateObserverTree(): DataGroupObserverTree {
|
|
1607
|
+
const ot = new DataGroupObserverTree(this._$propertyMap)
|
|
1608
|
+
const observers = this._$observers
|
|
1609
|
+
for (let i = 0; i < observers.length; i += 1) {
|
|
1610
|
+
const { dataPaths, observer } = observers[i]!
|
|
1611
|
+
ot.addObserver(observer, dataPaths)
|
|
1612
|
+
}
|
|
1613
|
+
return ot
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/** @internal */
|
|
1617
|
+
_$getAllLifetimeFuncs(): LifetimeFuncs {
|
|
1618
|
+
const ret = Object.create(null) as LifetimeFuncs
|
|
1619
|
+
const lifetimes = this._$lifetimes
|
|
1620
|
+
for (let i = 0; i < lifetimes.length; i += 1) {
|
|
1621
|
+
const { name, func } = lifetimes[i]!
|
|
1622
|
+
if (ret[name]) {
|
|
1623
|
+
ret[name]!.add(func)
|
|
1624
|
+
} else {
|
|
1625
|
+
const fa = ret[name] = new FuncArr()
|
|
1626
|
+
fa.add(func)
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
return ret
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
/** @internal */
|
|
1633
|
+
_$getAllPageLifetimeFuncs(): PageLifetimeFuncs {
|
|
1634
|
+
const ret = Object.create(null) as PageLifetimeFuncs
|
|
1635
|
+
const pageLifetimes = this._$pageLifetimes
|
|
1636
|
+
if (!pageLifetimes) return ret
|
|
1637
|
+
for (let i = 0; i < pageLifetimes.length; i += 1) {
|
|
1638
|
+
const { name, func } = pageLifetimes[i]!
|
|
1639
|
+
if (ret[name]) {
|
|
1640
|
+
ret[name]!.add(func)
|
|
1641
|
+
} else {
|
|
1642
|
+
const fa = ret[name] = new FuncArr()
|
|
1643
|
+
fa.add(func)
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
return ret
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
export type GeneralBehavior = Behavior<
|
|
1651
|
+
Record<string, any>,
|
|
1652
|
+
Record<string, any>,
|
|
1653
|
+
Record<string, any>,
|
|
1654
|
+
any
|
|
1655
|
+
>
|