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/README-ES.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# dynamic-modal
|
|
2
|
+
|
|
3
|
+
`dynamic-modal` es una librería construida en typescript para crear modales reutilizables en aplicaciones React y Next.js, utilizando JSON para configurar su estructura sin necesidad de escribir HTML. Esto facilita su construcción y personalización, permitiendo abrir y cerrar modales mediante un custom hook.
|
|
4
|
+
|
|
5
|
+
## Requisitos
|
|
6
|
+
|
|
7
|
+
Para que `dynamic-modal` funcione correctamente, debes instalar las siguientes dependencias:
|
|
8
|
+
|
|
9
|
+
- React 18
|
|
10
|
+
- React-DOM 18
|
|
11
|
+
- react-hook-form
|
|
12
|
+
- NextUI
|
|
13
|
+
|
|
14
|
+
Además, es compatible con **Next.js 14**.
|
|
15
|
+
|
|
16
|
+
## Instalación
|
|
17
|
+
|
|
18
|
+
Puedes instalar la librería a través de npm:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install dynamic-modal
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Configuración para Next.js 14
|
|
25
|
+
En el layout principal de tu aplicación Next.js, debes envolver tu aplicación con el componente `NextUIProvider` para que `dynamic-modal` funcione adecuadamente. Aquí un ejemplo:
|
|
26
|
+
|
|
27
|
+
```jsx
|
|
28
|
+
import { NextUIProvider } from '@nextui-org/react';
|
|
29
|
+
|
|
30
|
+
function MyApp({ Component, pageProps }) {
|
|
31
|
+
return (
|
|
32
|
+
<NextUIProvider>
|
|
33
|
+
<Component {...pageProps} />
|
|
34
|
+
</NextUIProvider>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default MyApp;
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Uso
|
|
43
|
+
Para controlar la apertura y cierre del modal, obtén el custom hook `useModalHandler` y llama a `openModal` cuando necesites mostrar el modal.
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
import { useModalHandler, DynamicModal } from 'dynamic-modal';
|
|
47
|
+
import { Button } from '@nextui-org/react';
|
|
48
|
+
|
|
49
|
+
function ExampleComponent() {
|
|
50
|
+
const { openModal, modalProps } = useModalHandler();
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<Button
|
|
55
|
+
onClick={() => {
|
|
56
|
+
openModal(testModal.default({}, (data) => {
|
|
57
|
+
console.log('modal data', data);
|
|
58
|
+
}));
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Open modal
|
|
62
|
+
</Button>
|
|
63
|
+
|
|
64
|
+
<DynamicModal {...modalProps} />
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default ExampleComponent;
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Ejemplos
|
|
74
|
+
La carpeta `examples` en el repositorio contiene distintos modos de configuración para ayudarte a personalizar tu modal.
|
|
75
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# dynamic-modal
|
|
2
|
+
|
|
3
|
+
`dynamic-modal` is a TypeScript library for creating reusable modals in React and Next.js applications. It uses JSON objects to configure the modal structure, eliminating the need to write HTML. This approach simplifies modal creation and customization, allowing you to open and close modals using a custom hook.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
To use `dynamic-modal` properly, ensure you have the following dependencies installed:
|
|
8
|
+
|
|
9
|
+
- React 18
|
|
10
|
+
- React-DOM 18
|
|
11
|
+
- react-hook-form
|
|
12
|
+
- NextUI
|
|
13
|
+
|
|
14
|
+
Additionally, `dynamic-modal` is compatible with **Next.js 14**.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install the library via npm:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install dynamic-modal
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Setup for Next.js 14
|
|
25
|
+
In the main layout of your Next.js application, wrap your app with the `NextUIProvider` component to ensure `dynamic-modal` functions properly. Here’s an example:
|
|
26
|
+
|
|
27
|
+
```jsx
|
|
28
|
+
import { NextUIProvider } from '@nextui-org/react';
|
|
29
|
+
|
|
30
|
+
function MyApp({ Component, pageProps }) {
|
|
31
|
+
return (
|
|
32
|
+
<NextUIProvider>
|
|
33
|
+
<Component {...pageProps} />
|
|
34
|
+
</NextUIProvider>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default MyApp;
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
To control the modal’s open and close states, use the `useModalHandler` custom hook and call `openModal` whenever you need to display the modal.
|
|
44
|
+
|
|
45
|
+
```jsx
|
|
46
|
+
import { useModalHandler, DynamicModal } from 'dynamic-modal';
|
|
47
|
+
import { Button } from '@nextui-org/react';
|
|
48
|
+
|
|
49
|
+
function ExampleComponent() {
|
|
50
|
+
const { openModal, modalProps } = useModalHandler();
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<Button
|
|
55
|
+
onClick={() => {
|
|
56
|
+
openModal(testModal.default({}, (data) => {
|
|
57
|
+
console.log('modal data', data);
|
|
58
|
+
}));
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Open modal
|
|
62
|
+
</Button>
|
|
63
|
+
|
|
64
|
+
<DynamicModal {...modalProps} />
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default ExampleComponent;
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
The examples folder in the repository contains different configuration modes to help you customize your modal.
|
|
75
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import globals from "globals";
|
|
2
|
+
import pluginJs from "@eslint/js";
|
|
3
|
+
import tseslint from "typescript-eslint";
|
|
4
|
+
import pluginReact from "eslint-plugin-react";
|
|
5
|
+
|
|
6
|
+
export default [
|
|
7
|
+
{ files: ['**/*.{ts,tsx}'] },
|
|
8
|
+
{ languageOptions: { globals: globals.browser } },
|
|
9
|
+
{ settings: { react: { version: 'detect' } } },
|
|
10
|
+
{ ignores : ['dist/**', 'node_modules/**'] },
|
|
11
|
+
pluginJs.configs.recommended,
|
|
12
|
+
...tseslint.configs.recommended,
|
|
13
|
+
pluginReact.configs.flat.recommended,
|
|
14
|
+
];
|
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { IModalConfigLoader } from '../src/interfaces/modal'
|
|
2
|
+
import { IOption } from '../src/interfaces/option'
|
|
3
|
+
|
|
4
|
+
type IncomingProps = {
|
|
5
|
+
typeList: Array<IOption>
|
|
6
|
+
|
|
7
|
+
//Define function to receive selected element and formData (if you need to use) make read or select operations and build
|
|
8
|
+
//next select array of elements
|
|
9
|
+
optionReadAction : (typeId: string, formData: never) => Promise<Array<IOption>>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type ResultProps = Omit<IncomingProps,'optionReadAction'|'typeList'> & Record<'typeId'|'optionId', string>
|
|
13
|
+
|
|
14
|
+
export interface ILiveDataModal {
|
|
15
|
+
default: IModalConfigLoader<IncomingProps, ResultProps>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const liveDataModal: ILiveDataModal = {
|
|
19
|
+
default: (props, action) => {
|
|
20
|
+
return {
|
|
21
|
+
reservedData: {}, //Put here any data that you want store and receive into modal output
|
|
22
|
+
fields: [ //Put here any elements for render into modal
|
|
23
|
+
{
|
|
24
|
+
elementType: 'select',
|
|
25
|
+
label: 'Type',
|
|
26
|
+
options: props.typeList,
|
|
27
|
+
name: 'typeId', //Element observed
|
|
28
|
+
validation: {
|
|
29
|
+
required: true,
|
|
30
|
+
message: 'Please select a valid type'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
elementType: 'select',
|
|
35
|
+
label: 'Options',
|
|
36
|
+
options: [],
|
|
37
|
+
name: 'optionId',
|
|
38
|
+
validation: {
|
|
39
|
+
required: true,
|
|
40
|
+
message: 'Please select a valid option'
|
|
41
|
+
},
|
|
42
|
+
liveData: {
|
|
43
|
+
action: props.optionReadAction, //Define live data action (function that receive data selected from other field for execute and build other options)
|
|
44
|
+
condition: 'typeId' //Define element into modal was to observed
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
styles: { // Put here styles for modal like height or width
|
|
49
|
+
width: '500px'
|
|
50
|
+
},
|
|
51
|
+
title: `Title of live data modal`, //Title of modal
|
|
52
|
+
action: {
|
|
53
|
+
name: 'Save', //Name of action button
|
|
54
|
+
action //Function to receive data from modal
|
|
55
|
+
},
|
|
56
|
+
cancel: {
|
|
57
|
+
name: 'Cancel' //Name of cancel button
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default liveDataModal
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { IModalConfigLoader } from '../src/interfaces/modal'
|
|
2
|
+
|
|
3
|
+
type IncomingProps = {
|
|
4
|
+
input1: string
|
|
5
|
+
store?: boolean
|
|
6
|
+
clear?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type ResultProps = IncomingProps
|
|
10
|
+
|
|
11
|
+
export interface ISimpleModal {
|
|
12
|
+
default: IModalConfigLoader<IncomingProps, ResultProps> //Define others modals if you want
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const simpleModal: ISimpleModal = {
|
|
16
|
+
default: (props, action) => {
|
|
17
|
+
return {
|
|
18
|
+
reservedData: {}, //Put here any data that you want store and receive into modal output
|
|
19
|
+
fields: [ //Put here any elements for render into modal
|
|
20
|
+
{
|
|
21
|
+
elementType: 'input',
|
|
22
|
+
label: 'Input 1',
|
|
23
|
+
defaultValue: props.input1,
|
|
24
|
+
name: 'input1',
|
|
25
|
+
validation: {
|
|
26
|
+
required: true,
|
|
27
|
+
message: 'This field is required',
|
|
28
|
+
regex: undefined //Use regex for custom validation
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
elementType: 'group', //Put here groups of element into same line and customize using styles
|
|
33
|
+
groups: [
|
|
34
|
+
{
|
|
35
|
+
elementType: 'toggle',
|
|
36
|
+
label: 'Store',
|
|
37
|
+
defaultValue: `${props.store ?? false}`,
|
|
38
|
+
name: 'store',
|
|
39
|
+
styles: {
|
|
40
|
+
width: '50%'
|
|
41
|
+
},
|
|
42
|
+
validation: {
|
|
43
|
+
required: false
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
elementType: 'toggle',
|
|
48
|
+
label: 'Clear',
|
|
49
|
+
defaultValue: `${props.clear ?? false}`,
|
|
50
|
+
name: 'clear',
|
|
51
|
+
styles: {
|
|
52
|
+
width: '50%'
|
|
53
|
+
},
|
|
54
|
+
validation: {
|
|
55
|
+
required: false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
styles: { // Put here styles for modal like height or width
|
|
62
|
+
width: '500px'
|
|
63
|
+
},
|
|
64
|
+
title: `Title of simple modal`, //Title of modal
|
|
65
|
+
action: {
|
|
66
|
+
name: 'Save', //Name of action button
|
|
67
|
+
action //Function to receive data from modal
|
|
68
|
+
},
|
|
69
|
+
cancel: {
|
|
70
|
+
name: 'Cancel' //Name of cancel button
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default simpleModal
|
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dynamic-modal",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The dynamic-modal is a solution of creation differents modals into project using a json configuration file",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"lint": "eslint .",
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://gitlab.com/f.salazar/dynamic-modal.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"modal",
|
|
17
|
+
"react",
|
|
18
|
+
"next",
|
|
19
|
+
"dynamic",
|
|
20
|
+
"json"
|
|
21
|
+
],
|
|
22
|
+
"author": "Francisco J. Salazar G",
|
|
23
|
+
"license": "ISC",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://gitlab.com/f.salazar/dynamic-modal/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://gitlab.com/f.salazar/dynamic-modal#readme",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@eslint/js": "^9.13.0",
|
|
30
|
+
"@types/react": "^18.3.12",
|
|
31
|
+
"@types/react-dom": "^18.3.1",
|
|
32
|
+
"@typescript-eslint/eslint-plugin": "^8.11.0",
|
|
33
|
+
"@typescript-eslint/parser": "^8.11.0",
|
|
34
|
+
"eslint": "^9.13.0",
|
|
35
|
+
"eslint-plugin-react": "^7.37.2",
|
|
36
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
37
|
+
"globals": "^15.11.0",
|
|
38
|
+
"typescript": "^5.0.0",
|
|
39
|
+
"typescript-eslint": "^8.11.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@nextui-org/react": "^2.2.9",
|
|
43
|
+
"react": "^18.0.0",
|
|
44
|
+
"react-dom": " ^18.0.0",
|
|
45
|
+
"react-hook-form": "^7.0.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { ChangeEvent, FC, useMemo } from 'react'
|
|
2
|
+
import { IFileResult, IInputUpload } from '../../interfaces/input-upload'
|
|
3
|
+
import { generateId } from '../../tools/general'
|
|
4
|
+
|
|
5
|
+
const InputUpload: FC<IInputUpload> = ({ onChange, ...props }: IInputUpload) => {
|
|
6
|
+
|
|
7
|
+
const onChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
|
|
8
|
+
if (props.read && event.target.files) {
|
|
9
|
+
readFileBlob(event.target.files[0], false)
|
|
10
|
+
.then((result) => onChange(result))
|
|
11
|
+
.catch((err) => {
|
|
12
|
+
console.error('file read error', err)
|
|
13
|
+
onChange({name:'', size: 0, data: ''})
|
|
14
|
+
})
|
|
15
|
+
} else {
|
|
16
|
+
onChange(event.target.files)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const readFileBlob = (blob: File, image: boolean): Promise<IFileResult> => {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const fileReader = new FileReader()
|
|
23
|
+
if(image) fileReader.readAsDataURL(blob)
|
|
24
|
+
else fileReader.readAsText(blob)
|
|
25
|
+
|
|
26
|
+
fileReader.onload = () => {
|
|
27
|
+
const fileResult: IFileResult = {
|
|
28
|
+
name: blob.name,
|
|
29
|
+
size: blob.size,
|
|
30
|
+
data: fileReader.result as string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
resolve(fileResult)
|
|
34
|
+
}
|
|
35
|
+
fileReader.onerror = (error) => {
|
|
36
|
+
reject(error)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const elementId = useMemo<string>(
|
|
42
|
+
() => {
|
|
43
|
+
return props.id ?? generateId()
|
|
44
|
+
}, [props.id]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className='flex flex-col w-full gap-1 text-center'>
|
|
49
|
+
{props.label && <label className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" htmlFor={`file-input-${elementId}`}>{props.label}</label>}
|
|
50
|
+
<input
|
|
51
|
+
className="file:transition-all file:delay-150 block w-full text-sm text-slate-500
|
|
52
|
+
file:mr-4 file:py-2 file:px-4 file:rounded-md
|
|
53
|
+
file:border-0 file:text-sm file:font-semibold
|
|
54
|
+
file:bg-gray-100 file:text-blue-600
|
|
55
|
+
hover:file:bg-blue-700 hover:file:text-white cursor-pointer disabled:cursor-not-allowed"
|
|
56
|
+
aria-describedby={`file-input-${elementId}-help`}
|
|
57
|
+
id={`file-input-${elementId}`}
|
|
58
|
+
type="file"
|
|
59
|
+
onChange={onChangeHandler}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
<p
|
|
63
|
+
className="mt-1 text-sm text-gray-500 dark:text-gray-300 text-start"
|
|
64
|
+
id={`file-input-${elementId}-help`}>
|
|
65
|
+
{props.helpText?.toUpperCase()}
|
|
66
|
+
</p>
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default InputUpload
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { FC, useEffect } from 'react'
|
|
2
|
+
import { Controller } from 'react-hook-form'
|
|
3
|
+
import { useFieldRender } from '../../hooks/field-render'
|
|
4
|
+
import { generateId } from '../../tools/general'
|
|
5
|
+
import { Autocomplete, AutocompleteItem } from '@nextui-org/react'
|
|
6
|
+
import { IMakeAutoCompleteProps } from '../../interfaces/make-autocomplete'
|
|
7
|
+
|
|
8
|
+
const MakeAutocomplete: FC<IMakeAutoCompleteProps> = ({ element, ...props }) => {
|
|
9
|
+
const { render, enable, disable, checkField, liveData, liveSearching } = useFieldRender({ element, ...props })
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const subscription = props.watch((value, { name, type }) => checkField(value, { name, type }))
|
|
13
|
+
return () => subscription.unsubscribe()
|
|
14
|
+
}, [checkField, props, props.watch])
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
render
|
|
18
|
+
? <Controller
|
|
19
|
+
control={props.control}
|
|
20
|
+
name={element.name}
|
|
21
|
+
rules={{
|
|
22
|
+
required: element.validation.required,
|
|
23
|
+
pattern: {
|
|
24
|
+
value: element.validation.regex as RegExp ?? /(.*)/,
|
|
25
|
+
message: element.validation.message ?? ''
|
|
26
|
+
}
|
|
27
|
+
}}
|
|
28
|
+
render={({ field: { onChange, value }, fieldState: { invalid } }) => (
|
|
29
|
+
<Autocomplete
|
|
30
|
+
size='sm'
|
|
31
|
+
{...element}
|
|
32
|
+
isDisabled={disable || !enable || element.disabled}
|
|
33
|
+
id={element.id ?? generateId()}
|
|
34
|
+
onSelectionChange={onChange}
|
|
35
|
+
label={liveSearching ? 'Loading...' : element.label}
|
|
36
|
+
selectedKey={value}
|
|
37
|
+
errorMessage={invalid ? (element.validation.message ?? '') : undefined}
|
|
38
|
+
>
|
|
39
|
+
{
|
|
40
|
+
(liveData || (element.options ?? [])).map((opt) => (
|
|
41
|
+
<AutocompleteItem key={opt.id} value={opt.id}>{opt.name}</AutocompleteItem>
|
|
42
|
+
))
|
|
43
|
+
}
|
|
44
|
+
</Autocomplete>
|
|
45
|
+
)}
|
|
46
|
+
/>
|
|
47
|
+
: <></>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default MakeAutocomplete
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React, { FC } from 'react'
|
|
2
|
+
import { IMakeButtonProps } from '../../interfaces/make-button'
|
|
3
|
+
|
|
4
|
+
const MakeButton: FC<IMakeButtonProps> = ({
|
|
5
|
+
element: { text, ...element }
|
|
6
|
+
}) => {
|
|
7
|
+
return (
|
|
8
|
+
<button
|
|
9
|
+
className='transition-all delay-100 hover:translate-y-1 bg-blue-500 text-white hover:bg-blue-700 rounded w-11/12 h-[40px] p-2'
|
|
10
|
+
{...element}
|
|
11
|
+
>
|
|
12
|
+
{text}
|
|
13
|
+
</button>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default MakeButton
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { FC, useEffect } from 'react'
|
|
2
|
+
import { Controller } from 'react-hook-form'
|
|
3
|
+
import { useFieldRender } from '../../hooks/field-render'
|
|
4
|
+
import { IMakeInputProps } from '../../interfaces/make-field'
|
|
5
|
+
import { Input } from '@nextui-org/react'
|
|
6
|
+
import { generateId } from '../../tools/general'
|
|
7
|
+
|
|
8
|
+
const MakeInput: FC<IMakeInputProps> = ({ element, ...props }) => {
|
|
9
|
+
const { render, enable, checkField } = useFieldRender({ element, ...props })
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const subscription = props.watch((value, { name, type }) => checkField(value, { name, type }))
|
|
13
|
+
return () => subscription.unsubscribe()
|
|
14
|
+
}, [checkField, props])
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
render
|
|
18
|
+
? <Controller
|
|
19
|
+
control={props.control}
|
|
20
|
+
name={element.name}
|
|
21
|
+
rules={{
|
|
22
|
+
required: element.validation.required,
|
|
23
|
+
pattern: {
|
|
24
|
+
value: element.validation.regex as RegExp ?? /(.*)/,
|
|
25
|
+
message: element.validation.message ?? ''
|
|
26
|
+
}
|
|
27
|
+
}}
|
|
28
|
+
render={({ field: { onChange, value }, fieldState: { invalid } }) => (
|
|
29
|
+
<Input
|
|
30
|
+
size='sm'
|
|
31
|
+
{...element}
|
|
32
|
+
id={element.id ?? generateId()}
|
|
33
|
+
onChange={onChange}
|
|
34
|
+
value={value ?? ''}
|
|
35
|
+
errorMessage={invalid ? (element.validation.message ?? '') : undefined}
|
|
36
|
+
disabled={element.disabled ?? !enable}
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
/>
|
|
40
|
+
: null
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default MakeInput
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, { FC, useEffect } from 'react'
|
|
2
|
+
import { Controller } from 'react-hook-form'
|
|
3
|
+
import { useFieldRender } from '../../hooks/field-render'
|
|
4
|
+
import { generateId } from '../../tools/general'
|
|
5
|
+
import { IMakeMultiSelectProps } from '../../interfaces/make-multi-select'
|
|
6
|
+
import { Select, SelectItem } from '@nextui-org/react'
|
|
7
|
+
|
|
8
|
+
const MakeMultiSelect: FC<IMakeMultiSelectProps> = ({ element, ...props }) => {
|
|
9
|
+
const { render, enable, checkField, liveData, liveSearching } = useFieldRender({ element, ...props })
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const subscription = props.watch((value, { name, type }) => checkField(value, { name, type }))
|
|
13
|
+
return () => subscription.unsubscribe()
|
|
14
|
+
}, [checkField, props, props.watch])
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
render
|
|
18
|
+
? <Controller
|
|
19
|
+
control={props.control}
|
|
20
|
+
name={element.name}
|
|
21
|
+
rules={{
|
|
22
|
+
required: element.validation.required,
|
|
23
|
+
pattern: {
|
|
24
|
+
value: element.validation.regex as RegExp ?? /(.*)/,
|
|
25
|
+
message: element.validation.message ?? ''
|
|
26
|
+
}
|
|
27
|
+
}}
|
|
28
|
+
render={({ field: { onChange, value }, fieldState: { invalid } }) => (
|
|
29
|
+
<Select
|
|
30
|
+
size='sm'
|
|
31
|
+
{...element}
|
|
32
|
+
id={element.id ?? generateId()}
|
|
33
|
+
onSelectionChange={onChange}
|
|
34
|
+
label={liveSearching ? 'Loading...' : element.label}
|
|
35
|
+
selectionMode="multiple"
|
|
36
|
+
selectedKeys={value}
|
|
37
|
+
errorMessage={invalid ? (element.validation.message ?? '') : undefined}
|
|
38
|
+
disabled={element.disabled ?? !enable}
|
|
39
|
+
className='max-w-lg'
|
|
40
|
+
>
|
|
41
|
+
{
|
|
42
|
+
(liveData || (element.options ?? [])).map((opt) => (
|
|
43
|
+
<SelectItem key={opt.id} value={opt.id}>{opt.name}</SelectItem>
|
|
44
|
+
))
|
|
45
|
+
}
|
|
46
|
+
</Select>
|
|
47
|
+
)}
|
|
48
|
+
/>
|
|
49
|
+
: null
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default MakeMultiSelect
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { FC, useEffect } from 'react'
|
|
2
|
+
import { Controller } from 'react-hook-form'
|
|
3
|
+
import { useFieldRender } from '../../hooks/field-render'
|
|
4
|
+
import { generateId } from '../../tools/general'
|
|
5
|
+
import { IMakeSelectProps } from '../../interfaces/make-select'
|
|
6
|
+
import { Select, SelectItem } from '@nextui-org/react'
|
|
7
|
+
|
|
8
|
+
const MakeSelect: FC<IMakeSelectProps> = ({ element, ...props }) => {
|
|
9
|
+
const { render, enable, checkField, liveData, liveSearching } = useFieldRender({ element, ...props })
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const subscription = props.watch((value, { name, type }) => checkField(value, { name, type }))
|
|
13
|
+
return () => subscription.unsubscribe()
|
|
14
|
+
}, [checkField, props, props.watch])
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
render
|
|
18
|
+
? <Controller
|
|
19
|
+
control={props.control}
|
|
20
|
+
name={element.name}
|
|
21
|
+
rules={{
|
|
22
|
+
required: element.validation.required,
|
|
23
|
+
pattern: {
|
|
24
|
+
value: element.validation.regex as RegExp ?? /(.*)/,
|
|
25
|
+
message: element.validation.message ?? ''
|
|
26
|
+
}
|
|
27
|
+
}}
|
|
28
|
+
render={({ field: { onChange, value }, fieldState: { invalid } }) => (
|
|
29
|
+
<Select
|
|
30
|
+
size='sm'
|
|
31
|
+
{...element}
|
|
32
|
+
id={element.id ?? generateId()}
|
|
33
|
+
onChange={onChange}
|
|
34
|
+
label={liveSearching ? 'Loading...' : element.label}
|
|
35
|
+
selectedKeys={[value]}
|
|
36
|
+
errorMessage={invalid ? (element.validation.message ?? '') : undefined}
|
|
37
|
+
disabled={element.disabled ?? !enable}
|
|
38
|
+
>
|
|
39
|
+
{
|
|
40
|
+
(liveData || (element.options ?? [])).map((opt) => (
|
|
41
|
+
<SelectItem key={opt.id} value={opt.id}>{opt.name}</SelectItem>
|
|
42
|
+
))
|
|
43
|
+
}
|
|
44
|
+
</Select>
|
|
45
|
+
)}
|
|
46
|
+
/>
|
|
47
|
+
: <></>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default MakeSelect
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React, { FC } from 'react'
|
|
2
|
+
import { IMakeTextProps } from '../../interfaces/make-text'
|
|
3
|
+
|
|
4
|
+
const MakeText: FC<IMakeTextProps> = ({
|
|
5
|
+
element
|
|
6
|
+
}) => {
|
|
7
|
+
return (
|
|
8
|
+
<div className='text-xs text-center'>
|
|
9
|
+
<p style={element.styles}>
|
|
10
|
+
{element.text}
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default MakeText
|