@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,297 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
4
|
+
import get from 'lodash/get';
|
|
5
|
+
import set from 'lodash/set';
|
|
6
|
+
import Select from '@material-ui/core/Select';
|
|
7
|
+
import Input from '@material-ui/core/Input';
|
|
8
|
+
import MenuItem from '@material-ui/core/MenuItem';
|
|
9
|
+
import debug from 'debug';
|
|
10
|
+
|
|
11
|
+
import Toggle from './toggle';
|
|
12
|
+
import TwoChoice from '../two-choice';
|
|
13
|
+
import SettingsRadioLabel from './settings-radio-label';
|
|
14
|
+
import { NumberTextField } from '../index';
|
|
15
|
+
import Checkbox from '../checkbox';
|
|
16
|
+
|
|
17
|
+
const log = debug('pie-lib:config-ui:settings:panel');
|
|
18
|
+
|
|
19
|
+
const labelValue = {
|
|
20
|
+
label: PropTypes.string,
|
|
21
|
+
value: PropTypes.string
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const baseTypes = {
|
|
25
|
+
label: PropTypes.string,
|
|
26
|
+
value: PropTypes.string,
|
|
27
|
+
onChange: PropTypes.func
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const CheckboxChoice = ({ label, value, onChange }) => {
|
|
31
|
+
return (
|
|
32
|
+
<Checkbox
|
|
33
|
+
checked={value}
|
|
34
|
+
label={label}
|
|
35
|
+
onChange={event => {
|
|
36
|
+
onChange(event.target.checked);
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
CheckboxChoice.propTypes = {
|
|
43
|
+
label: PropTypes.string,
|
|
44
|
+
value: PropTypes.bool,
|
|
45
|
+
onChange: PropTypes.func
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const Radio = ({ classes, label, value, onChange, choices }) => {
|
|
49
|
+
return (
|
|
50
|
+
<TwoChoice
|
|
51
|
+
className={classes.radioSettings}
|
|
52
|
+
direction="vertical"
|
|
53
|
+
customLabel={SettingsRadioLabel}
|
|
54
|
+
value={value}
|
|
55
|
+
header={label}
|
|
56
|
+
one={choices[0]}
|
|
57
|
+
two={choices[1]}
|
|
58
|
+
onChange={onChange}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
Radio.propTypes = { ...baseTypes, choices: PropTypes.arrayOf(PropTypes.shape(labelValue)) };
|
|
64
|
+
|
|
65
|
+
const StyledRadio = withStyles({
|
|
66
|
+
radioSettings: {
|
|
67
|
+
marginTop: '4px',
|
|
68
|
+
paddingBottom: '4px',
|
|
69
|
+
width: '100%',
|
|
70
|
+
'& > label': {
|
|
71
|
+
color: 'rgba(0, 0, 0, 0.89)',
|
|
72
|
+
transform: 'translate(0, 10px) scale(1)',
|
|
73
|
+
fontSize: '14px'
|
|
74
|
+
},
|
|
75
|
+
'& > div': {
|
|
76
|
+
marginTop: '20px'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
label: {
|
|
80
|
+
display: 'none'
|
|
81
|
+
}
|
|
82
|
+
})(Radio);
|
|
83
|
+
|
|
84
|
+
const Dropdown = withStyles({
|
|
85
|
+
label: {
|
|
86
|
+
margin: 0,
|
|
87
|
+
fontSize: '14px'
|
|
88
|
+
},
|
|
89
|
+
wrapper: {
|
|
90
|
+
marginTop: '4px',
|
|
91
|
+
border: '2px solid lightgrey',
|
|
92
|
+
borderRadius: '4px',
|
|
93
|
+
padding: '0 8px'
|
|
94
|
+
}
|
|
95
|
+
})(({ classes, label, value, onChange, choices = [] }) => {
|
|
96
|
+
return (
|
|
97
|
+
<div>
|
|
98
|
+
{label && <p className={classes.label}>{label}</p>}
|
|
99
|
+
<Select
|
|
100
|
+
className={classes.wrapper}
|
|
101
|
+
value={value || (choices && choices[0])}
|
|
102
|
+
onChange={({ target }) => onChange(target.value)}
|
|
103
|
+
input={<Input id={`dropdown-${label}`} />}
|
|
104
|
+
disableUnderline
|
|
105
|
+
>
|
|
106
|
+
{choices.map((l, index) => (
|
|
107
|
+
<MenuItem key={index} value={l}>
|
|
108
|
+
{l}
|
|
109
|
+
</MenuItem>
|
|
110
|
+
))}
|
|
111
|
+
</Select>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
Dropdown.propTypes = { ...baseTypes, choices: PropTypes.arrayOf(PropTypes.string) };
|
|
117
|
+
|
|
118
|
+
const NumberField = withStyles({
|
|
119
|
+
field: {
|
|
120
|
+
width: '35%',
|
|
121
|
+
marginRight: '24px',
|
|
122
|
+
marginTop: '8px'
|
|
123
|
+
},
|
|
124
|
+
wrapper: {
|
|
125
|
+
marginTop: '4px',
|
|
126
|
+
border: '2px solid lightgrey',
|
|
127
|
+
borderRadius: '4px',
|
|
128
|
+
padding: '0 8px'
|
|
129
|
+
}
|
|
130
|
+
})(({ classes, label, value, onChange = () => {}, suffix, min, max }) => {
|
|
131
|
+
return (
|
|
132
|
+
<NumberTextField
|
|
133
|
+
label={label || 'Label'}
|
|
134
|
+
value={value}
|
|
135
|
+
max={max}
|
|
136
|
+
min={min}
|
|
137
|
+
onChange={(ev, value) => onChange(value)}
|
|
138
|
+
suffix={suffix}
|
|
139
|
+
className={classes.field}
|
|
140
|
+
showErrorWhenOutsideRange
|
|
141
|
+
inputClassName={classes.wrapper}
|
|
142
|
+
disableUnderline
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
NumberField.propTypes = {
|
|
148
|
+
...baseTypes,
|
|
149
|
+
classes: PropTypes.object,
|
|
150
|
+
suffix: PropTypes.string,
|
|
151
|
+
min: PropTypes.number,
|
|
152
|
+
max: PropTypes.number,
|
|
153
|
+
value: PropTypes.number
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const ToggleWrapper = ({ label, value, onChange }) => (
|
|
157
|
+
<Toggle label={label} checked={!!value} toggle={onChange} />
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
ToggleWrapper.propTypes = { ...baseTypes, value: PropTypes.bool };
|
|
161
|
+
|
|
162
|
+
const tagMap = {
|
|
163
|
+
toggle: ToggleWrapper,
|
|
164
|
+
radio: StyledRadio,
|
|
165
|
+
dropdown: Dropdown,
|
|
166
|
+
numberField: NumberField,
|
|
167
|
+
checkbox: CheckboxChoice
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const Group = withStyles(() => ({
|
|
171
|
+
group: {
|
|
172
|
+
margin: '0 0 25px 0'
|
|
173
|
+
},
|
|
174
|
+
groupHeader: {
|
|
175
|
+
color: '#495B8F',
|
|
176
|
+
fontSize: '16px',
|
|
177
|
+
fontWeight: 600,
|
|
178
|
+
marginBottom: '8px'
|
|
179
|
+
},
|
|
180
|
+
numberFields: {
|
|
181
|
+
fontSize: '0.85rem',
|
|
182
|
+
marginBottom: 0
|
|
183
|
+
}
|
|
184
|
+
}))(props => {
|
|
185
|
+
const { classes, model, label, group, configuration, onChange } = props;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param group - the group of settings
|
|
189
|
+
* @param key - the key(or path) to be used to set or get from model or configuration
|
|
190
|
+
* @param innerKey - the key(or path) to be used to get from the group (used only for numberField type)
|
|
191
|
+
* @returns tag that corresponds to element type */
|
|
192
|
+
const getTag = (group, key, innerKey) => {
|
|
193
|
+
const { isConfigProperty, ...properties } = get(group, innerKey || key);
|
|
194
|
+
const value = isConfigProperty ? get(configuration, key) : get(model, key);
|
|
195
|
+
const tagProps = { ...properties, key, value };
|
|
196
|
+
const Tag = tagMap[tagProps.type];
|
|
197
|
+
|
|
198
|
+
return <Tag key={key} {...tagProps} onChange={v => onChange(key, v, isConfigProperty)} />;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const content = (group, key) => {
|
|
202
|
+
const currentGroup = group[key];
|
|
203
|
+
|
|
204
|
+
if (!currentGroup) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const { type, label, fields, choices } = currentGroup;
|
|
209
|
+
|
|
210
|
+
if (type === 'numberFields') {
|
|
211
|
+
return (
|
|
212
|
+
<div key={`numberField-${label}`}>
|
|
213
|
+
<p className={classes.numberFields}>{label}</p>
|
|
214
|
+
{Object.keys(fields).map(fieldKey => {
|
|
215
|
+
return getTag(group, `${key}.${fieldKey}`, `${key}.fields.${fieldKey}`);
|
|
216
|
+
})}
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (type === 'checkboxes') {
|
|
222
|
+
return (
|
|
223
|
+
<div key={`checkbox-${label}`}>
|
|
224
|
+
<p>{label}</p>
|
|
225
|
+
{Object.keys(choices).map(choiceKey => {
|
|
226
|
+
return getTag(group, `${key}.${choiceKey}`, `${key}.choices.${choiceKey}`);
|
|
227
|
+
})}
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// if type is toggle, radio, dropdown or numberField
|
|
233
|
+
return getTag(group, key);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div className={classes.group}>
|
|
238
|
+
<div className={classes.groupHeader}>{label}</div>
|
|
239
|
+
|
|
240
|
+
{Object.keys(group).map(key => {
|
|
241
|
+
return content(group, key);
|
|
242
|
+
})}
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
export class Panel extends React.Component {
|
|
248
|
+
static propTypes = {
|
|
249
|
+
model: PropTypes.object,
|
|
250
|
+
configuration: PropTypes.object,
|
|
251
|
+
groups: PropTypes.object,
|
|
252
|
+
onChangeModel: PropTypes.func,
|
|
253
|
+
onChangeConfiguration: PropTypes.func
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
static defaultProps = {
|
|
257
|
+
onChangeModel: () => {},
|
|
258
|
+
onChangeConfiguration: () => {}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
change = (key, value, isConfigProperty = false) => {
|
|
262
|
+
log('[changeModel]', key, value);
|
|
263
|
+
const { onChangeModel, onChangeConfiguration } = this.props;
|
|
264
|
+
const model = { ...this.props.model };
|
|
265
|
+
const configuration = { ...this.props.configuration };
|
|
266
|
+
|
|
267
|
+
if (isConfigProperty) {
|
|
268
|
+
set(configuration, key, value);
|
|
269
|
+
onChangeConfiguration(configuration, key);
|
|
270
|
+
} else {
|
|
271
|
+
set(model, key, value);
|
|
272
|
+
onChangeModel(model, key);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
render() {
|
|
277
|
+
const { groups, model, configuration } = this.props;
|
|
278
|
+
log('render:', model);
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div>
|
|
282
|
+
{Object.keys(groups).map(g => (
|
|
283
|
+
<Group
|
|
284
|
+
label={g}
|
|
285
|
+
key={g}
|
|
286
|
+
model={model}
|
|
287
|
+
configuration={configuration}
|
|
288
|
+
group={groups[g]}
|
|
289
|
+
onChange={this.change}
|
|
290
|
+
/>
|
|
291
|
+
))}
|
|
292
|
+
</div>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export default Panel;
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
color: 'rgba(0, 0, 0, 0.89)',
|
|
9
|
+
fontSize: '12px',
|
|
10
|
+
left: '-5px',
|
|
11
|
+
position: 'relative'
|
|
12
|
+
}
|
|
13
|
+
})(({ label, value, checked, onChange, classes }) => (
|
|
14
|
+
<FormControlLabel
|
|
15
|
+
value={value}
|
|
16
|
+
classes={classes}
|
|
17
|
+
control={<Radio checked={checked} onChange={onChange} />}
|
|
18
|
+
label={label}
|
|
19
|
+
/>
|
|
20
|
+
));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import InputLabel from '@material-ui/core/InputLabel';
|
|
4
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
5
|
+
import Switch from '@material-ui/core/Switch';
|
|
6
|
+
|
|
7
|
+
const Toggle = withStyles(theme => ({
|
|
8
|
+
toggle: {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
width: '100%',
|
|
11
|
+
justifyContent: 'space-between'
|
|
12
|
+
},
|
|
13
|
+
label: {
|
|
14
|
+
color: 'rgba(0, 0, 0, 0.89)',
|
|
15
|
+
fontSize: '14px',
|
|
16
|
+
paddingTop: theme.spacing.unit * 2
|
|
17
|
+
}
|
|
18
|
+
}))(({ checked, label, toggle, classes }) => (
|
|
19
|
+
<div className={classes.toggle}>
|
|
20
|
+
<InputLabel className={classes.label}>{label}</InputLabel>
|
|
21
|
+
<Switch checked={checked} onChange={e => toggle(e.target.checked)} />
|
|
22
|
+
</div>
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
Toggle.propTypes = {
|
|
26
|
+
checked: PropTypes.bool,
|
|
27
|
+
label: PropTypes.string.isRequired,
|
|
28
|
+
toggle: PropTypes.func.isRequired
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default Toggle;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import MuiTabs from '@material-ui/core/Tabs';
|
|
4
|
+
import MuiTab from '@material-ui/core/Tab';
|
|
5
|
+
import PropTypes from 'prop-types';
|
|
6
|
+
import { withStyles } from '@material-ui/core';
|
|
7
|
+
|
|
8
|
+
export class Tabs extends React.Component {
|
|
9
|
+
static propTypes = {
|
|
10
|
+
classes: PropTypes.object,
|
|
11
|
+
className: PropTypes.string,
|
|
12
|
+
contentClassName: PropTypes.string,
|
|
13
|
+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
constructor(props) {
|
|
17
|
+
super(props);
|
|
18
|
+
this.state = {
|
|
19
|
+
value: 0
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
handleChange = (event, value) => {
|
|
24
|
+
this.setState({ value });
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
render() {
|
|
28
|
+
const { value } = this.state;
|
|
29
|
+
const { children, className, contentClassName, classes } = this.props;
|
|
30
|
+
|
|
31
|
+
const tabClasses = {
|
|
32
|
+
root: classes.tabRoot
|
|
33
|
+
};
|
|
34
|
+
return (
|
|
35
|
+
<div className={className}>
|
|
36
|
+
<MuiTabs indicatorColor="primary" value={value} onChange={this.handleChange}>
|
|
37
|
+
{React.Children.map(children, (c, index) =>
|
|
38
|
+
c && c.props.title ? (
|
|
39
|
+
<MuiTab classes={tabClasses} key={index} label={c.props.title} />
|
|
40
|
+
) : null
|
|
41
|
+
)}
|
|
42
|
+
</MuiTabs>
|
|
43
|
+
<div className={contentClassName}>{children[value]}</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default withStyles(() => ({
|
|
50
|
+
tabRoot: {}
|
|
51
|
+
}))(Tabs);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
4
|
+
import uniq from 'lodash/uniq';
|
|
5
|
+
import Chip from '@material-ui/core/Chip';
|
|
6
|
+
import MuiBox from '../mui-box';
|
|
7
|
+
|
|
8
|
+
const ENTER = 13;
|
|
9
|
+
|
|
10
|
+
const Tag = withStyles(() => ({
|
|
11
|
+
tag: {
|
|
12
|
+
padding: '0px',
|
|
13
|
+
margin: '1px'
|
|
14
|
+
}
|
|
15
|
+
}))(({ classes, label, onDelete }) => (
|
|
16
|
+
<Chip className={classes.tag} label={label} onDelete={onDelete} />
|
|
17
|
+
));
|
|
18
|
+
|
|
19
|
+
Tag.propTypes = {
|
|
20
|
+
label: PropTypes.string.isRequired,
|
|
21
|
+
onDelete: PropTypes.func.isRequired
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class TagsInput extends React.Component {
|
|
25
|
+
static propTypes = {
|
|
26
|
+
classes: PropTypes.object.isRequired,
|
|
27
|
+
tags: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
28
|
+
onChange: PropTypes.func.isRequired
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
constructor(props) {
|
|
32
|
+
super(props);
|
|
33
|
+
this.state = {
|
|
34
|
+
value: '',
|
|
35
|
+
focused: false
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
this.onKeyDown = event => {
|
|
39
|
+
if (event.keyCode === ENTER && this.state.value !== '') {
|
|
40
|
+
const tag = this.state.value.trim();
|
|
41
|
+
const newTags = uniq(this.props.tags.concat([tag]));
|
|
42
|
+
|
|
43
|
+
if (newTags.length !== this.props.tags.length) {
|
|
44
|
+
this.props.onChange(newTags);
|
|
45
|
+
this.setState({ value: '' });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
this.onChange = event => {
|
|
51
|
+
this.setState({ value: event.target.value });
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
this.deleteTag = tag => {
|
|
55
|
+
const { tags } = this.props;
|
|
56
|
+
|
|
57
|
+
const tagIndex = tags.indexOf(tag);
|
|
58
|
+
if (tagIndex !== -1) {
|
|
59
|
+
tags.splice(tagIndex, 1);
|
|
60
|
+
this.props.onChange(tags);
|
|
61
|
+
this.input.focus();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onFocus = () => {
|
|
67
|
+
this.setState({ focused: true });
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
onBlur = () => {
|
|
71
|
+
this.setState({ focused: false });
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
render() {
|
|
75
|
+
const { classes, tags } = this.props;
|
|
76
|
+
return (
|
|
77
|
+
<MuiBox focused={this.state.focused}>
|
|
78
|
+
<div className={classes.tagsInput}>
|
|
79
|
+
{(tags || []).map((t, index) => (
|
|
80
|
+
<Tag key={index} label={t} onDelete={() => this.deleteTag(t)} />
|
|
81
|
+
))}
|
|
82
|
+
<input
|
|
83
|
+
ref={r => (this.input = r)}
|
|
84
|
+
onKeyDown={this.onKeyDown}
|
|
85
|
+
onChange={this.onChange}
|
|
86
|
+
className={classes.input}
|
|
87
|
+
value={this.state.value}
|
|
88
|
+
onFocus={this.onFocus}
|
|
89
|
+
onBlur={this.onBlur}
|
|
90
|
+
type="text"
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
</MuiBox>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const styles = theme => ({
|
|
99
|
+
tagsInput: {
|
|
100
|
+
border: 'solid 0px white',
|
|
101
|
+
display: 'flex',
|
|
102
|
+
flexWrap: 'wrap'
|
|
103
|
+
},
|
|
104
|
+
input: {
|
|
105
|
+
padding: '2px',
|
|
106
|
+
margin: '1px',
|
|
107
|
+
minWidth: '30px',
|
|
108
|
+
width: '100%',
|
|
109
|
+
flex: '1',
|
|
110
|
+
border: 'solid 0px white',
|
|
111
|
+
height: '28px',
|
|
112
|
+
fontSize: theme.typography.fontSize,
|
|
113
|
+
fontFamily: theme.typography.fontFamily,
|
|
114
|
+
outline: 'none',
|
|
115
|
+
'&:focus': {
|
|
116
|
+
outline: 'none'
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export default withStyles(styles)(TagsInput);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { InputContainer } from '@pie-lib/render-ui';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import RadioWithLabel from './radio-with-label';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import classNames from 'classnames';
|
|
6
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
7
|
+
|
|
8
|
+
const styles = theme => ({
|
|
9
|
+
group: {
|
|
10
|
+
display: 'flex',
|
|
11
|
+
flexWrap: 'wrap',
|
|
12
|
+
paddingLeft: 0,
|
|
13
|
+
marginTop: theme.spacing.unit
|
|
14
|
+
},
|
|
15
|
+
vertical: {
|
|
16
|
+
flexDirection: 'column'
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
class RawNChoice extends React.Component {
|
|
21
|
+
static propTypes = {
|
|
22
|
+
header: PropTypes.string.isRequired,
|
|
23
|
+
className: PropTypes.string,
|
|
24
|
+
customLabel: PropTypes.func,
|
|
25
|
+
opts: PropTypes.array.isRequired,
|
|
26
|
+
value: PropTypes.string,
|
|
27
|
+
onChange: PropTypes.func.isRequired,
|
|
28
|
+
direction: PropTypes.oneOf(['horizontal', 'vertical']),
|
|
29
|
+
classes: PropTypes.object.isRequired
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
handleChange = event => {
|
|
33
|
+
this.props.onChange(event.currentTarget.value);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
render() {
|
|
37
|
+
const { header, className, classes, customLabel, opts, value, direction } = this.props;
|
|
38
|
+
|
|
39
|
+
const preppedOpts = opts.map(o => {
|
|
40
|
+
return typeof o === 'string' ? { label: o, value: o } : o;
|
|
41
|
+
});
|
|
42
|
+
const LabelComponent = customLabel || RadioWithLabel;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<InputContainer label={header} className={className}>
|
|
46
|
+
<div className={classNames(classes.group, classes[direction])}>
|
|
47
|
+
{preppedOpts.map((o, index) => (
|
|
48
|
+
<LabelComponent
|
|
49
|
+
value={o.value}
|
|
50
|
+
key={index}
|
|
51
|
+
checked={o.value === value}
|
|
52
|
+
onChange={this.handleChange}
|
|
53
|
+
label={o.label}
|
|
54
|
+
/>
|
|
55
|
+
))}
|
|
56
|
+
</div>
|
|
57
|
+
</InputContainer>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const NChoice = withStyles(styles)(RawNChoice);
|
|
63
|
+
|
|
64
|
+
const labelValue = PropTypes.shape({ label: PropTypes.string, value: PropTypes.string });
|
|
65
|
+
|
|
66
|
+
class TwoChoice extends React.Component {
|
|
67
|
+
static propTypes = {
|
|
68
|
+
header: PropTypes.string.isRequired,
|
|
69
|
+
value: PropTypes.string.isRequired,
|
|
70
|
+
onChange: PropTypes.func.isRequired,
|
|
71
|
+
one: PropTypes.oneOfType([labelValue, PropTypes.string]),
|
|
72
|
+
two: PropTypes.oneOfType([labelValue, PropTypes.string]),
|
|
73
|
+
className: PropTypes.string,
|
|
74
|
+
customLabel: PropTypes.func
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
render() {
|
|
78
|
+
const { one, two, header, className, customLabel, value, onChange } = this.props;
|
|
79
|
+
const opts = [one, two];
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<NChoice
|
|
83
|
+
customLabel={customLabel}
|
|
84
|
+
header={header}
|
|
85
|
+
className={className}
|
|
86
|
+
opts={opts}
|
|
87
|
+
value={value}
|
|
88
|
+
onChange={onChange}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default withStyles(styles)(TwoChoice);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
const withStatefulModel = Component => {
|
|
5
|
+
class Stateful extends React.Component {
|
|
6
|
+
static propTypes = {
|
|
7
|
+
model: PropTypes.object.isRequired,
|
|
8
|
+
onChange: PropTypes.func.isRequired
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
constructor(props) {
|
|
12
|
+
super(props);
|
|
13
|
+
this.state = {
|
|
14
|
+
model: props.model
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
componentWillReceiveProps(props) {
|
|
19
|
+
this.setState({ model: props.model });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onChange = model => {
|
|
23
|
+
this.setState({ model }, () => {
|
|
24
|
+
this.props.onChange(this.state.model);
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
render() {
|
|
29
|
+
return <Component model={this.state.model} onChange={this.onChange} />;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return Stateful;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default withStatefulModel;
|
package/NEXT.CHANGELOG.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[]
|