goblin-laboratory 2.2.1 → 2.2.2
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/.editorconfig +9 -9
- package/.eslintrc.js +28 -28
- package/.zou-flow +3 -3
- package/README.md +107 -107
- package/carnotzet.js +10 -10
- package/config.js +13 -13
- package/laboratory.js +13 -13
- package/lib/.webpack-config.js +53 -53
- package/lib/carnotzet.js +118 -118
- package/lib/helpers.js +16 -16
- package/lib/index.js +66 -66
- package/package.json +47 -47
- package/widgets/connect-helpers/arrayEquals.js +5 -5
- package/widgets/connect-helpers/arraysEquals.js +24 -24
- package/widgets/connect-helpers/c.js +99 -99
- package/widgets/connect-helpers/join-models.js +16 -16
- package/widgets/connect-helpers/with-c.js +276 -276
- package/widgets/devtools.js +5 -5
- package/widgets/disconnect-overlay/styles.js +50 -50
- package/widgets/disconnect-overlay/widget.js +40 -40
- package/widgets/fields-view/widget.js +34 -34
- package/widgets/form/index.js +79 -79
- package/widgets/frame/widget.js +47 -47
- package/widgets/frontend-form/reducer.js +18 -18
- package/widgets/frontend-form/widget.js +15 -15
- package/widgets/importer/default.js +14 -14
- package/widgets/importer/importer.js +54 -53
- package/widgets/importer/index.js +4 -4
- package/widgets/index-browsers.js +195 -195
- package/widgets/index-electron-ws.js +153 -153
- package/widgets/index-electron.js +69 -69
- package/widgets/index.js +1 -1
- package/widgets/laboratory/service.js +542 -542
- package/widgets/laboratory/widget.js +98 -98
- package/widgets/maintenance/styles.js +38 -38
- package/widgets/maintenance/widget.js +65 -65
- package/widgets/props-binder/widget.js +48 -48
- package/widgets/renderer.js +85 -85
- package/widgets/root/index.js +54 -54
- package/widgets/searchkit/index.js +68 -68
- package/widgets/store/backend-reducer.js +116 -116
- package/widgets/store/commands-reducer.js +14 -14
- package/widgets/store/middlewares.js +171 -171
- package/widgets/store/network-reducer.js +23 -23
- package/widgets/store/root-reducer.js +35 -35
- package/widgets/store/store.js +40 -40
- package/widgets/store/widgets-reducer.js +95 -95
- package/widgets/theme-context/js-to-css.js +20 -20
- package/widgets/theme-context/widget.js +130 -130
- package/widgets/view/index.js +31 -31
- package/widgets/widget/index.js +1205 -1205
- package/widgets/widget/utils/connect.js +47 -47
- package/widgets/widget/utils/connectBackend.js +48 -48
- package/widgets/widget/utils/connectWidget.js +31 -31
- package/widgets/widget/utils/manifest.txt +134 -134
- package/widgets/widget/utils/shallowEqualShredder.js +36 -36
- package/widgets/widget/utils/widgets-actions.js +21 -21
- package/widgets/widget/utils/wrapMapStateToProps.js +26 -26
- package/widgets/with-desktop-id/widget.js +20 -20
- package/widgets/with-model/context.js +5 -5
- package/widgets/with-model/widget.js +42 -42
- package/widgets/with-workitem/widget.js +30 -30
|
@@ -1,276 +1,276 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import Widget from 'goblin-laboratory/widgets/widget';
|
|
3
|
-
import {ConnectedProp, ConnectedPropData} from './c.js';
|
|
4
|
-
import Shredder from 'xcraft-core-shredder';
|
|
5
|
-
import arrayEquals from './arrayEquals.js';
|
|
6
|
-
import WithModel from '../with-model/widget.js';
|
|
7
|
-
import joinModels from './join-models.js';
|
|
8
|
-
import ModelContext from '../with-model/context.js';
|
|
9
|
-
|
|
10
|
-
function isShredderOrImmutable(obj) {
|
|
11
|
-
return obj && (Shredder.isShredder(obj) || Shredder.isImmutable(obj));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Wrap a component that does not support connected props to support them.
|
|
16
|
-
*
|
|
17
|
-
* For example with a simple text field:
|
|
18
|
-
* ```javascript
|
|
19
|
-
* <TextFieldNC
|
|
20
|
-
* value="42"
|
|
21
|
-
* onChange={this.handleChange}
|
|
22
|
-
* />
|
|
23
|
-
* ```
|
|
24
|
-
* It can be wrapped with:
|
|
25
|
-
* ```javascript
|
|
26
|
-
* const TextField = withC(TextFieldNC, {value: 'onChange'})
|
|
27
|
-
* ```
|
|
28
|
-
* And then the prop "value" can be connected to the state using:
|
|
29
|
-
* ```javascript
|
|
30
|
-
* <TextFieldNC
|
|
31
|
-
* value={C('.age')}
|
|
32
|
-
* />
|
|
33
|
-
* ```
|
|
34
|
-
* Two functions can be applied, when reading and writing to the state:
|
|
35
|
-
* ```javascript
|
|
36
|
-
* <TextFieldNC
|
|
37
|
-
* value={C('.age', age => age + '', age => Number(age))}
|
|
38
|
-
* />
|
|
39
|
-
* ```
|
|
40
|
-
*
|
|
41
|
-
* The spread syntax can be used to connect multiple props at once:
|
|
42
|
-
* ```javascript
|
|
43
|
-
* function mapAge(age){
|
|
44
|
-
* return {
|
|
45
|
-
* text: age + '',
|
|
46
|
-
* backgroundColor: age >= 18 ? 'green' : 'red',
|
|
47
|
-
* }
|
|
48
|
-
* }
|
|
49
|
-
* <Label
|
|
50
|
-
* {...C('.age', mapName}
|
|
51
|
-
* />
|
|
52
|
-
* ```
|
|
53
|
-
*
|
|
54
|
-
* It is possible to connect a prop to multiple values in the state:
|
|
55
|
-
* ```javascript
|
|
56
|
-
* <TextFieldNC
|
|
57
|
-
* value={C(['.age', '.limit'], (age, limit) => age > limit ? age : limit)}
|
|
58
|
-
* />
|
|
59
|
-
* ```
|
|
60
|
-
*
|
|
61
|
-
* Note: When the path is "null" or "undefined", the prop "text" will receive "undefined"
|
|
62
|
-
* ```javascript
|
|
63
|
-
* const path = id ? `backend.${id}.value` : null;
|
|
64
|
-
* <Label
|
|
65
|
-
* text={C(path)}
|
|
66
|
-
* />
|
|
67
|
-
* ```
|
|
68
|
-
*
|
|
69
|
-
* @param {Component} Component - Any React component.
|
|
70
|
-
* @param {Object} dispatchProps - (optional) Mapping between value props and dispatch props.
|
|
71
|
-
* @param {Object} [options] - Options.
|
|
72
|
-
* @param {String} [options.modelProp] - Set context.model given the path in the prop "modelProp".
|
|
73
|
-
* @return {Widget} A widget supporting connected props.
|
|
74
|
-
*/
|
|
75
|
-
export default function withC(Component, dispatchProps = {}, {modelProp} = {}) {
|
|
76
|
-
// Component used after connect
|
|
77
|
-
// It applies "inFunc" to the connected props and
|
|
78
|
-
// prevents giving internal props (starting with "_") to the underlying component
|
|
79
|
-
const ConnectedPropsMapper = (props) => {
|
|
80
|
-
let {_connectedProps, _connectedProp, ...otherProps} = props;
|
|
81
|
-
const newProps = {};
|
|
82
|
-
for (const prop of _connectedProps) {
|
|
83
|
-
const inFunc = prop.inFunc;
|
|
84
|
-
if (inFunc) {
|
|
85
|
-
const name = prop.name;
|
|
86
|
-
if (name === '_connectedProp') {
|
|
87
|
-
if (Array.isArray(prop.fullPath)) {
|
|
88
|
-
_connectedProp = inFunc(..._connectedProp);
|
|
89
|
-
} else {
|
|
90
|
-
_connectedProp = inFunc(_connectedProp);
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
if (Array.isArray(prop.fullPath)) {
|
|
94
|
-
newProps[name] = inFunc(...props[name]);
|
|
95
|
-
} else {
|
|
96
|
-
newProps[name] = inFunc(props[name]);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Do not spread an immutable to the props but spread it's content
|
|
102
|
-
if (isShredderOrImmutable(_connectedProp)) {
|
|
103
|
-
_connectedProp = _connectedProp.toObject();
|
|
104
|
-
}
|
|
105
|
-
if (modelProp) {
|
|
106
|
-
const connectedModelProp = _connectedProps.find(
|
|
107
|
-
(prop) => prop.name === modelProp
|
|
108
|
-
);
|
|
109
|
-
if (connectedModelProp) {
|
|
110
|
-
const path = connectedModelProp.path;
|
|
111
|
-
const model = Array.isArray(path) ? path[0] : path;
|
|
112
|
-
return (
|
|
113
|
-
<WithModel model={model}>
|
|
114
|
-
<Component {...otherProps} {..._connectedProp} {...newProps} />
|
|
115
|
-
</WithModel>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return <Component {...otherProps} {..._connectedProp} {...newProps} />;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
// Map state to props
|
|
123
|
-
// Replace connected props by their corresponding value in the state
|
|
124
|
-
const ConnectedComponent = Widget.connect(
|
|
125
|
-
(state, props) => {
|
|
126
|
-
const newProps = {};
|
|
127
|
-
for (const prop of props._connectedProps) {
|
|
128
|
-
const fullPath = prop.fullPath;
|
|
129
|
-
let value;
|
|
130
|
-
if (Array.isArray(fullPath)) {
|
|
131
|
-
value = fullPath.map((p) => state.get(p));
|
|
132
|
-
// As 'value' is always a new array, define equals to prevent rerenders
|
|
133
|
-
value.equals = arrayEquals;
|
|
134
|
-
} else {
|
|
135
|
-
value = state.get(fullPath);
|
|
136
|
-
}
|
|
137
|
-
newProps[prop.name] = value;
|
|
138
|
-
}
|
|
139
|
-
return newProps;
|
|
140
|
-
},
|
|
141
|
-
() => ({}) // Do not add "dispatch" to the props
|
|
142
|
-
)(ConnectedPropsMapper);
|
|
143
|
-
|
|
144
|
-
// Component used before connect
|
|
145
|
-
// It determines if there are connected props, handles actions
|
|
146
|
-
// to change the value of the props and transforms relative to absolute paths
|
|
147
|
-
class WithC extends Widget {
|
|
148
|
-
constructor() {
|
|
149
|
-
super(...arguments);
|
|
150
|
-
this.addContextToPath = this.addContextToPath.bind(this);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
addContextToPath(path) {
|
|
154
|
-
if (path === null || path === undefined) {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
const model = this.props.model || this.context.model;
|
|
158
|
-
return joinModels(model, path);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
handlePropChange(propName, value) {
|
|
162
|
-
const path = this.addContextToPath(this.props[propName].path);
|
|
163
|
-
if (!path) {
|
|
164
|
-
throw new Error(`Path is not defined`);
|
|
165
|
-
}
|
|
166
|
-
// Dispatch backend quest or frontend action
|
|
167
|
-
const [root, id, ...pathArray] = path.split('.');
|
|
168
|
-
const valuePath = pathArray.join('.');
|
|
169
|
-
if (root === 'backend') {
|
|
170
|
-
// If the 'change' quest is called here, it doesn't compensate and
|
|
171
|
-
// the value displayed in an input flickers.
|
|
172
|
-
// this.doFor(id, 'change', {
|
|
173
|
-
// path,
|
|
174
|
-
// newValue: value,
|
|
175
|
-
// });
|
|
176
|
-
|
|
177
|
-
// TODO: rename this action
|
|
178
|
-
this.rawDispatch({
|
|
179
|
-
type: 'FIELD-CHANGED',
|
|
180
|
-
path,
|
|
181
|
-
value,
|
|
182
|
-
});
|
|
183
|
-
} else if (root === 'widgets') {
|
|
184
|
-
this.dispatchTo(id, {
|
|
185
|
-
type: 'CHANGE',
|
|
186
|
-
path: valuePath,
|
|
187
|
-
newValue: value,
|
|
188
|
-
});
|
|
189
|
-
} else {
|
|
190
|
-
throw new Error(`Model path starting with '${root}' is not supported.`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Render function used with connected props
|
|
195
|
-
renderConnected() {
|
|
196
|
-
const onChangeProps = {};
|
|
197
|
-
const connectedProps = [];
|
|
198
|
-
const undefinedProps = {};
|
|
199
|
-
|
|
200
|
-
for (const name of this.connectedPropNames) {
|
|
201
|
-
const prop = this.props[name];
|
|
202
|
-
prop.name = name;
|
|
203
|
-
|
|
204
|
-
// Add context to path
|
|
205
|
-
if (Array.isArray(prop.path)) {
|
|
206
|
-
// Handle array of paths (for extra arguments to inFunc)
|
|
207
|
-
prop.fullPath = prop.path.map(this.addContextToPath);
|
|
208
|
-
} else {
|
|
209
|
-
prop.fullPath = this.addContextToPath(prop.path);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (prop.fullPath === null || prop.fullPath === undefined) {
|
|
213
|
-
// No path, the prop will receive 'undefined'
|
|
214
|
-
undefinedProps[name] = undefined;
|
|
215
|
-
} else {
|
|
216
|
-
// There is a path, add the prop to the list of connected props
|
|
217
|
-
connectedProps.push(prop);
|
|
218
|
-
|
|
219
|
-
// Setup a dispatch prop to change the prop value
|
|
220
|
-
if (name in dispatchProps) {
|
|
221
|
-
const dispatchPropName = dispatchProps[name];
|
|
222
|
-
const outFunc = prop.outFunc;
|
|
223
|
-
if (outFunc) {
|
|
224
|
-
onChangeProps[dispatchPropName] = (value) =>
|
|
225
|
-
this.handlePropChange(name, outFunc(value));
|
|
226
|
-
} else {
|
|
227
|
-
onChangeProps[dispatchPropName] = (value) =>
|
|
228
|
-
this.handlePropChange(name, value);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return (
|
|
234
|
-
<ConnectedComponent
|
|
235
|
-
{...onChangeProps}
|
|
236
|
-
{...this.props}
|
|
237
|
-
{...undefinedProps}
|
|
238
|
-
_connectedProps={connectedProps}
|
|
239
|
-
/>
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Render function used when there is no connected prop
|
|
244
|
-
renderNotConnected() {
|
|
245
|
-
return <Component {...this.props} />;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
render() {
|
|
249
|
-
// Find connected props
|
|
250
|
-
const connectedPropNames = [];
|
|
251
|
-
for (const [name, value] of Object.entries(this.props)) {
|
|
252
|
-
if (value instanceof ConnectedProp) {
|
|
253
|
-
connectedPropNames.push(name);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
// Find special prop made by ...C() syntax
|
|
257
|
-
if (this.props._connectedProp instanceof ConnectedPropData) {
|
|
258
|
-
connectedPropNames.push('_connectedProp');
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Optimize render if there is no connected prop
|
|
262
|
-
if (connectedPropNames.length > 0) {
|
|
263
|
-
this.connectedPropNames = connectedPropNames;
|
|
264
|
-
return this.renderConnected();
|
|
265
|
-
} else {
|
|
266
|
-
return this.renderNotConnected();
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return (props) => (
|
|
272
|
-
<ModelContext.Consumer>
|
|
273
|
-
{(model) => <WithC model={model} {...props} />}
|
|
274
|
-
</ModelContext.Consumer>
|
|
275
|
-
);
|
|
276
|
-
}
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Widget from 'goblin-laboratory/widgets/widget';
|
|
3
|
+
import {ConnectedProp, ConnectedPropData} from './c.js';
|
|
4
|
+
import Shredder from 'xcraft-core-shredder';
|
|
5
|
+
import arrayEquals from './arrayEquals.js';
|
|
6
|
+
import WithModel from '../with-model/widget.js';
|
|
7
|
+
import joinModels from './join-models.js';
|
|
8
|
+
import ModelContext from '../with-model/context.js';
|
|
9
|
+
|
|
10
|
+
function isShredderOrImmutable(obj) {
|
|
11
|
+
return obj && (Shredder.isShredder(obj) || Shredder.isImmutable(obj));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Wrap a component that does not support connected props to support them.
|
|
16
|
+
*
|
|
17
|
+
* For example with a simple text field:
|
|
18
|
+
* ```javascript
|
|
19
|
+
* <TextFieldNC
|
|
20
|
+
* value="42"
|
|
21
|
+
* onChange={this.handleChange}
|
|
22
|
+
* />
|
|
23
|
+
* ```
|
|
24
|
+
* It can be wrapped with:
|
|
25
|
+
* ```javascript
|
|
26
|
+
* const TextField = withC(TextFieldNC, {value: 'onChange'})
|
|
27
|
+
* ```
|
|
28
|
+
* And then the prop "value" can be connected to the state using:
|
|
29
|
+
* ```javascript
|
|
30
|
+
* <TextFieldNC
|
|
31
|
+
* value={C('.age')}
|
|
32
|
+
* />
|
|
33
|
+
* ```
|
|
34
|
+
* Two functions can be applied, when reading and writing to the state:
|
|
35
|
+
* ```javascript
|
|
36
|
+
* <TextFieldNC
|
|
37
|
+
* value={C('.age', age => age + '', age => Number(age))}
|
|
38
|
+
* />
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* The spread syntax can be used to connect multiple props at once:
|
|
42
|
+
* ```javascript
|
|
43
|
+
* function mapAge(age){
|
|
44
|
+
* return {
|
|
45
|
+
* text: age + '',
|
|
46
|
+
* backgroundColor: age >= 18 ? 'green' : 'red',
|
|
47
|
+
* }
|
|
48
|
+
* }
|
|
49
|
+
* <Label
|
|
50
|
+
* {...C('.age', mapName}
|
|
51
|
+
* />
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* It is possible to connect a prop to multiple values in the state:
|
|
55
|
+
* ```javascript
|
|
56
|
+
* <TextFieldNC
|
|
57
|
+
* value={C(['.age', '.limit'], (age, limit) => age > limit ? age : limit)}
|
|
58
|
+
* />
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* Note: When the path is "null" or "undefined", the prop "text" will receive "undefined"
|
|
62
|
+
* ```javascript
|
|
63
|
+
* const path = id ? `backend.${id}.value` : null;
|
|
64
|
+
* <Label
|
|
65
|
+
* text={C(path)}
|
|
66
|
+
* />
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @param {Component} Component - Any React component.
|
|
70
|
+
* @param {Object} dispatchProps - (optional) Mapping between value props and dispatch props.
|
|
71
|
+
* @param {Object} [options] - Options.
|
|
72
|
+
* @param {String} [options.modelProp] - Set context.model given the path in the prop "modelProp".
|
|
73
|
+
* @return {Widget} A widget supporting connected props.
|
|
74
|
+
*/
|
|
75
|
+
export default function withC(Component, dispatchProps = {}, {modelProp} = {}) {
|
|
76
|
+
// Component used after connect
|
|
77
|
+
// It applies "inFunc" to the connected props and
|
|
78
|
+
// prevents giving internal props (starting with "_") to the underlying component
|
|
79
|
+
const ConnectedPropsMapper = (props) => {
|
|
80
|
+
let {_connectedProps, _connectedProp, ...otherProps} = props;
|
|
81
|
+
const newProps = {};
|
|
82
|
+
for (const prop of _connectedProps) {
|
|
83
|
+
const inFunc = prop.inFunc;
|
|
84
|
+
if (inFunc) {
|
|
85
|
+
const name = prop.name;
|
|
86
|
+
if (name === '_connectedProp') {
|
|
87
|
+
if (Array.isArray(prop.fullPath)) {
|
|
88
|
+
_connectedProp = inFunc(..._connectedProp);
|
|
89
|
+
} else {
|
|
90
|
+
_connectedProp = inFunc(_connectedProp);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
if (Array.isArray(prop.fullPath)) {
|
|
94
|
+
newProps[name] = inFunc(...props[name]);
|
|
95
|
+
} else {
|
|
96
|
+
newProps[name] = inFunc(props[name]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Do not spread an immutable to the props but spread it's content
|
|
102
|
+
if (isShredderOrImmutable(_connectedProp)) {
|
|
103
|
+
_connectedProp = _connectedProp.toObject();
|
|
104
|
+
}
|
|
105
|
+
if (modelProp) {
|
|
106
|
+
const connectedModelProp = _connectedProps.find(
|
|
107
|
+
(prop) => prop.name === modelProp
|
|
108
|
+
);
|
|
109
|
+
if (connectedModelProp) {
|
|
110
|
+
const path = connectedModelProp.path;
|
|
111
|
+
const model = Array.isArray(path) ? path[0] : path;
|
|
112
|
+
return (
|
|
113
|
+
<WithModel model={model}>
|
|
114
|
+
<Component {...otherProps} {..._connectedProp} {...newProps} />
|
|
115
|
+
</WithModel>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return <Component {...otherProps} {..._connectedProp} {...newProps} />;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Map state to props
|
|
123
|
+
// Replace connected props by their corresponding value in the state
|
|
124
|
+
const ConnectedComponent = Widget.connect(
|
|
125
|
+
(state, props) => {
|
|
126
|
+
const newProps = {};
|
|
127
|
+
for (const prop of props._connectedProps) {
|
|
128
|
+
const fullPath = prop.fullPath;
|
|
129
|
+
let value;
|
|
130
|
+
if (Array.isArray(fullPath)) {
|
|
131
|
+
value = fullPath.map((p) => state.get(p));
|
|
132
|
+
// As 'value' is always a new array, define equals to prevent rerenders
|
|
133
|
+
value.equals = arrayEquals;
|
|
134
|
+
} else {
|
|
135
|
+
value = state.get(fullPath);
|
|
136
|
+
}
|
|
137
|
+
newProps[prop.name] = value;
|
|
138
|
+
}
|
|
139
|
+
return newProps;
|
|
140
|
+
},
|
|
141
|
+
() => ({}) // Do not add "dispatch" to the props
|
|
142
|
+
)(ConnectedPropsMapper);
|
|
143
|
+
|
|
144
|
+
// Component used before connect
|
|
145
|
+
// It determines if there are connected props, handles actions
|
|
146
|
+
// to change the value of the props and transforms relative to absolute paths
|
|
147
|
+
class WithC extends Widget {
|
|
148
|
+
constructor() {
|
|
149
|
+
super(...arguments);
|
|
150
|
+
this.addContextToPath = this.addContextToPath.bind(this);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
addContextToPath(path) {
|
|
154
|
+
if (path === null || path === undefined) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const model = this.props.model || this.context.model;
|
|
158
|
+
return joinModels(model, path);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
handlePropChange(propName, value) {
|
|
162
|
+
const path = this.addContextToPath(this.props[propName].path);
|
|
163
|
+
if (!path) {
|
|
164
|
+
throw new Error(`Path is not defined`);
|
|
165
|
+
}
|
|
166
|
+
// Dispatch backend quest or frontend action
|
|
167
|
+
const [root, id, ...pathArray] = path.split('.');
|
|
168
|
+
const valuePath = pathArray.join('.');
|
|
169
|
+
if (root === 'backend') {
|
|
170
|
+
// If the 'change' quest is called here, it doesn't compensate and
|
|
171
|
+
// the value displayed in an input flickers.
|
|
172
|
+
// this.doFor(id, 'change', {
|
|
173
|
+
// path,
|
|
174
|
+
// newValue: value,
|
|
175
|
+
// });
|
|
176
|
+
|
|
177
|
+
// TODO: rename this action
|
|
178
|
+
this.rawDispatch({
|
|
179
|
+
type: 'FIELD-CHANGED',
|
|
180
|
+
path,
|
|
181
|
+
value,
|
|
182
|
+
});
|
|
183
|
+
} else if (root === 'widgets') {
|
|
184
|
+
this.dispatchTo(id, {
|
|
185
|
+
type: 'CHANGE',
|
|
186
|
+
path: valuePath,
|
|
187
|
+
newValue: value,
|
|
188
|
+
});
|
|
189
|
+
} else {
|
|
190
|
+
throw new Error(`Model path starting with '${root}' is not supported.`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Render function used with connected props
|
|
195
|
+
renderConnected() {
|
|
196
|
+
const onChangeProps = {};
|
|
197
|
+
const connectedProps = [];
|
|
198
|
+
const undefinedProps = {};
|
|
199
|
+
|
|
200
|
+
for (const name of this.connectedPropNames) {
|
|
201
|
+
const prop = this.props[name];
|
|
202
|
+
prop.name = name;
|
|
203
|
+
|
|
204
|
+
// Add context to path
|
|
205
|
+
if (Array.isArray(prop.path)) {
|
|
206
|
+
// Handle array of paths (for extra arguments to inFunc)
|
|
207
|
+
prop.fullPath = prop.path.map(this.addContextToPath);
|
|
208
|
+
} else {
|
|
209
|
+
prop.fullPath = this.addContextToPath(prop.path);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (prop.fullPath === null || prop.fullPath === undefined) {
|
|
213
|
+
// No path, the prop will receive 'undefined'
|
|
214
|
+
undefinedProps[name] = undefined;
|
|
215
|
+
} else {
|
|
216
|
+
// There is a path, add the prop to the list of connected props
|
|
217
|
+
connectedProps.push(prop);
|
|
218
|
+
|
|
219
|
+
// Setup a dispatch prop to change the prop value
|
|
220
|
+
if (name in dispatchProps) {
|
|
221
|
+
const dispatchPropName = dispatchProps[name];
|
|
222
|
+
const outFunc = prop.outFunc;
|
|
223
|
+
if (outFunc) {
|
|
224
|
+
onChangeProps[dispatchPropName] = (value) =>
|
|
225
|
+
this.handlePropChange(name, outFunc(value));
|
|
226
|
+
} else {
|
|
227
|
+
onChangeProps[dispatchPropName] = (value) =>
|
|
228
|
+
this.handlePropChange(name, value);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return (
|
|
234
|
+
<ConnectedComponent
|
|
235
|
+
{...onChangeProps}
|
|
236
|
+
{...this.props}
|
|
237
|
+
{...undefinedProps}
|
|
238
|
+
_connectedProps={connectedProps}
|
|
239
|
+
/>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Render function used when there is no connected prop
|
|
244
|
+
renderNotConnected() {
|
|
245
|
+
return <Component {...this.props} />;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
render() {
|
|
249
|
+
// Find connected props
|
|
250
|
+
const connectedPropNames = [];
|
|
251
|
+
for (const [name, value] of Object.entries(this.props)) {
|
|
252
|
+
if (value instanceof ConnectedProp) {
|
|
253
|
+
connectedPropNames.push(name);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Find special prop made by ...C() syntax
|
|
257
|
+
if (this.props._connectedProp instanceof ConnectedPropData) {
|
|
258
|
+
connectedPropNames.push('_connectedProp');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Optimize render if there is no connected prop
|
|
262
|
+
if (connectedPropNames.length > 0) {
|
|
263
|
+
this.connectedPropNames = connectedPropNames;
|
|
264
|
+
return this.renderConnected();
|
|
265
|
+
} else {
|
|
266
|
+
return this.renderNotConnected();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return (props) => (
|
|
272
|
+
<ModelContext.Consumer>
|
|
273
|
+
{(model) => <WithC model={model} {...props} />}
|
|
274
|
+
</ModelContext.Consumer>
|
|
275
|
+
);
|
|
276
|
+
}
|
package/widgets/devtools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//import Perf from 'react-addons-perf';
|
|
2
|
-
//window.Perf = Perf;
|
|
3
|
-
|
|
4
|
-
const installDevTools = require('immutable-devtools');
|
|
5
|
-
installDevTools(require('immutable'));
|
|
1
|
+
//import Perf from 'react-addons-perf';
|
|
2
|
+
//window.Perf = Perf;
|
|
3
|
+
|
|
4
|
+
const installDevTools = require('immutable-devtools');
|
|
5
|
+
installDevTools(require('immutable'));
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
export const propNames = ['zIndex'];
|
|
2
|
-
|
|
3
|
-
export default function styles(theme, props) {
|
|
4
|
-
const {zIndex} = props;
|
|
5
|
-
|
|
6
|
-
const fullScreenStyle = {
|
|
7
|
-
visibility: 'visible',
|
|
8
|
-
position: 'fixed',
|
|
9
|
-
zIndex: zIndex || 20,
|
|
10
|
-
top: '0px',
|
|
11
|
-
left: '0px',
|
|
12
|
-
width: '100%',
|
|
13
|
-
height: '100%',
|
|
14
|
-
display: 'flex',
|
|
15
|
-
flexDirection: 'column',
|
|
16
|
-
justifyContent: 'center',
|
|
17
|
-
alignItems: 'center',
|
|
18
|
-
userSelect: 'none',
|
|
19
|
-
cursor: 'wait',
|
|
20
|
-
opacity: '80%',
|
|
21
|
-
backgroundColor: 'black',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const messageStyle = {
|
|
25
|
-
width: '80%',
|
|
26
|
-
textAlign: 'center',
|
|
27
|
-
marginTop: '50px',
|
|
28
|
-
border: '4px solid',
|
|
29
|
-
borderRadius: '20px',
|
|
30
|
-
padding: '20px',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const blinkStyle = {
|
|
34
|
-
animationName: {
|
|
35
|
-
'50%': {
|
|
36
|
-
opacity: 0,
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
animationDuration: '1.2s',
|
|
40
|
-
animationIterationCount: 'infinite',
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
fullScreen: fullScreenStyle,
|
|
45
|
-
message: messageStyle,
|
|
46
|
-
blink: blinkStyle,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/******************************************************************************/
|
|
1
|
+
export const propNames = ['zIndex'];
|
|
2
|
+
|
|
3
|
+
export default function styles(theme, props) {
|
|
4
|
+
const {zIndex} = props;
|
|
5
|
+
|
|
6
|
+
const fullScreenStyle = {
|
|
7
|
+
visibility: 'visible',
|
|
8
|
+
position: 'fixed',
|
|
9
|
+
zIndex: zIndex || 20,
|
|
10
|
+
top: '0px',
|
|
11
|
+
left: '0px',
|
|
12
|
+
width: '100%',
|
|
13
|
+
height: '100%',
|
|
14
|
+
display: 'flex',
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
justifyContent: 'center',
|
|
17
|
+
alignItems: 'center',
|
|
18
|
+
userSelect: 'none',
|
|
19
|
+
cursor: 'wait',
|
|
20
|
+
opacity: '80%',
|
|
21
|
+
backgroundColor: 'black',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const messageStyle = {
|
|
25
|
+
width: '80%',
|
|
26
|
+
textAlign: 'center',
|
|
27
|
+
marginTop: '50px',
|
|
28
|
+
border: '4px solid',
|
|
29
|
+
borderRadius: '20px',
|
|
30
|
+
padding: '20px',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const blinkStyle = {
|
|
34
|
+
animationName: {
|
|
35
|
+
'50%': {
|
|
36
|
+
opacity: 0,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
animationDuration: '1.2s',
|
|
40
|
+
animationIterationCount: 'infinite',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
fullScreen: fullScreenStyle,
|
|
45
|
+
message: messageStyle,
|
|
46
|
+
blink: blinkStyle,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/******************************************************************************/
|