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.
- package/README-ES.md +75 -0
- package/README.md +75 -0
- package/eslint.config.mjs +14 -0
- package/examples/enable-if.ts +0 -0
- package/examples/live-data.ts +63 -0
- package/examples/render-if.ts +0 -0
- package/examples/simple.ts +76 -0
- package/index.ts +3 -0
- package/package.json +47 -0
- package/src/components/input-upload/input-upload.tsx +71 -0
- package/src/components/make-autocomplete/make-autocomplete.tsx +51 -0
- package/src/components/make-button/make-button.tsx +17 -0
- package/src/components/make-input/make-input.tsx +44 -0
- package/src/components/make-multi-select/make-multi-select.tsx +53 -0
- package/src/components/make-select/make-select.tsx +51 -0
- package/src/components/make-text/make-text.tsx +16 -0
- package/src/components/make-textarea/make-textarea.tsx +45 -0
- package/src/components/make-title/make-title.tsx +12 -0
- package/src/components/make-toggle/make-toggle.tsx +44 -0
- package/src/components/make-upload/make-upload.tsx +40 -0
- package/src/components/portal/portal.tsx +36 -0
- package/src/hooks/field-render.ts +104 -0
- package/src/hooks/modal-handler.ts +37 -0
- package/src/interfaces/field.ts +30 -0
- package/src/interfaces/input-upload.ts +36 -0
- package/src/interfaces/make-autocomplete.ts +15 -0
- package/src/interfaces/make-button.ts +20 -0
- package/src/interfaces/make-field-group.ts +13 -0
- package/src/interfaces/make-field.ts +14 -0
- package/src/interfaces/make-multi-select.ts +14 -0
- package/src/interfaces/make-select.ts +14 -0
- package/src/interfaces/make-text.ts +12 -0
- package/src/interfaces/make-textarea.ts +11 -0
- package/src/interfaces/make-title.ts +3 -0
- package/src/interfaces/make-toggle.ts +9 -0
- package/src/interfaces/make-upload.ts +13 -0
- package/src/interfaces/modal.ts +50 -0
- package/src/interfaces/option.ts +4 -0
- package/src/interfaces/portal.ts +8 -0
- package/src/modal.tsx +168 -0
- package/src/tools/general.ts +6 -0
- 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
|
package/tsconfig.json
ADDED