@performant-software/semantic-components 0.6.2-beta.1 → 0.6.2-beta.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@performant-software/semantic-components",
|
|
3
|
-
"version": "0.6.2-beta.
|
|
3
|
+
"version": "0.6.2-beta.2",
|
|
4
4
|
"description": "A package of shared components based on the Semantic UI Framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./build/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"build": "webpack --mode production && flow-copy-source -v src types"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@performant-software/shared-components": "^0.6.2-beta.
|
|
15
|
+
"@performant-software/shared-components": "^0.6.2-beta.2",
|
|
16
16
|
"@react-google-maps/api": "^2.8.1",
|
|
17
17
|
"axios": "^0.26.1",
|
|
18
18
|
"i18next": "^19.4.4",
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { RichTextArea, UserDefinedFieldsService } from '@performant-software/shared-components';
|
|
4
|
+
import React, {
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useState,
|
|
9
|
+
type ComponentType
|
|
10
|
+
} from 'react';
|
|
11
|
+
import { Form } from 'semantic-ui-react';
|
|
12
|
+
import _ from 'underscore';
|
|
13
|
+
import DatePicker from './DatePicker';
|
|
14
|
+
|
|
15
|
+
type Props = {
|
|
16
|
+
defineableId?: number,
|
|
17
|
+
defineableType?: string,
|
|
18
|
+
isError: (key: string) => boolean,
|
|
19
|
+
item: any,
|
|
20
|
+
onChange: (obj: any) => void,
|
|
21
|
+
onClearValidationError: (...keys: Array<string>) => void
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const DataTypes = {
|
|
25
|
+
boolean: 'Boolean',
|
|
26
|
+
date: 'Date',
|
|
27
|
+
number: 'Number',
|
|
28
|
+
richText: 'RichText',
|
|
29
|
+
select: 'Select',
|
|
30
|
+
string: 'String',
|
|
31
|
+
text: 'Text'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const UserDefinedFieldsForm: ComponentType<any> = (props: Props) => {
|
|
35
|
+
const [fields, setFields] = useState([]);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sets the parsed value by parsing the JSON stored in the "user_defined" prop of the item.
|
|
39
|
+
*
|
|
40
|
+
* @type {unknown}
|
|
41
|
+
*/
|
|
42
|
+
const parsedValue = useMemo(() => {
|
|
43
|
+
let value = {};
|
|
44
|
+
|
|
45
|
+
if (_.isString(props.item.user_defined)) {
|
|
46
|
+
value = JSON.parse(props.item.user_defined);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return value;
|
|
50
|
+
}, [props.item.user_defined]);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns the key for the passed field.
|
|
54
|
+
*
|
|
55
|
+
* @type {function(*): string}
|
|
56
|
+
*/
|
|
57
|
+
const getFieldKey = useCallback((field) => `user_defined[${field.column_name}]`, []);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns true if an error exists on the state for the passed field.
|
|
61
|
+
*
|
|
62
|
+
* @type {function(*): *}
|
|
63
|
+
*/
|
|
64
|
+
const isError = useCallback((field) => props.isError(getFieldKey(field)), [getFieldKey, props.isError]);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Changes the value for the passed item.
|
|
68
|
+
*
|
|
69
|
+
* @type {(function(*, *): void)|*}
|
|
70
|
+
*/
|
|
71
|
+
const onChange = useCallback((field, value) => {
|
|
72
|
+
props.onChange(JSON.stringify({ ...parsedValue, [field.column_name]: value }));
|
|
73
|
+
|
|
74
|
+
// Clear the validation error if one exists
|
|
75
|
+
if (props.onClearValidationError) {
|
|
76
|
+
props.onClearValidationError(getFieldKey(field));
|
|
77
|
+
}
|
|
78
|
+
}, [getFieldKey, parsedValue, props.onChange, props.onClearValidationError]);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Renders the passed item.
|
|
82
|
+
*
|
|
83
|
+
* @type {function(*): *}
|
|
84
|
+
*/
|
|
85
|
+
const renderItem = useCallback((field) => {
|
|
86
|
+
let rendered;
|
|
87
|
+
|
|
88
|
+
const fieldValue = parsedValue && parsedValue[field.column_name];
|
|
89
|
+
|
|
90
|
+
if (field.data_type === DataTypes.string) {
|
|
91
|
+
rendered = (
|
|
92
|
+
<Form.Input
|
|
93
|
+
error={isError(field)}
|
|
94
|
+
label={field.column_name}
|
|
95
|
+
required={field.required}
|
|
96
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
97
|
+
value={fieldValue}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (field.data_type === DataTypes.number) {
|
|
103
|
+
rendered = (
|
|
104
|
+
<Form.Input
|
|
105
|
+
error={isError(field)}
|
|
106
|
+
label={field.column_name}
|
|
107
|
+
required={field.required}
|
|
108
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
109
|
+
value={fieldValue}
|
|
110
|
+
type='number'
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (field.data_type === DataTypes.select) {
|
|
116
|
+
rendered = (
|
|
117
|
+
<Form.Dropdown
|
|
118
|
+
clearable
|
|
119
|
+
error={isError(field)}
|
|
120
|
+
label={field.column_name}
|
|
121
|
+
multiple={field.allow_multiple}
|
|
122
|
+
required={field.required}
|
|
123
|
+
options={_.map(field.options, (option) => ({ key: option, value: option, text: option }))}
|
|
124
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
125
|
+
selectOnBlur={false}
|
|
126
|
+
selection
|
|
127
|
+
value={fieldValue}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (field.data_type === DataTypes.text) {
|
|
133
|
+
rendered = (
|
|
134
|
+
<Form.TextArea
|
|
135
|
+
error={isError(field)}
|
|
136
|
+
label={field.column_name}
|
|
137
|
+
required={field.required}
|
|
138
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
139
|
+
value={fieldValue}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (field.data_type === DataTypes.date) {
|
|
145
|
+
rendered = (
|
|
146
|
+
<Form.Input
|
|
147
|
+
error={isError(field)}
|
|
148
|
+
label={field.column_name}
|
|
149
|
+
required={field.required}
|
|
150
|
+
>
|
|
151
|
+
<DatePicker
|
|
152
|
+
onChange={(date) => onChange(field, date && date.toString())}
|
|
153
|
+
value={fieldValue && new Date(fieldValue)}
|
|
154
|
+
/>
|
|
155
|
+
</Form.Input>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (field.data_type === DataTypes.boolean) {
|
|
160
|
+
rendered = (
|
|
161
|
+
<Form.Checkbox
|
|
162
|
+
checked={!!fieldValue}
|
|
163
|
+
error={isError(field)}
|
|
164
|
+
label={field.column_name}
|
|
165
|
+
onChange={(e, { checked }) => onChange(field, checked)}
|
|
166
|
+
/>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (field.data_type === DataTypes.richText) {
|
|
171
|
+
rendered = (
|
|
172
|
+
<Form.Input
|
|
173
|
+
error={isError(field)}
|
|
174
|
+
label={field.column_name}
|
|
175
|
+
required={field.required}
|
|
176
|
+
>
|
|
177
|
+
<RichTextArea
|
|
178
|
+
onChange={(value) => onChange(field, value)}
|
|
179
|
+
value={fieldValue}
|
|
180
|
+
/>
|
|
181
|
+
</Form.Input>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return rendered;
|
|
186
|
+
}, [parsedValue, isError, onChange]);
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Fetches the user defined fields when the component mounts.
|
|
190
|
+
*/
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const params = {
|
|
193
|
+
defineable_id: props.defineableId,
|
|
194
|
+
defineable_type: props.defineableType
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
UserDefinedFieldsService
|
|
198
|
+
.fetchAll(params)
|
|
199
|
+
.then(({ data }) => setFields(data.user_defined_fields));
|
|
200
|
+
}, []);
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<>
|
|
204
|
+
{ _.map(fields, renderItem.bind(this)) }
|
|
205
|
+
</>
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export default UserDefinedFieldsForm;
|
package/src/index.js
CHANGED
|
@@ -83,6 +83,7 @@ export { default as Toaster } from './components/Toaster';
|
|
|
83
83
|
export { default as UserDefinedFieldModal } from './components/UserDefinedFieldModal';
|
|
84
84
|
export { default as UserDefinedFieldOptions } from './components/UserDefinedFieldOptions';
|
|
85
85
|
export { default as UserDefinedFieldsEmbeddedList } from './components/UserDefinedFieldsEmbeddedList';
|
|
86
|
+
export { default as UserDefinedFieldsForm } from './components/UserDefinedFieldsForm';
|
|
86
87
|
export { default as UserDefinedFieldsList } from './components/UserDefinedFieldsList';
|
|
87
88
|
export { default as VideoFrameSelector } from './components/VideoFrameSelector';
|
|
88
89
|
export { default as VideoPlayer } from './components/VideoPlayer';
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { RichTextArea, UserDefinedFieldsService } from '@performant-software/shared-components';
|
|
4
|
+
import React, {
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useState,
|
|
9
|
+
type ComponentType
|
|
10
|
+
} from 'react';
|
|
11
|
+
import { Form } from 'semantic-ui-react';
|
|
12
|
+
import _ from 'underscore';
|
|
13
|
+
import DatePicker from './DatePicker';
|
|
14
|
+
|
|
15
|
+
type Props = {
|
|
16
|
+
defineableId?: number,
|
|
17
|
+
defineableType?: string,
|
|
18
|
+
isError: (key: string) => boolean,
|
|
19
|
+
item: any,
|
|
20
|
+
onChange: (obj: any) => void,
|
|
21
|
+
onClearValidationError: (...keys: Array<string>) => void
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const DataTypes = {
|
|
25
|
+
boolean: 'Boolean',
|
|
26
|
+
date: 'Date',
|
|
27
|
+
number: 'Number',
|
|
28
|
+
richText: 'RichText',
|
|
29
|
+
select: 'Select',
|
|
30
|
+
string: 'String',
|
|
31
|
+
text: 'Text'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const UserDefinedFieldsForm: ComponentType<any> = (props: Props) => {
|
|
35
|
+
const [fields, setFields] = useState([]);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sets the parsed value by parsing the JSON stored in the "user_defined" prop of the item.
|
|
39
|
+
*
|
|
40
|
+
* @type {unknown}
|
|
41
|
+
*/
|
|
42
|
+
const parsedValue = useMemo(() => {
|
|
43
|
+
let value = {};
|
|
44
|
+
|
|
45
|
+
if (_.isString(props.item.user_defined)) {
|
|
46
|
+
value = JSON.parse(props.item.user_defined);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return value;
|
|
50
|
+
}, [props.item.user_defined]);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns the key for the passed field.
|
|
54
|
+
*
|
|
55
|
+
* @type {function(*): string}
|
|
56
|
+
*/
|
|
57
|
+
const getFieldKey = useCallback((field) => `user_defined[${field.column_name}]`, []);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns true if an error exists on the state for the passed field.
|
|
61
|
+
*
|
|
62
|
+
* @type {function(*): *}
|
|
63
|
+
*/
|
|
64
|
+
const isError = useCallback((field) => props.isError(getFieldKey(field)), [getFieldKey, props.isError]);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Changes the value for the passed item.
|
|
68
|
+
*
|
|
69
|
+
* @type {(function(*, *): void)|*}
|
|
70
|
+
*/
|
|
71
|
+
const onChange = useCallback((field, value) => {
|
|
72
|
+
props.onChange(JSON.stringify({ ...parsedValue, [field.column_name]: value }));
|
|
73
|
+
|
|
74
|
+
// Clear the validation error if one exists
|
|
75
|
+
if (props.onClearValidationError) {
|
|
76
|
+
props.onClearValidationError(getFieldKey(field));
|
|
77
|
+
}
|
|
78
|
+
}, [getFieldKey, parsedValue, props.onChange, props.onClearValidationError]);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Renders the passed item.
|
|
82
|
+
*
|
|
83
|
+
* @type {function(*): *}
|
|
84
|
+
*/
|
|
85
|
+
const renderItem = useCallback((field) => {
|
|
86
|
+
let rendered;
|
|
87
|
+
|
|
88
|
+
const fieldValue = parsedValue && parsedValue[field.column_name];
|
|
89
|
+
|
|
90
|
+
if (field.data_type === DataTypes.string) {
|
|
91
|
+
rendered = (
|
|
92
|
+
<Form.Input
|
|
93
|
+
error={isError(field)}
|
|
94
|
+
label={field.column_name}
|
|
95
|
+
required={field.required}
|
|
96
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
97
|
+
value={fieldValue}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (field.data_type === DataTypes.number) {
|
|
103
|
+
rendered = (
|
|
104
|
+
<Form.Input
|
|
105
|
+
error={isError(field)}
|
|
106
|
+
label={field.column_name}
|
|
107
|
+
required={field.required}
|
|
108
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
109
|
+
value={fieldValue}
|
|
110
|
+
type='number'
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (field.data_type === DataTypes.select) {
|
|
116
|
+
rendered = (
|
|
117
|
+
<Form.Dropdown
|
|
118
|
+
clearable
|
|
119
|
+
error={isError(field)}
|
|
120
|
+
label={field.column_name}
|
|
121
|
+
multiple={field.allow_multiple}
|
|
122
|
+
required={field.required}
|
|
123
|
+
options={_.map(field.options, (option) => ({ key: option, value: option, text: option }))}
|
|
124
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
125
|
+
selectOnBlur={false}
|
|
126
|
+
selection
|
|
127
|
+
value={fieldValue}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (field.data_type === DataTypes.text) {
|
|
133
|
+
rendered = (
|
|
134
|
+
<Form.TextArea
|
|
135
|
+
error={isError(field)}
|
|
136
|
+
label={field.column_name}
|
|
137
|
+
required={field.required}
|
|
138
|
+
onChange={(e, { value }) => onChange(field, value)}
|
|
139
|
+
value={fieldValue}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (field.data_type === DataTypes.date) {
|
|
145
|
+
rendered = (
|
|
146
|
+
<Form.Input
|
|
147
|
+
error={isError(field)}
|
|
148
|
+
label={field.column_name}
|
|
149
|
+
required={field.required}
|
|
150
|
+
>
|
|
151
|
+
<DatePicker
|
|
152
|
+
onChange={(date) => onChange(field, date && date.toString())}
|
|
153
|
+
value={fieldValue && new Date(fieldValue)}
|
|
154
|
+
/>
|
|
155
|
+
</Form.Input>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (field.data_type === DataTypes.boolean) {
|
|
160
|
+
rendered = (
|
|
161
|
+
<Form.Checkbox
|
|
162
|
+
checked={!!fieldValue}
|
|
163
|
+
error={isError(field)}
|
|
164
|
+
label={field.column_name}
|
|
165
|
+
onChange={(e, { checked }) => onChange(field, checked)}
|
|
166
|
+
/>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (field.data_type === DataTypes.richText) {
|
|
171
|
+
rendered = (
|
|
172
|
+
<Form.Input
|
|
173
|
+
error={isError(field)}
|
|
174
|
+
label={field.column_name}
|
|
175
|
+
required={field.required}
|
|
176
|
+
>
|
|
177
|
+
<RichTextArea
|
|
178
|
+
onChange={(value) => onChange(field, value)}
|
|
179
|
+
value={fieldValue}
|
|
180
|
+
/>
|
|
181
|
+
</Form.Input>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return rendered;
|
|
186
|
+
}, [parsedValue, isError, onChange]);
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Fetches the user defined fields when the component mounts.
|
|
190
|
+
*/
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const params = {
|
|
193
|
+
defineable_id: props.defineableId,
|
|
194
|
+
defineable_type: props.defineableType
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
UserDefinedFieldsService
|
|
198
|
+
.fetchAll(params)
|
|
199
|
+
.then(({ data }) => setFields(data.user_defined_fields));
|
|
200
|
+
}, []);
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<>
|
|
204
|
+
{ _.map(fields, renderItem.bind(this)) }
|
|
205
|
+
</>
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export default UserDefinedFieldsForm;
|
package/types/index.js.flow
CHANGED
|
@@ -83,6 +83,7 @@ export { default as Toaster } from './components/Toaster';
|
|
|
83
83
|
export { default as UserDefinedFieldModal } from './components/UserDefinedFieldModal';
|
|
84
84
|
export { default as UserDefinedFieldOptions } from './components/UserDefinedFieldOptions';
|
|
85
85
|
export { default as UserDefinedFieldsEmbeddedList } from './components/UserDefinedFieldsEmbeddedList';
|
|
86
|
+
export { default as UserDefinedFieldsForm } from './components/UserDefinedFieldsForm';
|
|
86
87
|
export { default as UserDefinedFieldsList } from './components/UserDefinedFieldsList';
|
|
87
88
|
export { default as VideoFrameSelector } from './components/VideoFrameSelector';
|
|
88
89
|
export { default as VideoPlayer } from './components/VideoPlayer';
|