skandha 0.7.0 → 0.9.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 +139 -228
- package/index.d.ts +5 -5
- package/index.js +4 -2
- package/internal/utils.d.ts +0 -1
- package/internal/utils.js +0 -3
- package/lib/Connector.d.ts +5 -0
- package/lib/Connector.js +98 -0
- package/lib/facet.d.ts +1 -8
- package/lib/facet.js +1 -44
- package/lib/patch.d.ts +0 -4
- package/lib/patch.js +0 -79
- package/lib/registerCtr.d.ts +6 -0
- package/lib/registerCtr.js +31 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,101 +4,70 @@
|
|
|
4
4
|
|
|
5
5
|
The goal of SkandhaJS is to provide data containers that have reusable behaviors, such as
|
|
6
6
|
selection, highlighting, filtering, drag-and-drop, etc. The only requirement for using these containers is
|
|
7
|
-
that every item in the container has a unique id.
|
|
8
|
-
make sure that they interact properly. For example, when selecting an item you may want the item also to
|
|
7
|
+
that every item in the container has a unique id. SkandhaJS allows you to combine behaviors and make them interact properly. For example, when selecting an item you may want the item also to
|
|
9
8
|
be highlighted. As we will see, these interactions are achieved by implementing callback functions.
|
|
10
9
|
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
**NOTE**
|
|
14
|
-
|
|
15
|
-
In a Skandha container, behaviors are implemented using classes that are called facets, and they are found by their class name. The recommendation is to add a `static className` function to each facet class. If you omit this function, then Skandha will use the automatically generated class name, which can cause problems with the mangling of classnames in your minification tool.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
10
|
## Links
|
|
20
11
|
|
|
21
12
|
- The [skandha](http://github.com/mnieber/skandha) library contains the basic building blocks
|
|
22
13
|
- The [skandha-facets](http://github.com/mnieber/skandha-facets) library contains useful facets such as selection, highlight, filtering, addition, etc.
|
|
23
14
|
- The [skandha-mobx](http://github.com/mnieber/skandha-mobx) library contains bindings to MobX
|
|
24
15
|
- The [aspiration](http://github.com/mnieber/aspiration) library handles aspect oriented programming (AOP)
|
|
16
|
+
- The [example code](http://github.com/mnieber/skandha-sample-app) shown in this README.
|
|
25
17
|
|
|
26
|
-
##
|
|
18
|
+
## Snippet
|
|
27
19
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
from
|
|
20
|
+
```
|
|
21
|
+
// file: Selection.ts
|
|
22
|
+
import { input, output, operation } from 'skandha';
|
|
23
|
+
import { host, stub } from 'aspiration';
|
|
32
24
|
|
|
33
|
-
|
|
25
|
+
export type SelectionParamsT = { itemId?: string; isShift?: boolean; isCtrl?: boolean; };
|
|
34
26
|
|
|
35
|
-
|
|
27
|
+
export class Selection<ValueT = any> {
|
|
28
|
+
static className = () => 'Selection';
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
@input selectableIds: Array<string> = stub();
|
|
31
|
+
@output ids: Array<string> = [];
|
|
32
|
+
@output anchorId?: string;
|
|
33
|
+
@output items: Array<ValueT> = stub();
|
|
40
34
|
|
|
35
|
+
@operation @host(['selectionParams'], selectItemDefaultCbs) selectItem(
|
|
36
|
+
selectionParams: SelectionParamsT
|
|
37
|
+
) {
|
|
38
|
+
return (cbs: Selection_selectItem) => {
|
|
39
|
+
cbs.select();
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
41
43
|
```
|
|
42
|
-
const ctr = createContainer();
|
|
43
|
-
ctr.selection.selectableIds = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
ctr.selection.selectItem({itemId: "5", isShift: true});
|
|
47
|
-
console.log(ctr.selection.ids);
|
|
48
|
-
```
|
|
45
|
+
## Fact: the Selection facet has input, data and output members
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
In the snippet above we see a so-called Facet class. A facet class implements a reusable behaviour. The `Selection` facet stores a list of selectable ids and has a `selectItem` function that allows you to select from those ids. The result of the selection is stored in the `ids`, `anchorId` and `items` fields. Note that `selectItem` is an empty function. The exact method for doing the selection is determined by a callback function, as we shall see later.
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
## Fact: the Selection facet has a static className function
|
|
50
|
+
|
|
51
|
+
Various facets are usually combined in a single container. To allow you to look up a facet by it's name, each facet class implements the static `className` function.
|
|
52
|
+
|
|
53
|
+
## Fact: the `selectItem` function uses a callback function
|
|
54
|
+
|
|
55
|
+
We shall see later how callbacks are installed in a facet. For now, it's sufficient to understand that `selectItem` (the host function) executes the `cbs.selectItem()` callback function. This callback function will have access to all parameters of the host function (in this case: `selectionParams`).
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
`selection.selectItem`. Why is this needed? The reason for using a callback function is that we
|
|
62
|
-
don't want to hard code how selection is performed (e.g. what the effect of the shift and ctrl key is),
|
|
63
|
-
because this would limit the reusability. The reason for storing the arguments of `selection.selectItem`
|
|
64
|
-
is that this makes it easier to pass these arguments to other functions that want to use
|
|
65
|
-
them. For example, if a side-effect of selection is to highlight the item, then we
|
|
66
|
-
would like to pass the `SelectionParamsT` instance also to a `highlightSelectedItem` policy function
|
|
67
|
-
(this will be explained in detail later). The helper class looks like this:
|
|
57
|
+
## Snippet
|
|
68
58
|
|
|
69
59
|
```
|
|
60
|
+
// file: Selection.ts
|
|
70
61
|
export class Selection_selectItem extends Cbs {
|
|
71
62
|
selectionParams: SelectionParamsT = stub();
|
|
72
63
|
select() {}
|
|
73
64
|
}
|
|
74
65
|
```
|
|
75
66
|
|
|
76
|
-
Next, we add the `Selection` class itself that has a `selectItem` function that checks the selection
|
|
77
|
-
parameters and then calls the `select` callback function.
|
|
78
|
-
|
|
79
67
|
```
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@data ids: Array<string> = [];
|
|
83
|
-
@data anchorId?: string;
|
|
84
|
-
@data items?: Array<ValueT>; // ignore this field for now...
|
|
68
|
+
// file: createContainer.ts
|
|
69
|
+
import { registerCtr } from 'skandha';
|
|
85
70
|
|
|
86
|
-
@operation @host selectItem(selectionParams: SelectionParamsT) {
|
|
87
|
-
return (cbs: Selection_selectItem) => {
|
|
88
|
-
if (!this.selectableIds.includes(selectionParams.itemId)) {
|
|
89
|
-
throw Error(`Invalid id: ${selectionParams.itemId}`);
|
|
90
|
-
}
|
|
91
|
-
cbs.select();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
And finally, we add the `createContainer` function that creates a `Selection` instance and implements
|
|
98
|
-
the `select` callback. We don't show the code of `handleSelectItem`, but you can see it
|
|
99
|
-
[here](https://github.com/mnieber/skandha-facets/blob/main/Selection.ts).
|
|
100
|
-
|
|
101
|
-
```
|
|
102
71
|
const createContainer() {
|
|
103
72
|
const ctr = {
|
|
104
73
|
selection: new Selection()
|
|
@@ -112,47 +81,94 @@ const createContainer() {
|
|
|
112
81
|
}
|
|
113
82
|
});
|
|
114
83
|
|
|
84
|
+
// If you omit `members` then all fields of `ctr` are included.
|
|
85
|
+
registerCtr(ctr, { name: 'TodosCtr', members: ['selection'] });
|
|
115
86
|
return ctr;
|
|
116
87
|
}
|
|
117
88
|
```
|
|
118
89
|
|
|
119
|
-
|
|
90
|
+
## Fact: a Cbs class defines a set of callbacks
|
|
91
|
+
|
|
92
|
+
In the above snippet we see that the `Selection_selectItem` class defines the callbacks that can be installed for the `selectItem` function of the `Selection` facet. This class also defines the parameters that are received by the host function and made available to each callback.
|
|
93
|
+
|
|
94
|
+
## Fact: the setCallbacks function installs the callbacks for the Selection facet
|
|
95
|
+
|
|
96
|
+
In the same snippet we see that `setCallbacks` is called to install the `select` callback function. This callback function forwards to a helper function called `handleSelectItem`. Note that the `Selection_selectItem` instance is available through the `this` constant.
|
|
97
|
+
|
|
98
|
+
## Fact: the registerCtr function registers the container with SkandhaJS
|
|
120
99
|
|
|
121
|
-
|
|
122
|
-
- The `stub` function is used in callback objects to indicate that although the field is initialized with `undefined` it will receive a value later.
|
|
100
|
+
The purpose of calling registerCtr on the container is twofold:
|
|
123
101
|
|
|
124
|
-
|
|
102
|
+
- it makes it possible to look up a facet by its name or class, e.g. `getf(ctr, Selection)` returns `ctr.selection`.
|
|
103
|
+
- it tells SkandhaJS what facets should be included in log reports. When the client calls any facet function that is decorated with @operation then SkandhaJS will log the entire container before and after the operation was executed. Logging can be enabled using the `setOptions` function.
|
|
125
104
|
|
|
126
|
-
|
|
127
|
-
when we add a second facet: Highlight. We will extend the example such that when the user selects an item,
|
|
128
|
-
it will also be highlighted. The same approach can be used to make sure that the Filtering and Highlight
|
|
129
|
-
facets work well together: if the filter is enabled, the highlight moves to an item that is not hidden by the
|
|
130
|
-
filter. In general, there are many possibilities to create complex behaviors this way.
|
|
105
|
+
## Snippet: the handleSelectItem functon
|
|
131
106
|
|
|
132
|
-
|
|
107
|
+
```
|
|
108
|
+
// file: handleSelectItem.ts
|
|
109
|
+
export function handleSelectItem(
|
|
110
|
+
facet: Selection,
|
|
111
|
+
{ itemId, isShift, isCtrl }: SelectionParamsT
|
|
112
|
+
) {
|
|
113
|
+
if (itemId === undefined) {
|
|
114
|
+
facet.ids = [];
|
|
115
|
+
facet.anchorId = undefined;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
133
118
|
|
|
119
|
+
if (isShift) {
|
|
120
|
+
// Omitted for brevity
|
|
121
|
+
} else if (isCtrl) {
|
|
122
|
+
facet.ids = facet.ids.includes(itemId)
|
|
123
|
+
? facet.ids.filter((x) => x !== itemId)
|
|
124
|
+
: [...facet.ids, itemId];
|
|
125
|
+
} else {
|
|
126
|
+
facet.ids = [itemId];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Move the anchor
|
|
130
|
+
if (!facet.anchorId || !(isCtrl || isShift)) {
|
|
131
|
+
facet.anchorId = itemId;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
134
|
```
|
|
135
|
+
|
|
136
|
+
## Fact: the handleSelectItem does the actual work of selecting items
|
|
137
|
+
|
|
138
|
+
For completeness, we show how `handleSelectItem` is implemented. Note that this function stores the results directly in the selection facet instance.
|
|
139
|
+
|
|
140
|
+
## Snippet: the Highlight facet
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
// file: Highlight.ts
|
|
135
144
|
export class Highlight_highlightItem extends Cbs {
|
|
136
145
|
id: string = stub();
|
|
137
146
|
scrollItemIntoView() {}
|
|
138
147
|
}
|
|
139
148
|
|
|
140
149
|
export class Highlight<ValueT = any> {
|
|
141
|
-
@data id
|
|
142
|
-
@data item?: ValueT;
|
|
150
|
+
@data id?: string;
|
|
151
|
+
@data item?: ValueT;
|
|
143
152
|
|
|
144
|
-
@operation @host highlightItem(id: string) {
|
|
153
|
+
@operation @host(["id"]) highlightItem(id: string) {
|
|
145
154
|
return (cbs: Highlight_highlightItem) => {
|
|
146
155
|
this.id = id;
|
|
147
|
-
|
|
156
|
+
if (cbs.scrollItemIntoView) {
|
|
157
|
+
cbs.scrollItemIntoView();
|
|
158
|
+
}
|
|
148
159
|
};
|
|
149
160
|
}
|
|
150
161
|
}
|
|
151
162
|
```
|
|
152
163
|
|
|
153
|
-
|
|
164
|
+
## Fact: the `Highlight` facet implements the highlight behaviour
|
|
165
|
+
|
|
166
|
+
The `Highlight` snippet has a similar structure as the `Selection` facet. It has a callback function for scrolling the highlighted item into view. Note that the `highlightItem` function does not set the `item` field, because the facet only deals with ids. We shall later see how the `item` field is set by looking up the item in an external collection.
|
|
167
|
+
|
|
168
|
+
## Snippet: a container with Selection and Highlight
|
|
154
169
|
|
|
155
170
|
```
|
|
171
|
+
// file: createContainer.ts
|
|
156
172
|
const createContainer() {
|
|
157
173
|
const ctr = {
|
|
158
174
|
selection: new Selection(),
|
|
@@ -163,9 +179,13 @@ const createContainer() {
|
|
|
163
179
|
selectItem: {
|
|
164
180
|
select(this: Selection_selectItem): {
|
|
165
181
|
handleSelectItem(ctr.selection, this.selectionParams);
|
|
182
|
+
|
|
166
183
|
if (!this.selectionParams.isShift && !this.selectionParams.isCtrl) {
|
|
167
184
|
ctr.highlight.highlightItem(this.selectionParams.itemId);
|
|
168
185
|
}
|
|
186
|
+
|
|
187
|
+
// The above 3 lines could be extracted in a reusable helper function, e.g.
|
|
188
|
+
// highlightFollowsSelection(ctr, this.selectionParams);
|
|
169
189
|
},
|
|
170
190
|
}
|
|
171
191
|
});
|
|
@@ -173,37 +193,14 @@ const createContainer() {
|
|
|
173
193
|
return ctr;
|
|
174
194
|
```
|
|
175
195
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
Now, let's add filtering. For brevity, the definition of `Filtering_apply` and `Filtering_enable`
|
|
179
|
-
is skipped. Also, we assume that the logic for highlighting the select item is captured in a policy
|
|
180
|
-
function called `highlightFollowsSelection`, and that `highlightIsCorrectedOnFilterChange` ensures
|
|
181
|
-
that there is still a highlighted item when the filter changes.
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
export class Filtering<ValueT = any> {
|
|
185
|
-
@data isEnabled: boolean = false;
|
|
186
|
-
@data filter: FilterT = (ValueT[]) => [];
|
|
187
|
-
@data inputItems?: Array<ValueT>;
|
|
188
|
-
|
|
189
|
-
@data get filteredItems() {
|
|
190
|
-
return this.isEnabled ? this.filter(this.inputItems) : this.inputItems;
|
|
191
|
-
}
|
|
196
|
+
## Fact: callbacks allow you to create co-ordinated behaviours
|
|
192
197
|
|
|
193
|
-
|
|
194
|
-
return (cbs: Filtering_apply) => {
|
|
195
|
-
this.filter = filter;
|
|
196
|
-
this.isEnabled = true;
|
|
197
|
-
};
|
|
198
|
-
}
|
|
198
|
+
Above we see for the first time how SkandhaJS helps you to create co-ordinated behaviours. When the client calls `ctr.selection.selectItem` and the `select` callback is executed, then the callback ensures that the selected item is also highlighted. As the comment in the snippet suggests, we can make the body of the `select` callback more readable by extracting some of the code in a `highlightFollowsSelection` helper function.
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
return (cbs: Filtering_setEnabled) => {
|
|
202
|
-
this.isEnabled = flag;
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
}
|
|
200
|
+
## Snippet: a container with Selection, Highlight and Filtering
|
|
206
201
|
|
|
202
|
+
```
|
|
203
|
+
// file: createContainer.ts
|
|
207
204
|
const createContainer() {
|
|
208
205
|
const ctr = {
|
|
209
206
|
selection: new Selection(),
|
|
@@ -232,129 +229,49 @@ const createContainer() {
|
|
|
232
229
|
}
|
|
233
230
|
```
|
|
234
231
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
- There are special callback functions called `enter` and `exit` that are called at the
|
|
238
|
-
beginning and end of the operation function. An example of using the `exit` callback is shown
|
|
239
|
-
above.
|
|
240
|
-
|
|
241
|
-
### Mapping data onto the container, and between facets
|
|
242
|
-
|
|
243
|
-
At this point we have a container with facets that perform useful operations, and that interact
|
|
244
|
-
to achieve interesting behaviours. However, we took a shortcut by hard coding the value of
|
|
245
|
-
`Selection.selectableIds`, and also we forgot to set the value of `Filtering.inputItems`. Skandha
|
|
246
|
-
comes with data mapping functions for this purpose. Let's extend the container with input
|
|
247
|
-
and output data structures:
|
|
248
|
-
|
|
249
|
-
```
|
|
250
|
-
const createContainer() {
|
|
251
|
-
// we keep all the existing code that was added above
|
|
252
|
-
|
|
253
|
-
ctr['inputs'] = {
|
|
254
|
-
todoById: {[id: string]: TodoT} = loadTodos();
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
ctr['outputs'] = {
|
|
258
|
-
filteredTodoById: {[id: string]: TodoT} = {};
|
|
259
|
-
};
|
|
232
|
+
## Fact: the `exit` callback function is called at the end of the host function
|
|
260
233
|
|
|
261
|
-
|
|
262
|
-
return ctr;
|
|
263
|
-
```
|
|
234
|
+
In the snippet we add a filtering facet that has an `apply` host function. In this host function, we can install an `exit` callback that corrects the highlight when the filter changes (because the filter may hide the highlighted item). The `exit` callback is called at the end of the host function. Similarly, the `enter` callback is called at the beginning.
|
|
264
235
|
|
|
265
|
-
|
|
266
|
-
As a first step, we need to connect `Filtering.inputItems` to `Inputs.todoById` and `Outputs.filteredTodoById`.
|
|
267
|
-
We can think of this as setting up a pipeline, where one of more source facet members are mapped onto a destination
|
|
268
|
-
facet member:
|
|
236
|
+
## Snippet: mapping data onto each facet
|
|
269
237
|
|
|
270
238
|
```
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
Notes:
|
|
287
|
-
|
|
288
|
-
- the first argument to `mapDataToFacet` is a reference to the the facet member that receives the data. SkandhaJS will patch the
|
|
289
|
-
facet, which - in the above example - means replacing the `inputItems` of `ctr.filtering` with a `get inputItems()` property.
|
|
290
|
-
- the second argument to `mapDataToFacet` is a getter function that takes the container and returns some data.
|
|
291
|
-
In this case, it uses the `getm` helper function to return `ctr.inputs.todoById`.
|
|
292
|
-
- the third argument is a transformation that is applied to the result of the getter function. In this case,
|
|
293
|
-
it means that `get inputItems()` returns `Object.values(getm(Inputs, 'todoById')(ctr))`.
|
|
294
|
-
- in the calls to `mapDataToFacet`, we have to be careful to correctly spell the facet member names, such as 'filtering'.
|
|
295
|
-
It's also possible to use a class instead of a member name. For example, we could replace `['filtering', 'inputItems']` with
|
|
296
|
-
`[Filtering, 'inputItems']`.
|
|
297
|
-
|
|
298
|
-
As the next step, we will connect the list of filtered items to `Selection.selectableIds` (so that selection
|
|
299
|
-
happens in the filtered list). In addition, we will add datamappings for `Selection.items`
|
|
300
|
-
and `Highlight.item` (these data mappings allow us to not only access the ids, but also the items themselves):
|
|
239
|
+
// file: createContainer.ts
|
|
240
|
+
import * from 'ramda' as R;
|
|
241
|
+
|
|
242
|
+
const connectContainer(ctr: any) {
|
|
243
|
+
const inputs = { todos: loadTodos() };
|
|
244
|
+
const outputs = { filteredTodoById: {} };
|
|
245
|
+
|
|
246
|
+
const con = createConnector({
|
|
247
|
+
selection: () => ctr.selection,
|
|
248
|
+
highlight: () => ctr.highlight,
|
|
249
|
+
filtering: () => ctr.filtering`,
|
|
250
|
+
inputs: () => inputs,
|
|
251
|
+
outputs: () => outputs,
|
|
252
|
+
});
|
|
301
253
|
|
|
302
|
-
|
|
303
|
-
mapDataToFacet(
|
|
304
|
-
['selection', 'selectableIds'],
|
|
305
|
-
getm(['outputs', 'filteredTodoById']),
|
|
306
|
-
(x: TodoByIdT) => Object.keys(x)
|
|
307
|
-
)(ctr);
|
|
308
|
-
|
|
309
|
-
mapDatasToFacet(
|
|
310
|
-
['selection', 'items'],
|
|
311
|
-
[
|
|
312
|
-
getm(['selections', 'ids']),
|
|
313
|
-
getm(['inputs', 'todoById']),
|
|
314
|
-
],
|
|
315
|
-
(ids: string[], todoById: TodoByIdT) => ids.map((id) => todoById[id])
|
|
316
|
-
)(ctr);
|
|
317
|
-
|
|
318
|
-
mapDatasToFacet(
|
|
319
|
-
[Highlight, 'item'],
|
|
320
|
-
[
|
|
321
|
-
getm([Highlight, 'id']),
|
|
322
|
-
getm([Inputs, 'todoById']),
|
|
323
|
-
],
|
|
324
|
-
(id: string, todoById: TodoByIdT) => todoById[id]
|
|
325
|
-
)(ctr);
|
|
326
|
-
```
|
|
254
|
+
const lookUpTodo = (id: string) => outputs.filteredTodoById[id];
|
|
327
255
|
|
|
328
|
-
|
|
329
|
-
|
|
256
|
+
con.filtering.inputItems = con.inputs.todos;
|
|
257
|
+
con.outputs.filteredTodoById = con.filtering.filteredItems.tf(R.indexBy('id));
|
|
258
|
+
con.selection.selectableIds = con.outputs.filteredTodoById.tf(R.keys);
|
|
259
|
+
con.selection.item = con.selection.ids.tf(lookUpTodos);
|
|
260
|
+
con.highlight.item = con.highlight.id.tf(lookUpTodo);
|
|
330
261
|
|
|
262
|
+
con.connect();
|
|
331
263
|
```
|
|
332
|
-
const filteredTodoById = ['outputs', 'filteredTodoById'];
|
|
333
|
-
const inputTodoById = ['inputs', 'todoById'];
|
|
334
|
-
const filteredItems = ['filtering', 'filteredItems'];
|
|
335
|
-
|
|
336
|
-
const policies = [
|
|
337
|
-
// selection
|
|
338
|
-
selectionUsesSelectableIds(filteredTodoById, Object.keys),
|
|
339
|
-
selectionUsesItemLookUpTable(inputTodoById),
|
|
340
264
|
|
|
341
|
-
|
|
342
|
-
highlightUsesItemLookUpTable(inputTodoById),
|
|
265
|
+
## Fact: the Connector class maps data between objects
|
|
343
266
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
mapDataToFacet(filteredTodoById, getm(filteredItems), listToItemById),
|
|
347
|
-
]
|
|
348
|
-
|
|
349
|
-
installPolicies<TodosCtr>(policies, ctr);
|
|
350
|
-
```
|
|
267
|
+
The Connector instance allows you to map data from one object onto another object. In our example, the `inputs.todos` field is mapped onto the `filtering.inputItems` field. What the `Connector` class does is turn `filtering.inputItems` into a `get` property that returns `filtering.inputItems`.
|
|
268
|
+
The snippet contains three steps. In the first step we declare the objects, such as `selection`, `filtering`, etc. For each object, we specify the getter function that returns the object. In the second step, we declare relations between the different fields of these objects. In this step, we can also declare one or more transformation functions (such as `lookUpTodo`) that should be used in the mapping. In the final step, we call `con.connect` to create the `get` properties that perform the declared mappings.
|
|
351
269
|
|
|
352
270
|
### Logging
|
|
353
271
|
|
|
354
|
-
By calling `setOptions({logging: true})`, the
|
|
355
|
-
after calling an operation
|
|
356
|
-
This will log Facet members that are decorated with `@data` in a way that looks similar to what you are
|
|
357
|
-
used to from Redux:
|
|
272
|
+
By calling `setOptions({logging: true})`, the SkandhaJS library allows you to inspect each facet before and
|
|
273
|
+
after calling an operation. An operation is any facet member function decorated with @operation.
|
|
274
|
+
This will log Facet members that are decorated with `@data` (or `@input`, or `@output`) in a way that looks similar to what you are used to from Redux:
|
|
358
275
|
|
|
359
276
|
- before and after each operation, the entire container that holds the facet is logged
|
|
360
277
|
- all data members of each facet in the container are included in the log
|
|
@@ -365,20 +282,15 @@ used to from Redux:
|
|
|
365
282
|
If you want to render the facets with React then we need to let React know when data changes.
|
|
366
283
|
The [skandha-mobx](http://github.com/mnieber/skandha-mobx) library was created for this purpose.
|
|
367
284
|
It has a `registerCtr` function that turns each `@data` member into a Mobx property that is
|
|
368
|
-
either observable or computed
|
|
285
|
+
either observable or computed:
|
|
369
286
|
|
|
370
287
|
```
|
|
371
288
|
registerCtr({
|
|
372
289
|
ctr: ctr,
|
|
373
|
-
|
|
374
|
-
name: 'TodosCtr',
|
|
375
|
-
},
|
|
290
|
+
options: { name: 'TodosCtr'},
|
|
376
291
|
});
|
|
377
292
|
```
|
|
378
293
|
|
|
379
|
-
This means that by decorating your render function with `observer` you will get a re-render for any changes
|
|
380
|
-
(in observable facet members) in the container.
|
|
381
|
-
|
|
382
294
|
## Conclusion
|
|
383
295
|
|
|
384
296
|
SkandhaJS introduces a lot of new patterns and ideas (that are of course not in fact new) to allow you to
|
|
@@ -389,5 +301,4 @@ a policy that says that changing the highlight disables editing. By using facets
|
|
|
389
301
|
editing-specific code directly in the highlight function (that would obviously be mixing concerns).
|
|
390
302
|
|
|
391
303
|
In my experience, using SkandhaJS leads to code reuse and improved structure. On top of that, it offers a good
|
|
392
|
-
debugging experience
|
|
393
|
-
it works nicely with MobX to render the container contents.
|
|
304
|
+
debugging experience by logging all changes in the container. Finally, it works nicely with MobX to render the container contents.
|
package/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export { getCtrState } from './internal/logging';
|
|
2
2
|
export { getOptions, setOptions } from './internal/options';
|
|
3
|
+
export { createConnector } from './lib/Connector';
|
|
3
4
|
export { getc, getFacetMemberNames } from './lib/ctr';
|
|
4
5
|
export { data, getDataMemberNames, getLoggedMemberNames, input, log, output, } from './lib/data';
|
|
5
|
-
export {
|
|
6
|
+
export { getf } from './lib/facet';
|
|
6
7
|
export { installPolicies } from './lib/install';
|
|
7
8
|
export { getOperationMemberNames, operation } from './lib/operation';
|
|
8
|
-
export {
|
|
9
|
+
export { mapDataToProp } from './lib/patch';
|
|
10
|
+
export { registerCtr } from './lib/registerCtr';
|
|
11
|
+
export type { OptionsT as RegisterCtrOptionsT } from './lib/registerCtr';
|
|
9
12
|
export declare type ClassT = any;
|
|
10
|
-
export declare type MemberNameT = string;
|
|
11
|
-
export declare type ClassMemberT = [ClassT, MemberNameT, any?];
|
|
12
|
-
export declare type GetterT<T = any> = (ctr: any) => T;
|
package/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { getCtrState } from './internal/logging';
|
|
2
2
|
export { getOptions, setOptions } from './internal/options';
|
|
3
|
+
export { createConnector } from './lib/Connector';
|
|
3
4
|
export { getc, getFacetMemberNames } from './lib/ctr';
|
|
4
5
|
export { data, getDataMemberNames, getLoggedMemberNames, input, log, output, } from './lib/data';
|
|
5
|
-
export {
|
|
6
|
+
export { getf } from './lib/facet';
|
|
6
7
|
export { installPolicies } from './lib/install';
|
|
7
8
|
export { getOperationMemberNames, operation } from './lib/operation';
|
|
8
|
-
export {
|
|
9
|
+
export { mapDataToProp } from './lib/patch';
|
|
10
|
+
export { registerCtr } from './lib/registerCtr';
|
package/internal/utils.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export declare function getOrCreate(obj: any, key: any, fn: any): any;
|
|
2
2
|
export declare const zip: (arr: any, ...arrs: any) => any;
|
|
3
|
-
export declare function getCtrClassAdmin(ctrClass: any): any;
|
|
4
3
|
export declare function getCtrAdmin(ctr: any): any;
|
|
5
4
|
export declare function getFacetClassAdmin(facetClass: any): any;
|
|
6
5
|
export declare function getFacetAdmin(facet: any): any;
|
package/internal/utils.js
CHANGED
|
@@ -23,9 +23,6 @@ export var zip = function (arr) {
|
|
|
23
23
|
return arrs.reduce(function (a, arr) { return __spreadArray(__spreadArray([], a, true), [arr[i]], false); }, [val]);
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
|
-
export function getCtrClassAdmin(ctrClass) {
|
|
27
|
-
return getOrCreate(ctrClass, symbols.admin, function () { return ({}); });
|
|
28
|
-
}
|
|
29
26
|
export function getCtrAdmin(ctr) {
|
|
30
27
|
return getOrCreate(ctr, symbols.admin, function () { return ({}); });
|
|
31
28
|
}
|
package/lib/Connector.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { mapDataToProp } from './patch';
|
|
2
|
+
var Connector = /** @class */ (function () {
|
|
3
|
+
function Connector(entities, options) {
|
|
4
|
+
var _this = this;
|
|
5
|
+
var _a;
|
|
6
|
+
this.entityByName = {};
|
|
7
|
+
this.connect = function () {
|
|
8
|
+
for (var _i = 0, _a = Object.values(_this.entityByName); _i < _a.length; _i++) {
|
|
9
|
+
var entity = _a[_i];
|
|
10
|
+
_this._connectEntity(entity);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
this._connectEntity = function (entity) {
|
|
14
|
+
var _loop_1 = function (entry) {
|
|
15
|
+
var name_1 = entry[0], field = entry[1];
|
|
16
|
+
var obj = entity.getObj();
|
|
17
|
+
if (_this.options.strict && !(name_1 in obj)) {
|
|
18
|
+
throw new Error("Cannot override the field ".concat(name_1, " in ").concat(obj, ".") +
|
|
19
|
+
'Field not found. Disable strict mode to ignore this error.');
|
|
20
|
+
}
|
|
21
|
+
mapDataToProp(obj, name_1, function () {
|
|
22
|
+
return field.getSource();
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
for (var _i = 0, _a = Object.entries(entity.fieldByName); _i < _a.length; _i++) {
|
|
26
|
+
var entry = _a[_i];
|
|
27
|
+
_loop_1(entry);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
this.options = {
|
|
31
|
+
strict: (_a = options === null || options === void 0 ? void 0 : options.strict) !== null && _a !== void 0 ? _a : true,
|
|
32
|
+
};
|
|
33
|
+
for (var _i = 0, _b = Object.entries(entities); _i < _b.length; _i++) {
|
|
34
|
+
var entry = _b[_i];
|
|
35
|
+
var name_2 = entry[0], getObj = entry[1];
|
|
36
|
+
this.entityByName[name_2] = new Entity(getObj);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return Connector;
|
|
40
|
+
}());
|
|
41
|
+
export var createConnector = function (entities) {
|
|
42
|
+
var con = new Connector(entities);
|
|
43
|
+
return new Proxy(con, {
|
|
44
|
+
get: function (target, key) {
|
|
45
|
+
// Code example:
|
|
46
|
+
//
|
|
47
|
+
// const con = createConnector({ foo: () => myFoo });
|
|
48
|
+
//
|
|
49
|
+
// When you call `con.foo` then you
|
|
50
|
+
// receive `con.entityByName.foo.fieldByName`.
|
|
51
|
+
//
|
|
52
|
+
// con.foo.bar = 5; // Equivalent to: con.entityByName.foo.fieldByName.bar = 5
|
|
53
|
+
//
|
|
54
|
+
// When you call `con.connect` then a normal lookup of the `connect`
|
|
55
|
+
// member in `con` is performed.
|
|
56
|
+
var _a, _b;
|
|
57
|
+
return (_b = (_a = target.entityByName[key]) === null || _a === void 0 ? void 0 : _a.fieldByName) !== null && _b !== void 0 ? _b : target[key];
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
var Entity = /** @class */ (function () {
|
|
62
|
+
function Entity(getObj) {
|
|
63
|
+
var _this = this;
|
|
64
|
+
this.getObj = getObj;
|
|
65
|
+
this.fieldByName = new Proxy({}, {
|
|
66
|
+
get: function (target, key) {
|
|
67
|
+
var _a;
|
|
68
|
+
return (_a = target[key]) !== null && _a !== void 0 ? _a : new Field(_this, key);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return Entity;
|
|
73
|
+
}());
|
|
74
|
+
var Field = /** @class */ (function () {
|
|
75
|
+
function Field(entity, name) {
|
|
76
|
+
var _this = this;
|
|
77
|
+
this.transforms = [];
|
|
78
|
+
this.getSource = function () {
|
|
79
|
+
var obj = _this.entity.getObj();
|
|
80
|
+
if (!(_this.name in obj)) {
|
|
81
|
+
throw new Error("Field ".concat(_this.name, " does not exist on object ").concat(obj));
|
|
82
|
+
}
|
|
83
|
+
var result = obj[_this.name];
|
|
84
|
+
for (var _i = 0, _a = _this.transforms; _i < _a.length; _i++) {
|
|
85
|
+
var tf = _a[_i];
|
|
86
|
+
result = tf(result);
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
};
|
|
90
|
+
this.entity = entity;
|
|
91
|
+
this.name = name;
|
|
92
|
+
}
|
|
93
|
+
Field.prototype.tf = function (transform) {
|
|
94
|
+
this.transforms.push(transform);
|
|
95
|
+
return this;
|
|
96
|
+
};
|
|
97
|
+
return Field;
|
|
98
|
+
}());
|
package/lib/facet.d.ts
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function facet(facetHost: any, facetMember: any, descriptor?: undefined): undefined;
|
|
1
|
+
import { ClassT } from '..';
|
|
3
2
|
export declare function facetClassName(facetClass: ClassT): any;
|
|
4
|
-
export declare function registerFacets(ctr: any, options: {
|
|
5
|
-
name?: string;
|
|
6
|
-
members?: string[];
|
|
7
|
-
ctrState?: Function;
|
|
8
|
-
}): void;
|
|
9
3
|
export declare function getf(facetClass: ClassT | string, ctr?: any): any;
|
|
10
|
-
export declare function getm<T = any>(classMember: ClassMemberT): GetterT<T>;
|
package/lib/facet.js
CHANGED
|
@@ -1,43 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { options as skandhaOptions } from '../internal/options';
|
|
3
|
-
import { getCtrAdmin, getCtrClassAdmin, getFacetAdmin, } from '../internal/utils';
|
|
4
|
-
import { setCtr } from './ctr';
|
|
5
|
-
// Registers facetMember as a facet in the facetHost class
|
|
6
|
-
export function facet(facetHost, facetMember, descriptor) {
|
|
7
|
-
var _a;
|
|
8
|
-
if (descriptor === void 0) { descriptor = undefined; }
|
|
9
|
-
var ctrClassAdmin = getCtrClassAdmin(facetHost.constructor);
|
|
10
|
-
ctrClassAdmin.facetMembers = (_a = ctrClassAdmin.facetMembers) !== null && _a !== void 0 ? _a : [];
|
|
11
|
-
ctrClassAdmin.facetMembers.push(facetMember);
|
|
12
|
-
return descriptor;
|
|
13
|
-
}
|
|
1
|
+
import { getCtrAdmin } from '../internal/utils';
|
|
14
2
|
export function facetClassName(facetClass) {
|
|
15
3
|
return facetClass.className ? facetClass.className() : facetClass.name;
|
|
16
4
|
}
|
|
17
|
-
// Registers members of ctr as facets in ctr. Each facet is registered by its class name.
|
|
18
|
-
export function registerFacets(ctr, options) {
|
|
19
|
-
var _a, _b, _c, _d, _e;
|
|
20
|
-
var ctrAdmin = getCtrAdmin(ctr);
|
|
21
|
-
ctrAdmin.logName = (_a = options.name) !== null && _a !== void 0 ? _a : ctr.constructor.name;
|
|
22
|
-
ctrAdmin.facetMembers = (_b = ctrAdmin.facetMembers) !== null && _b !== void 0 ? _b : [];
|
|
23
|
-
ctrAdmin.facetByFacetClassName = (_c = ctrAdmin.facetByFacetClassName) !== null && _c !== void 0 ? _c : {};
|
|
24
|
-
ctrAdmin.ctrStateOverride = options.ctrState;
|
|
25
|
-
((_e = (_d = options.members) !== null && _d !== void 0 ? _d : getCtrClassAdmin(ctr.constructor).facetMembers) !== null && _e !== void 0 ? _e : Object.getOwnPropertyNames(ctr)).forEach(function (member) {
|
|
26
|
-
ctrAdmin.facetMembers.push(member);
|
|
27
|
-
var facet = ctr[member];
|
|
28
|
-
setCtr(facet, ctr);
|
|
29
|
-
var facetAdmin = getFacetAdmin(facet);
|
|
30
|
-
facetAdmin.logName = "".concat(ctrAdmin.logName, "/").concat(facetClassName(facet.constructor));
|
|
31
|
-
var className = facetClassName(facet.constructor);
|
|
32
|
-
if (ctrAdmin.facetByFacetClassName[className] !== undefined) {
|
|
33
|
-
console.error("Two facets of same type ".concat(className, " in container ").concat(ctrAdmin.logName));
|
|
34
|
-
}
|
|
35
|
-
ctrAdmin.facetByFacetClassName[className] = facet;
|
|
36
|
-
});
|
|
37
|
-
if (skandhaOptions.logging) {
|
|
38
|
-
console.log('%c %s ctr initialized', 'color: gray', ctrAdmin.logName, getCtrState(ctr));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
5
|
export function getf(facetClass, ctr) {
|
|
42
6
|
var _a;
|
|
43
7
|
if (!ctr)
|
|
@@ -51,10 +15,3 @@ export function getf(facetClass, ctr) {
|
|
|
51
15
|
}
|
|
52
16
|
return facet;
|
|
53
17
|
}
|
|
54
|
-
export function getm(classMember) {
|
|
55
|
-
var f = function (ctr) { var _a; return getf(classMember[0], (_a = classMember[2]) !== null && _a !== void 0 ? _a : ctr)[classMember[1]]; };
|
|
56
|
-
// Set some properties on f to ease debugging later
|
|
57
|
-
f.facetClassName = facetClassName(classMember[0]);
|
|
58
|
-
f.facetMemberName = classMember[1];
|
|
59
|
-
return f;
|
|
60
|
-
}
|
package/lib/patch.d.ts
CHANGED
|
@@ -1,5 +1 @@
|
|
|
1
|
-
import { ClassMemberT, GetterT } from '..';
|
|
2
1
|
export declare function mapDataToProp(facet: any, prop: string, getter: () => any): void;
|
|
3
|
-
export declare function patchFacet(facet: any, members: any, options?: any): void;
|
|
4
|
-
export declare const mapDataToFacet: ([toFacetClass, toMember, toCtr]: ClassMemberT, getter: GetterT, transform?: Function) => (ctr: any) => void;
|
|
5
|
-
export declare const mapDatasToFacet: ([toFacetClass, toMember, toCtr]: ClassMemberT, getters: Array<GetterT>, transform: Function) => (ctr: any) => void;
|
package/lib/patch.js
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
var __assign = (this && this.__assign) || function () {
|
|
2
|
-
__assign = Object.assign || function(t) {
|
|
3
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
-
s = arguments[i];
|
|
5
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
-
t[p] = s[p];
|
|
7
|
-
}
|
|
8
|
-
return t;
|
|
9
|
-
};
|
|
10
|
-
return __assign.apply(this, arguments);
|
|
11
|
-
};
|
|
12
|
-
import { facetLogName } from '../internal/logging';
|
|
13
|
-
import { getDataMemberNames } from './data';
|
|
14
|
-
import { getf } from './facet';
|
|
15
1
|
export function mapDataToProp(facet, prop, getter) {
|
|
16
2
|
delete facet[prop];
|
|
17
3
|
Object.defineProperty(facet, prop, {
|
|
@@ -20,68 +6,3 @@ export function mapDataToProp(facet, prop, getter) {
|
|
|
20
6
|
configurable: true,
|
|
21
7
|
});
|
|
22
8
|
}
|
|
23
|
-
export function patchFacet(facet, members, options) {
|
|
24
|
-
var _a;
|
|
25
|
-
var dataMemberNames = getDataMemberNames(facet);
|
|
26
|
-
for (var prop in members) {
|
|
27
|
-
if ((_a = options === null || options === void 0 ? void 0 : options.warnIfNotADataMember) !== null && _a !== void 0 ? _a : true) {
|
|
28
|
-
if (!dataMemberNames.includes(prop)) {
|
|
29
|
-
console.error("Patching a property ".concat(prop, " that wasn't decorated with ") +
|
|
30
|
-
"@data, @input or @output in ".concat(facetLogName(facet)));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
delete facet[prop];
|
|
35
|
-
}
|
|
36
|
-
catch (_b) {
|
|
37
|
-
console.error("Could not delete property ".concat(prop, " from facet ").concat(facetLogName(facet)) +
|
|
38
|
-
'. In case you are using mobx (or skandha-mobx), this can happen ' +
|
|
39
|
-
'when makeObservable (or makeCtrObservable) is called before ' +
|
|
40
|
-
'patching a property. Try calling these functions as late as ' +
|
|
41
|
-
'possible.');
|
|
42
|
-
}
|
|
43
|
-
Object.defineProperty(facet, prop, __assign(__assign({}, members[prop]), { enumerable: true, configurable: true }));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
export var mapDataToFacet = function (_a, getter, transform) {
|
|
47
|
-
var toFacetClass = _a[0], toMember = _a[1], toCtr = _a[2];
|
|
48
|
-
return function (ctr) {
|
|
49
|
-
var _a;
|
|
50
|
-
var patch = (_a = {},
|
|
51
|
-
_a[toMember] = {
|
|
52
|
-
get: function () {
|
|
53
|
-
// This context variable is not necessary, it has redundant data and
|
|
54
|
-
// could be inlined, but it makes debugging easier.
|
|
55
|
-
var context = {
|
|
56
|
-
ctr: toCtr !== null && toCtr !== void 0 ? toCtr : ctr,
|
|
57
|
-
getter: getter,
|
|
58
|
-
toClassMember: [toFacetClass, toMember],
|
|
59
|
-
data: getter(ctr),
|
|
60
|
-
};
|
|
61
|
-
return transform ? transform(context.data) : context.data;
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
_a);
|
|
65
|
-
patchFacet(getf(toFacetClass, toCtr !== null && toCtr !== void 0 ? toCtr : ctr), patch);
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
export var mapDatasToFacet = function (_a, getters, transform) {
|
|
69
|
-
var toFacetClass = _a[0], toMember = _a[1], toCtr = _a[2];
|
|
70
|
-
return function (ctr) {
|
|
71
|
-
var _a;
|
|
72
|
-
var patch = (_a = {},
|
|
73
|
-
_a[toMember] = {
|
|
74
|
-
get: function () {
|
|
75
|
-
var context = {
|
|
76
|
-
ctr: toCtr !== null && toCtr !== void 0 ? toCtr : ctr,
|
|
77
|
-
getters: getters,
|
|
78
|
-
toClassMember: [toFacetClass, toMember],
|
|
79
|
-
datas: getters.map(function (getter) { return getter(ctr); }),
|
|
80
|
-
};
|
|
81
|
-
return transform.apply(void 0, context.datas);
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
_a);
|
|
85
|
-
patchFacet(getf(toFacetClass, toCtr !== null && toCtr !== void 0 ? toCtr : ctr), patch);
|
|
86
|
-
};
|
|
87
|
-
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getCtrState } from '../internal/logging';
|
|
2
|
+
import { options as skandhaOptions } from '../internal/options';
|
|
3
|
+
import { getCtrAdmin, getFacetAdmin } from '../internal/utils';
|
|
4
|
+
import { setCtr } from './ctr';
|
|
5
|
+
import { facetClassName } from './facet';
|
|
6
|
+
export function registerCtr(ctr, options) {
|
|
7
|
+
var _a, _b, _c;
|
|
8
|
+
var ctrAdmin = getCtrAdmin(ctr);
|
|
9
|
+
ctrAdmin.logName = options.name;
|
|
10
|
+
ctrAdmin.facetMembers = (_a = ctrAdmin.facetMembers) !== null && _a !== void 0 ? _a : [];
|
|
11
|
+
ctrAdmin.facetByFacetClassName = (_b = ctrAdmin.facetByFacetClassName) !== null && _b !== void 0 ? _b : {};
|
|
12
|
+
ctrAdmin.ctrStateOverride = options.ctrState;
|
|
13
|
+
((_c = options.members) !== null && _c !== void 0 ? _c : Object.getOwnPropertyNames(ctr)).forEach(function (member) {
|
|
14
|
+
var facet = ctr[member];
|
|
15
|
+
setCtr(facet, ctr);
|
|
16
|
+
ctrAdmin.facetMembers.push(member);
|
|
17
|
+
if (!facet.constructor.className) {
|
|
18
|
+
console.error("Facet ".concat(member, " of container ").concat(ctrAdmin.logName, " ") +
|
|
19
|
+
"does not have a static className function");
|
|
20
|
+
}
|
|
21
|
+
var className = facetClassName(facet.constructor);
|
|
22
|
+
getFacetAdmin(facet).logName = "".concat(ctrAdmin.logName, "/").concat(className);
|
|
23
|
+
if (ctrAdmin.facetByFacetClassName[className] !== undefined) {
|
|
24
|
+
console.error("Two facets of same type ".concat(className, " in container ").concat(ctrAdmin.logName));
|
|
25
|
+
}
|
|
26
|
+
ctrAdmin.facetByFacetClassName[className] = facet;
|
|
27
|
+
});
|
|
28
|
+
if (skandhaOptions.logging) {
|
|
29
|
+
console.log('%c %s ctr initialized', 'color: gray', ctrAdmin.logName, getCtrState(ctr));
|
|
30
|
+
}
|
|
31
|
+
}
|