@pie-lib/config-ui 9.0.3-next.2 → 10.0.0-beta.1
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/CHANGELOG.json +605 -0
- package/CHANGELOG.md +976 -328
- package/lib/alert-dialog.js +44 -0
- package/lib/alert-dialog.js.map +1 -0
- package/lib/checkbox.js +5 -5
- package/lib/checkbox.js.map +1 -1
- package/lib/choice-configuration/feedback-menu.js +42 -54
- package/lib/choice-configuration/feedback-menu.js.map +1 -1
- package/lib/choice-configuration/index.js +102 -86
- package/lib/choice-configuration/index.js.map +1 -1
- package/lib/choice-utils.js +9 -7
- package/lib/choice-utils.js.map +1 -1
- package/lib/feedback-config/feedback-selector.js +45 -49
- package/lib/feedback-config/feedback-selector.js.map +1 -1
- package/lib/feedback-config/group.js +13 -13
- package/lib/feedback-config/group.js.map +1 -1
- package/lib/feedback-config/index.js +57 -56
- package/lib/feedback-config/index.js.map +1 -1
- package/lib/form-section.js +19 -15
- package/lib/form-section.js.map +1 -1
- package/lib/help.js +31 -38
- package/lib/help.js.map +1 -1
- package/lib/index.js +73 -52
- package/lib/index.js.map +1 -1
- package/lib/input.js +36 -41
- package/lib/input.js.map +1 -1
- package/lib/inputs.js +42 -29
- package/lib/inputs.js.map +1 -1
- package/lib/langs.js +38 -46
- package/lib/langs.js.map +1 -1
- package/lib/layout/config-layout.js +36 -42
- package/lib/layout/config-layout.js.map +1 -1
- package/lib/layout/index.js +3 -3
- package/lib/layout/index.js.map +1 -1
- package/lib/layout/layout-contents.js +35 -35
- package/lib/layout/layout-contents.js.map +1 -1
- package/lib/layout/settings-box.js +29 -40
- package/lib/layout/settings-box.js.map +1 -1
- package/lib/mui-box/index.js +4 -4
- package/lib/mui-box/index.js.map +1 -1
- package/lib/number-text-field-custom.js +364 -0
- package/lib/number-text-field-custom.js.map +1 -0
- package/lib/number-text-field.js +46 -46
- package/lib/number-text-field.js.map +1 -1
- package/lib/radio-with-label.js +5 -5
- package/lib/radio-with-label.js.map +1 -1
- package/lib/settings/display-size.js +12 -10
- package/lib/settings/display-size.js.map +1 -1
- package/lib/settings/index.js +81 -14
- package/lib/settings/index.js.map +1 -1
- package/lib/settings/panel.js +297 -79
- package/lib/settings/panel.js.map +1 -1
- package/lib/settings/settings-radio-label.js +43 -0
- package/lib/settings/settings-radio-label.js.map +1 -0
- package/lib/settings/toggle.js +9 -7
- package/lib/settings/toggle.js.map +1 -1
- package/lib/tabs/index.js +29 -36
- package/lib/tabs/index.js.map +1 -1
- package/lib/tags-input/index.js +33 -41
- package/lib/tags-input/index.js.map +1 -1
- package/lib/two-choice.js +46 -50
- package/lib/two-choice.js.map +1 -1
- package/lib/with-stateful-model.js +24 -31
- package/lib/with-stateful-model.js.map +1 -1
- package/package.json +9 -7
- package/src/alert-dialog.jsx +43 -0
- package/src/checkbox.jsx +63 -0
- package/src/choice-configuration/feedback-menu.jsx +103 -0
- package/src/choice-configuration/index.jsx +319 -0
- package/src/choice-utils.js +30 -0
- package/src/feedback-config/feedback-selector.jsx +116 -0
- package/src/feedback-config/group.jsx +54 -0
- package/src/feedback-config/index.jsx +98 -0
- package/src/form-section.jsx +23 -0
- package/src/help.jsx +88 -0
- package/src/index.js +58 -0
- package/src/input.jsx +72 -0
- package/src/inputs.jsx +96 -0
- package/src/langs.jsx +122 -0
- package/src/layout/config-layout.jsx +64 -0
- package/src/layout/index.js +4 -0
- package/src/layout/layout-contents.jsx +60 -0
- package/src/layout/settings-box.jsx +31 -0
- package/src/mui-box/index.jsx +64 -0
- package/src/number-text-field-custom.jsx +276 -0
- package/src/number-text-field.jsx +196 -0
- package/src/radio-with-label.jsx +18 -0
- package/src/settings/display-size.jsx +52 -0
- package/src/settings/index.js +75 -0
- package/src/settings/panel.jsx +297 -0
- package/src/settings/settings-radio-label.jsx +20 -0
- package/src/settings/toggle.jsx +31 -0
- package/src/tabs/index.jsx +51 -0
- package/src/tags-input/index.jsx +121 -0
- package/src/two-choice.jsx +94 -0
- package/src/with-stateful-model.jsx +36 -0
- package/NEXT.CHANGELOG.json +0 -1
- package/__mocks__/@pie-lib/editable-html.jsx +0 -3
- package/lib/input-container.js +0 -59
- package/lib/input-container.js.map +0 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
export class SettingsBox extends React.Component {
|
|
6
|
+
static propTypes = {
|
|
7
|
+
classes: PropTypes.object.isRequired,
|
|
8
|
+
className: PropTypes.string,
|
|
9
|
+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
|
|
10
|
+
};
|
|
11
|
+
static defaultProps = {};
|
|
12
|
+
render() {
|
|
13
|
+
const { classes, className, children } = this.props;
|
|
14
|
+
return <div className={classNames(classes.settingsBox, className)}>{children}</div>;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const styles = () => ({
|
|
18
|
+
settingsBox: {
|
|
19
|
+
backgroundColor: '#FFF',
|
|
20
|
+
border: '2px solid #EEE',
|
|
21
|
+
display: 'flex',
|
|
22
|
+
flexDirection: 'column',
|
|
23
|
+
justifyContent: 'flex-start',
|
|
24
|
+
minWidth: '275px',
|
|
25
|
+
maxWidth: '300px',
|
|
26
|
+
padding: '24px 8px 24px 24px',
|
|
27
|
+
width: '80%',
|
|
28
|
+
zIndex: 99
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
export default withStyles(styles)(SettingsBox);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
4
|
+
import debug from 'debug';
|
|
5
|
+
import classNames from 'classnames';
|
|
6
|
+
|
|
7
|
+
const log = debug('pie-elements:config-ui:mui-box');
|
|
8
|
+
|
|
9
|
+
const MuiBox = withStyles(theme => {
|
|
10
|
+
const light = theme.palette.type === 'light';
|
|
11
|
+
const bottomLineColor = light ? 'rgba(0, 0, 0, 0.42)' : 'rgba(255, 255, 255, 0.7)';
|
|
12
|
+
|
|
13
|
+
log(theme.palette.primary[theme.palette.type || 'light']);
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
muiBox: {
|
|
17
|
+
paddingTop: theme.spacing.unit,
|
|
18
|
+
paddingBottom: theme.spacing.unit,
|
|
19
|
+
position: 'relative',
|
|
20
|
+
'&:before': {
|
|
21
|
+
left: 0,
|
|
22
|
+
right: 0,
|
|
23
|
+
bottom: 0,
|
|
24
|
+
height: '1px',
|
|
25
|
+
content: '""',
|
|
26
|
+
position: 'absolute',
|
|
27
|
+
transition: 'background-color 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
|
|
28
|
+
pointerEvents: 'none',
|
|
29
|
+
backgroundColor: bottomLineColor
|
|
30
|
+
},
|
|
31
|
+
'&:hover:before': {
|
|
32
|
+
height: '2px'
|
|
33
|
+
},
|
|
34
|
+
'&:after': {
|
|
35
|
+
left: 0,
|
|
36
|
+
right: 0,
|
|
37
|
+
bottom: 0,
|
|
38
|
+
height: '2px',
|
|
39
|
+
content: '""',
|
|
40
|
+
position: 'absolute',
|
|
41
|
+
transform: 'scaleX(0)',
|
|
42
|
+
transition: 'transform 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms',
|
|
43
|
+
pointerEvents: 'none',
|
|
44
|
+
backgroundColor: theme.palette.primary[theme.palette.type] //'#304ffe'
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
focused: {
|
|
48
|
+
'&:after': {
|
|
49
|
+
transform: 'scaleX(1)'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
})(({ children, classes, focused }) => {
|
|
54
|
+
const names = classNames(classes.muiBox, focused && classes.focused);
|
|
55
|
+
|
|
56
|
+
return <div className={names}>{children}</div>;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
MuiBox.propTypes = {
|
|
60
|
+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
61
|
+
focused: PropTypes.bool.isRequired
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default MuiBox;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import TextField from '@material-ui/core/TextField';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
6
|
+
import isFinite from 'lodash/isFinite';
|
|
7
|
+
import IconButton from '@material-ui/core/IconButton';
|
|
8
|
+
import InputAdornment from '@material-ui/core/InputAdornment';
|
|
9
|
+
import Remove from '@material-ui/icons/Remove';
|
|
10
|
+
import Add from '@material-ui/icons/Add';
|
|
11
|
+
|
|
12
|
+
const styles = theme => ({
|
|
13
|
+
input: {
|
|
14
|
+
'& input[type=number]': {
|
|
15
|
+
'-moz-appearance': 'textfield'
|
|
16
|
+
},
|
|
17
|
+
'& input[type=number]::-webkit-outer-spin-button': {
|
|
18
|
+
'-webkit-appearance': 'none',
|
|
19
|
+
margin: 0
|
|
20
|
+
},
|
|
21
|
+
'& input[type=number]::-webkit-inner-spin-button': {
|
|
22
|
+
'-webkit-appearance': 'none',
|
|
23
|
+
margin: 0
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
iconButton: {
|
|
27
|
+
padding: '2px'
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const fallbackNumber = (min, max) => {
|
|
32
|
+
if (!isFinite(min) && !isFinite(max)) {
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!isFinite(min) && isFinite(max)) {
|
|
37
|
+
return max;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (isFinite(min)) {
|
|
41
|
+
return min;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export class NumberTextFieldCustom extends React.Component {
|
|
46
|
+
static propTypes = {
|
|
47
|
+
classes: PropTypes.object.isRequired,
|
|
48
|
+
className: PropTypes.string,
|
|
49
|
+
customValues: PropTypes.array,
|
|
50
|
+
disabled: PropTypes.bool,
|
|
51
|
+
error: PropTypes.bool,
|
|
52
|
+
inputClassName: PropTypes.string,
|
|
53
|
+
onChange: PropTypes.func.isRequired,
|
|
54
|
+
onlyIntegersAllowed: PropTypes.bool,
|
|
55
|
+
value: PropTypes.number,
|
|
56
|
+
min: PropTypes.number,
|
|
57
|
+
max: PropTypes.number,
|
|
58
|
+
step: PropTypes.number,
|
|
59
|
+
label: PropTypes.string,
|
|
60
|
+
disableUnderline: PropTypes.bool,
|
|
61
|
+
variant: PropTypes.string
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
static defaultProps = {
|
|
65
|
+
step: 1,
|
|
66
|
+
customValues: [],
|
|
67
|
+
textAlign: 'center',
|
|
68
|
+
variant: 'standard',
|
|
69
|
+
onlyIntegersAllowed: false
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
constructor(props) {
|
|
73
|
+
super(props);
|
|
74
|
+
|
|
75
|
+
const { value, currentIndex } = this.normalizeValueAndIndex(props.customValues, props.value);
|
|
76
|
+
|
|
77
|
+
this.state = {
|
|
78
|
+
value,
|
|
79
|
+
currentIndex
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (value !== props.value) {
|
|
83
|
+
this.props.onChange({}, value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.onChange = this.onChange.bind(this);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
UNSAFE_componentWillReceiveProps(props) {
|
|
90
|
+
const { value, currentIndex } = this.normalizeValueAndIndex(props.customValues, props.value);
|
|
91
|
+
|
|
92
|
+
this.setState({ value, currentIndex });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
clamp(value) {
|
|
96
|
+
const { min, max, customValues } = this.props;
|
|
97
|
+
|
|
98
|
+
if ((customValues || []).length > 0) {
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!isFinite(value)) {
|
|
103
|
+
return fallbackNumber(min, max);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (isFinite(max)) {
|
|
107
|
+
value = Math.min(value, max);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (isFinite(min)) {
|
|
111
|
+
value = Math.max(value, min);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
normalizeValueAndIndex = (customValues, number) => {
|
|
118
|
+
const value = this.clamp(number);
|
|
119
|
+
const currentIndex = (customValues || []).findIndex(val => val === value);
|
|
120
|
+
|
|
121
|
+
if ((customValues || []).length > 0 && currentIndex === -1) {
|
|
122
|
+
const closestValue = this.getClosestValue(customValues, value);
|
|
123
|
+
|
|
124
|
+
return { value: closestValue.value, currentIndex: closestValue.index };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { value, currentIndex };
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
getClosestValue = (customValues, number) =>
|
|
131
|
+
customValues.reduce(
|
|
132
|
+
(closest, value, index) =>
|
|
133
|
+
Math.abs(value - number) < Math.abs(closest.value - number) ? { value, index } : closest,
|
|
134
|
+
{ value: customValues[0], index: 0 }
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
onBlur = event => {
|
|
138
|
+
const { customValues, onlyIntegersAllowed } = this.props;
|
|
139
|
+
const { value } = event.target;
|
|
140
|
+
const rawNumber = onlyIntegersAllowed ? parseInt(value) : parseFloat(value);
|
|
141
|
+
|
|
142
|
+
const { value: number, currentIndex } = this.normalizeValueAndIndex(customValues, rawNumber);
|
|
143
|
+
|
|
144
|
+
if (number !== this.state.value) {
|
|
145
|
+
this.setState(
|
|
146
|
+
{
|
|
147
|
+
value: number.toString(),
|
|
148
|
+
currentIndex
|
|
149
|
+
},
|
|
150
|
+
() => this.props.onChange(event, number)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
onChange(event) {
|
|
156
|
+
const { value } = event.target;
|
|
157
|
+
|
|
158
|
+
this.setState({ value });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
changeValue(event, sign = 1, shouldUpdate = false) {
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
|
|
164
|
+
const { customValues, step, onlyIntegersAllowed, onChange } = this.props;
|
|
165
|
+
const { currentIndex, value } = this.state;
|
|
166
|
+
const updatedIndex = currentIndex + sign * 1;
|
|
167
|
+
let number;
|
|
168
|
+
|
|
169
|
+
if (customValues.length > 0) {
|
|
170
|
+
if (updatedIndex < 0 || updatedIndex >= customValues.length) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
number = customValues[updatedIndex];
|
|
175
|
+
} else {
|
|
176
|
+
const rawNumber = onlyIntegersAllowed ? parseInt(value) : parseFloat(value);
|
|
177
|
+
const updatedValue = (rawNumber * 10000 + step * sign * 10000) / 10000;
|
|
178
|
+
number = this.clamp(updatedValue);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.setState(
|
|
182
|
+
{
|
|
183
|
+
value: number.toString(),
|
|
184
|
+
currentIndex: updatedIndex
|
|
185
|
+
},
|
|
186
|
+
() => {
|
|
187
|
+
if (shouldUpdate) {
|
|
188
|
+
onChange(event, number);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
render() {
|
|
195
|
+
const {
|
|
196
|
+
className,
|
|
197
|
+
classes,
|
|
198
|
+
label,
|
|
199
|
+
disabled,
|
|
200
|
+
error,
|
|
201
|
+
min,
|
|
202
|
+
max,
|
|
203
|
+
inputClassName,
|
|
204
|
+
disableUnderline,
|
|
205
|
+
helperText,
|
|
206
|
+
variant,
|
|
207
|
+
textAlign
|
|
208
|
+
} = this.props;
|
|
209
|
+
const { value } = this.state;
|
|
210
|
+
const names = classNames(className, classes.input);
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<TextField
|
|
214
|
+
variant={variant}
|
|
215
|
+
inputRef={ref => (this.inputRef = ref)}
|
|
216
|
+
disabled={disabled}
|
|
217
|
+
label={label}
|
|
218
|
+
value={value}
|
|
219
|
+
error={error}
|
|
220
|
+
helperText={helperText}
|
|
221
|
+
onChange={this.onChange}
|
|
222
|
+
onBlur={this.onBlur}
|
|
223
|
+
onKeyPress={e => {
|
|
224
|
+
// once the Enter key is pressed, we force input blur
|
|
225
|
+
if (e.key === 'Enter' && this.inputRef) {
|
|
226
|
+
this.inputRef.blur();
|
|
227
|
+
}
|
|
228
|
+
}}
|
|
229
|
+
onKeyDown={e => {
|
|
230
|
+
if (e.key === 'ArrowUp') {
|
|
231
|
+
this.changeValue(e);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (e.key === 'ArrowDown') {
|
|
235
|
+
this.changeValue(e, -1);
|
|
236
|
+
}
|
|
237
|
+
}}
|
|
238
|
+
type="number"
|
|
239
|
+
className={names}
|
|
240
|
+
InputProps={{
|
|
241
|
+
className: inputClassName,
|
|
242
|
+
disableUnderline: disableUnderline,
|
|
243
|
+
startAdornment: (
|
|
244
|
+
<InputAdornment position="start">
|
|
245
|
+
<IconButton
|
|
246
|
+
className={classes.iconButton}
|
|
247
|
+
disabled={disabled}
|
|
248
|
+
onClick={e => this.changeValue(e, -1, true)}
|
|
249
|
+
>
|
|
250
|
+
<Remove fontSize="small" />
|
|
251
|
+
</IconButton>
|
|
252
|
+
</InputAdornment>
|
|
253
|
+
),
|
|
254
|
+
endAdornment: (
|
|
255
|
+
<InputAdornment position="end">
|
|
256
|
+
<IconButton
|
|
257
|
+
className={classes.iconButton}
|
|
258
|
+
disabled={disabled}
|
|
259
|
+
onClick={e => this.changeValue(e, 1, true)}
|
|
260
|
+
>
|
|
261
|
+
<Add fontSize="small" />
|
|
262
|
+
</IconButton>
|
|
263
|
+
</InputAdornment>
|
|
264
|
+
)
|
|
265
|
+
}}
|
|
266
|
+
inputProps={{
|
|
267
|
+
style: { textAlign },
|
|
268
|
+
min,
|
|
269
|
+
max
|
|
270
|
+
}}
|
|
271
|
+
/>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export default withStyles(styles)(NumberTextFieldCustom);
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import TextField from '@material-ui/core/TextField';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
6
|
+
import debug from 'debug';
|
|
7
|
+
import isFinite from 'lodash/isFinite';
|
|
8
|
+
import InputAdornment from '@material-ui/core/InputAdornment';
|
|
9
|
+
const log = debug('@pie-lib:config-ui:number-text-field');
|
|
10
|
+
|
|
11
|
+
const styles = theme => ({
|
|
12
|
+
root: { marginRight: theme.spacing.unit }
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const fallbackNumber = (min, max) => {
|
|
16
|
+
if (!isFinite(min) && !isFinite(max)) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
if (!isFinite(min) && isFinite(max)) {
|
|
20
|
+
return max;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (isFinite(min)) {
|
|
24
|
+
return min;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export class NumberTextField extends React.Component {
|
|
29
|
+
static propTypes = {
|
|
30
|
+
disabled: PropTypes.bool,
|
|
31
|
+
classes: PropTypes.object.isRequired,
|
|
32
|
+
className: PropTypes.string,
|
|
33
|
+
inputClassName: PropTypes.string,
|
|
34
|
+
onChange: PropTypes.func.isRequired,
|
|
35
|
+
value: PropTypes.number,
|
|
36
|
+
min: PropTypes.number,
|
|
37
|
+
max: PropTypes.number,
|
|
38
|
+
label: PropTypes.string,
|
|
39
|
+
suffix: PropTypes.string,
|
|
40
|
+
showErrorWhenOutsideRange: PropTypes.bool,
|
|
41
|
+
disableUnderline: PropTypes.bool,
|
|
42
|
+
variant: PropTypes.string
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
static defaultProps = {
|
|
46
|
+
showErrorWhenOutsideRange: false
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
constructor(props) {
|
|
50
|
+
super(props);
|
|
51
|
+
|
|
52
|
+
const value = this.clamp(props.value);
|
|
53
|
+
|
|
54
|
+
this.state = {
|
|
55
|
+
value
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
if (value !== props.value) {
|
|
59
|
+
this.props.onChange({}, value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.onChange = this.onChange.bind(this);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
UNSAFE_componentWillReceiveProps(props) {
|
|
66
|
+
const value = this.clamp(props.value);
|
|
67
|
+
this.setState({ value });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
clamp(value) {
|
|
71
|
+
if (!isFinite(value)) {
|
|
72
|
+
return fallbackNumber(this.props.min, this.props.max);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const { min, max } = this.props;
|
|
76
|
+
|
|
77
|
+
if (isFinite(max)) {
|
|
78
|
+
value = Math.min(value, max);
|
|
79
|
+
}
|
|
80
|
+
if (isFinite(min)) {
|
|
81
|
+
value = Math.max(value, min);
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* on Blur (this can be triggered by pressing Enter, see below)
|
|
88
|
+
* we check the entered value and reset it if needed
|
|
89
|
+
*/
|
|
90
|
+
onBlur = event => {
|
|
91
|
+
const value = event.target.value;
|
|
92
|
+
|
|
93
|
+
const rawNumber = parseFloat(value);
|
|
94
|
+
log('rawNumber: ', rawNumber);
|
|
95
|
+
|
|
96
|
+
const number = this.clamp(rawNumber);
|
|
97
|
+
log('number: ', number);
|
|
98
|
+
|
|
99
|
+
if (number !== this.state.value) {
|
|
100
|
+
log('trigger update...');
|
|
101
|
+
this.setState({ value: number.toString() }, () => {
|
|
102
|
+
this.props.onChange(event, number);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
onChange(event) {
|
|
108
|
+
const value = event.target.value;
|
|
109
|
+
this.setState({ value });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
errorMessage = () => {
|
|
113
|
+
const { min, max } = this.props;
|
|
114
|
+
if (min && max) {
|
|
115
|
+
return `The value must be between ${min} and ${max}`;
|
|
116
|
+
}
|
|
117
|
+
if (min) {
|
|
118
|
+
return `The value must be greater than ${min}`;
|
|
119
|
+
}
|
|
120
|
+
if (max) {
|
|
121
|
+
return `The value must be less than ${max}`;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* if the input has to show error when outside range,
|
|
127
|
+
* and the entered value is not matching the requirements
|
|
128
|
+
* we display error message
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
getError = () => {
|
|
132
|
+
const { value } = this.state;
|
|
133
|
+
const float = parseFloat(value);
|
|
134
|
+
const clamped = this.clamp(float);
|
|
135
|
+
if (clamped !== float) {
|
|
136
|
+
return this.errorMessage();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
render() {
|
|
141
|
+
const {
|
|
142
|
+
className,
|
|
143
|
+
classes,
|
|
144
|
+
label,
|
|
145
|
+
disabled,
|
|
146
|
+
suffix,
|
|
147
|
+
min,
|
|
148
|
+
max,
|
|
149
|
+
inputClassName,
|
|
150
|
+
disableUnderline,
|
|
151
|
+
showErrorWhenOutsideRange,
|
|
152
|
+
variant
|
|
153
|
+
} = this.props;
|
|
154
|
+
const names = classNames(classes.root, className);
|
|
155
|
+
|
|
156
|
+
const error = showErrorWhenOutsideRange && this.getError();
|
|
157
|
+
return (
|
|
158
|
+
<TextField
|
|
159
|
+
variant={variant || 'standard'}
|
|
160
|
+
inputRef={ref => {
|
|
161
|
+
this.inputRef = ref;
|
|
162
|
+
}}
|
|
163
|
+
disabled={disabled}
|
|
164
|
+
label={label}
|
|
165
|
+
value={this.state.value}
|
|
166
|
+
error={!!error}
|
|
167
|
+
helperText={error}
|
|
168
|
+
onChange={this.onChange}
|
|
169
|
+
onBlur={this.onBlur}
|
|
170
|
+
onKeyPress={e => {
|
|
171
|
+
// once the Enter key is pressed, we force input blur
|
|
172
|
+
if (e.key === 'Enter' && this.inputRef) {
|
|
173
|
+
this.inputRef.blur();
|
|
174
|
+
}
|
|
175
|
+
}}
|
|
176
|
+
type="number"
|
|
177
|
+
className={names}
|
|
178
|
+
InputLabelProps={{
|
|
179
|
+
shrink: true
|
|
180
|
+
}}
|
|
181
|
+
InputProps={{
|
|
182
|
+
endAdornment: suffix && <InputAdornment position="end">{suffix}</InputAdornment>,
|
|
183
|
+
className: inputClassName,
|
|
184
|
+
disableUnderline: disableUnderline
|
|
185
|
+
}}
|
|
186
|
+
inputProps={{
|
|
187
|
+
min,
|
|
188
|
+
max
|
|
189
|
+
}}
|
|
190
|
+
margin="normal"
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export default withStyles(styles)(NumberTextField);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
|
2
|
+
import Radio from '@material-ui/core/Radio';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
5
|
+
|
|
6
|
+
export default withStyles({
|
|
7
|
+
label: {
|
|
8
|
+
left: '-5px',
|
|
9
|
+
position: 'relative'
|
|
10
|
+
}
|
|
11
|
+
})(({ label, value, checked, onChange, classes }) => (
|
|
12
|
+
<FormControlLabel
|
|
13
|
+
value={value}
|
|
14
|
+
classes={classes}
|
|
15
|
+
control={<Radio checked={checked} onChange={onChange} />}
|
|
16
|
+
label={label}
|
|
17
|
+
/>
|
|
18
|
+
));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import Typography from '@material-ui/core/Typography';
|
|
4
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
5
|
+
import NumberTextField from '../number-text-field';
|
|
6
|
+
|
|
7
|
+
const DisplaySize = withStyles(theme => ({
|
|
8
|
+
displaySize: {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
paddingTop: theme.spacing.unit
|
|
11
|
+
}
|
|
12
|
+
}))(({ size, label, classes, onChange }) => {
|
|
13
|
+
const updateSize = (key, v) => {
|
|
14
|
+
onChange({ ...size, [key]: v });
|
|
15
|
+
};
|
|
16
|
+
return (
|
|
17
|
+
<div>
|
|
18
|
+
<Typography>{label}</Typography>
|
|
19
|
+
<div className={classes.displaySize}>
|
|
20
|
+
<NumberTextField
|
|
21
|
+
label="Width"
|
|
22
|
+
type="number"
|
|
23
|
+
variant="outlined"
|
|
24
|
+
value={size.width}
|
|
25
|
+
min={150}
|
|
26
|
+
max={1000}
|
|
27
|
+
onChange={(e, v) => updateSize('width', v)}
|
|
28
|
+
/>
|
|
29
|
+
<NumberTextField
|
|
30
|
+
label="Height"
|
|
31
|
+
type="number"
|
|
32
|
+
variant="outlined"
|
|
33
|
+
min={150}
|
|
34
|
+
max={1000}
|
|
35
|
+
value={size.height}
|
|
36
|
+
onChange={(e, v) => updateSize('height', v)}
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
DisplaySize.propTypes = {
|
|
44
|
+
size: PropTypes.shape({
|
|
45
|
+
width: PropTypes.number.isRequired,
|
|
46
|
+
height: PropTypes.number.isRequired
|
|
47
|
+
}).isRequired,
|
|
48
|
+
label: PropTypes.string.isRequired,
|
|
49
|
+
onChange: PropTypes.func
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default DisplaySize;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import Panel from './panel';
|
|
2
|
+
|
|
3
|
+
export { Panel };
|
|
4
|
+
|
|
5
|
+
export const toggle = (label, isConfigProperty = false) => ({
|
|
6
|
+
type: 'toggle',
|
|
7
|
+
label,
|
|
8
|
+
isConfigProperty
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const toChoice = opt => {
|
|
12
|
+
if (typeof opt === 'string') {
|
|
13
|
+
return { label: opt, value: opt };
|
|
14
|
+
} else {
|
|
15
|
+
return opt;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const radio = function() {
|
|
20
|
+
const args = Array.prototype.slice.call(arguments);
|
|
21
|
+
const [label, choices, isConfigProperty = false] = args;
|
|
22
|
+
return {
|
|
23
|
+
type: 'radio',
|
|
24
|
+
label,
|
|
25
|
+
choices: choices && choices.map(o => toChoice(o)),
|
|
26
|
+
isConfigProperty
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const dropdown = (label, choices, isConfigProperty = false) => {
|
|
31
|
+
return {
|
|
32
|
+
type: 'dropdown',
|
|
33
|
+
label,
|
|
34
|
+
choices,
|
|
35
|
+
isConfigProperty
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const numberField = (label, options, isConfigProperty = false) => ({
|
|
40
|
+
...options,
|
|
41
|
+
label,
|
|
42
|
+
type: 'numberField',
|
|
43
|
+
isConfigProperty
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export const numberFields = (label, fields, isConfigProperty = false) => {
|
|
47
|
+
Object.keys(fields).map(key => {
|
|
48
|
+
fields[key] = numberField(fields[key].label, fields[key], isConfigProperty);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
type: 'numberFields',
|
|
53
|
+
label,
|
|
54
|
+
fields
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const checkbox = (label, settings, isConfigProperty = false) => ({
|
|
59
|
+
...settings,
|
|
60
|
+
label,
|
|
61
|
+
type: 'checkbox',
|
|
62
|
+
isConfigProperty
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const checkboxes = (label, choices, isConfigProperty = false) => {
|
|
66
|
+
Object.keys(choices).map(key => {
|
|
67
|
+
choices[key] = checkbox(choices[key].label, choices[key], isConfigProperty);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
type: 'checkboxes',
|
|
72
|
+
label,
|
|
73
|
+
choices
|
|
74
|
+
};
|
|
75
|
+
};
|