@pareto-engineering/design-system 4.2.1 → 4.2.3

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.
Files changed (55) hide show
  1. package/dist/cjs/f/FormInput/FormInput.js +6 -0
  2. package/dist/cjs/f/common/utils/index.js +8 -1
  3. package/dist/cjs/f/common/utils/validators.js +17 -0
  4. package/dist/cjs/f/fields/ChoicesInput/common/Choice/Choice.js +3 -2
  5. package/dist/cjs/f/fields/FileUpload/FileUpload.js +228 -0
  6. package/dist/cjs/f/fields/FileUpload/common/FilePreview/FilePreview.js +90 -0
  7. package/dist/cjs/f/fields/FileUpload/common/FilePreview/index.js +13 -0
  8. package/dist/cjs/f/fields/FileUpload/common/FilePreview/styles.scss +92 -0
  9. package/dist/cjs/f/fields/FileUpload/common/index.js +12 -0
  10. package/dist/cjs/f/fields/FileUpload/fileUploadOptions.js +31 -0
  11. package/dist/cjs/f/fields/FileUpload/index.js +20 -0
  12. package/dist/cjs/f/fields/FileUpload/styles.scss +73 -0
  13. package/dist/cjs/f/fields/index.js +14 -1
  14. package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +34 -7
  15. package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  16. package/dist/cjs/g/FormBuilder/common/Renderer/Renderer.js +9 -3
  17. package/dist/cjs/g/FormBuilder/common/Renderer/common/Section/Section.js +2 -2
  18. package/dist/es/f/FormInput/FormInput.js +7 -1
  19. package/dist/es/f/common/utils/index.js +2 -1
  20. package/dist/es/f/common/utils/validators.js +10 -0
  21. package/dist/es/f/fields/ChoicesInput/common/Choice/Choice.js +3 -2
  22. package/dist/es/f/fields/FileUpload/FileUpload.js +218 -0
  23. package/dist/es/f/fields/FileUpload/common/FilePreview/FilePreview.js +80 -0
  24. package/dist/es/f/fields/FileUpload/common/FilePreview/index.js +2 -0
  25. package/dist/es/f/fields/FileUpload/common/FilePreview/styles.scss +92 -0
  26. package/dist/es/f/fields/FileUpload/common/index.js +2 -0
  27. package/dist/es/f/fields/FileUpload/fileUploadOptions.js +25 -0
  28. package/dist/es/f/fields/FileUpload/index.js +3 -0
  29. package/dist/es/f/fields/FileUpload/styles.scss +73 -0
  30. package/dist/es/f/fields/index.js +2 -1
  31. package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +35 -8
  32. package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  33. package/dist/es/g/FormBuilder/common/Renderer/Renderer.js +9 -3
  34. package/dist/es/g/FormBuilder/common/Renderer/common/Section/Section.js +2 -1
  35. package/package.json +3 -3
  36. package/src/stories/f/FileUpload.stories.jsx +55 -0
  37. package/src/stories/g/FormBuilder.stories.jsx +19 -97
  38. package/src/ui/f/FormInput/FormInput.jsx +11 -0
  39. package/src/ui/f/common/utils/index.js +1 -0
  40. package/src/ui/f/common/utils/validators.js +10 -0
  41. package/src/ui/f/fields/ChoicesInput/common/Choice/Choice.jsx +2 -0
  42. package/src/ui/f/fields/FileUpload/FileUpload.jsx +277 -0
  43. package/src/ui/f/fields/FileUpload/common/FilePreview/FilePreview.jsx +115 -0
  44. package/src/ui/f/fields/FileUpload/common/FilePreview/index.js +2 -0
  45. package/src/ui/f/fields/FileUpload/common/FilePreview/styles.scss +92 -0
  46. package/src/ui/f/fields/FileUpload/common/index.js +2 -0
  47. package/src/ui/f/fields/FileUpload/fileUploadOptions.js +32 -0
  48. package/src/ui/f/fields/FileUpload/index.js +3 -0
  49. package/src/ui/f/fields/FileUpload/styles.scss +73 -0
  50. package/src/ui/f/fields/index.js +1 -0
  51. package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.jsx +53 -11
  52. package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  53. package/src/ui/g/FormBuilder/common/Renderer/Renderer.jsx +13 -3
  54. package/src/ui/g/FormBuilder/common/Renderer/common/Section/Section.jsx +7 -2
  55. package/tests/__snapshots__/Storyshots.test.js.snap +163 -159
@@ -0,0 +1,277 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+ import * as React from 'react'
3
+
4
+ import PropTypes from 'prop-types'
5
+
6
+ import { useField } from 'formik'
7
+
8
+ import styleNames from '@pareto-engineering/bem/exports'
9
+
10
+ import './styles.scss'
11
+
12
+ import { FilePreview } from './common'
13
+
14
+ import { FormLabel, FormDescription } from '../../common'
15
+
16
+ // Local Definitions
17
+
18
+ const baseClassName = styleNames.base
19
+
20
+ const componentClassName = 'file-upload'
21
+
22
+ /**
23
+ * This is the component description.
24
+ */
25
+ const FileUpload = ({
26
+ id,
27
+ className:userClassName,
28
+ style,
29
+ label,
30
+ description,
31
+ accept,
32
+ multiple,
33
+ name,
34
+ disabled,
35
+ maxCount,
36
+ maxCountError,
37
+ maxSize,
38
+ maxSizeError,
39
+ onChange,
40
+ onDelete,
41
+ // TODO: TBD when connecting with BE
42
+ // onPreview,
43
+ // progress,
44
+ // percent,
45
+ // uploadStatus,
46
+ filePreviewAlignment,
47
+ optional,
48
+ labelColor,
49
+ color,
50
+ // ...otherProps
51
+ }) => {
52
+ const [field, , helpers] = useField({ name })
53
+
54
+ const { value } = field
55
+
56
+ const { setError, setValue, setTouched } = helpers
57
+
58
+ const handleChange = (event) => {
59
+ setTouched(true, false)
60
+ const newFiles = Array.from(event.currentTarget.files)
61
+ const currentFiles = value || []
62
+
63
+ const currentCount = currentFiles.length + newFiles.length
64
+ if (maxCount && currentCount > maxCount) {
65
+ setError(maxCountError || `Maximum of ${maxCount} files allowed`)
66
+ return
67
+ }
68
+
69
+ const duplicateFiles = newFiles.filter(
70
+ (newFile) => currentFiles.some((currentFile) => currentFile.name === newFile.name),
71
+ )
72
+
73
+ if (duplicateFiles.length > 0) {
74
+ setError('You cannot upload the same file twice.')
75
+ return
76
+ }
77
+
78
+ if (maxSize) {
79
+ const oversizedFiles = newFiles.filter((file) => file.size > maxSize)
80
+ if (oversizedFiles.length > 0) {
81
+ setError(maxSizeError || `File size exceeds the limit of ${maxSize} bytes`)
82
+ return
83
+ }
84
+ }
85
+
86
+ const updatedFiles = [...currentFiles, ...newFiles]
87
+
88
+ setValue(updatedFiles)
89
+ onChange?.(updatedFiles)
90
+
91
+ // eslint-disable-next-line no-param-reassign
92
+ event.target.value = null
93
+ }
94
+
95
+ const handleDelete = (fileIndex) => {
96
+ const updatedFiles = value.filter((_, i) => i !== fileIndex)
97
+ setValue(updatedFiles)
98
+
99
+ onDelete?.(fileIndex)
100
+ }
101
+
102
+ const acceptOptions = Array.isArray(accept) ? accept?.join(',') : accept
103
+
104
+ return (
105
+ <div
106
+ id={id}
107
+ className={[
108
+
109
+ baseClassName,
110
+
111
+ componentClassName,
112
+ userClassName,
113
+ `preview-${filePreviewAlignment}`,
114
+ `y-${color}`,
115
+ ]
116
+ .filter((e) => e)
117
+ .join(' ')}
118
+ style={style}
119
+ >
120
+ <p>{label}</p>
121
+ <FormDescription className="s-1" description={description} name={name} />
122
+ {!disabled && (
123
+ <FormLabel
124
+ name={name}
125
+ color={labelColor}
126
+ optional={optional}
127
+ >
128
+ <input
129
+ id={name}
130
+ className="file"
131
+ type="file"
132
+ accept={acceptOptions}
133
+ multiple={multiple || (maxCount && maxCount > 0)}
134
+ disabled={disabled}
135
+ onChange={handleChange}
136
+ />
137
+ <span className="ai-icon">
138
+ U
139
+ </span>
140
+ <span>
141
+ Attach file
142
+ </span>
143
+ </FormLabel>
144
+ )}
145
+ {value?.length > 0 && (
146
+ <div className="attached-files">
147
+ <p>Attached files:</p>
148
+ <div className="files">
149
+ {value.map((file, indx) => (
150
+ <FilePreview
151
+ file={file}
152
+ key={file.name}
153
+ disabled={disabled}
154
+ handleDelete={() => handleDelete(indx)}
155
+ />
156
+ ))}
157
+ </div>
158
+ </div>
159
+ )}
160
+ </div>
161
+ )
162
+ }
163
+
164
+ FileUpload.propTypes = {
165
+ /**
166
+ * The HTML id for this element
167
+ */
168
+ id:PropTypes.string,
169
+
170
+ /**
171
+ * The HTML class names for this element
172
+ */
173
+ className:PropTypes.string,
174
+
175
+ /**
176
+ * The React-written, css properties for this element.
177
+ */
178
+ style:PropTypes.objectOf(PropTypes.string),
179
+
180
+ /**
181
+ * The label text for the file input
182
+ */
183
+ label:PropTypes.string,
184
+
185
+ /**
186
+ * The description text below the label
187
+ */
188
+ description:PropTypes.string,
189
+
190
+ /**
191
+ * The color of the text
192
+ */
193
+ color:PropTypes.string,
194
+
195
+ /**
196
+ * The accepted file types (e.g., 'image/*')
197
+ */
198
+ accept:PropTypes.string,
199
+
200
+ /**
201
+ * Whether to allow multiple file uploads
202
+ */
203
+ multiple:PropTypes.bool,
204
+
205
+ /**
206
+ * The name attribute for the input
207
+ */
208
+ name:PropTypes.string.isRequired,
209
+
210
+ /**
211
+ * Whether the the input should be disabled
212
+ */
213
+ disabled:PropTypes.bool,
214
+
215
+ /**
216
+ * The maximum number of files allowed
217
+ */
218
+ maxCount:PropTypes.number,
219
+
220
+ /**
221
+ * The error message when max count is exceeded
222
+ */
223
+ maxCountError:PropTypes.string,
224
+
225
+ /**
226
+ * The maximum size of files allowed (in bytes)
227
+ */
228
+ maxSize:PropTypes.number,
229
+
230
+ /**
231
+ * The error message when file size exceeds max size
232
+ */
233
+ maxSizeError:PropTypes.string,
234
+
235
+ /**
236
+ * The upload progress value (0-100)
237
+ */
238
+ // progress:PropTypes.number,
239
+
240
+ /**
241
+ * The function to handle file change events
242
+ */
243
+ onChange:PropTypes.func,
244
+
245
+ /**
246
+ * The function to handle file delete events
247
+ */
248
+ onDelete:PropTypes.func,
249
+
250
+ /**
251
+ * The function to handle file download events
252
+ */
253
+ // onDownload:PropTypes.func,
254
+
255
+ /**
256
+ * The function to handle file preview events
257
+ */
258
+ // onPreview:PropTypes.func,
259
+
260
+ /**
261
+ * The upload status of the file if being sent to an external service (e.g., 'uploading', 'done')
262
+ */
263
+ // uploadStatus:PropTypes.oneOf(['uploading', 'done', 'failed']),
264
+
265
+ /**
266
+ * The alignment for file previews WRT the upload input
267
+ */
268
+ filePreviewAlignment:PropTypes.oneOf(['left', 'right', 'top', 'bottom']),
269
+ }
270
+
271
+ FileUpload.defaultProps = {
272
+ disabled :false,
273
+ color :'paragraph',
274
+ filePreviewAlignment:'bottom',
275
+ }
276
+
277
+ export default FileUpload
@@ -0,0 +1,115 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+ import * as React from 'react'
3
+
4
+ import PropTypes from 'prop-types'
5
+
6
+ import styleNames from '@pareto-engineering/bem/exports'
7
+
8
+ // Local Definitions
9
+
10
+ import './styles.scss'
11
+
12
+ import { ProgressBar } from 'ui/a'
13
+
14
+ import { Button } from 'ui/b'
15
+
16
+ import { fileUploadOptions } from '../../fileUploadOptions'
17
+
18
+ const baseClassName = styleNames.base
19
+
20
+ const componentClassName = 'file-preview'
21
+
22
+ const getFileType = (file) => {
23
+ const mimeType = file.type
24
+ const extension = file.name.split('.').pop().toLowerCase()
25
+ const fileType = fileUploadOptions.find(
26
+ (option) => option.value.includes(mimeType) || option.value.includes(extension))
27
+
28
+ return fileType ? fileType.key : 'unknown'
29
+ }
30
+
31
+ /**
32
+ * This is the component description.
33
+ */
34
+ const FilePreview = ({
35
+ id,
36
+ className:userClassName,
37
+ style,
38
+ file,
39
+ disabled,
40
+ handleDelete,
41
+ // ...otherProps
42
+ }) => {
43
+ const type = getFileType(file)
44
+
45
+ return (
46
+ <div
47
+ id={id}
48
+ className={[
49
+
50
+ baseClassName,
51
+
52
+ componentClassName,
53
+ userClassName,
54
+ ]
55
+ .filter((e) => e)
56
+ .join(' ')}
57
+ style={style}
58
+ >
59
+ <div className="identity">
60
+ <span
61
+ className={
62
+ [
63
+ 'type',
64
+ type.toLowerCase(),
65
+ ]
66
+ .filter((e) => e)
67
+ .join(' ')
68
+ }
69
+ >
70
+ {type}
71
+ </span>
72
+ <span title={file.name} className="name">{file.name}</span>
73
+ <Button isCompact isSimple onClick={handleDelete}>
74
+ <span className="icon x-ui-icons c-x">Y</span>
75
+ </Button>
76
+ </div>
77
+ {!disabled && (
78
+ <div className="progress-status">
79
+ <div className="status">
80
+ <span className="icon">I</span>
81
+ <span>Uploaded</span>
82
+ </div>
83
+ <ProgressBar
84
+ color="green"
85
+ progress={100}
86
+ height="3px"
87
+ />
88
+ </div>
89
+ )}
90
+ </div>
91
+ )
92
+ }
93
+
94
+ FilePreview.propTypes = {
95
+ /**
96
+ * The HTML id for this element
97
+ */
98
+ id:PropTypes.string,
99
+
100
+ /**
101
+ * The HTML class names for this element
102
+ */
103
+ className:PropTypes.string,
104
+
105
+ /**
106
+ * The React-written, css properties for this element.
107
+ */
108
+ style:PropTypes.objectOf(PropTypes.string),
109
+ }
110
+
111
+ FilePreview.defaultProps = {
112
+ // someProp:false
113
+ }
114
+
115
+ export default FilePreview
@@ -0,0 +1,2 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+ export { default as FilePreview } from './FilePreview'
@@ -0,0 +1,92 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+
3
+ @use "@pareto-engineering/bem";
4
+
5
+ $default-padding: 1rem;
6
+ $files-per-row: 3;
7
+ $total-gaps: $files-per-row - 1;
8
+ $combined-gap: calc(var(--gap) * $total-gaps);
9
+ $width-without-gaps: calc(100% - $combined-gap);
10
+ $default-file-width: calc($width-without-gaps / $files-per-row);
11
+
12
+ .#{bem.$base}.file-preview {
13
+ border: var(--theme-default-border-style) var(--ui-lines);
14
+ border-radius: calc(var(--theme-default-border-radius) / 2);
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: calc(var(--gap) / 4);
18
+ padding: $default-padding;
19
+ width: $default-file-width;
20
+
21
+ > .identity {
22
+ align-items: center;
23
+ display: flex;
24
+ gap: calc(var(--gap) / 2);
25
+
26
+ > .type {
27
+ padding-block: calc($default-padding / 4);
28
+ padding-inline: calc($default-padding / 2);
29
+
30
+ // TODO: update the colors to use variables. These are colors from the new DS
31
+ &.pdf {
32
+ background-color: #fdead7;
33
+ color: #b93814;
34
+ }
35
+
36
+ &.file {
37
+ background-color: #eef2f6;
38
+ color: #364151;
39
+ }
40
+
41
+ &.txt {
42
+ background-color: #d1dfff;
43
+ color: #004eeb;
44
+ }
45
+
46
+
47
+ &.vid {
48
+ background-color: #ebe9fe;
49
+ color: #5a25dc;
50
+ }
51
+
52
+ &.img {
53
+ background-color: #cbfbef;
54
+ color: #107569;
55
+ }
56
+
57
+ &.aud {
58
+ background-color: #fef7c3;
59
+ color: #a15c08;
60
+ }
61
+ }
62
+
63
+ > .name {
64
+ overflow: hidden;
65
+ text-overflow: ellipsis;
66
+ white-space: nowrap;
67
+ }
68
+
69
+ > .#{bem.$base}.button {
70
+ cursor: pointer;
71
+ margin-left: auto;
72
+ }
73
+ }
74
+
75
+ > .progress-status {
76
+ > .status {
77
+ align-items: center;
78
+ // TODO handle as a prop
79
+ color: var(--green);
80
+ display: flex;
81
+ gap: calc(var(--gap) / 2);
82
+
83
+ > .icon {
84
+ background-color: var(--green);
85
+ border-radius: 50%;
86
+ color: var(--on-green);
87
+ font-size: calc(var(--s-2) * 1rem);
88
+ padding: calc($default-padding / 4);
89
+ }
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,2 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+ export { FilePreview } from './FilePreview'
@@ -0,0 +1,32 @@
1
+ export const fileUploadOptions = [
2
+ {
3
+ key :'FILE',
4
+ value:'.doc,.docx,.odt,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.text',
5
+ label:'Documents',
6
+ },
7
+ {
8
+ key :'TXT',
9
+ value:'.xls,.xlsx,.ods,.csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet,text/csv',
10
+ label:'Spreadsheets',
11
+ },
12
+ {
13
+ key :'PDF',
14
+ value:'.pdf,application/pdf',
15
+ label:'PDF',
16
+ },
17
+ {
18
+ key :'VID',
19
+ value:'.mp4,.ogg,.webm,video/mp4,video/ogg,video/webm',
20
+ label:'Videos',
21
+ },
22
+ {
23
+ key :'IMG',
24
+ value:'.png,.jpeg,.jpg,.gif,.webp,image/png,image/jpeg,image/gif,image/webp',
25
+ label:'Images',
26
+ },
27
+ {
28
+ key :'AUD',
29
+ value:'.mp3,.ogg,.wav,audio/mpeg,audio/ogg,audio/wav',
30
+ label:'Audio',
31
+ },
32
+ ]
@@ -0,0 +1,3 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+ export { default as FileUpload } from './FileUpload'
3
+ export { fileUploadOptions } from './fileUploadOptions'
@@ -0,0 +1,73 @@
1
+ /* @pareto-engineering/generator-front 1.1.1-alpha.2 */
2
+
3
+ @use "@pareto-engineering/bem";
4
+
5
+ $default-padding: 1rem;
6
+ $default-margin: 1rem;
7
+
8
+ .#{bem.$base}.file-upload {
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: var(--gap);
12
+
13
+
14
+ > p {
15
+ color: var(--y);
16
+ margin: 0;
17
+ }
18
+
19
+ > .#{bem.$base}.description {
20
+ height: auto;
21
+ }
22
+
23
+ > .#{bem.$base}.form-label {
24
+ align-items: center;
25
+ border: var(--theme-default-border-style) var(--ui-lines);
26
+ border-radius: calc(var(--theme-default-border-radius) / 2);
27
+ color: var(--heading);
28
+ cursor: pointer;
29
+ display: flex;
30
+ gap: calc(var(--gap) / 2);
31
+ padding: $default-padding;
32
+ width: fit-content;
33
+
34
+ &:has(>input:disabled) {
35
+ --icon-color: var(--hard-disabled);
36
+ color: var(--hard-disabled);
37
+ cursor: not-allowed;
38
+ }
39
+
40
+ &:has(>input:not(:disabled)) {
41
+ &:hover,
42
+ &:active {
43
+ border: var(--theme-hover-input-border);
44
+ }
45
+ }
46
+
47
+ > .ai-icon {
48
+ color: var(--icon-color, var(--interactive));
49
+ font-size: calc(var(--s2) * 1rem);
50
+ }
51
+
52
+ > input[type="file"] {
53
+ display: none;
54
+ }
55
+ }
56
+
57
+ > .attached-files {
58
+ display: flex;
59
+ gap: var(--gap);
60
+
61
+ > p {
62
+ margin: 0;
63
+ }
64
+
65
+ > .files {
66
+ align-items: center;
67
+ display: flex;
68
+ flex-wrap: wrap;
69
+ gap: var(--gap);
70
+ width: 100%;
71
+ }
72
+ }
73
+ }
@@ -9,3 +9,4 @@ export { Checkbox } from './Checkbox'
9
9
  export { QueryChoices } from './QueryChoices'
10
10
  export { LinkInput } from './LinkInput'
11
11
  export { EditorInput } from './EditorInput'
12
+ export { FileUpload, fileUploadOptions } from './FileUpload'