dynamic-modal 1.1.1 → 1.1.7

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 (52) hide show
  1. package/README-ES.md +217 -217
  2. package/README.md +216 -215
  3. package/dist/components/input-upload/input-upload.js +1 -1
  4. package/dist/components/make-button/make-button.js +7 -17
  5. package/dist/components/make-input/make-input.js +9 -19
  6. package/dist/components/make-select/make-select.js +9 -19
  7. package/dist/components/make-textarea/make-textarea.js +9 -19
  8. package/dist/components/make-toggle/make-toggle.js +9 -19
  9. package/dist/components/make-upload/make-upload.js +2 -2
  10. package/dist/components/portal/portal.js +7 -17
  11. package/dist/context/component/component-state.js +7 -17
  12. package/dist/hooks/field-render.js +1 -1
  13. package/dist/interfaces/field.d.ts +3 -2
  14. package/dist/interfaces/make-select.d.ts +2 -1
  15. package/dist/interfaces/modal.d.ts +2 -1
  16. package/dist/modal.js +38 -19
  17. package/eslint.config.mjs +14 -14
  18. package/examples/enable-if.ts +127 -127
  19. package/examples/live-data.ts +61 -61
  20. package/examples/render-if.ts +128 -128
  21. package/examples/simple.ts +74 -74
  22. package/index.ts +5 -5
  23. package/package.json +46 -47
  24. package/src/components/input-upload/input-upload.tsx +67 -67
  25. package/src/components/make-button/make-button.tsx +18 -18
  26. package/src/components/make-description/make-description.tsx +15 -15
  27. package/src/components/make-input/make-input.tsx +53 -53
  28. package/src/components/make-select/make-select.tsx +55 -55
  29. package/src/components/make-textarea/make-textarea.tsx +53 -53
  30. package/src/components/make-toggle/make-toggle.tsx +53 -53
  31. package/src/components/make-upload/make-upload.tsx +40 -40
  32. package/src/components/portal/portal.tsx +37 -37
  33. package/src/context/component/component-state.tsx +17 -17
  34. package/src/hooks/field-render.ts +109 -109
  35. package/src/hooks/modal-handler.ts +38 -38
  36. package/src/interfaces/component-state.ts +33 -33
  37. package/src/interfaces/field.ts +37 -36
  38. package/src/interfaces/input-upload.ts +21 -21
  39. package/src/interfaces/make-button.ts +19 -19
  40. package/src/interfaces/make-description.ts +14 -14
  41. package/src/interfaces/make-field-group.ts +14 -14
  42. package/src/interfaces/make-input.ts +14 -14
  43. package/src/interfaces/make-select.ts +15 -14
  44. package/src/interfaces/make-textarea.ts +11 -11
  45. package/src/interfaces/make-toggle.ts +9 -9
  46. package/src/interfaces/make-upload.ts +14 -14
  47. package/src/interfaces/modal.ts +47 -45
  48. package/src/interfaces/option.ts +3 -3
  49. package/src/interfaces/portal.ts +8 -8
  50. package/src/modal.tsx +196 -164
  51. package/src/tools/general.ts +8 -8
  52. package/tsconfig.json +13 -13
package/src/modal.tsx CHANGED
@@ -1,164 +1,196 @@
1
- 'use client'
2
-
3
- import React, { useContext, useEffect, useState } from 'react'
4
- import { useForm } from 'react-hook-form'
5
- import { Portal } from './components/portal/portal'
6
- import MakeToggle from './components/make-toggle/make-toggle'
7
- import MakeInput from './components/make-input/make-input'
8
- import MakeSelect from './components/make-select/make-select'
9
- import MakeTextarea from './components/make-textarea/make-textarea'
10
- import { IModal, IModalField, IModalConfigProps, IFormField } from './interfaces/modal'
11
- import MakeDescription from './components/make-description/make-description'
12
- import { IFieldProps } from './interfaces/field'
13
- import MakeUpload from './components/make-upload/make-upload'
14
- import MakeButton from './components/make-button/make-button'
15
- import { IMakeInput } from './interfaces/make-input'
16
- import { IMakeSelect } from './interfaces/make-select'
17
- import { IMakeTextarea } from './interfaces/make-textarea'
18
- import { IMakeToggle } from './interfaces/make-toggle'
19
- import { IMakeDescription } from './interfaces/make-description'
20
- import { IMakeUpload } from './interfaces/make-upload'
21
- import { IMakeButton } from './interfaces/make-button'
22
- import { ComponentStateContext } from './context/component/component-state'
23
-
24
- export const Modal = ({ open, close, config }: IModal) => {
25
- const { ModalButtonAction, ModalButtonCancel } = useContext(ComponentStateContext)
26
- const [modalReady, setModalReady] = useState<IModalConfigProps | undefined>(undefined)
27
- const [defaultLoaded, setDefaultLoaded] = useState<boolean>(false)
28
-
29
- const {
30
- control,
31
- handleSubmit,
32
- getValues,
33
- unregister,
34
- setValue,
35
- watch
36
- } = useForm()
37
-
38
- const formValueHandler = (element: IFormField) => {
39
- if (['group', 'upload', 'text'].includes(element.elementType)) return
40
- if(!element.defaultValue && element.renderIf) {
41
- unregister(element.name)
42
- return
43
- }
44
-
45
- const defaultValue = element.defaultValue ?? ''
46
- const parsedValue: boolean | string | Array<string> | undefined = defaultValue === 'true' ? true : defaultValue === 'false' ? false : defaultValue
47
- setValue(element.name, parsedValue ?? '')
48
- }
49
-
50
- const autoLoadGroup = (groupFields: Array<IModalField>) => {
51
- groupFields.forEach(element => {
52
- formValueHandler(element as IFormField)
53
- })
54
- }
55
-
56
- const autoLoadField = (modalFields: Array<IModalField>) => {
57
- if (defaultLoaded) return
58
-
59
- modalFields.forEach(element => {
60
- if (element.elementType === 'group') {
61
- autoLoadGroup(element.groups)
62
- return
63
- }
64
- formValueHandler(element as IFormField)
65
- })
66
-
67
- setDefaultLoaded(true)
68
- }
69
-
70
- const getRender = ({ elementType, ...element }: IModalField, index: number, isEndOfRender: boolean = false) => {
71
- if (isEndOfRender && modalReady) setTimeout(() => autoLoadField(modalReady.fields), 200)
72
-
73
- const props: IFieldProps = {
74
- control,
75
- watch,
76
- setValue,
77
- unregister
78
- }
79
-
80
- return elementType === 'input'
81
- ? <MakeInput {...props} key={`modal-input-${index}`} element={element as IMakeInput} />
82
- : elementType === 'select'
83
- ? <MakeSelect {...props} key={`modal-select-${index}`} element={element as IMakeSelect} />
84
- : elementType === 'textarea'
85
- ? <MakeTextarea {...props} key={`modal-textarea-${index}`} element={element as IMakeTextarea} />
86
- : elementType === 'toggle'
87
- ? <MakeToggle {...props} key={`modal-toggle-${index}`} element={element as IMakeToggle} />
88
- : elementType === 'text'
89
- ? <MakeDescription {...props} key={`modal-text-${index}`} element={element as IMakeDescription} />
90
- : elementType === 'upload'
91
- ? <MakeUpload {...props} key={`modal-upload-${index}`} element={element as IMakeUpload} />
92
- : elementType === 'button'
93
- ? <MakeButton {...props} key={`modal-button-${index}`} element={element as IMakeButton} />
94
- : null
95
- }
96
-
97
- const closeHandler = (): void => {
98
- if (modalReady?.onClose) modalReady.onClose()
99
-
100
- setTimeout(() => {
101
- const form = getValues()
102
- unregister(Object.keys(form))
103
- setModalReady(undefined)
104
- setDefaultLoaded(false)
105
- close()
106
- }, 200)
107
- }
108
-
109
- const actionHandler = (data: Record<string, string | number | object>): void => {
110
- modalReady?.out({ ...(modalReady?.reservedData ?? {}), ...data })
111
- closeHandler()
112
- }
113
-
114
- useEffect(() => {
115
- if (open && !modalReady) setModalReady(config)
116
- }, [config, modalReady, open])
117
-
118
- return (
119
- modalReady
120
- ? <Portal closeTime={200} portalOpen={open} portalTag={'#modal-portal'}>
121
- <div className='rounded bg-white relative w-auto h-auto min-h-[200px] min-w-[500px]' style={modalReady.style} >
122
- <form className='flex flex-col p-4 gap-4' autoComplete='off' onSubmit={handleSubmit(actionHandler)}>
123
- <h2 className='text-bold text-center border-b pb-4 font-semibold'>{modalReady.title}</h2>
124
- <div
125
- className='flex flex-col gap-4 py-4'
126
- style={{
127
- overflowY: modalReady.overFlowBody ? 'auto' : undefined,
128
- height: modalReady.overFlowBody,
129
- minHeight: modalReady.minHeightBody
130
- }}
131
- >
132
- {
133
- modalReady.fields.map((element, index) => {
134
- const isEndOfRender: boolean = index + 1 === modalReady.fields.length
135
-
136
- if (element.elementType === 'group') {
137
- return (
138
- <div key={`modal-group-${index}`} className='flex flex-col w-full gap-2'>
139
- { element.title && <h3 className='font-bold border-b-2 pb-2 mb-2'>{element.title}</h3> }
140
- <div key={`modal-group-${index}`} className='flex gap-4 w-full' style={element.style}>
141
- {
142
- element.groups
143
- .filter(sub => ['input', 'select', 'toggle', 'multiselect', 'upload', 'button', 'autocomplete'].includes(sub.elementType))
144
- .map((sub, subIndex) => getRender(sub, index + subIndex, isEndOfRender))
145
- }
146
- </div>
147
- </div>
148
- )
149
- } else { return getRender(element, index, isEndOfRender) }
150
- })
151
- }
152
- </div>
153
- <div className='flex gap-4 items-center justify-center border-t' style={modalReady?.actions.containerStyle}>
154
- {modalReady.actions.cancel && <ModalButtonCancel {...modalReady.actions.cancel} onClick={closeHandler} />}
155
- <ModalButtonAction {...modalReady.actions.action} type='submit' />
156
- </div>
157
- </form>
158
- </div>
159
- </Portal>
160
- : null
161
- )
162
- }
163
-
164
- export default Modal
1
+ 'use client'
2
+
3
+ import React, { useContext, useEffect, useState } from 'react'
4
+ import { useForm } from 'react-hook-form'
5
+ import { Portal } from './components/portal/portal'
6
+ import MakeToggle from './components/make-toggle/make-toggle'
7
+ import MakeInput from './components/make-input/make-input'
8
+ import MakeSelect from './components/make-select/make-select'
9
+ import MakeTextarea from './components/make-textarea/make-textarea'
10
+ import { IModal, IModalField, IModalConfigProps, IFormField } from './interfaces/modal'
11
+ import MakeDescription from './components/make-description/make-description'
12
+ import { IFieldProps } from './interfaces/field'
13
+ import MakeUpload from './components/make-upload/make-upload'
14
+ import MakeButton from './components/make-button/make-button'
15
+ import { IMakeInput } from './interfaces/make-input'
16
+ import { IMakeSelect } from './interfaces/make-select'
17
+ import { IMakeTextarea } from './interfaces/make-textarea'
18
+ import { IMakeToggle } from './interfaces/make-toggle'
19
+ import { IMakeDescription } from './interfaces/make-description'
20
+ import { IMakeUpload } from './interfaces/make-upload'
21
+ import { IMakeButton } from './interfaces/make-button'
22
+ import { ComponentStateContext } from './context/component/component-state'
23
+
24
+ export const Modal = ({ open, close, config }: IModal) => {
25
+ const { ModalButtonAction, ModalButtonCancel } = useContext(ComponentStateContext)
26
+ const [modalReady, setModalReady] = useState<IModalConfigProps | undefined>(undefined)
27
+ const [defaultLoaded, setDefaultLoaded] = useState<boolean>(false)
28
+
29
+ const {
30
+ control,
31
+ handleSubmit,
32
+ getValues,
33
+ unregister,
34
+ setValue,
35
+ watch,
36
+ trigger,
37
+ getFieldState
38
+ } = useForm()
39
+
40
+ const formValueHandler = (element: IFormField) => {
41
+ if (['group', 'upload', 'text'].includes(element.elementType)) return
42
+ if(!element.defaultValue && element.renderIf) {
43
+ unregister(element.name)
44
+ return
45
+ }
46
+
47
+ const defaultValue = element.defaultValue ?? ''
48
+ const parsedValue: boolean | string | Array<string> | undefined = defaultValue === 'true' ? true : defaultValue === 'false' ? false : defaultValue
49
+ setValue(element.name, parsedValue ?? '')
50
+ }
51
+
52
+ const autoLoadGroup = (groupFields: Array<IModalField>) => {
53
+ groupFields.forEach(element => {
54
+ formValueHandler(element as IFormField)
55
+ })
56
+ }
57
+
58
+ const autoLoadField = (modalFields: Array<IModalField>) => {
59
+ if (defaultLoaded) return
60
+
61
+ modalFields.forEach(element => {
62
+ if (element.elementType === 'group') {
63
+ autoLoadGroup(element.groups)
64
+ return
65
+ }
66
+ formValueHandler(element as IFormField)
67
+ })
68
+
69
+ setDefaultLoaded(true)
70
+ }
71
+
72
+
73
+ const getUseSubmit = (modalConfig: IModalConfigProps): boolean => {
74
+ const useSubmit = modalConfig.useSubmit === undefined ? true : modalConfig.useSubmit === false ? false : true
75
+
76
+ return useSubmit
77
+ }
78
+
79
+ const getRender = ({ elementType, ...element }: IModalField, index: number, isEndOfRender: boolean = false) => {
80
+ if (isEndOfRender && modalReady) setTimeout(() => autoLoadField(modalReady.fields), 200)
81
+
82
+ const props: IFieldProps = {
83
+ control,
84
+ watch,
85
+ setValue,
86
+ unregister
87
+ }
88
+
89
+ return elementType === 'input'
90
+ ? <MakeInput {...props} key={`modal-input-${index}`} element={element as IMakeInput} />
91
+ : elementType === 'select'
92
+ ? <MakeSelect {...props} key={`modal-select-${index}`} element={element as IMakeSelect} />
93
+ : elementType === 'textarea'
94
+ ? <MakeTextarea {...props} key={`modal-textarea-${index}`} element={element as IMakeTextarea} />
95
+ : elementType === 'toggle'
96
+ ? <MakeToggle {...props} key={`modal-toggle-${index}`} element={element as IMakeToggle} />
97
+ : elementType === 'text'
98
+ ? <MakeDescription {...props} key={`modal-text-${index}`} element={element as IMakeDescription} />
99
+ : elementType === 'upload'
100
+ ? <MakeUpload {...props} key={`modal-upload-${index}`} element={element as IMakeUpload} />
101
+ : elementType === 'button'
102
+ ? <MakeButton {...props} key={`modal-button-${index}`} element={element as IMakeButton} />
103
+ : null
104
+ }
105
+
106
+
107
+ const closeHandler = (): void => {
108
+ if (modalReady?.onClose) modalReady.onClose()
109
+
110
+ setTimeout(() => {
111
+ const form = getValues()
112
+ unregister(Object.keys(form))
113
+ setModalReady(undefined)
114
+ setDefaultLoaded(false)
115
+ close()
116
+ }, 200)
117
+ }
118
+
119
+ const manualSubmit = async (): Promise<void> => {
120
+ const form = getValues()
121
+ const fields = Object.keys(form)
122
+ await trigger(fields)
123
+
124
+ const validations: Array<boolean> = fields.map(field => {
125
+ const { invalid } = getFieldState(field)
126
+
127
+ return invalid
128
+ })
129
+
130
+ const result = validations.some(isInvalid => isInvalid) ? undefined : form
131
+
132
+ if(!result) return
133
+
134
+ actionHandler({ ...(modalReady?.reservedData ?? {}), ...form })
135
+ }
136
+
137
+ const actionHandler = (data: Record<string, string | number | object>): void => {
138
+ modalReady?.out({ ...(modalReady?.reservedData ?? {}), ...data })
139
+ closeHandler()
140
+ }
141
+
142
+ useEffect(() => {
143
+ if (open && !modalReady) setModalReady(config)
144
+ }, [config, modalReady, open])
145
+
146
+ return (
147
+ modalReady
148
+ ? <Portal closeTime={200} portalOpen={open} portalTag={'#modal-portal'}>
149
+ <div className='rounded bg-white relative w-auto h-auto min-h-[200px] min-w-[500px]' style={modalReady.style} >
150
+ <form className='flex flex-col p-4 gap-4' autoComplete='off' onSubmit={handleSubmit(actionHandler)}>
151
+ <h2 className='text-bold text-center border-b pb-4 font-semibold'>{modalReady.title}</h2>
152
+ <div
153
+ className='flex flex-col gap-4 py-4'
154
+ style={{
155
+ overflowY: modalReady.overFlowBody ? 'auto' : undefined,
156
+ height: modalReady.overFlowBody,
157
+ minHeight: modalReady.minHeightBody
158
+ }}
159
+ >
160
+ {
161
+ modalReady.fields.map((element, index) => {
162
+ const isEndOfRender: boolean = index + 1 === modalReady.fields.length
163
+
164
+ if (element.elementType === 'group') {
165
+ return (
166
+ <div key={`modal-group-${index}`} className='flex flex-col w-full gap-2'>
167
+ { element.title && <h3 className='font-bold border-b-2 pb-2 mb-2'>{element.title}</h3> }
168
+ <div key={`modal-group-${index}`} className='flex gap-4 w-full' style={element.style}>
169
+ {
170
+ element.groups
171
+ .filter(sub => ['input', 'select', 'toggle', 'multiselect', 'upload', 'button', 'autocomplete'].includes(sub.elementType))
172
+ .map((sub, subIndex) => getRender(sub, index + subIndex, isEndOfRender))
173
+ }
174
+ </div>
175
+ </div>
176
+ )
177
+ } else { return getRender(element, index, isEndOfRender) }
178
+ })
179
+ }
180
+ </div>
181
+ <div className='flex gap-4 items-center justify-center border-t' style={modalReady?.actions.containerStyle}>
182
+ {modalReady.actions.cancel && <ModalButtonCancel {...modalReady.actions.cancel} onClick={closeHandler} />}
183
+ {
184
+ getUseSubmit(modalReady) ?
185
+ <ModalButtonAction {...modalReady.actions.action} type='submit' />:
186
+ <ModalButtonAction {...modalReady.actions.action} onClick={manualSubmit} type='button' />
187
+ }
188
+ </div>
189
+ </form>
190
+ </div>
191
+ </Portal>
192
+ : null
193
+ )
194
+ }
195
+
196
+ export default Modal
@@ -1,8 +1,8 @@
1
- 'use client'
2
-
3
- export const generateId = (): string => {
4
- return Math.random()
5
- .toString(36)
6
- .split('.')[1]
7
- .substring(0, 6)
8
- }
1
+ 'use client'
2
+
3
+ export const generateId = (): string => {
4
+ return Math.random()
5
+ .toString(36)
6
+ .split('.')[1]
7
+ .substring(0, 6)
8
+ }
package/tsconfig.json CHANGED
@@ -1,13 +1,13 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2016",
4
- "module": "commonjs",
5
- "declaration": true,
6
- "outDir": "./dist",
7
- "strict": true,
8
- "jsx": "react",
9
- "esModuleInterop": true,
10
- "skipLibCheck": true
11
- },
12
- "include": ["src/**/*"]
13
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2016",
4
+ "module": "commonjs",
5
+ "declaration": true,
6
+ "outDir": "./dist",
7
+ "strict": true,
8
+ "jsx": "react",
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src/**/*"]
13
+ }