@pareto-engineering/design-system 4.2.1-alpha.0 → 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 (73) hide show
  1. package/dist/cjs/a/XMLEditor/XMLEditor.js +3 -15
  2. package/dist/cjs/f/FormInput/FormInput.js +6 -0
  3. package/dist/cjs/f/common/utils/index.js +8 -1
  4. package/dist/cjs/f/common/utils/validators.js +17 -0
  5. package/dist/cjs/f/fields/ChoicesInput/common/Choice/Choice.js +3 -2
  6. package/dist/cjs/f/fields/EditorInput/EditorInput.js +2 -15
  7. package/dist/cjs/f/fields/EditorInput/common/StopPropagationPlugin.js +2 -7
  8. package/dist/cjs/f/fields/FileUpload/FileUpload.js +228 -0
  9. package/dist/cjs/f/fields/FileUpload/common/FilePreview/FilePreview.js +90 -0
  10. package/dist/cjs/f/fields/FileUpload/common/FilePreview/index.js +13 -0
  11. package/dist/cjs/f/fields/FileUpload/common/FilePreview/styles.scss +92 -0
  12. package/dist/cjs/f/fields/FileUpload/common/index.js +12 -0
  13. package/dist/cjs/f/fields/FileUpload/fileUploadOptions.js +31 -0
  14. package/dist/cjs/f/fields/FileUpload/index.js +20 -0
  15. package/dist/cjs/f/fields/FileUpload/styles.scss +73 -0
  16. package/dist/cjs/f/fields/SelectInput/common/Menu/Menu.js +1 -1
  17. package/dist/cjs/f/fields/SelectInput/common/Multiple/Multiple.js +1 -1
  18. package/dist/cjs/f/fields/SelectInput/styles.scss +1 -1
  19. package/dist/cjs/f/fields/index.js +14 -1
  20. package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +34 -7
  21. package/dist/cjs/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  22. package/dist/cjs/g/FormBuilder/common/Renderer/Renderer.js +9 -3
  23. package/dist/cjs/g/FormBuilder/common/Renderer/common/Section/Section.js +2 -2
  24. package/dist/es/a/XMLEditor/XMLEditor.js +3 -15
  25. package/dist/es/f/FormInput/FormInput.js +7 -1
  26. package/dist/es/f/common/utils/index.js +2 -1
  27. package/dist/es/f/common/utils/validators.js +10 -0
  28. package/dist/es/f/fields/ChoicesInput/common/Choice/Choice.js +3 -2
  29. package/dist/es/f/fields/EditorInput/EditorInput.js +2 -15
  30. package/dist/es/f/fields/EditorInput/common/StopPropagationPlugin.js +2 -6
  31. package/dist/es/f/fields/FileUpload/FileUpload.js +218 -0
  32. package/dist/es/f/fields/FileUpload/common/FilePreview/FilePreview.js +80 -0
  33. package/dist/es/f/fields/FileUpload/common/FilePreview/index.js +2 -0
  34. package/dist/es/f/fields/FileUpload/common/FilePreview/styles.scss +92 -0
  35. package/dist/es/f/fields/FileUpload/common/index.js +2 -0
  36. package/dist/es/f/fields/FileUpload/fileUploadOptions.js +25 -0
  37. package/dist/es/f/fields/FileUpload/index.js +3 -0
  38. package/dist/es/f/fields/FileUpload/styles.scss +73 -0
  39. package/dist/es/f/fields/SelectInput/common/Menu/Menu.js +1 -1
  40. package/dist/es/f/fields/SelectInput/common/Multiple/Multiple.js +1 -1
  41. package/dist/es/f/fields/SelectInput/styles.scss +1 -1
  42. package/dist/es/f/fields/index.js +2 -1
  43. package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.js +35 -8
  44. package/dist/es/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  45. package/dist/es/g/FormBuilder/common/Renderer/Renderer.js +9 -3
  46. package/dist/es/g/FormBuilder/common/Renderer/common/Section/Section.js +2 -1
  47. package/package.json +4 -4
  48. package/src/stories/f/FileUpload.stories.jsx +55 -0
  49. package/src/stories/g/FormBuilder.stories.jsx +19 -97
  50. package/src/ui/a/XMLEditor/XMLEditor.jsx +1 -15
  51. package/src/ui/f/FormInput/FormInput.jsx +11 -0
  52. package/src/ui/f/common/utils/index.js +1 -0
  53. package/src/ui/f/common/utils/validators.js +10 -0
  54. package/src/ui/f/fields/ChoicesInput/common/Choice/Choice.jsx +2 -0
  55. package/src/ui/f/fields/EditorInput/EditorInput.jsx +1 -15
  56. package/src/ui/f/fields/EditorInput/common/StopPropagationPlugin.jsx +2 -6
  57. package/src/ui/f/fields/FileUpload/FileUpload.jsx +277 -0
  58. package/src/ui/f/fields/FileUpload/common/FilePreview/FilePreview.jsx +115 -0
  59. package/src/ui/f/fields/FileUpload/common/FilePreview/index.js +2 -0
  60. package/src/ui/f/fields/FileUpload/common/FilePreview/styles.scss +92 -0
  61. package/src/ui/f/fields/FileUpload/common/index.js +2 -0
  62. package/src/ui/f/fields/FileUpload/fileUploadOptions.js +32 -0
  63. package/src/ui/f/fields/FileUpload/index.js +3 -0
  64. package/src/ui/f/fields/FileUpload/styles.scss +73 -0
  65. package/src/ui/f/fields/SelectInput/common/Menu/Menu.jsx +1 -0
  66. package/src/ui/f/fields/SelectInput/common/Multiple/Multiple.jsx +1 -1
  67. package/src/ui/f/fields/SelectInput/styles.scss +1 -1
  68. package/src/ui/f/fields/index.js +1 -0
  69. package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/InputBuilder.jsx +53 -11
  70. package/src/ui/g/FormBuilder/common/Builder/common/InputBuilder/styles.scss +11 -0
  71. package/src/ui/g/FormBuilder/common/Renderer/Renderer.jsx +13 -3
  72. package/src/ui/g/FormBuilder/common/Renderer/common/Section/Section.jsx +7 -2
  73. package/tests/__snapshots__/Storyshots.test.js.snap +163 -159
@@ -41,109 +41,28 @@ export const Builder = {
41
41
  )
42
42
  },
43
43
  }
44
+
44
45
  const sampleFormData = {
45
46
  sections:[
46
47
  {
47
- title :'This is a survey of some of the best LLM models out there',
48
- description:'{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Best LLM models for software developers","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h3"},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Chat GPT","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Perplexity","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Gemini","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Claude","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Please add more exciting models that we might missed","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"You can make them ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"bold","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" or ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"Italic or ","type":"text","version":1},{"detail":0,"format":10,"mode":"normal","style":"","text":"underlined","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"detail":0,"format":4,"mode":"normal","style":"color: #110b53;","text":"You can also color them and strike through them!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}',
48
+ title :'Hello',
49
+ description:'{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Test file upload ","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}',
49
50
  key :'section-0',
50
51
  inputs :[
51
52
  {
52
- name :'section_0_input_0',
53
- label :'Name the Model',
54
- type :'text',
55
- required :true,
56
- options :[],
57
- conditionalLogic:null,
58
- description :'',
59
- },
60
- {
61
- name :'section_0_input_1',
62
- label :'Describe the Model in detail',
63
- type :'textarea',
64
- required :false,
65
- options :[],
66
- conditionalLogic:null,
67
- description :'brief model description',
68
- },
69
- ],
70
- orderedInputDragIds:[],
71
- navigation :{
72
- nextSection:'next',
73
- },
74
- },
75
- {
76
- title :'Rate the Models',
77
- description:'{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Best LLM models for software developers","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Chat GPT","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Perplexity","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Gemini","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Claude","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Please add more exciting models that we might missed","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"You can make them ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"bold","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" or ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"Italic or ","type":"text","version":1},{"detail":0,"format":10,"mode":"normal","style":"","text":"underlined","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"detail":0,"format":4,"mode":"normal","style":"","text":"You can also color them and strike through them!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}',
78
- key :'section-1',
79
- inputs :[
80
- {
81
- type :'choices',
82
- options:[
83
- {
84
- key :'sections-1-inputs-0-options--1',
85
- value :'Chat GPT',
86
- label :'Chat GPT',
87
- nextSection:'',
88
- },
89
- {
90
- key :'sections-1-inputs-0-options-0',
91
- value :'Gemini',
92
- label :'Gemini',
93
- nextSection:'',
94
- },
53
+ name :'section_0_input_0',
54
+ label :'The file upload ',
55
+ type :'file',
56
+ required :false,
57
+ options :[],
58
+ conditionalLogic :null,
59
+ description :'',
60
+ maxCount :3,
61
+ showSpecificFileTypes:true,
62
+ accept :[
63
+ '.mp4,.ogg,.webm,video/mp4,video/ogg,video/webm',
64
+ '.pdf,application/pdf',
95
65
  ],
96
- required :true,
97
- description:'',
98
- name :'section_1_input_0',
99
- label :'Which is better between the 2?',
100
- },
101
- {
102
- name :'section_1_input_1',
103
- label :'Choose the worst of them ',
104
- type :'multiple',
105
- required:false,
106
- options :[
107
- {
108
- key :'sections-1-inputs-1-options--1',
109
- value :'Chat GPT',
110
- label :'Chat GPT',
111
- nextSection:'',
112
- },
113
- {
114
- key :'sections-1-inputs-1-options-0',
115
- value :'Gemini',
116
- label :'Gemini',
117
- nextSection:'',
118
- },
119
- {
120
- key :'sections-1-inputs-1-options-1',
121
- value :'Claude',
122
- label :'Claude',
123
- nextSection:'',
124
- },
125
- ],
126
- conditionalLogic:null,
127
- description :'',
128
- },
129
- ],
130
- orderedInputDragIds:[],
131
- navigation :{
132
- nextSection:'next',
133
- },
134
- },
135
- {
136
- title :'General sentiments',
137
- description:'{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Best LLM models for software developers","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h3"},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Chat GPT","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Perplexity","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Gemini","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Claude","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Please add more exciting models that we might missed","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"You can make them ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"bold","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" or ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"Italic or ","type":"text","version":1},{"detail":0,"format":10,"mode":"normal","style":"","text":"underlined","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1},{"children":[{"detail":0,"format":4,"mode":"normal","style":"","text":"You can also color them and strike through them!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}',
138
- key :'section-2',
139
- inputs :[
140
- {
141
- type :'textarea',
142
- options :[],
143
- required :true,
144
- description:'',
145
- name :'section_2_input_0',
146
- label :'Give a final brief description of your findings',
147
66
  },
148
67
  ],
149
68
  orderedInputDragIds:[],
@@ -168,7 +87,10 @@ export const Renderer = {
168
87
 
169
88
  return (
170
89
  <>
171
- <FormBuilder {...args} onRendererFormSave={handleFormDataUpdateWithValues} />
90
+ <FormBuilder
91
+ {...args}
92
+ onRendererFormSave={handleFormDataUpdateWithValues}
93
+ />
172
94
  <pre>{JSON.stringify(values, null, 2)}</pre>
173
95
  </>
174
96
  )
@@ -58,8 +58,6 @@ const XMLEditor = ({
58
58
  config,
59
59
  onChange,
60
60
  onBlur,
61
- stopAllPropagation,
62
- stopPropagationKeys,
63
61
  }) => {
64
62
  const editorRef = useRef()
65
63
 
@@ -93,9 +91,7 @@ const XMLEditor = ({
93
91
  }),
94
92
  EditorView.domEventHandlers({
95
93
  keydown(e) {
96
- if (stopPropagationKeys?.includes(e.key) || stopAllPropagation) {
97
- e.stopPropagation()
98
- }
94
+ e.stopPropagation()
99
95
  },
100
96
  }),
101
97
  ],
@@ -172,20 +168,10 @@ XMLEditor.propTypes = {
172
168
  */
173
169
  gutterWidth:PropTypes.string,
174
170
 
175
- /**
176
- * The keys to stop propagation on.
177
- */
178
- stopPropagationKeys:PropTypes.arrayOf(PropTypes.string),
179
-
180
171
  /**
181
172
  * The callback for when the editor loses focus.
182
173
  */
183
174
  onBlur:PropTypes.func,
184
-
185
- /**
186
- * Whether to stop all propagation of keys to keep focus on the editor.
187
- */
188
- stopAllPropagation:PropTypes.bool,
189
175
  }
190
176
 
191
177
  XMLEditor.defaultProps = {
@@ -17,6 +17,7 @@ import {
17
17
  QueryChoices,
18
18
  LinkInput,
19
19
  EditorInput,
20
+ FileUpload,
20
21
  } from '../fields'
21
22
 
22
23
  import './styles.scss'
@@ -147,6 +148,16 @@ const FormInput = ({
147
148
  )
148
149
  }
149
150
 
151
+ if (type === 'file') {
152
+ return (
153
+ <FileUpload
154
+ className={newClassName}
155
+ disabled={disabled}
156
+ {...otherProps}
157
+ />
158
+ )
159
+ }
160
+
150
161
  if (extraTypes?.[type]) {
151
162
  const Component = extraTypes[type]
152
163
  return (
@@ -1 +1,2 @@
1
1
  export { lookUpInputValueFromFetchedOptions } from './lookUpInputValueFromFetchedOptions'
2
+ export { integerAndGreaterThanZero } from './validators'
@@ -0,0 +1,10 @@
1
+ export const integerAndGreaterThanZero = (value) => {
2
+ let error = ''
3
+ if ((value && value <= 0) || value === 0) {
4
+ error = 'Value must be greator than zero.'
5
+ }
6
+ if (value && value % 1 !== 0) {
7
+ error = 'You must enter a whole number.'
8
+ }
9
+ return error
10
+ }
@@ -27,6 +27,7 @@ const Choice = ({
27
27
  multiple,
28
28
  validate,
29
29
  disabled,
30
+ ...otherProps
30
31
  }) => {
31
32
  const type = multiple ? 'checkbox' : 'radio'
32
33
 
@@ -53,6 +54,7 @@ const Choice = ({
53
54
  id={id}
54
55
  name={name}
55
56
  {...field}
57
+ {...otherProps}
56
58
  value={value}
57
59
  disabled={disabled}
58
60
  />
@@ -51,7 +51,6 @@ const EditorInput = ({
51
51
  style,
52
52
  name,
53
53
  label,
54
- // validate,
55
54
  resize,
56
55
  color,
57
56
  rows,
@@ -60,7 +59,6 @@ const EditorInput = ({
60
59
  description,
61
60
  disabled,
62
61
  showDebugger,
63
- stopPropagationKeys,
64
62
  setEditorState,
65
63
  // ...otherProps
66
64
  }) => {
@@ -186,9 +184,7 @@ const EditorInput = ({
186
184
  <TabIndentationPlugin />
187
185
  {setEditorState && <ExposeEditorStatePlugin setEditorState={setEditorState} />}
188
186
  <HistoryPlugin />
189
- { stopPropagationKeys && (
190
- <StopPropagationPlugin stopPropagationKeys={stopPropagationKeys} />
191
- )}
187
+ <StopPropagationPlugin />
192
188
  <FormDescription className="s-1" description={description} name={name} />
193
189
  { showDebugger && <TreeViewPlugin /> }
194
190
  </div>
@@ -222,11 +218,6 @@ EditorInput.propTypes = {
222
218
  */
223
219
  label:PropTypes.string.isRequired,
224
220
 
225
- /**
226
- * The input value validator function
227
- */
228
- // validate:PropTypes.func,
229
-
230
221
  /**
231
222
  * The number of rows int the text area
232
223
  */
@@ -266,11 +257,6 @@ EditorInput.propTypes = {
266
257
  * Whether to show the debugger or not
267
258
  */
268
259
  showDebugger:PropTypes.bool,
269
-
270
- /**
271
- * Map to stop propagation of the given keys
272
- */
273
- stopPropagationKeys:PropTypes.arrayOf(PropTypes.string),
274
260
  }
275
261
 
276
262
  EditorInput.defaultProps = {
@@ -1,16 +1,12 @@
1
1
  import { useInsertionEffect } from 'react'
2
2
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
3
3
 
4
- const StopPropagationPlugin = ({
5
- stopPropagationKeys,
6
- }) => {
4
+ const StopPropagationPlugin = () => {
7
5
  const [editor] = useLexicalComposerContext()
8
6
 
9
7
  useInsertionEffect(() => {
10
8
  const onKeyDown = (e) => {
11
- if (stopPropagationKeys?.includes(e.key)) {
12
- e.stopPropagation()
13
- }
9
+ e.stopPropagation()
14
10
  }
15
11
 
16
12
  return editor.registerRootListener(
@@ -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