@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.1",
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.1",
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;
@@ -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';