dynamic-modal 1.0.0

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 (42) hide show
  1. package/README-ES.md +75 -0
  2. package/README.md +75 -0
  3. package/eslint.config.mjs +14 -0
  4. package/examples/enable-if.ts +0 -0
  5. package/examples/live-data.ts +63 -0
  6. package/examples/render-if.ts +0 -0
  7. package/examples/simple.ts +76 -0
  8. package/index.ts +3 -0
  9. package/package.json +47 -0
  10. package/src/components/input-upload/input-upload.tsx +71 -0
  11. package/src/components/make-autocomplete/make-autocomplete.tsx +51 -0
  12. package/src/components/make-button/make-button.tsx +17 -0
  13. package/src/components/make-input/make-input.tsx +44 -0
  14. package/src/components/make-multi-select/make-multi-select.tsx +53 -0
  15. package/src/components/make-select/make-select.tsx +51 -0
  16. package/src/components/make-text/make-text.tsx +16 -0
  17. package/src/components/make-textarea/make-textarea.tsx +45 -0
  18. package/src/components/make-title/make-title.tsx +12 -0
  19. package/src/components/make-toggle/make-toggle.tsx +44 -0
  20. package/src/components/make-upload/make-upload.tsx +40 -0
  21. package/src/components/portal/portal.tsx +36 -0
  22. package/src/hooks/field-render.ts +104 -0
  23. package/src/hooks/modal-handler.ts +37 -0
  24. package/src/interfaces/field.ts +30 -0
  25. package/src/interfaces/input-upload.ts +36 -0
  26. package/src/interfaces/make-autocomplete.ts +15 -0
  27. package/src/interfaces/make-button.ts +20 -0
  28. package/src/interfaces/make-field-group.ts +13 -0
  29. package/src/interfaces/make-field.ts +14 -0
  30. package/src/interfaces/make-multi-select.ts +14 -0
  31. package/src/interfaces/make-select.ts +14 -0
  32. package/src/interfaces/make-text.ts +12 -0
  33. package/src/interfaces/make-textarea.ts +11 -0
  34. package/src/interfaces/make-title.ts +3 -0
  35. package/src/interfaces/make-toggle.ts +9 -0
  36. package/src/interfaces/make-upload.ts +13 -0
  37. package/src/interfaces/modal.ts +50 -0
  38. package/src/interfaces/option.ts +4 -0
  39. package/src/interfaces/portal.ts +8 -0
  40. package/src/modal.tsx +168 -0
  41. package/src/tools/general.ts +6 -0
  42. package/tsconfig.json +13 -0
package/src/modal.tsx ADDED
@@ -0,0 +1,168 @@
1
+ 'use client'
2
+ import React, { useEffect, useState } from 'react'
3
+ import { useForm } from 'react-hook-form'
4
+ import { Button } from '@nextui-org/react'
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 MakeMultiSelect from './components/make-multi-select/make-multi-select'
12
+ import MakeText from './components/make-text/make-text'
13
+ import { IFieldProps } from './interfaces/field'
14
+ import MakeUpload from './components/make-upload/make-upload'
15
+ import MakeButton from './components/make-button/make-button'
16
+ import MakeAutocomplete from './components/make-autocomplete/make-autocomplete'
17
+
18
+ export const Modal = ({ open, close, config }: IModal) => {
19
+ const [modalReady, setModalReady] = useState<IModalConfigProps | undefined>(undefined)
20
+ const [defaultLoaded, setDefaultLoaded] = useState<boolean>(false)
21
+
22
+ const {
23
+ control,
24
+ handleSubmit,
25
+ getValues,
26
+ unregister,
27
+ setValue,
28
+ watch
29
+ } = useForm()
30
+
31
+ const formValueHandler = (element: IFormField) => {
32
+ if (['group', 'upload', 'text'].includes(element.elementType)) return
33
+ const defaultValue = element.defaultValue ?? ''
34
+ const parsedValue: boolean | string | Array<string> |undefined = defaultValue === 'true' ? true : defaultValue === 'false' ? false : defaultValue
35
+ setValue(element.name, parsedValue ?? '')
36
+ }
37
+
38
+ const autoLoadGroup = (groupFields: Array<IModalField>) => {
39
+ groupFields.forEach(element => {
40
+ formValueHandler(element as IFormField)
41
+ })
42
+ }
43
+
44
+ const autoLoadField = (modalFields: Array<IModalField>) => {
45
+ if (defaultLoaded) return
46
+
47
+ modalFields.forEach(element => {
48
+ if (element.elementType === 'group') {
49
+ autoLoadGroup(element.groups)
50
+ return
51
+ }
52
+ formValueHandler(element as IFormField)
53
+ })
54
+
55
+ setDefaultLoaded(true)
56
+ }
57
+
58
+ const getRender = (element: IModalField, index: number, isEndOfRender: boolean = false) => {
59
+ if (isEndOfRender && modalReady) setTimeout(() => autoLoadField(modalReady.fields), 200)
60
+
61
+ const props: IFieldProps = {
62
+ control,
63
+ watch,
64
+ setValue
65
+ }
66
+
67
+ return element.elementType === 'input'
68
+ ? <MakeInput {...props} key={`modal-input-${index}`} element={element} />
69
+ : element.elementType === 'select'
70
+ ? <MakeSelect {...props} key={`modal-select-${index}`} element={element} />
71
+ : element.elementType === 'textarea'
72
+ ? <MakeTextarea {...props} key={`modal-textarea-${index}`} element={element} />
73
+ : element.elementType === 'toggle'
74
+ ? <MakeToggle {...props} key={`modal-toggle-${index}`} element={element} />
75
+ : element.elementType === 'multiselect'
76
+ ? <MakeMultiSelect {...props} key={`modal-multiselect-${index}`} element={element} />
77
+ : element.elementType === 'text'
78
+ ? <MakeText {...props} key={`modal-text-${index}`} element={element} />
79
+ : element.elementType === 'upload'
80
+ ? <MakeUpload {...props} key={`modal-upload-${index}`} element={element} />
81
+ : element.elementType === 'button'
82
+ ? <MakeButton {...props} key={`modal-button-${index}`} element={element} />
83
+ : element.elementType === 'autocomplete'
84
+ ? <MakeAutocomplete {...props} key={`modal-autocomplete-${index}`} element={element} />
85
+ : null
86
+ }
87
+
88
+ const closeHandler = (): void => {
89
+ if (modalReady?.cancel && modalReady.cancel.action) modalReady.cancel.action()
90
+ setTimeout(() => {
91
+ const form = getValues()
92
+ unregister(Object.keys(form))
93
+ setModalReady(undefined)
94
+ setDefaultLoaded(false)
95
+ close()
96
+ }, 200)
97
+ }
98
+
99
+ const actionHandler = (data: Record<string, string | number | object>): void => {
100
+ if (modalReady?.action && modalReady.action.action) modalReady.action.action({ ...modalReady?.reservedData, ...data })
101
+ closeHandler()
102
+ }
103
+
104
+ useEffect(() => {
105
+ if(open && !modalReady) setModalReady(config)
106
+ }, [config, modalReady, open])
107
+
108
+ return (
109
+ modalReady
110
+ ? <Portal closeTime={200} portalOpen={open} portalTag={'#modal-portal'}>
111
+ <div className='rounded bg-white relative w-auto h-auto min-h-[200px] min-w-[500px]' style={{ ...modalReady.styles }} >
112
+ <form className='flex flex-col p-4 gap-4' autoComplete='off' onSubmit={handleSubmit(actionHandler)}>
113
+ <h2 className='text-bold text-center border-b pb-4'>{modalReady.title}</h2>
114
+ <div
115
+ className='flex flex-col items-center gap-4 py-4'
116
+ style={{
117
+ overflowY: modalReady.overFlowBody ? 'auto' : undefined,
118
+ height: modalReady.overFlowBody,
119
+ minHeight: modalReady.minHeightBody
120
+ }}
121
+ >
122
+ {
123
+ modalReady.fields.map((element, index) => {
124
+ const isEndOfRender : boolean = index + 1 === modalReady.fields.length
125
+
126
+ if (element.elementType === 'group') {
127
+ return (
128
+ <div key={`modal-group-${index}`} className='flex gap-4 w-full' style={element.style} >
129
+ {
130
+ element.groups
131
+ .filter(sub => ['input', 'select', 'toggle', 'multiselect', 'upload', 'button'].includes(sub.elementType))
132
+ .map((sub, subIndex) => getRender(sub, index + subIndex, isEndOfRender))
133
+ }
134
+ </div>
135
+ )
136
+ } else { return getRender(element, index, isEndOfRender) }
137
+ })
138
+ }
139
+ </div>
140
+ <div className='flex items-center justify-around gap-4 py-2 border-t'>
141
+ {modalReady.cancel &&
142
+ <Button
143
+ variant='bordered'
144
+ onClick={closeHandler}
145
+ type='reset'
146
+ className='w-[140px]'
147
+ >
148
+ {modalReady.cancel.name ?? 'Close'}
149
+ </Button>
150
+ }
151
+ {modalReady.action &&
152
+ <Button
153
+ color='primary'
154
+ type='submit'
155
+ className='w-[140px]'
156
+ >
157
+ {modalReady.action.name ?? 'Ok'}
158
+ </Button>
159
+ }
160
+ </div>
161
+ </form>
162
+ </div>
163
+ </Portal>
164
+ : null
165
+ )
166
+ }
167
+
168
+ export default Modal
@@ -0,0 +1,6 @@
1
+ export const generateId = (): string => {
2
+ return Math.random()
3
+ .toString(36)
4
+ .split('.')[1]
5
+ .substring(0, 6)
6
+ }
package/tsconfig.json ADDED
@@ -0,0 +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
+ }