cx 26.0.4 → 26.0.6
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/build/data/ArrayElementView.spec.js +1 -1
- package/build/hooks/store.spec.js +1 -1
- package/build/hooks/useTrigger.spec.js +1 -1
- package/build/ui/Controller.spec.js +6 -2
- package/build/ui/app/startAppLoop.d.ts +3 -2
- package/build/ui/app/startHotAppLoop.d.ts +3 -2
- package/build/ui/layout/ContentPlaceholder.spec.js +12 -12
- package/build.js +129 -129
- package/dist/manifest.js +844 -844
- package/package.json +3 -2
- package/src/charts/Chart.ts +108 -108
- package/src/data/ArrayElementView.ts +90 -90
- package/src/data/AugmentedViewBase.ts +88 -88
- package/src/data/Binding.ts +104 -104
- package/src/data/ExposedRecordView.ts +95 -95
- package/src/data/ExposedValueView.ts +89 -89
- package/src/data/Expression.spec.ts +229 -229
- package/src/data/Expression.ts +233 -233
- package/src/data/Grouper.spec.ts +57 -57
- package/src/data/Grouper.ts +158 -158
- package/src/data/NestedDataView.ts +43 -43
- package/src/data/ReadOnlyDataView.ts +39 -39
- package/src/data/Ref.ts +104 -104
- package/src/data/Selector.ts +10 -10
- package/src/data/Store.ts +52 -52
- package/src/data/StoreProxy.ts +19 -19
- package/src/data/StoreRef.ts +66 -66
- package/src/data/StringTemplate.spec.ts +132 -132
- package/src/data/StringTemplate.ts +93 -93
- package/src/data/StructuredSelector.spec.ts +113 -113
- package/src/data/StructuredSelector.ts +146 -146
- package/src/data/SubscribableView.ts +63 -63
- package/src/data/ZoomIntoPropertyView.spec.ts +64 -64
- package/src/data/ZoomIntoPropertyView.ts +45 -45
- package/src/data/computable.spec.ts +62 -62
- package/src/data/createAccessorModelProxy.ts +60 -60
- package/src/data/createStructuredSelector.ts +62 -62
- package/src/data/getAccessor.spec.ts +11 -11
- package/src/data/getAccessor.ts +74 -74
- package/src/data/getSelector.spec.ts +43 -43
- package/src/data/getSelector.ts +66 -66
- package/src/data/ops/filter.spec.ts +35 -35
- package/src/data/ops/filter.ts +9 -9
- package/src/data/ops/merge.ts +13 -13
- package/src/data/ops/removeTreeNodes.spec.ts +37 -37
- package/src/data/ops/removeTreeNodes.ts +15 -15
- package/src/data/ops/updateArray.spec.ts +69 -69
- package/src/data/ops/updateArray.ts +31 -31
- package/src/data/ops/updateTree.ts +23 -23
- package/src/data/test-types.ts +7 -7
- package/src/hooks/useTrigger.ts +26 -26
- package/src/index.scss +6 -6
- package/src/jsx-runtime.ts +72 -72
- package/src/svg/BoundedObject.ts +101 -101
- package/src/ui/CSSHelper.ts +17 -17
- package/src/ui/ContentResolver.ts +124 -124
- package/src/ui/Controller.ts +189 -189
- package/src/ui/Culture.ts +159 -159
- package/src/ui/DataProxy.ts +55 -55
- package/src/ui/FocusManager.ts +171 -171
- package/src/ui/Instance.ts +868 -868
- package/src/ui/RenderingContext.ts +99 -99
- package/src/ui/Rescope.ts +49 -49
- package/src/ui/StructuredInstanceDataAccessor.ts +32 -32
- package/src/ui/VDOM.ts +34 -34
- package/src/ui/adapter/ArrayAdapter.spec.ts +55 -55
- package/src/ui/adapter/ArrayAdapter.ts +226 -226
- package/src/ui/adapter/TreeAdapter.spec.ts +76 -76
- package/src/ui/adapter/TreeAdapter.ts +185 -185
- package/src/ui/app/History.ts +133 -133
- package/src/ui/app/Url.spec.ts +50 -50
- package/src/ui/app/startAppLoop.tsx +2 -1
- package/src/ui/app/startHotAppLoop.ts +2 -1
- package/src/ui/createFunctionalComponent.ts +65 -65
- package/src/ui/index.ts +45 -45
- package/src/ui/layout/Content.ts +30 -30
- package/src/ui/layout/FirstVisibleChildLayout.ts +60 -60
- package/src/ui/selection/KeySelection.ts +165 -165
- package/src/ui/selection/PropertySelection.ts +87 -87
- package/src/ui/selection/Selection.ts +118 -118
- package/src/util/Format.ts +267 -267
- package/src/util/browserSupportsPassiveEventHandlers.ts +20 -20
- package/src/util/color/rgbToHsl.ts +35 -35
- package/src/util/getActiveElement.ts +4 -4
- package/src/util/hasKey.ts +18 -18
- package/src/util/index.ts +55 -55
- package/src/util/innerTextTrim.ts +10 -10
- package/src/util/isArray.ts +3 -3
- package/src/util/isDataRecord.ts +5 -5
- package/src/util/isDefined.ts +3 -3
- package/src/util/isString.ts +3 -3
- package/src/widgets/Sandbox.ts +103 -103
- package/src/widgets/autoFocus.ts +9 -9
- package/src/widgets/cx.ts +63 -63
- package/src/widgets/drag-drop/ops.tsx +1 -1
- package/src/widgets/grid/GridCell.ts +143 -143
- package/src/widgets/icons/calendar.tsx +17 -17
- package/src/widgets/icons/check.tsx +13 -13
- package/src/widgets/icons/clear.tsx +15 -15
- package/src/widgets/icons/close.tsx +20 -20
- package/src/widgets/icons/cx.tsx +38 -38
- package/src/widgets/icons/drop-down.tsx +15 -15
- package/src/widgets/icons/file.tsx +13 -13
- package/src/widgets/icons/folder-open.tsx +15 -15
- package/src/widgets/icons/folder.tsx +13 -13
- package/src/widgets/icons/forward.tsx +22 -22
- package/src/widgets/icons/loading.tsx +24 -24
- package/src/widgets/icons/menu.tsx +17 -17
- package/src/widgets/icons/pixel-picker.tsx +18 -18
- package/src/widgets/icons/search.tsx +13 -13
- package/src/widgets/icons/sort-asc.tsx +14 -14
- package/src/widgets/icons/square.tsx +18 -18
- package/src/widgets/nav/Route.ts +142 -142
- package/src/widgets/overlay/ContextMenu.ts +42 -42
- package/src/widgets/overlay/Dropdown.tsx +762 -762
- package/src/widgets/overlay/MsgBox.tsx +141 -141
- package/src/widgets/overlay/Toast.ts +111 -111
- package/src/widgets/overlay/Window.tsx +299 -299
- package/src/widgets/overlay/alerts.ts +46 -46
- package/src/widgets/overlay/captureMouse.ts +195 -195
- package/src/widgets/overlay/createHotPromiseWindowFactory.ts +72 -72
- package/src/widgets/overlay/index.d.ts +11 -11
- package/src/widgets/overlay/index.ts +11 -11
- package/src/widgets/overlay/tooltip-ops.ts +173 -173
- package/build/ui/PureContainer.spec.d.ts +0 -1
- package/build/ui/PureContainer.spec.js +0 -149
- package/dist/manifest.d.ts +0 -1443
|
@@ -1,124 +1,124 @@
|
|
|
1
|
-
import { PureContainerBase, PureContainerConfig } from "./PureContainer";
|
|
2
|
-
import { isPromise } from "../util/isPromise";
|
|
3
|
-
import { RenderingContext } from "./RenderingContext";
|
|
4
|
-
import { BooleanProp, StructuredProp, ResolveStructuredProp } from "./Prop";
|
|
5
|
-
import { Instance } from "./Instance";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Configuration for ContentResolver widget.
|
|
9
|
-
*
|
|
10
|
-
* The params type parameter enables type inference for the onResolve callback:
|
|
11
|
-
* - Literal values (numbers, strings, booleans) preserve their types
|
|
12
|
-
* - AccessorChain<T> resolves to T
|
|
13
|
-
* - Bind/Tpl/Expr resolve to any (type cannot be determined at compile time)
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```typescript
|
|
17
|
-
* <ContentResolver
|
|
18
|
-
* params={{
|
|
19
|
-
* count: 42, // number
|
|
20
|
-
* name: model.user.name // AccessorChain<string> -> string
|
|
21
|
-
* }}
|
|
22
|
-
* onResolve={(params) => {
|
|
23
|
-
* // params.count is number, params.name is string
|
|
24
|
-
* }}
|
|
25
|
-
* />
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export interface ContentResolverConfig<P extends StructuredProp = StructuredProp> extends PureContainerConfig {
|
|
29
|
-
/** Parameters that trigger content resolution when changed. */
|
|
30
|
-
params?: P;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Callback function that resolves content based on params. Can return content directly or a Promise.
|
|
34
|
-
* The params type is inferred from the params property - literal values and AccessorChain<T>
|
|
35
|
-
* preserve their types, while bindings (bind/tpl/expr) resolve to `any`.
|
|
36
|
-
*/
|
|
37
|
-
onResolve?: string | ((params: ResolveStructuredProp<P>, instance: Instance) => any);
|
|
38
|
-
|
|
39
|
-
/** How to combine resolved content with initial content. Default is 'replace'. */
|
|
40
|
-
mode?: "replace" | "prepend" | "append";
|
|
41
|
-
|
|
42
|
-
/** Indicates if content is being loaded. */
|
|
43
|
-
loading?: BooleanProp;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class ContentResolver<P extends StructuredProp = StructuredProp> extends PureContainerBase<
|
|
47
|
-
ContentResolverConfig<P>
|
|
48
|
-
> {
|
|
49
|
-
constructor(config?: ContentResolverConfig<P>) {
|
|
50
|
-
super(config);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
declare mode: "replace" | "prepend" | "append";
|
|
54
|
-
declare onResolve?: string | ((params: ResolveStructuredProp<P>, instance: Instance) => any);
|
|
55
|
-
declare initialItems: any;
|
|
56
|
-
|
|
57
|
-
declareData(...args: any[]): void {
|
|
58
|
-
super.declareData(...args, {
|
|
59
|
-
params: { structured: true },
|
|
60
|
-
loading: undefined,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
init(): void {
|
|
65
|
-
super.init();
|
|
66
|
-
this.initialItems = this.layout ? this.layout.items : this.items;
|
|
67
|
-
this.clear();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
initInstance(context: RenderingContext, instance: any): void {
|
|
71
|
-
instance.content = this.initialItems;
|
|
72
|
-
instance.cachedParams = {}; //unique value which will never pass the equality check
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
prepareData(context: RenderingContext, instance: any): void {
|
|
76
|
-
let { data } = instance;
|
|
77
|
-
|
|
78
|
-
if (data.params !== instance.cachedParams && this.onResolve) {
|
|
79
|
-
instance.cachedParams = data.params;
|
|
80
|
-
let content = instance.invoke("onResolve", data.params, instance);
|
|
81
|
-
if (isPromise(content)) {
|
|
82
|
-
instance.set("loading", true);
|
|
83
|
-
this.setContent(instance, null);
|
|
84
|
-
content.then((cnt: any) => {
|
|
85
|
-
this.setContent(instance, cnt);
|
|
86
|
-
instance.setState({ cacheBuster: {} });
|
|
87
|
-
instance.set("loading", false);
|
|
88
|
-
});
|
|
89
|
-
} else this.setContent(instance, content);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
setContent(instance: any, content: any): void {
|
|
94
|
-
if (content) {
|
|
95
|
-
this.clear();
|
|
96
|
-
switch (this.mode) {
|
|
97
|
-
case "prepend":
|
|
98
|
-
this.add(content);
|
|
99
|
-
this.add(this.initialItems);
|
|
100
|
-
break;
|
|
101
|
-
|
|
102
|
-
case "append":
|
|
103
|
-
this.add(this.initialItems);
|
|
104
|
-
this.add(content);
|
|
105
|
-
break;
|
|
106
|
-
|
|
107
|
-
case "replace":
|
|
108
|
-
this.add(content);
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
instance.content = this.layout ? this.layout.items : this.items;
|
|
112
|
-
this.clear();
|
|
113
|
-
} else instance.content = this.initialItems;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
explore(context: RenderingContext, instance: any): void {
|
|
117
|
-
//a little bit hacky
|
|
118
|
-
if (this.layout) this.layout.items = instance.content;
|
|
119
|
-
else this.items = instance.content;
|
|
120
|
-
super.explore(context, instance);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
ContentResolver.prototype.mode = "replace";
|
|
1
|
+
import { PureContainerBase, PureContainerConfig } from "./PureContainer";
|
|
2
|
+
import { isPromise } from "../util/isPromise";
|
|
3
|
+
import { RenderingContext } from "./RenderingContext";
|
|
4
|
+
import { BooleanProp, StructuredProp, ResolveStructuredProp } from "./Prop";
|
|
5
|
+
import { Instance } from "./Instance";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for ContentResolver widget.
|
|
9
|
+
*
|
|
10
|
+
* The params type parameter enables type inference for the onResolve callback:
|
|
11
|
+
* - Literal values (numbers, strings, booleans) preserve their types
|
|
12
|
+
* - AccessorChain<T> resolves to T
|
|
13
|
+
* - Bind/Tpl/Expr resolve to any (type cannot be determined at compile time)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* <ContentResolver
|
|
18
|
+
* params={{
|
|
19
|
+
* count: 42, // number
|
|
20
|
+
* name: model.user.name // AccessorChain<string> -> string
|
|
21
|
+
* }}
|
|
22
|
+
* onResolve={(params) => {
|
|
23
|
+
* // params.count is number, params.name is string
|
|
24
|
+
* }}
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export interface ContentResolverConfig<P extends StructuredProp = StructuredProp> extends PureContainerConfig {
|
|
29
|
+
/** Parameters that trigger content resolution when changed. */
|
|
30
|
+
params?: P;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Callback function that resolves content based on params. Can return content directly or a Promise.
|
|
34
|
+
* The params type is inferred from the params property - literal values and AccessorChain<T>
|
|
35
|
+
* preserve their types, while bindings (bind/tpl/expr) resolve to `any`.
|
|
36
|
+
*/
|
|
37
|
+
onResolve?: string | ((params: ResolveStructuredProp<P>, instance: Instance) => any);
|
|
38
|
+
|
|
39
|
+
/** How to combine resolved content with initial content. Default is 'replace'. */
|
|
40
|
+
mode?: "replace" | "prepend" | "append";
|
|
41
|
+
|
|
42
|
+
/** Indicates if content is being loaded. */
|
|
43
|
+
loading?: BooleanProp;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class ContentResolver<P extends StructuredProp = StructuredProp> extends PureContainerBase<
|
|
47
|
+
ContentResolverConfig<P>
|
|
48
|
+
> {
|
|
49
|
+
constructor(config?: ContentResolverConfig<P>) {
|
|
50
|
+
super(config);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare mode: "replace" | "prepend" | "append";
|
|
54
|
+
declare onResolve?: string | ((params: ResolveStructuredProp<P>, instance: Instance) => any);
|
|
55
|
+
declare initialItems: any;
|
|
56
|
+
|
|
57
|
+
declareData(...args: any[]): void {
|
|
58
|
+
super.declareData(...args, {
|
|
59
|
+
params: { structured: true },
|
|
60
|
+
loading: undefined,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
init(): void {
|
|
65
|
+
super.init();
|
|
66
|
+
this.initialItems = this.layout ? this.layout.items : this.items;
|
|
67
|
+
this.clear();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
initInstance(context: RenderingContext, instance: any): void {
|
|
71
|
+
instance.content = this.initialItems;
|
|
72
|
+
instance.cachedParams = {}; //unique value which will never pass the equality check
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
prepareData(context: RenderingContext, instance: any): void {
|
|
76
|
+
let { data } = instance;
|
|
77
|
+
|
|
78
|
+
if (data.params !== instance.cachedParams && this.onResolve) {
|
|
79
|
+
instance.cachedParams = data.params;
|
|
80
|
+
let content = instance.invoke("onResolve", data.params, instance);
|
|
81
|
+
if (isPromise(content)) {
|
|
82
|
+
instance.set("loading", true);
|
|
83
|
+
this.setContent(instance, null);
|
|
84
|
+
content.then((cnt: any) => {
|
|
85
|
+
this.setContent(instance, cnt);
|
|
86
|
+
instance.setState({ cacheBuster: {} });
|
|
87
|
+
instance.set("loading", false);
|
|
88
|
+
});
|
|
89
|
+
} else this.setContent(instance, content);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setContent(instance: any, content: any): void {
|
|
94
|
+
if (content) {
|
|
95
|
+
this.clear();
|
|
96
|
+
switch (this.mode) {
|
|
97
|
+
case "prepend":
|
|
98
|
+
this.add(content);
|
|
99
|
+
this.add(this.initialItems);
|
|
100
|
+
break;
|
|
101
|
+
|
|
102
|
+
case "append":
|
|
103
|
+
this.add(this.initialItems);
|
|
104
|
+
this.add(content);
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case "replace":
|
|
108
|
+
this.add(content);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
instance.content = this.layout ? this.layout.items : this.items;
|
|
112
|
+
this.clear();
|
|
113
|
+
} else instance.content = this.initialItems;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
explore(context: RenderingContext, instance: any): void {
|
|
117
|
+
//a little bit hacky
|
|
118
|
+
if (this.layout) this.layout.items = instance.content;
|
|
119
|
+
else this.items = instance.content;
|
|
120
|
+
super.explore(context, instance);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
ContentResolver.prototype.mode = "replace";
|
package/src/ui/Controller.ts
CHANGED
|
@@ -1,189 +1,189 @@
|
|
|
1
|
-
import { computable, ComputableSelector, InferSelectorValue } from "../data/computable";
|
|
2
|
-
import { Component, ComponentConstructor, ComponentConfigType } from "../util/Component";
|
|
3
|
-
import { isArray } from "../util/isArray";
|
|
4
|
-
import { isFunction } from "../util/isFunction";
|
|
5
|
-
import { StoreProxy } from "../data/StoreProxy";
|
|
6
|
-
import { RenderingContext } from "./RenderingContext";
|
|
7
|
-
import { View, ViewMethods } from "../data/View";
|
|
8
|
-
import { Widget } from "./Widget";
|
|
9
|
-
import { Instance } from "./Instance";
|
|
10
|
-
|
|
11
|
-
const computablePrefix = "computable-";
|
|
12
|
-
const triggerPrefix = "trigger-";
|
|
13
|
-
|
|
14
|
-
interface ComputableEntry {
|
|
15
|
-
name: string;
|
|
16
|
-
selector: any;
|
|
17
|
-
type: "computable" | "trigger";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Controller methods interface for ThisType in config objects
|
|
21
|
-
export interface ControllerMethods {
|
|
22
|
-
store: View;
|
|
23
|
-
instance: Instance;
|
|
24
|
-
widget?: Widget;
|
|
25
|
-
invokeParentMethod(method: string, ...args: any[]): any;
|
|
26
|
-
addTrigger<Selectors extends readonly ComputableSelector[]>(
|
|
27
|
-
name: string,
|
|
28
|
-
args: [...Selectors],
|
|
29
|
-
callback: (...values: { [K in keyof Selectors]: InferSelectorValue<Selectors[K]> }) => any,
|
|
30
|
-
autoRun?: boolean,
|
|
31
|
-
): void;
|
|
32
|
-
addComputable<Selectors extends readonly ComputableSelector[]>(
|
|
33
|
-
name: string,
|
|
34
|
-
args: [...Selectors],
|
|
35
|
-
callback: (...values: { [K in keyof Selectors]: InferSelectorValue<Selectors[K]> }) => any,
|
|
36
|
-
): void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface BaseControllerConfig {
|
|
40
|
-
store?: View;
|
|
41
|
-
instance?: Instance;
|
|
42
|
-
widget?: Widget;
|
|
43
|
-
|
|
44
|
-
onInit?: (context: RenderingContext) => void;
|
|
45
|
-
onExplore?: (context: RenderingContext) => void;
|
|
46
|
-
onPrepare?: (context: RenderingContext) => void;
|
|
47
|
-
onCleanup?: (context: RenderingContext) => void;
|
|
48
|
-
onDestroy?: () => void;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Controller config object type with Controller methods AND its own methods available on 'this'
|
|
52
|
-
export type ControllerConfig<T extends BaseControllerConfig = BaseControllerConfig> = T &
|
|
53
|
-
ThisType<ControllerMethods & T>;
|
|
54
|
-
|
|
55
|
-
/** Controller factory function type */
|
|
56
|
-
export type ControllerFactory<T extends BaseControllerConfig = BaseControllerConfig> = (
|
|
57
|
-
config: ViewMethods,
|
|
58
|
-
) => ControllerConfig<T>;
|
|
59
|
-
|
|
60
|
-
export class Controller extends Component implements ControllerMethods {
|
|
61
|
-
initialized?: boolean;
|
|
62
|
-
onInit?(context: RenderingContext): void;
|
|
63
|
-
onExplore?(context: RenderingContext): void;
|
|
64
|
-
onPrepare?(context: RenderingContext): void;
|
|
65
|
-
onCleanup?(context: RenderingContext): void;
|
|
66
|
-
onDestroy?(): void;
|
|
67
|
-
declare instance: Instance;
|
|
68
|
-
declare store: View;
|
|
69
|
-
declare widget?: Widget;
|
|
70
|
-
computables?: Record<string, ComputableEntry>;
|
|
71
|
-
|
|
72
|
-
constructor(config?: BaseControllerConfig) {
|
|
73
|
-
super(config);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
init(context?: RenderingContext): void {
|
|
77
|
-
if (!this.initialized) {
|
|
78
|
-
this.initialized = true;
|
|
79
|
-
if (this.onInit && context) this.onInit(context);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
explore(context: RenderingContext): void {
|
|
84
|
-
let { store } = this.instance;
|
|
85
|
-
this.store = store; //in rare cases instance may change its store
|
|
86
|
-
|
|
87
|
-
if (!this.initialized) {
|
|
88
|
-
this.init(context);
|
|
89
|
-
//forgive if the developer forgets to call super.init()
|
|
90
|
-
this.initialized = true;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (this.computables) {
|
|
94
|
-
for (let key in this.computables) {
|
|
95
|
-
let x = this.computables[key];
|
|
96
|
-
let v = x.selector(store.getData());
|
|
97
|
-
if (x.type == "computable") store.set(x.name, v);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (this.onExplore) {
|
|
102
|
-
this.onExplore(context);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
prepare(context: RenderingContext): void {
|
|
107
|
-
if (this.onPrepare) {
|
|
108
|
-
this.onPrepare(context);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
cleanup(context: RenderingContext): void {
|
|
113
|
-
if (this.onCleanup) {
|
|
114
|
-
this.onCleanup(context);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
addComputable<Selectors extends readonly ComputableSelector[]>(
|
|
119
|
-
name: string,
|
|
120
|
-
args: [...Selectors],
|
|
121
|
-
callback: (...values: { [K in keyof Selectors]: InferSelectorValue<Selectors[K]> }) => any,
|
|
122
|
-
): void {
|
|
123
|
-
if (!isArray(args)) throw new Error("Second argument to the addComputable method should be an array.");
|
|
124
|
-
let selector = computable(...args, callback).memoize();
|
|
125
|
-
if (!this.computables) this.computables = {};
|
|
126
|
-
this.computables[computablePrefix + name] = { name, selector, type: "computable" };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
addTrigger<Selectors extends readonly ComputableSelector[]>(
|
|
130
|
-
name: string,
|
|
131
|
-
args: [...Selectors],
|
|
132
|
-
callback: (...values: { [K in keyof Selectors]: InferSelectorValue<Selectors[K]> }) => any,
|
|
133
|
-
autoRun?: boolean,
|
|
134
|
-
): void {
|
|
135
|
-
if (!isArray(args)) throw new Error("Second argument to the addTrigger method should be an array.");
|
|
136
|
-
let selector = computable(...args, callback).memoize(!autoRun && this.store.getData());
|
|
137
|
-
if (!this.computables) this.computables = {};
|
|
138
|
-
this.computables[triggerPrefix + name] = { name, selector, type: "trigger" };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
removeTrigger(name: string): void {
|
|
142
|
-
if (this.computables) delete this.computables[triggerPrefix + name];
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
removeComputable(name: string): void {
|
|
146
|
-
if (this.computables) delete this.computables[computablePrefix + name];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
invokeParentMethod(methodName: string, ...args: any[]): any {
|
|
150
|
-
let parent = this.instance.parent;
|
|
151
|
-
if (!parent) throw new Error("Cannot invoke a parent controller method as the instance does not have a parent.");
|
|
152
|
-
return parent.invokeControllerMethod(methodName, ...args);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
invokeMethod(methodName: string, ...args: any[]): any {
|
|
156
|
-
return this.instance.invokeControllerMethod(methodName, ...args);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
Controller.namespace = "ui.controller.";
|
|
161
|
-
|
|
162
|
-
Controller.factory = function (alias: any, config?: any, more?: any) {
|
|
163
|
-
if (isFunction(alias)) {
|
|
164
|
-
let cfg = {
|
|
165
|
-
...config,
|
|
166
|
-
...more,
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
if (cfg.instance) {
|
|
170
|
-
//in rare cases instance.store may change, so we cannot rely on the store passed through configuration
|
|
171
|
-
cfg.store = new StoreProxy(() => cfg.instance.store);
|
|
172
|
-
Object.assign(cfg, cfg.store.getMethods());
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
let result = alias(cfg);
|
|
176
|
-
if (result instanceof Controller) return result;
|
|
177
|
-
|
|
178
|
-
return Controller.create({
|
|
179
|
-
...config,
|
|
180
|
-
...more,
|
|
181
|
-
...result,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return Controller.create({
|
|
186
|
-
...config,
|
|
187
|
-
...more,
|
|
188
|
-
});
|
|
189
|
-
};
|
|
1
|
+
import { computable, ComputableSelector, InferSelectorValue } from "../data/computable";
|
|
2
|
+
import { Component, ComponentConstructor, ComponentConfigType } from "../util/Component";
|
|
3
|
+
import { isArray } from "../util/isArray";
|
|
4
|
+
import { isFunction } from "../util/isFunction";
|
|
5
|
+
import { StoreProxy } from "../data/StoreProxy";
|
|
6
|
+
import { RenderingContext } from "./RenderingContext";
|
|
7
|
+
import { View, ViewMethods } from "../data/View";
|
|
8
|
+
import { Widget } from "./Widget";
|
|
9
|
+
import { Instance } from "./Instance";
|
|
10
|
+
|
|
11
|
+
const computablePrefix = "computable-";
|
|
12
|
+
const triggerPrefix = "trigger-";
|
|
13
|
+
|
|
14
|
+
interface ComputableEntry {
|
|
15
|
+
name: string;
|
|
16
|
+
selector: any;
|
|
17
|
+
type: "computable" | "trigger";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Controller methods interface for ThisType in config objects
|
|
21
|
+
export interface ControllerMethods {
|
|
22
|
+
store: View;
|
|
23
|
+
instance: Instance;
|
|
24
|
+
widget?: Widget;
|
|
25
|
+
invokeParentMethod(method: string, ...args: any[]): any;
|
|
26
|
+
addTrigger<Selectors extends readonly ComputableSelector[]>(
|
|
27
|
+
name: string,
|
|
28
|
+
args: [...Selectors],
|
|
29
|
+
callback: (...values: { [K in keyof Selectors]: InferSelectorValue<Selectors[K]> }) => any,
|
|
30
|
+
autoRun?: boolean,
|
|
31
|
+
): void;
|
|
32
|
+
addComputable<Selectors extends readonly ComputableSelector[]>(
|
|
33
|
+
name: string,
|
|
34
|
+
args: [...Selectors],
|
|
35
|
+
callback: (...values: { [K in keyof Selectors]: InferSelectorValue<Selectors[K]> }) => any,
|
|
36
|
+
): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface BaseControllerConfig {
|
|
40
|
+
store?: View;
|
|
41
|
+
instance?: Instance;
|
|
42
|
+
widget?: Widget;
|
|
43
|
+
|
|
44
|
+
onInit?: (context: RenderingContext) => void;
|
|
45
|
+
onExplore?: (context: RenderingContext) => void;
|
|
46
|
+
onPrepare?: (context: RenderingContext) => void;
|
|
47
|
+
onCleanup?: (context: RenderingContext) => void;
|
|
48
|
+
onDestroy?: () => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Controller config object type with Controller methods AND its own methods available on 'this'
|
|
52
|
+
export type ControllerConfig<T extends BaseControllerConfig = BaseControllerConfig> = T &
|
|
53
|
+
ThisType<ControllerMethods & T>;
|
|
54
|
+
|
|
55
|
+
/** Controller factory function type */
|
|
56
|
+
export type ControllerFactory<T extends BaseControllerConfig = BaseControllerConfig> = (
|
|
57
|
+
config: ViewMethods,
|
|
58
|
+
) => ControllerConfig<T>;
|
|
59
|
+
|
|
60
|
+
export class Controller extends Component implements ControllerMethods {
|
|
61
|
+
initialized?: boolean;
|
|
62
|
+
onInit?(context: RenderingContext): void;
|
|
63
|
+
onExplore?(context: RenderingContext): void;
|
|
64
|
+
onPrepare?(context: RenderingContext): void;
|
|
65
|
+
onCleanup?(context: RenderingContext): void;
|
|
66
|
+
onDestroy?(): void;
|
|
67
|
+
declare instance: Instance;
|
|
68
|
+
declare store: View;
|
|
69
|
+
declare widget?: Widget;
|
|
70
|
+
computables?: Record<string, ComputableEntry>;
|
|
71
|
+
|
|
72
|
+
constructor(config?: BaseControllerConfig) {
|
|
73
|
+
super(config);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
init(context?: RenderingContext): void {
|
|
77
|
+
if (!this.initialized) {
|
|
78
|
+
this.initialized = true;
|
|
79
|
+
if (this.onInit && context) this.onInit(context);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
explore(context: RenderingContext): void {
|
|
84
|
+
let { store } = this.instance;
|
|
85
|
+
this.store = store; //in rare cases instance may change its store
|
|
86
|
+
|
|
87
|
+
if (!this.initialized) {
|
|
88
|
+
this.init(context);
|
|
89
|
+
//forgive if the developer forgets to call super.init()
|
|
90
|
+
this.initialized = true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (this.computables) {
|
|
94
|
+
for (let key in this.computables) {
|
|
95
|
+
let x = this.computables[key];
|
|
96
|
+
let v = x.selector(store.getData());
|
|
97
|
+
if (x.type == "computable") store.set(x.name, v);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (this.onExplore) {
|
|
102
|
+
this.onExplore(context);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
prepare(context: RenderingContext): void {
|
|
107
|
+
if (this.onPrepare) {
|
|
108
|
+
this.onPrepare(context);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
cleanup(context: RenderingContext): void {
|
|
113
|
+
if (this.onCleanup) {
|
|
114
|
+
this.onCleanup(context);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
addComputable<Selectors extends readonly ComputableSelector[]>(
|
|
119
|
+
name: string,
|
|
120
|
+
args: [...Selectors],
|
|
121
|
+
callback: (...values: { [K in keyof Selectors]: InferSelectorValue<Selectors[K]> }) => any,
|
|
122
|
+
): void {
|
|
123
|
+
if (!isArray(args)) throw new Error("Second argument to the addComputable method should be an array.");
|
|
124
|
+
let selector = computable(...args, callback).memoize();
|
|
125
|
+
if (!this.computables) this.computables = {};
|
|
126
|
+
this.computables[computablePrefix + name] = { name, selector, type: "computable" };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
addTrigger<Selectors extends readonly ComputableSelector[]>(
|
|
130
|
+
name: string,
|
|
131
|
+
args: [...Selectors],
|
|
132
|
+
callback: (...values: { [K in keyof Selectors]: InferSelectorValue<Selectors[K]> }) => any,
|
|
133
|
+
autoRun?: boolean,
|
|
134
|
+
): void {
|
|
135
|
+
if (!isArray(args)) throw new Error("Second argument to the addTrigger method should be an array.");
|
|
136
|
+
let selector = computable(...args, callback).memoize(!autoRun && this.store.getData());
|
|
137
|
+
if (!this.computables) this.computables = {};
|
|
138
|
+
this.computables[triggerPrefix + name] = { name, selector, type: "trigger" };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
removeTrigger(name: string): void {
|
|
142
|
+
if (this.computables) delete this.computables[triggerPrefix + name];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
removeComputable(name: string): void {
|
|
146
|
+
if (this.computables) delete this.computables[computablePrefix + name];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
invokeParentMethod(methodName: string, ...args: any[]): any {
|
|
150
|
+
let parent = this.instance.parent;
|
|
151
|
+
if (!parent) throw new Error("Cannot invoke a parent controller method as the instance does not have a parent.");
|
|
152
|
+
return parent.invokeControllerMethod(methodName, ...args);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
invokeMethod(methodName: string, ...args: any[]): any {
|
|
156
|
+
return this.instance.invokeControllerMethod(methodName, ...args);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
Controller.namespace = "ui.controller.";
|
|
161
|
+
|
|
162
|
+
Controller.factory = function (alias: any, config?: any, more?: any) {
|
|
163
|
+
if (isFunction(alias)) {
|
|
164
|
+
let cfg = {
|
|
165
|
+
...config,
|
|
166
|
+
...more,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
if (cfg.instance) {
|
|
170
|
+
//in rare cases instance.store may change, so we cannot rely on the store passed through configuration
|
|
171
|
+
cfg.store = new StoreProxy(() => cfg.instance.store);
|
|
172
|
+
Object.assign(cfg, cfg.store.getMethods());
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let result = alias(cfg);
|
|
176
|
+
if (result instanceof Controller) return result;
|
|
177
|
+
|
|
178
|
+
return Controller.create({
|
|
179
|
+
...config,
|
|
180
|
+
...more,
|
|
181
|
+
...result,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return Controller.create({
|
|
186
|
+
...config,
|
|
187
|
+
...more,
|
|
188
|
+
});
|
|
189
|
+
};
|