skandha 0.6.0 → 0.7.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 +237 -232
- package/index.d.ts +8 -8
- package/index.js +8 -8
- package/internal/logging.d.ts +2 -1
- package/internal/logging.js +15 -14
- package/internal/symbols.js +1 -1
- package/internal/utils.js +10 -6
- package/lib/ctr.js +1 -1
- package/lib/data.js +1 -1
- package/lib/facet.d.ts +3 -2
- package/lib/facet.js +23 -15
- package/lib/operation.js +4 -4
- package/lib/patch.d.ts +2 -2
- package/lib/patch.js +12 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,25 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Rationale
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Skandha proposes an alternative solution (somewhat inspired by Erlang) that aims to let the programmer re-use existing logic,
|
|
12
|
-
while allowing them to use their own data-structures. This is achieved by mapping the user's data-structures onto a set of
|
|
13
|
-
minimal interfaces called facets. These facets perform tasks by interacting via so-called policies. By keeping facets
|
|
14
|
-
minimal, they can be generic, which leads to better code reuse.
|
|
5
|
+
The goal of SkandhaJS is to provide data containers that have reusable behaviors, such as
|
|
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. When combining behaviors in a container, you need to
|
|
8
|
+
make sure that they interact properly. For example, when selecting an item you may want the item also to
|
|
9
|
+
be highlighted. As we will see, these interactions are achieved by implementing callback functions.
|
|
15
10
|
|
|
16
11
|
---
|
|
17
12
|
|
|
18
13
|
**NOTE**
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
need to disable the mangling of classnames in your minification tool, or disable minification completely. A future version of Skandha will allow you to declare
|
|
22
|
-
facet names by adding a property in the facet class, or specify the names when calling
|
|
23
|
-
`registerFacets`.
|
|
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.
|
|
24
16
|
|
|
25
17
|
---
|
|
26
18
|
|
|
@@ -34,127 +26,166 @@ facet names by adding a property in the facet class, or specify the names when c
|
|
|
34
26
|
## Example code
|
|
35
27
|
|
|
36
28
|
The full example code for this README can be found [here](http://github.com/mnieber/skandha-sample-app). Note that this repository
|
|
37
|
-
contains two version of the example code. The
|
|
29
|
+
contains two version of the example code. The first version creates all the behaviours from scratch. Although it gives a good insight into
|
|
38
30
|
the Skandha principles, the code will look bulky. The second version is reflective of a real use-case as it uses the reusable behaviours
|
|
39
31
|
from [skandha-facets](http://github.com/mnieber/skandha-facets).
|
|
40
32
|
|
|
41
33
|
## Explanation
|
|
42
34
|
|
|
43
|
-
The
|
|
44
|
-
and functions. The example shows how to add reusable selection, highlight and filtering behaviour to a container
|
|
45
|
-
with Todo items.
|
|
46
|
-
|
|
47
|
-
### Containers and Facets
|
|
48
|
-
|
|
49
|
-
A container is a set of related data, e.g. todo items together with data about selection, highlight, etc. A facet is a
|
|
50
|
-
member of a container that represents a single aspect such as `Selection`. In the example below we see a `TodosCtr`
|
|
51
|
-
with five facets: `Selection`, `Highlight`, `Filtering`, `Inputs` and `Outputs`. As we shall see, `Selection`,
|
|
52
|
-
`Highlight` and `Filtering` are generic (not tied to the concept of a Todo) and therefore reusable.
|
|
35
|
+
### The Selection facet
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
data-sources that are outside of the container, but it's easier to reason about the container if they exist.
|
|
58
|
-
|
|
59
|
-
Our initial code is:
|
|
37
|
+
As mentioned above, behaviors in Skandha are implemented as classes that are called facets. Let's start by looking at a reusable
|
|
38
|
+
Selection facet. Before we show how it's implemented, here is an example of using it to select a range of
|
|
39
|
+
items from id `2` to id `5`:
|
|
60
40
|
|
|
61
41
|
```
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
class Highlight<TodoT> {}
|
|
65
|
-
|
|
66
|
-
class Filtering<TodoT> {}
|
|
67
|
-
|
|
68
|
-
class Inputs {
|
|
69
|
-
@data todoById: TodoByIdT = {};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
class Outputs {
|
|
73
|
-
@data filteredTodoById: TodoByIdT = {};
|
|
74
|
-
}
|
|
42
|
+
const ctr = createContainer();
|
|
43
|
+
ctr.selection.selectableIds = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
|
75
44
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@facet selection: Selection<TodoT> = new Selection<TodoT>();
|
|
80
|
-
@facet highlight: Highlight<TodoT> = new Highlight<TodoT>();
|
|
81
|
-
@facet filtering: Filtering<TodoT> = new Filtering<TodoT>();
|
|
82
|
-
}
|
|
45
|
+
ctr.selection.selectItem({itemId: "2"});
|
|
46
|
+
ctr.selection.selectItem({itemId: "5", isShift: true});
|
|
47
|
+
console.log(ctr.selection.ids);
|
|
83
48
|
```
|
|
84
49
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- The `@data` and `@operation` decorators mark the fields in the facet that make up its abstract interface.
|
|
88
|
-
Clients should use only these fields.
|
|
89
|
-
|
|
90
|
-
### Implementing the Selection facet
|
|
91
|
-
|
|
92
|
-
We will first implement the Selection facet. The key requirements are that:
|
|
50
|
+
Note that the key function call here is `selection.selectItem`. Let's look at how it's implemented. First of all, we need a type that holds the selection parameters:
|
|
93
51
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
52
|
+
```
|
|
53
|
+
export type SelectionParamsT = {
|
|
54
|
+
itemId: string | undefined;
|
|
55
|
+
isShift?: boolean;
|
|
56
|
+
isCtrl?: boolean;
|
|
57
|
+
};
|
|
58
|
+
```
|
|
100
59
|
|
|
101
|
-
|
|
60
|
+
The next step is less obvious: we define a helper class that declares the arguments and callbacks of
|
|
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:
|
|
102
68
|
|
|
103
69
|
```
|
|
104
|
-
export class
|
|
70
|
+
export class Selection_selectItem extends Cbs {
|
|
105
71
|
selectionParams: SelectionParamsT = stub();
|
|
106
|
-
|
|
72
|
+
select() {}
|
|
107
73
|
}
|
|
74
|
+
```
|
|
75
|
+
|
|
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.
|
|
108
78
|
|
|
109
|
-
|
|
79
|
+
```
|
|
80
|
+
class Selection<ValueT> {
|
|
110
81
|
@data selectableIds: Array<string> = stub();
|
|
111
82
|
@data ids: Array<string> = [];
|
|
112
83
|
@data anchorId?: string;
|
|
113
|
-
@data items?: Array<ValueT>;
|
|
84
|
+
@data items?: Array<ValueT>; // ignore this field for now...
|
|
114
85
|
|
|
115
86
|
@operation @host selectItem(selectionParams: SelectionParamsT) {
|
|
116
|
-
return (cbs:
|
|
87
|
+
return (cbs: Selection_selectItem) => {
|
|
117
88
|
if (!this.selectableIds.includes(selectionParams.itemId)) {
|
|
118
89
|
throw Error(`Invalid id: ${selectionParams.itemId}`);
|
|
119
90
|
}
|
|
120
|
-
cbs.
|
|
91
|
+
cbs.select();
|
|
121
92
|
}
|
|
122
93
|
}
|
|
123
94
|
}
|
|
124
95
|
```
|
|
125
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
|
+
const createContainer() {
|
|
103
|
+
const ctr = {
|
|
104
|
+
selection: new Selection()
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
setCallbacks(ctr.selection, {
|
|
108
|
+
selectItem: {
|
|
109
|
+
select(this: Selection_selectItem): {
|
|
110
|
+
handleSelectItem(ctr.selection, this.selectionParams);
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return ctr;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
126
119
|
Notes:
|
|
127
120
|
|
|
128
|
-
- The `@host` decorator comes from the [Aspiration](http://github.com/mnieber/aspiration) library.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
- The `stub` function is used in callback objects to indicate that although the field is initialized with `undefined`
|
|
133
|
-
it will receive a value later.
|
|
121
|
+
- The `@host` decorator comes from the [Aspiration](http://github.com/mnieber/aspiration) library.
|
|
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.
|
|
123
|
+
|
|
124
|
+
## Combining Selection with Highlight
|
|
134
125
|
|
|
135
|
-
|
|
126
|
+
So far, we have shown a rather indirect implementation of selection. This indirect approach becomes useful
|
|
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.
|
|
136
131
|
|
|
137
|
-
|
|
138
|
-
show the code. For brevity, we will leave out the code for the callback objects such as
|
|
139
|
-
`Highlight_highlightItem` (they are straightforward and not that interesting):
|
|
132
|
+
Let's start by looking at the code of the `Highlight` facet:
|
|
140
133
|
|
|
141
134
|
```
|
|
135
|
+
export class Highlight_highlightItem extends Cbs {
|
|
136
|
+
id: string = stub();
|
|
137
|
+
scrollItemIntoView() {}
|
|
138
|
+
}
|
|
139
|
+
|
|
142
140
|
export class Highlight<ValueT = any> {
|
|
143
141
|
@data id: string | undefined;
|
|
144
|
-
@data item?: ValueT;
|
|
142
|
+
@data item?: ValueT; // ignore this field for now...
|
|
145
143
|
|
|
146
144
|
@operation @host highlightItem(id: string) {
|
|
147
145
|
return (cbs: Highlight_highlightItem) => {
|
|
148
146
|
this.id = id;
|
|
147
|
+
maybe(cbs.scrollItemIntoView).bind(cbs)();
|
|
149
148
|
};
|
|
150
149
|
}
|
|
151
150
|
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
We can now update `createContainer` with the `Highlight` facet:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
const createContainer() {
|
|
157
|
+
const ctr = {
|
|
158
|
+
selection: new Selection(),
|
|
159
|
+
highlight: new Highlight()
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
setCallbacks(ctr.selection, {
|
|
163
|
+
selectItem: {
|
|
164
|
+
select(this: Selection_selectItem): {
|
|
165
|
+
handleSelectItem(ctr.selection, this.selectionParams);
|
|
166
|
+
if (!this.selectionParams.isShift && !this.selectionParams.isCtrl) {
|
|
167
|
+
ctr.highlight.highlightItem(this.selectionParams.itemId);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return ctr;
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Adding the Filtering facet
|
|
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.
|
|
152
182
|
|
|
183
|
+
```
|
|
153
184
|
export class Filtering<ValueT = any> {
|
|
154
185
|
@data isEnabled: boolean = false;
|
|
155
|
-
@data filter: FilterT = () => [];
|
|
156
|
-
|
|
186
|
+
@data filter: FilterT = (ValueT[]) => [];
|
|
157
187
|
@data inputItems?: Array<ValueT>;
|
|
188
|
+
|
|
158
189
|
@data get filteredItems() {
|
|
159
190
|
return this.isEnabled ? this.filter(this.inputItems) : this.inputItems;
|
|
160
191
|
}
|
|
@@ -172,172 +203,150 @@ export class Filtering<ValueT = any> {
|
|
|
172
203
|
};
|
|
173
204
|
}
|
|
174
205
|
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Implementing the callback objects
|
|
178
|
-
|
|
179
|
-
We've set up the basic structure of the facets, but the callbacks still have to be implemented.
|
|
180
|
-
This step happens in the container class. We will use the following requirements:
|
|
181
|
-
|
|
182
|
-
- the `cbs.selectItem` callback should make a selection that takes the shift and control keys into account.
|
|
183
|
-
We will not show the code (you can see it
|
|
184
|
-
[here](https://github.com/mnieber/skandha-facets/blob/main/Selection.ts)) but assume we have a library
|
|
185
|
-
function `handleSelectItem` for this.
|
|
186
|
-
- items that are selected should become highlighted. We will introduce a reusable policy
|
|
187
|
-
function for this called `highlightFollowsSelection`.
|
|
188
|
-
- if applying the filter hides the highlighted item then we want to correct the highlight.
|
|
189
|
-
We will use a another reusable policy called `highlightIsCorrectedOnFilterChange` (shown
|
|
190
|
-
[here](https://github.com/mnieber/skandha-facets/blob/main/policies/highlightIsCorrectedOnFilterChange.ts))
|
|
191
206
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
handleSelectItem(ctr.selection, this.selectionParams);
|
|
208
|
-
highlightFollowsSelection(ctr.selection, this.selectionParams); // See below
|
|
209
|
-
},
|
|
210
|
-
}
|
|
211
|
-
});
|
|
207
|
+
const createContainer() {
|
|
208
|
+
const ctr = {
|
|
209
|
+
selection: new Selection(),
|
|
210
|
+
highlight: new Highlight(),
|
|
211
|
+
filtering: new Filtering()
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
setCallbacks(ctr.selection, {
|
|
215
|
+
selectItem: {
|
|
216
|
+
select(this: Selection_selectItem): {
|
|
217
|
+
handleSelectItem(ctr.selection, this.selectionParams);
|
|
218
|
+
highlightFollowsSelection(ctr, this.selectionParams);
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
});
|
|
212
222
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
},
|
|
223
|
+
setCallbacks(filtering, {
|
|
224
|
+
apply: {
|
|
225
|
+
exit() {
|
|
226
|
+
highlightIsCorrectedOnFilterChange(ctr);
|
|
218
227
|
},
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
228
|
+
},
|
|
229
|
+
});
|
|
222
230
|
|
|
223
|
-
|
|
224
|
-
selection: Selection,
|
|
225
|
-
selectionParams: SelectionParamsT
|
|
226
|
-
) {
|
|
227
|
-
const {isCtrl, isShift, itemId} = selectionParams;
|
|
228
|
-
const ctr = getc(selection);
|
|
229
|
-
if (!isCtrl && !isShift) {
|
|
230
|
-
getf(Highlight, ctr).highlightItem(itemId);
|
|
231
|
-
}
|
|
231
|
+
return ctr;
|
|
232
232
|
}
|
|
233
233
|
```
|
|
234
234
|
|
|
235
235
|
Notes:
|
|
236
236
|
|
|
237
|
-
-
|
|
238
|
-
|
|
239
|
-
|
|
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
240
|
|
|
241
241
|
### Mapping data onto the container, and between facets
|
|
242
242
|
|
|
243
243
|
At this point we have a container with facets that perform useful operations, and that interact
|
|
244
|
-
to achieve interesting behaviours. However,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
+
};
|
|
248
256
|
|
|
257
|
+
ctr['outputs'] = {
|
|
258
|
+
filteredTodoById: {[id: string]: TodoT} = {};
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
registerFacets(ctr, { name: 'TodosCtr' });
|
|
262
|
+
return ctr;
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Note the call to `registerFacets`, which is needed for the data mapping functions that we are about to introduce.
|
|
249
266
|
As a first step, we need to connect `Filtering.inputItems` to `Inputs.todoById` and `Outputs.filteredTodoById`.
|
|
250
267
|
We can think of this as setting up a pipeline, where one of more source facet members are mapped onto a destination
|
|
251
268
|
facet member:
|
|
252
269
|
|
|
253
270
|
```
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
)(this);
|
|
268
|
-
}
|
|
271
|
+
mapDataToFacet(
|
|
272
|
+
['filtering', 'inputItems'],
|
|
273
|
+
getm(['inputs', 'todoById']),
|
|
274
|
+
(x: TodoByIdT) => Object.values(x)
|
|
275
|
+
)(ctr);
|
|
276
|
+
|
|
277
|
+
const listToItemById = items => items.reduce((acc: any, x: TodoT) => ({ ...acc, [x.id]: x }), {})
|
|
278
|
+
|
|
279
|
+
mapDataToFacet(
|
|
280
|
+
['outputs', 'filteredTodoById'],
|
|
281
|
+
getm(['filtering', 'filteredItems']),
|
|
282
|
+
listToItemById,
|
|
283
|
+
)(ctr);
|
|
269
284
|
```
|
|
270
285
|
|
|
271
286
|
Notes:
|
|
272
287
|
|
|
273
|
-
- the first argument to `mapDataToFacet` is a
|
|
274
|
-
|
|
275
|
-
- the second argument to `mapDataToFacet` is
|
|
276
|
-
|
|
277
|
-
getter function.
|
|
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`.
|
|
278
292
|
- the third argument is a transformation that is applied to the result of the getter function. In this case,
|
|
279
|
-
it means that `get inputItems()` returns `Object.values(getm(Inputs, 'todoById')(ctr))
|
|
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']`.
|
|
280
297
|
|
|
281
298
|
As the next step, we will connect the list of filtered items to `Selection.selectableIds` (so that selection
|
|
282
299
|
happens in the filtered list). In addition, we will add datamappings for `Selection.items`
|
|
283
|
-
and `
|
|
300
|
+
and `Highlight.item` (these data mappings allow us to not only access the ids, but also the items themselves):
|
|
284
301
|
|
|
285
302
|
```
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
],
|
|
310
|
-
(id: string, todoById: TodoByIdT) => todoById[id]
|
|
311
|
-
)(this);
|
|
312
|
-
}
|
|
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);
|
|
313
326
|
```
|
|
314
327
|
|
|
315
328
|
We are done: the facets in the container are connected. Note that if we put these mapping policies in a reusable
|
|
316
|
-
library then we can shorten
|
|
329
|
+
library then we can shorten the code to the following:
|
|
317
330
|
|
|
318
331
|
```
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
]
|
|
338
|
-
|
|
339
|
-
installPolicies<TodosCtr>(policies, this);
|
|
340
|
-
}
|
|
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
|
+
|
|
341
|
+
// highlighting
|
|
342
|
+
highlightUsesItemLookUpTable(inputTodoById),
|
|
343
|
+
|
|
344
|
+
// filtering
|
|
345
|
+
filteringUsesInputItems(inputTodoById, Object.values),
|
|
346
|
+
mapDataToFacet(filteredTodoById, getm(filteredItems), listToItemById),
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
installPolicies<TodosCtr>(policies, ctr);
|
|
341
350
|
```
|
|
342
351
|
|
|
343
352
|
### Logging
|
|
@@ -355,25 +364,21 @@ used to from Redux:
|
|
|
355
364
|
|
|
356
365
|
If you want to render the facets with React then we need to let React know when data changes.
|
|
357
366
|
The [skandha-mobx](http://github.com/mnieber/skandha-mobx) library was created for this purpose.
|
|
358
|
-
It turns each `@data` member into a Mobx property that is
|
|
359
|
-
|
|
360
|
-
in the container:
|
|
367
|
+
It has a `registerCtr` function that turns each `@data` member into a Mobx property that is
|
|
368
|
+
either observable or computed (this function call replaces the call to `registerFacets`):
|
|
361
369
|
|
|
362
370
|
```
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
this._applyPolicies();
|
|
370
|
-
|
|
371
|
-
// Turn all facets in the container into observable objects.
|
|
372
|
-
// Members annotated with @data will become either @observable or @computed.
|
|
373
|
-
makeCtrObservable(this);
|
|
374
|
-
}
|
|
371
|
+
registerCtr({
|
|
372
|
+
ctr: ctr,
|
|
373
|
+
details: {
|
|
374
|
+
name: 'TodosCtr',
|
|
375
|
+
},
|
|
376
|
+
});
|
|
375
377
|
```
|
|
376
378
|
|
|
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
|
+
|
|
377
382
|
## Conclusion
|
|
378
383
|
|
|
379
384
|
SkandhaJS introduces a lot of new patterns and ideas (that are of course not in fact new) to allow you to
|
package/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
1
|
+
export { getCtrState } from './internal/logging';
|
|
2
|
+
export { getOptions, setOptions } from './internal/options';
|
|
3
|
+
export { getc, getFacetMemberNames } from './lib/ctr';
|
|
4
|
+
export { data, getDataMemberNames, getLoggedMemberNames, input, log, output, } from './lib/data';
|
|
5
|
+
export { facet, getf, getm, registerFacets } from './lib/facet';
|
|
6
|
+
export { installPolicies } from './lib/install';
|
|
7
|
+
export { getOperationMemberNames, operation } from './lib/operation';
|
|
8
|
+
export { mapDatasToFacet, mapDataToFacet, mapDataToProp, patchFacet, } from './lib/patch';
|
|
9
9
|
export declare type ClassT = any;
|
|
10
10
|
export declare type MemberNameT = string;
|
|
11
11
|
export declare type ClassMemberT = [ClassT, MemberNameT, any?];
|
package/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
1
|
+
export { getCtrState } from './internal/logging';
|
|
2
|
+
export { getOptions, setOptions } from './internal/options';
|
|
3
|
+
export { getc, getFacetMemberNames } from './lib/ctr';
|
|
4
|
+
export { data, getDataMemberNames, getLoggedMemberNames, input, log, output, } from './lib/data';
|
|
5
|
+
export { facet, getf, getm, registerFacets } from './lib/facet';
|
|
6
|
+
export { installPolicies } from './lib/install';
|
|
7
|
+
export { getOperationMemberNames, operation } from './lib/operation';
|
|
8
|
+
export { mapDatasToFacet, mapDataToFacet, mapDataToProp, patchFacet, } from './lib/patch';
|
package/internal/logging.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export declare function _defaultFacetLogName(facet: any): string;
|
|
|
2
2
|
export declare function facetLogName(facet: any): any;
|
|
3
3
|
export declare function log(facet: any, operationMember: any, args: any, start: any): void;
|
|
4
4
|
export declare function facetState(facet: any): {};
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function defaultGetCtrState(ctr: any): any;
|
|
6
|
+
export declare function getCtrState(ctr: any): any;
|
package/internal/logging.js
CHANGED
|
@@ -9,13 +9,13 @@ var __assign = (this && this.__assign) || function () {
|
|
|
9
9
|
};
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { options } from
|
|
12
|
+
import { getCtrAdmin, getFacetAdmin } from '../internal/utils';
|
|
13
|
+
import { getc, getFacetMemberNames } from '../lib/ctr';
|
|
14
|
+
import { getLoggedMemberNames } from '../lib/data';
|
|
15
|
+
import { options } from './options';
|
|
16
16
|
export function _defaultFacetLogName(facet) {
|
|
17
17
|
var ctr = getc(facet);
|
|
18
|
-
var prefix = ctr ? ctr.constructor.name +
|
|
18
|
+
var prefix = ctr ? ctr.constructor.name + '/' : '';
|
|
19
19
|
return prefix + facet.constructor.name;
|
|
20
20
|
}
|
|
21
21
|
export function facetLogName(facet) {
|
|
@@ -25,26 +25,23 @@ export function facetLogName(facet) {
|
|
|
25
25
|
function camelToSnake(string) {
|
|
26
26
|
return string
|
|
27
27
|
.replace(/[\w]([A-Z])/g, function (m) {
|
|
28
|
-
return m[0] +
|
|
28
|
+
return m[0] + '_' + m[1];
|
|
29
29
|
})
|
|
30
30
|
.toLowerCase();
|
|
31
31
|
}
|
|
32
32
|
var opName = function (operationMember) { return camelToSnake(operationMember).toUpperCase(); };
|
|
33
33
|
export function log(facet, operationMember, args, start) {
|
|
34
|
-
var _a;
|
|
35
34
|
var ctr = getc(facet);
|
|
36
|
-
var ctrAdmin = ctr ? getCtrAdmin(ctr) : undefined;
|
|
37
|
-
var getCtrState = (_a = ctrAdmin === null || ctrAdmin === void 0 ? void 0 : ctrAdmin.ctrStateOverride) !== null && _a !== void 0 ? _a : ctrState;
|
|
38
35
|
var getState = ctr ? function () { return getCtrState(ctr); } : function () { return facetState(facet); };
|
|
39
36
|
var operationName = opName(operationMember);
|
|
40
|
-
var label = facetLogName(facet) +
|
|
37
|
+
var label = facetLogName(facet) + '.' + operationName;
|
|
41
38
|
if (start) {
|
|
42
39
|
console.group(label);
|
|
43
|
-
console.log(
|
|
44
|
-
console.log(
|
|
40
|
+
console.log('%c args: ', 'color: gray', args);
|
|
41
|
+
console.log('%c state', 'color: gray', getState());
|
|
45
42
|
}
|
|
46
43
|
else {
|
|
47
|
-
console.log(
|
|
44
|
+
console.log('%c next', 'color: gray', getState());
|
|
48
45
|
// @ts-ignore
|
|
49
46
|
console.groupEnd(label);
|
|
50
47
|
}
|
|
@@ -61,7 +58,7 @@ export function facetState(facet) {
|
|
|
61
58
|
}
|
|
62
59
|
}, {});
|
|
63
60
|
}
|
|
64
|
-
export function
|
|
61
|
+
export function defaultGetCtrState(ctr) {
|
|
65
62
|
if (ctr) {
|
|
66
63
|
return getFacetMemberNames(ctr).reduce(function (acc, facetMemberName) {
|
|
67
64
|
var _a;
|
|
@@ -70,3 +67,7 @@ export function ctrState(ctr) {
|
|
|
70
67
|
}, {});
|
|
71
68
|
}
|
|
72
69
|
}
|
|
70
|
+
export function getCtrState(ctr) {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
return ((_b = (_a = getCtrAdmin(ctr)) === null || _a === void 0 ? void 0 : _a.ctrStateOverride) !== null && _b !== void 0 ? _b : defaultGetCtrState)(ctr);
|
|
73
|
+
}
|
package/internal/symbols.js
CHANGED
package/internal/utils.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
|
|
2
|
-
for (var i = 0,
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
2
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
3
|
+
if (ar || !(i in from)) {
|
|
4
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
5
|
+
ar[i] = from[i];
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
5
9
|
};
|
|
6
|
-
import { symbols } from
|
|
10
|
+
import { symbols } from './symbols';
|
|
7
11
|
export function getOrCreate(obj, key, fn) {
|
|
8
12
|
if (!obj[key]) {
|
|
9
13
|
obj[key] = fn();
|
|
@@ -16,7 +20,7 @@ export var zip = function (arr) {
|
|
|
16
20
|
arrs[_i - 1] = arguments[_i];
|
|
17
21
|
}
|
|
18
22
|
return arr.map(function (val, i) {
|
|
19
|
-
return arrs.reduce(function (a, arr) { return __spreadArray(__spreadArray([], a), [arr[i]]); }, [val]);
|
|
23
|
+
return arrs.reduce(function (a, arr) { return __spreadArray(__spreadArray([], a, true), [arr[i]], false); }, [val]);
|
|
20
24
|
});
|
|
21
25
|
};
|
|
22
26
|
export function getCtrClassAdmin(ctrClass) {
|
package/lib/ctr.js
CHANGED
package/lib/data.js
CHANGED
package/lib/facet.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ClassT, GetterT
|
|
1
|
+
import { ClassMemberT, ClassT, GetterT } from '..';
|
|
2
2
|
export declare function facet(facetHost: any, facetMember: any, descriptor?: undefined): undefined;
|
|
3
|
+
export declare function facetClassName(facetClass: ClassT): any;
|
|
3
4
|
export declare function registerFacets(ctr: any, options: {
|
|
4
5
|
name?: string;
|
|
5
6
|
members?: string[];
|
|
6
7
|
ctrState?: Function;
|
|
7
8
|
}): void;
|
|
8
|
-
export declare function getf(facetClass: ClassT, ctr?: any): any;
|
|
9
|
+
export declare function getf(facetClass: ClassT | string, ctr?: any): any;
|
|
9
10
|
export declare function getm<T = any>(classMember: ClassMemberT): GetterT<T>;
|
package/lib/facet.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { getCtrState } from '../internal/logging';
|
|
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
|
|
5
6
|
export function facet(facetHost, facetMember, descriptor) {
|
|
6
7
|
var _a;
|
|
7
8
|
if (descriptor === void 0) { descriptor = undefined; }
|
|
@@ -10,10 +11,14 @@ export function facet(facetHost, facetMember, descriptor) {
|
|
|
10
11
|
ctrClassAdmin.facetMembers.push(facetMember);
|
|
11
12
|
return descriptor;
|
|
12
13
|
}
|
|
14
|
+
export function facetClassName(facetClass) {
|
|
15
|
+
return facetClass.className ? facetClass.className() : facetClass.name;
|
|
16
|
+
}
|
|
17
|
+
// Registers members of ctr as facets in ctr. Each facet is registered by its class name.
|
|
13
18
|
export function registerFacets(ctr, options) {
|
|
14
19
|
var _a, _b, _c, _d, _e;
|
|
15
|
-
var ctrName = (_a = options.name) !== null && _a !== void 0 ? _a : ctr.constructor.name;
|
|
16
20
|
var ctrAdmin = getCtrAdmin(ctr);
|
|
21
|
+
ctrAdmin.logName = (_a = options.name) !== null && _a !== void 0 ? _a : ctr.constructor.name;
|
|
17
22
|
ctrAdmin.facetMembers = (_b = ctrAdmin.facetMembers) !== null && _b !== void 0 ? _b : [];
|
|
18
23
|
ctrAdmin.facetByFacetClassName = (_c = ctrAdmin.facetByFacetClassName) !== null && _c !== void 0 ? _c : {};
|
|
19
24
|
ctrAdmin.ctrStateOverride = options.ctrState;
|
|
@@ -22,31 +27,34 @@ export function registerFacets(ctr, options) {
|
|
|
22
27
|
var facet = ctr[member];
|
|
23
28
|
setCtr(facet, ctr);
|
|
24
29
|
var facetAdmin = getFacetAdmin(facet);
|
|
25
|
-
facetAdmin.logName =
|
|
26
|
-
var className = facet.constructor
|
|
30
|
+
facetAdmin.logName = "".concat(ctrAdmin.logName, "/").concat(facetClassName(facet.constructor));
|
|
31
|
+
var className = facetClassName(facet.constructor);
|
|
27
32
|
if (ctrAdmin.facetByFacetClassName[className] !== undefined) {
|
|
28
|
-
console.error("Two facets of same type "
|
|
33
|
+
console.error("Two facets of same type ".concat(className, " in container ").concat(ctrAdmin.logName));
|
|
29
34
|
}
|
|
30
35
|
ctrAdmin.facetByFacetClassName[className] = facet;
|
|
31
|
-
if (skandhaOptions.logging) {
|
|
32
|
-
console.log("%c Ctr initialized", "color: gray", getCtrState(ctr));
|
|
33
|
-
}
|
|
34
36
|
});
|
|
37
|
+
if (skandhaOptions.logging) {
|
|
38
|
+
console.log('%c %s ctr initialized', 'color: gray', ctrAdmin.logName, getCtrState(ctr));
|
|
39
|
+
}
|
|
35
40
|
}
|
|
36
41
|
export function getf(facetClass, ctr) {
|
|
37
42
|
var _a;
|
|
38
43
|
if (!ctr)
|
|
39
44
|
return function (ctr) { return getf(facetClass, ctr); };
|
|
40
|
-
var facet =
|
|
45
|
+
var facet = typeof facetClass === 'string'
|
|
46
|
+
? ctr[facetClass]
|
|
47
|
+
: (_a = getCtrAdmin(ctr).facetByFacetClassName) === null || _a === void 0 ? void 0 : _a[facetClassName(facetClass)];
|
|
41
48
|
if (!facet) {
|
|
42
|
-
|
|
49
|
+
var facetName = facetClass === 'string' ? facetClass : facetClassName(facetClass);
|
|
50
|
+
console.error("No facet ".concat(facetName, " in container ").concat(getCtrAdmin(ctr).logName));
|
|
43
51
|
}
|
|
44
52
|
return facet;
|
|
45
53
|
}
|
|
46
54
|
export function getm(classMember) {
|
|
47
55
|
var f = function (ctr) { var _a; return getf(classMember[0], (_a = classMember[2]) !== null && _a !== void 0 ? _a : ctr)[classMember[1]]; };
|
|
48
|
-
// Set some properties on f to
|
|
49
|
-
f.facetClassName = classMember[0]
|
|
56
|
+
// Set some properties on f to ease debugging later
|
|
57
|
+
f.facetClassName = facetClassName(classMember[0]);
|
|
50
58
|
f.facetMemberName = classMember[1];
|
|
51
59
|
return f;
|
|
52
60
|
}
|
package/lib/operation.js
CHANGED
|
@@ -34,9 +34,9 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
34
34
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import { getFacetClassAdmin } from
|
|
37
|
+
import { log } from '../internal/logging';
|
|
38
|
+
import { options } from '../internal/options';
|
|
39
|
+
import { getFacetClassAdmin } from '../internal/utils';
|
|
40
40
|
function _operation(target, propertyName, descriptor, punctualOptions) {
|
|
41
41
|
var _a;
|
|
42
42
|
var f = descriptor.value;
|
|
@@ -44,7 +44,7 @@ function _operation(target, propertyName, descriptor, punctualOptions) {
|
|
|
44
44
|
facetClassAdmin.operationMemberNames =
|
|
45
45
|
(_a = facetClassAdmin.operationMemberNames) !== null && _a !== void 0 ? _a : [];
|
|
46
46
|
facetClassAdmin.operationMemberNames.push(propertyName);
|
|
47
|
-
if (typeof descriptor.value ===
|
|
47
|
+
if (typeof descriptor.value === 'function') {
|
|
48
48
|
descriptor.value = !!punctualOptions.async
|
|
49
49
|
? function () {
|
|
50
50
|
var _a;
|
package/lib/patch.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClassMemberT, GetterT } from '..';
|
|
2
2
|
export declare function mapDataToProp(facet: any, prop: string, getter: () => any): void;
|
|
3
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
|
|
4
|
+
export declare const mapDataToFacet: ([toFacetClass, toMember, toCtr]: ClassMemberT, getter: GetterT, transform?: Function) => (ctr: any) => void;
|
|
5
5
|
export declare const mapDatasToFacet: ([toFacetClass, toMember, toCtr]: ClassMemberT, getters: Array<GetterT>, transform: Function) => (ctr: any) => void;
|
package/lib/patch.js
CHANGED
|
@@ -9,9 +9,9 @@ var __assign = (this && this.__assign) || function () {
|
|
|
9
9
|
};
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { facetLogName } from '../internal/logging';
|
|
13
|
+
import { getDataMemberNames } from './data';
|
|
14
|
+
import { getf } from './facet';
|
|
15
15
|
export function mapDataToProp(facet, prop, getter) {
|
|
16
16
|
delete facet[prop];
|
|
17
17
|
Object.defineProperty(facet, prop, {
|
|
@@ -26,19 +26,19 @@ export function patchFacet(facet, members, options) {
|
|
|
26
26
|
for (var prop in members) {
|
|
27
27
|
if ((_a = options === null || options === void 0 ? void 0 : options.warnIfNotADataMember) !== null && _a !== void 0 ? _a : true) {
|
|
28
28
|
if (!dataMemberNames.includes(prop)) {
|
|
29
|
-
console.error("Patching a property "
|
|
30
|
-
|
|
29
|
+
console.error("Patching a property ".concat(prop, " that wasn't decorated with ") +
|
|
30
|
+
"@data, @input or @output in ".concat(facetLogName(facet)));
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
try {
|
|
34
34
|
delete facet[prop];
|
|
35
35
|
}
|
|
36
36
|
catch (_b) {
|
|
37
|
-
console.error("Could not delete property "
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
42
|
}
|
|
43
43
|
Object.defineProperty(facet, prop, __assign(__assign({}, members[prop]), { enumerable: true, configurable: true }));
|
|
44
44
|
}
|
|
@@ -50,6 +50,8 @@ export var mapDataToFacet = function (_a, getter, transform) {
|
|
|
50
50
|
var patch = (_a = {},
|
|
51
51
|
_a[toMember] = {
|
|
52
52
|
get: function () {
|
|
53
|
+
// This context variable is not necessary, it has redundant data and
|
|
54
|
+
// could be inlined, but it makes debugging easier.
|
|
53
55
|
var context = {
|
|
54
56
|
ctr: toCtr !== null && toCtr !== void 0 ? toCtr : ctr,
|
|
55
57
|
getter: getter,
|