skandha 0.10.2 → 0.12.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 +59 -74
- package/index.d.ts +5 -4
- package/index.js +4 -3
- package/internal/logging.js +18 -31
- package/internal/options.d.ts +0 -3
- package/internal/options.js +4 -6
- package/internal/symbols.js +1 -1
- package/internal/utils.js +53 -20
- package/lib/cleanUp.d.ts +2 -0
- package/lib/cleanUp.js +14 -0
- package/lib/data.js +6 -8
- package/lib/facet.js +4 -4
- package/lib/install.js +1 -1
- package/lib/operation.js +17 -64
- package/lib/patch.d.ts +7 -2
- package/lib/patch.js +16 -11
- package/lib/registerCtr.d.ts +1 -1
- package/lib/registerCtr.js +8 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,22 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Every facet provides an interface that supports a particular behaviour. This interface is used throughout the application, and ensures that the basic mechanism (e.g. for doing selection) is the same everywhere. The details of the behaviour depend on callback functions that the programmer provides:
|
|
8
|
-
|
|
9
|
-
1. Callbacks decide what exactly happens when an item is selected, highlighted, deleted etc.
|
|
10
|
-
2. Callbacks implement interactions. For example: when selecting an item, also higlight it.
|
|
11
|
-
|
|
12
|
-
The second point is important for limiting the mixing of concerns that happens when you are combining multiple behaviours. This mixing of concerns is unavoidable, but should be limited to specific places that can be easily inspected.
|
|
5
|
+
SkandhaJS is a library that provides data containers with reusable behaviors, or "facets," that are implemented as classes. Examples of facets are selection, highlighting, filtering, drag-and-drop, etc. These facets provide interfaces for specific behaviors and rely on callback functions provided by the programmer to specify the details of those behaviors. The goal of SkandhaJS is to provide a way to create interactions between different behaviors in a consistent and modular way.
|
|
13
6
|
|
|
14
7
|
As a brief illustration, consider the example of selecting a todo in a list of todos:
|
|
15
8
|
|
|
16
|
-
1. The application
|
|
9
|
+
1. The application has a local state with a list of todos. The state contains a `todoSelection` facet so that clients can make a selection.
|
|
17
10
|
2. When the user clicks on a todo, the todo-list component calls `todoSelection.selectItem(todo.id, shift, ctrl)`.
|
|
18
|
-
3. The `
|
|
19
|
-
4.
|
|
20
|
-
5. The `select` callback performs the selection and a side-effect: it highlights the item as well.
|
|
11
|
+
3. The `todoSelection.selectItem` function runs the `select` function from its `callbackMap`.
|
|
12
|
+
4. The `select` callback performs the selection and a side-effect: it highlights the item as well.
|
|
21
13
|
|
|
22
14
|
## Benefits
|
|
23
15
|
|
|
@@ -25,7 +17,7 @@ There are three ways in which facets help you to write generic code:
|
|
|
25
17
|
|
|
26
18
|
1. They can be reused in any container, regardless of what data-type is stored in the container. This means that selection, filtering and drag-and-drop work the same everywhere in the application.
|
|
27
19
|
2. They allow you to capture the interaction between behaviours in reusable functions.
|
|
28
|
-
3. They allow you to handle user-interaction in UI components in a uniform way. All UI components that receive a selection facet will use the same interface to make a selection (and in theory the component can remain agnostic of the
|
|
20
|
+
3. They allow you to handle user-interaction in UI components in a uniform way. All UI components that receive a selection facet will use the same interface to make a selection (and in theory the component can remain agnostic of how selection works, or even of the type of items that are being selected).
|
|
29
21
|
|
|
30
22
|
## Links
|
|
31
23
|
|
|
@@ -57,8 +49,7 @@ In Skandha, a behaviour such as selection is implemented in a "facet" class. The
|
|
|
57
49
|
```
|
|
58
50
|
// file: Selection.ts
|
|
59
51
|
import { input, output, operation } from 'skandha';
|
|
60
|
-
import {
|
|
61
|
-
import { Selection_selectItem, SelectionParamsT } from 'SelectionCbs';
|
|
52
|
+
import { DefineCbs, getCallbacks, withCbs, stub } from 'aspiration';
|
|
62
53
|
|
|
63
54
|
export class Selection<ValueT = any> {
|
|
64
55
|
static className = () => 'Selection';
|
|
@@ -68,30 +59,24 @@ export class Selection<ValueT = any> {
|
|
|
68
59
|
@output anchorId?: string;
|
|
69
60
|
@output items: Array<ValueT> = stub();
|
|
70
61
|
|
|
71
|
-
@operation @
|
|
72
|
-
|
|
73
|
-
) {
|
|
74
|
-
const cbs = getCallbacks<Selection_selectItem>(this);
|
|
62
|
+
@operation @withCbs() selectItem(args: SelectionParamsT) {
|
|
63
|
+
const cbs = getCallbacks(this) as SelectionCbs['selectItem'];
|
|
75
64
|
cbs.select();
|
|
76
65
|
}
|
|
77
66
|
}
|
|
78
|
-
```
|
|
79
67
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
68
|
+
type Cbs<T> = {
|
|
69
|
+
selectItem: {
|
|
70
|
+
selectItem(): void;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
85
73
|
|
|
86
|
-
export
|
|
87
|
-
selectionParams: SelectionParamsT = stub();
|
|
88
|
-
select() {}
|
|
89
|
-
}
|
|
74
|
+
export type SelectionCbs<T = any> = DefineCbs<Selection<T>, Cbs<T>>;
|
|
90
75
|
```
|
|
91
76
|
|
|
92
77
|
## Facets have @input, @data and @output members
|
|
93
78
|
|
|
94
|
-
In `Selection.ts` we see the Selection facet class. It has a `selectableIds` field that stores the list of ids from which we can select. The `@input` decorator marks this field as an input. The `ids`, `anchorId` and `items` fields
|
|
79
|
+
In `Selection.ts` we see the Selection facet class. It has a `selectableIds` field that stores the list of ids from which we can select. The `@input` decorator marks this field as an input. The `ids`, `anchorId` and `items` fields store the result of calling `selectItem()` and are marked as `@output` fields. Fields that are decorated with `@data` are used both as output and input. These decorators help with logging, and they also make it possible to bind the facets to state-management solutions such as MobX.
|
|
95
80
|
|
|
96
81
|
## Operations are decorated with `@operation`.
|
|
97
82
|
|
|
@@ -101,24 +86,38 @@ The `Selection` facet has a `selectItem()` operation that allows you to select f
|
|
|
101
86
|
|
|
102
87
|
As a side effect of calling an operation, the entire container is logged to the console (if logging is enabled); this includes the Selection facet, and any other facets that the container may have (such as filtering, or highlight). The log message contains all @input, @data and @output fields of all facets in the container.
|
|
103
88
|
|
|
104
|
-
## The `@
|
|
89
|
+
## The `@withCbs` decorator is used to enable callbacks in an operation.
|
|
105
90
|
|
|
106
|
-
The `@
|
|
91
|
+
The `@withCbs` decorator comes from the `Aspiration` library. When you call a function `f` that is decorated with `@withCbs` then the following happens:
|
|
107
92
|
|
|
108
93
|
- Aspiration looks up the callbackMap object (that was installed with `setCallbackMap()`) for the given operation;
|
|
109
94
|
- it copies the operation arguments to the callbackMap object, so that callback functions can access these arguments;
|
|
110
|
-
- if calls `f(arguments)
|
|
95
|
+
- if calls `f(arguments)`, which internally calls the installed callback functions;
|
|
111
96
|
- it restores the callbackMap object to its previous state. This ensures that nested calls work correctly.
|
|
112
97
|
|
|
113
98
|
If the original function `f` wants to access the callbackMap object then it uses the `getCallbacks` helper function. Note that the client code calls `f` in the usual way; it remains agnostic of the fact that callbacks are used to help implement `f`.
|
|
114
99
|
|
|
115
|
-
## Callback functions have access to all operation arguments
|
|
116
|
-
|
|
117
|
-
Each callback function of the callbackMap object has access to all arguments of the operation. In our example, the argument of the `selectItem` operation is named `selectionParams`. Therefore, a `selectionParams` field also exists in the `Selection_selectItem` class that stores the callbackMap. We must manually pass the list of argument names to the `@host` decorator, so that SkandhaJS can copy these values (unfortunately, introspection in typescript is not good enough to automate this).
|
|
118
|
-
|
|
119
100
|
## Facets have a static `className()` function
|
|
120
101
|
|
|
121
102
|
To allow SkandhaJS to log the container, you need to add the `className()` function to each facet class, so that Skandha knows the name of the facet.
|
|
103
|
+
The `className()` function is also used to look up facets in containers. Note that if you subclass a facet class then you should (in most cases)
|
|
104
|
+
not override the `className()`. By keeping the original `className()` function, you will be able to replace facets with their subclassed versions without affecting the facet lookup mechanism.
|
|
105
|
+
|
|
106
|
+
## Facets that use inheritance must have a static `skandhaSymbol` member
|
|
107
|
+
|
|
108
|
+
The skandhaSymbol member is a workaround for a change in Typescript that affects the
|
|
109
|
+
`experimentalDecorators` feature. Add this member if the facet class inherits from some
|
|
110
|
+
other class.
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
// file: Selection.ts
|
|
114
|
+
import { Selection } from 'skandha-facets';
|
|
115
|
+
|
|
116
|
+
export class MySelection extends Selection {
|
|
117
|
+
static className = () => 'MySelection';
|
|
118
|
+
static skandhaSymbol = Symbol('MySelection');
|
|
119
|
+
}
|
|
120
|
+
```
|
|
122
121
|
|
|
123
122
|
## Example: a container with Selection, Highlight and Filtering
|
|
124
123
|
|
|
@@ -127,7 +126,7 @@ In this example we will take a look at a todos container. It uses the `Selection
|
|
|
127
126
|
|
|
128
127
|
```
|
|
129
128
|
// file: createContainer.ts
|
|
130
|
-
import {
|
|
129
|
+
import { SelectionCbs } from 'SelectionCbs';
|
|
131
130
|
import * as R from 'ramda';
|
|
132
131
|
|
|
133
132
|
const createContainer() {
|
|
@@ -139,9 +138,9 @@ const createContainer() {
|
|
|
139
138
|
|
|
140
139
|
setCallbackMap(todosCtr.selection, {
|
|
141
140
|
selectItem: {
|
|
142
|
-
select(this:
|
|
143
|
-
handleSelectItem(todosCtr.selection, this.
|
|
144
|
-
highlightFollowsSelection(todosCtr, this.
|
|
141
|
+
select(this: SelectionCbs['selectItem']): {
|
|
142
|
+
handleSelectItem(todosCtr.selection, this.args);
|
|
143
|
+
highlightFollowsSelection(todosCtr, this.args);
|
|
145
144
|
},
|
|
146
145
|
}
|
|
147
146
|
});
|
|
@@ -203,13 +202,13 @@ export function handleSelectItem(
|
|
|
203
202
|
|
|
204
203
|
We call `setCallbackMap()` to install a callbackMap object for each operation of the `Selection` facet. In our case we only have one operation (`selectItem()`) that has only one callback (`select()`). This callback function calls `handleSelectItem()` to take care of doing the selection. This is a typical pattern: you want the call to `setCallbackMap()` to be as short and readable as possible, so that it's easy to understand what happens in each operation of each facet.
|
|
205
204
|
|
|
206
|
-
##
|
|
205
|
+
## The callback function can access the callbackMap object through `this`.
|
|
207
206
|
|
|
208
|
-
|
|
207
|
+
When the clients calls `todosCtr.selection.selectItem()` and `select()` is called back, then all arguments of the operation are available through `this`. In our case, we see that the `select()` callback function is accessing `this.args`. See the `aspiration` library documentation for details.
|
|
209
208
|
|
|
210
|
-
##
|
|
209
|
+
## Callbacks can take care of side effects
|
|
211
210
|
|
|
212
|
-
|
|
211
|
+
An important advantage of using callbacks is that they can also take care of side effects. In this case, it calls `highlightFollowsSelection()` to ensure that selected items are also highlighted. Note that the callback function is able to connect selection and highlight because it has access to the entire container. Keeping the code of `setCallbackMap()` short also helps the reader to understand which interactions take place. This is vital, because we are purposely mixing concerns in this part of the code, which means that there is a higher risk of confusing the reader.
|
|
213
212
|
|
|
214
213
|
## The `exit()` callback function is called at the end of an operation
|
|
215
214
|
|
|
@@ -226,7 +225,7 @@ We've seen how facets are implemented, and how they can be added to a container
|
|
|
226
225
|
```
|
|
227
226
|
// file: createContainer.ts
|
|
228
227
|
import * from 'ramda' as R;
|
|
229
|
-
import { mapDataToProps
|
|
228
|
+
import { mapDataToProps } from 'skandha';
|
|
230
229
|
|
|
231
230
|
class TodosData {
|
|
232
231
|
static className = () => 'Inputs';
|
|
@@ -247,30 +246,20 @@ const createContainer = () => {
|
|
|
247
246
|
filtering: new Filtering(),
|
|
248
247
|
}
|
|
249
248
|
todosCtr.data.todos = loadTodos();
|
|
250
|
-
|
|
251
249
|
const getTodoById = (id?: string) => id ? todosCtr.data.todoById[id] : undefined;
|
|
252
250
|
|
|
253
|
-
mapDataToProps(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
() => todosCtr.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
() => todosCtr.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
),
|
|
266
|
-
pmap(
|
|
267
|
-
[todosCtr.selection, 'items'],
|
|
268
|
-
() => R.map(getTodoById, todosCtr.selection.ids)
|
|
269
|
-
),
|
|
270
|
-
pmap(
|
|
271
|
-
[todosCtr.highlight, 'item'],
|
|
272
|
-
() => getTodoById(todosCtr.highlight.id)
|
|
273
|
-
),
|
|
251
|
+
mapDataToProps(todosCtr,
|
|
252
|
+
filtering: {
|
|
253
|
+
inputItems: () => todosCtr.data.todos,
|
|
254
|
+
filteredItems: () => todosCtr.filtering.filteredItems,
|
|
255
|
+
},
|
|
256
|
+
selection: {
|
|
257
|
+
selectableIds: () => R.map(R.prop('id'), todosCtr.data.filteredTodos),
|
|
258
|
+
items: () => R.map(getTodoById, todosCtr.selection.ids),
|
|
259
|
+
},
|
|
260
|
+
highlight: {
|
|
261
|
+
item: () => getTodoById(todosCtr.highlight.id)
|
|
262
|
+
}
|
|
274
263
|
)
|
|
275
264
|
}
|
|
276
265
|
|
|
@@ -279,15 +268,11 @@ setOptions({logging: true});
|
|
|
279
268
|
|
|
280
269
|
## The `mapDataToProps()` function maps data between objects
|
|
281
270
|
|
|
282
|
-
The `mapDataToProp()` function takes a container, field name and function and installs a `get` property (on the given container, using the given field name) that executes the given function.
|
|
283
|
-
|
|
284
|
-
## The `pmap()` function adds type-checking
|
|
285
|
-
|
|
286
|
-
The `pmap` function does not really do anything at run-time, but it adds type-checking at compile-time. It takes two arguments. The first argument is an array that has a container and a container-member. The second argument is a function. The `pmap` function checks that the output of this function can be stored in the container-member (if not, then a compile-time error results). It returns an array that contains its two arguments (because that is the format that `mapDataToProps` expects).
|
|
271
|
+
The `mapDataToProp()` function takes a container, field name and function and installs a `get` property (on the given container, using the given field name) that executes the given function. In our example, we see that the `todosCtr.data.todos` field is mapped onto the `todosCtr.filtering.inputItems` field.
|
|
287
272
|
|
|
288
273
|
## Logging can be enabled using `setOptions()`
|
|
289
274
|
|
|
290
|
-
If you call `setOptions({logging: true})` then the SkandhaJS library logs each facet before and after each operation. All facet members that are decorated with `@data` (or `@input`, or `@output`) are logged in a way
|
|
275
|
+
If you call `setOptions({logging: true})` then the SkandhaJS library logs each facet before and after each operation. All facet members that are decorated with `@data` (or `@input`, or `@output`) are logged in a way similar to Redux:
|
|
291
276
|
|
|
292
277
|
- before and after each operation, the entire container that holds the facet is logged
|
|
293
278
|
- all data members of each facet in the container are included in the log
|
package/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export { getCtrState } from './internal/logging';
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
2
|
+
export { getOptions, setOptions } from './internal/options';
|
|
3
|
+
export { addCleanUpFunctionToCtr, cleanUpCtr } from './lib/cleanUp';
|
|
4
|
+
export { getFacetMemberNames, getc } from './lib/ctr';
|
|
4
5
|
export { data, getDataMemberNames, getLoggedMemberNames, input, log, output, } from './lib/data';
|
|
5
6
|
export { getf } from './lib/facet';
|
|
6
7
|
export { installPolicies } from './lib/install';
|
|
7
8
|
export { getOperationMemberNames, operation } from './lib/operation';
|
|
8
|
-
export { mapDataToProp, mapDataToProps
|
|
9
|
+
export { mapDataToProp, mapDataToProps } from './lib/patch';
|
|
9
10
|
export { registerCtr } from './lib/registerCtr';
|
|
10
11
|
export type { OptionsT as RegisterCtrOptionsT } from './lib/registerCtr';
|
|
11
|
-
export
|
|
12
|
+
export type ClassT = any;
|
package/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { getCtrState } from './internal/logging';
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
2
|
+
export { getOptions, setOptions } from './internal/options';
|
|
3
|
+
export { addCleanUpFunctionToCtr, cleanUpCtr } from './lib/cleanUp';
|
|
4
|
+
export { getFacetMemberNames, getc } from './lib/ctr';
|
|
4
5
|
export { data, getDataMemberNames, getLoggedMemberNames, input, log, output, } from './lib/data';
|
|
5
6
|
export { getf } from './lib/facet';
|
|
6
7
|
export { installPolicies } from './lib/install';
|
|
7
8
|
export { getOperationMemberNames, operation } from './lib/operation';
|
|
8
|
-
export { mapDataToProp, mapDataToProps
|
|
9
|
+
export { mapDataToProp, mapDataToProps } from './lib/patch';
|
|
9
10
|
export { registerCtr } from './lib/registerCtr';
|
package/internal/logging.js
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
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
1
|
import { getCtrAdmin, getFacetAdmin } from '../internal/utils';
|
|
13
|
-
import {
|
|
2
|
+
import { getFacetMemberNames, getc } from '../lib/ctr';
|
|
14
3
|
import { getLoggedMemberNames } from '../lib/data';
|
|
15
4
|
import { options } from './options';
|
|
16
5
|
export function _defaultFacetLogName(facet) {
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
const ctr = getc(facet);
|
|
7
|
+
const prefix = ctr ? ctr.constructor.name + '/' : '';
|
|
19
8
|
return prefix + facet.constructor.name;
|
|
20
9
|
}
|
|
21
10
|
export function facetLogName(facet) {
|
|
@@ -29,41 +18,39 @@ function camelToSnake(string) {
|
|
|
29
18
|
})
|
|
30
19
|
.toLowerCase();
|
|
31
20
|
}
|
|
32
|
-
|
|
21
|
+
const opName = (operationMember) => camelToSnake(operationMember).toUpperCase();
|
|
33
22
|
export function log(facet, operationMember, args, start) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
const ctr = getc(facet);
|
|
24
|
+
const getState = ctr ? () => getCtrState(ctr) : () => facetState(facet);
|
|
25
|
+
const operationName = opName(operationMember);
|
|
26
|
+
const label = facetLogName(facet) + '.' + operationName;
|
|
38
27
|
if (start) {
|
|
39
28
|
console.group(label);
|
|
40
|
-
console.log('%c
|
|
41
|
-
console.log('%c
|
|
29
|
+
console.log('%c args: ', 'color: gray', args);
|
|
30
|
+
console.log('%c state', 'color: gray', getState());
|
|
42
31
|
}
|
|
43
32
|
else {
|
|
44
|
-
console.log('%c
|
|
33
|
+
console.log('%c next', 'color: gray', getState());
|
|
45
34
|
// @ts-ignore
|
|
46
35
|
console.groupEnd(label);
|
|
47
36
|
}
|
|
48
37
|
}
|
|
49
38
|
export function facetState(facet) {
|
|
50
|
-
return getLoggedMemberNames(facet).reduce(
|
|
51
|
-
var _a;
|
|
39
|
+
return getLoggedMemberNames(facet).reduce((acc, loggedMember) => {
|
|
52
40
|
try {
|
|
53
|
-
|
|
54
|
-
return
|
|
41
|
+
const data = options.formatObject(facet[loggedMember]);
|
|
42
|
+
return Object.assign(Object.assign({}, acc), { [loggedMember]: data });
|
|
55
43
|
}
|
|
56
|
-
catch (
|
|
44
|
+
catch (_a) {
|
|
57
45
|
return acc;
|
|
58
46
|
}
|
|
59
47
|
}, {});
|
|
60
48
|
}
|
|
61
49
|
export function defaultGetCtrState(ctr) {
|
|
62
50
|
if (ctr) {
|
|
63
|
-
return getFacetMemberNames(ctr).reduce(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return __assign(__assign({}, acc), (_a = {}, _a[facetMemberName] = facetState(facet), _a));
|
|
51
|
+
return getFacetMemberNames(ctr).reduce((acc, facetMemberName) => {
|
|
52
|
+
const facet = ctr[facetMemberName];
|
|
53
|
+
return Object.assign(Object.assign({}, acc), { [facetMemberName]: facetState(facet) });
|
|
67
54
|
}, {});
|
|
68
55
|
}
|
|
69
56
|
}
|
package/internal/options.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
export declare const options: {
|
|
2
2
|
logging: boolean;
|
|
3
3
|
formatObject: (x: any) => any;
|
|
4
|
-
decorateCb: (x: any) => any;
|
|
5
4
|
};
|
|
6
5
|
export declare const getOptions: () => {
|
|
7
6
|
logging: boolean;
|
|
8
7
|
formatObject: (x: any) => any;
|
|
9
|
-
decorateCb: (x: any) => any;
|
|
10
8
|
};
|
|
11
9
|
export declare const setOptions: (moreOptions: any) => void;
|
|
12
|
-
export declare const decorateCb: (x: any) => any;
|
package/internal/options.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const options = {
|
|
2
2
|
logging: false,
|
|
3
|
-
formatObject:
|
|
4
|
-
decorateCb: function (x) { return x; },
|
|
3
|
+
formatObject: (x) => x,
|
|
5
4
|
};
|
|
6
|
-
export
|
|
7
|
-
export
|
|
5
|
+
export const getOptions = () => options;
|
|
6
|
+
export const setOptions = (moreOptions) => {
|
|
8
7
|
Object.assign(options, moreOptions);
|
|
9
8
|
};
|
|
10
|
-
export var decorateCb = function (x) { return options.decorateCb(x); };
|
package/internal/symbols.js
CHANGED
package/internal/utils.js
CHANGED
|
@@ -1,34 +1,67 @@
|
|
|
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));
|
|
9
|
-
};
|
|
10
1
|
import { symbols } from './symbols';
|
|
2
|
+
// This class is a workaround for a problem that occurs when you
|
|
3
|
+
// try to store information using facet.constructor[symbols.admin] = ...
|
|
4
|
+
// For some reason, when you inherit from a Facet class then the original
|
|
5
|
+
// class and the enherited class share the same facet.constructor[symbols.admin]
|
|
6
|
+
// instance.
|
|
7
|
+
const facetClassAdminMap = new WeakMap();
|
|
11
8
|
export function getOrCreate(obj, key, fn) {
|
|
12
9
|
if (!obj[key]) {
|
|
13
10
|
obj[key] = fn();
|
|
14
11
|
}
|
|
15
12
|
return obj[key];
|
|
16
13
|
}
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
for (var _i = 1; _i < arguments.length; _i++) {
|
|
20
|
-
arrs[_i - 1] = arguments[_i];
|
|
21
|
-
}
|
|
22
|
-
return arr.map(function (val, i) {
|
|
23
|
-
return arrs.reduce(function (a, arr) { return __spreadArray(__spreadArray([], a, true), [arr[i]], false); }, [val]);
|
|
24
|
-
});
|
|
14
|
+
export const zip = (arr, ...arrs) => {
|
|
15
|
+
return arr.map((val, i) => arrs.reduce((a, arr) => [...a, arr[i]], [val]));
|
|
25
16
|
};
|
|
26
17
|
export function getCtrAdmin(ctr) {
|
|
27
|
-
return getOrCreate(ctr, symbols.admin,
|
|
18
|
+
return getOrCreate(ctr, symbols.admin, () => ({}));
|
|
28
19
|
}
|
|
29
20
|
export function getFacetClassAdmin(facetClass) {
|
|
30
|
-
|
|
21
|
+
if (!!facetClass.skandhaSymbol) {
|
|
22
|
+
return getOrCreate(facetClassAdminMap, facetClass.skandhaSymbol, () => {
|
|
23
|
+
// This function returns the initial value for the facetClassAdminMap.
|
|
24
|
+
// If the facetClass has a superClass, then we return a copy of the
|
|
25
|
+
// facetClassAdmin of the superClass.
|
|
26
|
+
const superClass = Object.getPrototypeOf(facetClass);
|
|
27
|
+
if (superClass !== Function.prototype && superClass !== null) {
|
|
28
|
+
return deepCopy(getFacetClassAdmin(superClass));
|
|
29
|
+
}
|
|
30
|
+
return {};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return getOrCreate(facetClass, symbols.admin, () => ({}));
|
|
31
34
|
}
|
|
32
35
|
export function getFacetAdmin(facet) {
|
|
33
|
-
return getOrCreate(facet, symbols.admin,
|
|
36
|
+
return getOrCreate(facet, symbols.admin, () => ({}));
|
|
37
|
+
}
|
|
38
|
+
function deepCopy(obj) {
|
|
39
|
+
var copy;
|
|
40
|
+
// Handle the 3 simple types, and null or undefined
|
|
41
|
+
if (null == obj || 'object' != typeof obj)
|
|
42
|
+
return obj;
|
|
43
|
+
// Handle Date
|
|
44
|
+
if (obj instanceof Date) {
|
|
45
|
+
copy = new Date();
|
|
46
|
+
copy.setTime(obj.getTime());
|
|
47
|
+
return copy;
|
|
48
|
+
}
|
|
49
|
+
// Handle Array
|
|
50
|
+
if (obj instanceof Array) {
|
|
51
|
+
copy = [];
|
|
52
|
+
for (var i = 0, len = obj.length; i < len; i++) {
|
|
53
|
+
copy[i] = deepCopy(obj[i]);
|
|
54
|
+
}
|
|
55
|
+
return copy;
|
|
56
|
+
}
|
|
57
|
+
// Handle Object
|
|
58
|
+
if (obj instanceof Object) {
|
|
59
|
+
copy = {};
|
|
60
|
+
for (var attr in obj) {
|
|
61
|
+
if (obj.hasOwnProperty(attr))
|
|
62
|
+
copy[attr] = deepCopy(obj[attr]);
|
|
63
|
+
}
|
|
64
|
+
return copy;
|
|
65
|
+
}
|
|
66
|
+
throw new Error("Unable to copy obj! Its type isn't supported.");
|
|
34
67
|
}
|
package/lib/cleanUp.d.ts
ADDED
package/lib/cleanUp.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getCtrAdmin } from '../internal/utils';
|
|
2
|
+
export function addCleanUpFunctionToCtr(ctr, f) {
|
|
3
|
+
var _a;
|
|
4
|
+
const ctrAdmin = getCtrAdmin(ctr);
|
|
5
|
+
ctrAdmin.cleanUpFunctions = (_a = ctrAdmin.cleanUpFunctions) !== null && _a !== void 0 ? _a : [];
|
|
6
|
+
ctrAdmin.cleanUpFunctions.push(f);
|
|
7
|
+
}
|
|
8
|
+
export function cleanUpCtr(ctr) {
|
|
9
|
+
var _a;
|
|
10
|
+
const ctrAdmin = getCtrAdmin(ctr);
|
|
11
|
+
ctrAdmin.cleanUpFunctions = (_a = ctrAdmin.cleanUpFunctions) !== null && _a !== void 0 ? _a : [];
|
|
12
|
+
ctrAdmin.cleanUpFunctions.forEach((x) => x());
|
|
13
|
+
ctrAdmin.cleanUpFunctions = [];
|
|
14
|
+
}
|
package/lib/data.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { getFacetClassAdmin } from '../internal/utils';
|
|
2
|
-
export function log(dataHost, dataMember, descriptor) {
|
|
2
|
+
export function log(dataHost, dataMember, descriptor = undefined) {
|
|
3
3
|
var _a;
|
|
4
|
-
|
|
5
|
-
var facetClassAdmin = getFacetClassAdmin(dataHost.constructor);
|
|
4
|
+
const facetClassAdmin = getFacetClassAdmin(dataHost.constructor);
|
|
6
5
|
facetClassAdmin.loggedMembers = (_a = facetClassAdmin.loggedMembers) !== null && _a !== void 0 ? _a : {};
|
|
7
6
|
facetClassAdmin.loggedMembers[dataMember] = true;
|
|
8
7
|
return descriptor;
|
|
9
8
|
}
|
|
10
|
-
export function data(dataHost, dataMember, descriptor) {
|
|
9
|
+
export function data(dataHost, dataMember, descriptor = undefined) {
|
|
11
10
|
var _a;
|
|
12
|
-
|
|
13
|
-
var facetClassAdmin = getFacetClassAdmin(dataHost.constructor);
|
|
11
|
+
const facetClassAdmin = getFacetClassAdmin(dataHost.constructor);
|
|
14
12
|
facetClassAdmin.dataMembers = (_a = facetClassAdmin.dataMembers) !== null && _a !== void 0 ? _a : {};
|
|
15
13
|
facetClassAdmin.dataMembers[dataMember] = true;
|
|
16
14
|
return log(dataHost, dataMember, descriptor);
|
|
@@ -23,5 +21,5 @@ export function getDataMemberNames(facet) {
|
|
|
23
21
|
var _a;
|
|
24
22
|
return Object.keys((_a = getFacetClassAdmin(facet.constructor).dataMembers) !== null && _a !== void 0 ? _a : {});
|
|
25
23
|
}
|
|
26
|
-
export
|
|
27
|
-
export
|
|
24
|
+
export const input = data;
|
|
25
|
+
export const output = data;
|
package/lib/facet.js
CHANGED
|
@@ -5,13 +5,13 @@ export function facetClassName(facetClass) {
|
|
|
5
5
|
export function getf(facetClass, ctr) {
|
|
6
6
|
var _a;
|
|
7
7
|
if (!ctr)
|
|
8
|
-
return
|
|
9
|
-
|
|
8
|
+
return (ctr) => getf(facetClass, ctr);
|
|
9
|
+
const facet = typeof facetClass === 'string'
|
|
10
10
|
? ctr[facetClass]
|
|
11
11
|
: (_a = getCtrAdmin(ctr).facetByFacetClassName) === null || _a === void 0 ? void 0 : _a[facetClassName(facetClass)];
|
|
12
12
|
if (!facet) {
|
|
13
|
-
|
|
14
|
-
console.error(
|
|
13
|
+
const facetName = facetClass === 'string' ? facetClass : facetClassName(facetClass);
|
|
14
|
+
console.error(`No facet ${facetName} in container ${getCtrAdmin(ctr).logName}`);
|
|
15
15
|
}
|
|
16
16
|
return facet;
|
|
17
17
|
}
|
package/lib/install.js
CHANGED
package/lib/operation.js
CHANGED
|
@@ -7,78 +7,35 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (_) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
10
|
import { log } from '../internal/logging';
|
|
38
11
|
import { options } from '../internal/options';
|
|
39
12
|
import { getFacetClassAdmin } from '../internal/utils';
|
|
40
13
|
function _operation(target, propertyName, descriptor, punctualOptions) {
|
|
41
14
|
var _a;
|
|
42
|
-
|
|
43
|
-
|
|
15
|
+
const f = descriptor.value;
|
|
16
|
+
const facetClassAdmin = getFacetClassAdmin(target.constructor);
|
|
44
17
|
facetClassAdmin.operationMemberNames =
|
|
45
18
|
(_a = facetClassAdmin.operationMemberNames) !== null && _a !== void 0 ? _a : [];
|
|
46
19
|
facetClassAdmin.operationMemberNames.push(propertyName);
|
|
47
20
|
if (typeof descriptor.value === 'function') {
|
|
48
21
|
descriptor.value = !!punctualOptions.async
|
|
49
|
-
? function () {
|
|
22
|
+
? function (...args) {
|
|
50
23
|
var _a;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return
|
|
58
|
-
switch (_b.label) {
|
|
59
|
-
case 0:
|
|
60
|
-
facet = this;
|
|
61
|
-
isLogging = options.logging && ((_a = punctualOptions.log) !== null && _a !== void 0 ? _a : true);
|
|
62
|
-
isLogging && log(facet, propertyName, args, true);
|
|
63
|
-
return [4 /*yield*/, f.bind(this).apply(void 0, args)];
|
|
64
|
-
case 1:
|
|
65
|
-
returnValue = _b.sent();
|
|
66
|
-
isLogging && log(facet, propertyName, args, false);
|
|
67
|
-
return [2 /*return*/, Promise.resolve(returnValue)];
|
|
68
|
-
}
|
|
69
|
-
});
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const facet = this;
|
|
26
|
+
const isLogging = options.logging && ((_a = punctualOptions.log) !== null && _a !== void 0 ? _a : true);
|
|
27
|
+
isLogging && log(facet, propertyName, args, true);
|
|
28
|
+
const returnValue = yield f.bind(this)(...args);
|
|
29
|
+
isLogging && log(facet, propertyName, args, false);
|
|
30
|
+
return Promise.resolve(returnValue);
|
|
70
31
|
});
|
|
71
32
|
}
|
|
72
|
-
: function () {
|
|
33
|
+
: function (...args) {
|
|
73
34
|
var _a;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
args[_i] = arguments[_i];
|
|
77
|
-
}
|
|
78
|
-
var facet = this;
|
|
79
|
-
var isLogging = options.logging && ((_a = punctualOptions.log) !== null && _a !== void 0 ? _a : true);
|
|
35
|
+
const facet = this;
|
|
36
|
+
const isLogging = options.logging && ((_a = punctualOptions.log) !== null && _a !== void 0 ? _a : true);
|
|
80
37
|
isLogging && log(facet, propertyName, args, true);
|
|
81
|
-
|
|
38
|
+
const returnValue = f.bind(this)(...args);
|
|
82
39
|
isLogging && log(facet, propertyName, args, false);
|
|
83
40
|
return returnValue;
|
|
84
41
|
};
|
|
@@ -89,17 +46,13 @@ export function getOperationMemberNames(facet) {
|
|
|
89
46
|
var _a;
|
|
90
47
|
return (_a = getFacetClassAdmin(facet.constructor).operationMemberNames) !== null && _a !== void 0 ? _a : [];
|
|
91
48
|
}
|
|
92
|
-
export function operation() {
|
|
93
|
-
var args = [];
|
|
94
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
95
|
-
args[_i] = arguments[_i];
|
|
96
|
-
}
|
|
49
|
+
export function operation(...args) {
|
|
97
50
|
if (args.length === 1) {
|
|
98
|
-
|
|
51
|
+
const wrapped = (target, propertyName, descriptor) => {
|
|
99
52
|
return _operation(target, propertyName, descriptor, args[0]);
|
|
100
53
|
};
|
|
101
54
|
return wrapped;
|
|
102
55
|
}
|
|
103
|
-
|
|
56
|
+
const [target, propertyName, descriptor] = args;
|
|
104
57
|
return _operation(target, propertyName, descriptor, {});
|
|
105
58
|
}
|
package/lib/patch.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export declare function mapDataToProp<F, P extends keyof F>(facet: F, prop: P, getter: () => F[P]): void;
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
type Mappings<F> = {
|
|
3
|
+
[K in keyof F]?: {
|
|
4
|
+
[P in keyof F[K]]?: () => F[K][P];
|
|
5
|
+
};
|
|
6
|
+
};
|
|
7
|
+
export declare function mapDataToProps<T>(target: T, mappings: Mappings<T>): void;
|
|
8
|
+
export {};
|
package/lib/patch.js
CHANGED
|
@@ -6,16 +6,21 @@ export function mapDataToProp(facet, prop, getter) {
|
|
|
6
6
|
configurable: true,
|
|
7
7
|
});
|
|
8
8
|
}
|
|
9
|
-
export function mapDataToProps() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
export function mapDataToProps(target, mappings) {
|
|
10
|
+
for (const key in mappings) {
|
|
11
|
+
if (mappings.hasOwnProperty(key)) {
|
|
12
|
+
const facet = target[key];
|
|
13
|
+
const propMappings = mappings[key];
|
|
14
|
+
if (facet && propMappings) {
|
|
15
|
+
for (const prop in propMappings) {
|
|
16
|
+
if (propMappings.hasOwnProperty(prop)) {
|
|
17
|
+
const getter = propMappings[prop];
|
|
18
|
+
if (getter) {
|
|
19
|
+
mapDataToProp(facet, prop, getter);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
13
25
|
}
|
|
14
|
-
for (var _a = 0, mappings_1 = mappings; _a < mappings_1.length; _a++) {
|
|
15
|
-
var mapping = mappings_1[_a];
|
|
16
|
-
mapDataToProp(mapping[0][0], mapping[0][1], mapping[1]);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
export function pmap(member, getter) {
|
|
20
|
-
return [member, getter];
|
|
21
26
|
}
|
package/lib/registerCtr.d.ts
CHANGED
package/lib/registerCtr.js
CHANGED
|
@@ -5,23 +5,23 @@ import { setCtr } from './ctr';
|
|
|
5
5
|
import { facetClassName } from './facet';
|
|
6
6
|
export function registerCtr(ctr, options) {
|
|
7
7
|
var _a, _b, _c;
|
|
8
|
-
|
|
8
|
+
const ctrAdmin = getCtrAdmin(ctr);
|
|
9
9
|
ctrAdmin.logName = options.name;
|
|
10
10
|
ctrAdmin.facetMembers = (_a = ctrAdmin.facetMembers) !== null && _a !== void 0 ? _a : [];
|
|
11
11
|
ctrAdmin.facetByFacetClassName = (_b = ctrAdmin.facetByFacetClassName) !== null && _b !== void 0 ? _b : {};
|
|
12
12
|
ctrAdmin.ctrStateOverride = options.ctrState;
|
|
13
|
-
((_c = options.facets) !== null && _c !== void 0 ? _c : Object.getOwnPropertyNames(ctr)).forEach(
|
|
14
|
-
|
|
13
|
+
((_c = options.facets) !== null && _c !== void 0 ? _c : Object.getOwnPropertyNames(ctr)).forEach((facetName) => {
|
|
14
|
+
const facet = ctr[facetName];
|
|
15
15
|
setCtr(facet, ctr);
|
|
16
16
|
ctrAdmin.facetMembers.push(facetName);
|
|
17
17
|
if (!facet.constructor.className) {
|
|
18
|
-
console.error(
|
|
19
|
-
|
|
18
|
+
console.error(`Facet ${facetName} of container ${ctrAdmin.logName} ` +
|
|
19
|
+
`does not have a static className function`);
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
getFacetAdmin(facet).logName =
|
|
21
|
+
const className = facetClassName(facet.constructor);
|
|
22
|
+
getFacetAdmin(facet).logName = `${ctrAdmin.logName}/${className}`;
|
|
23
23
|
if (ctrAdmin.facetByFacetClassName[className] !== undefined) {
|
|
24
|
-
console.error(
|
|
24
|
+
console.error(`Two facets of same type ${className} in container ${ctrAdmin.logName}`);
|
|
25
25
|
}
|
|
26
26
|
ctrAdmin.facetByFacetClassName[className] = facet;
|
|
27
27
|
});
|