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/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
@@ -0,0 +1,3 @@
1
+ export { default as DynamicModal } from './src/modal';
2
+ export { IModalConfigLoader, IModalConfigProps, IModalRenderCondition, IModalField, IModalLiveDataCondition } from './src/interfaces/modal'
3
+ export { useModalHandler } from './src/hooks/modal-handler'
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