# dynamic-modal

`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.

## Requisitos

Para que `dynamic-modal` funcione correctamente, debes instalar las siguientes dependencias:

- React

Además, es compatible con **Next.js**.

## Instalación

Puedes instalar la librería a través de npm:

```bash
npm install dynamic-modal
```

## Configuración para Next.js (14 o 15)
Define los componentes que seran usados por el modal, crea el archivo y configura todos los componentes del modal. Aqui un ejemplo utilizando HeroUI (anteriormente NextUI):

```jsx
'use client'
import { Autocomplete, AutocompleteItem, Button, Input, Select, SelectItem, Switch, Textarea } from "@heroui/react"
import { IComponentState } from "dynamic-modal/src/interfaces/component-state"

export const ModalComponents: IComponentState = {
  ModalButtonCancel: (props) => {
    return (
      <Button 
        {...props} 
        color={props.color as "default" | "primary" | "secondary" | "success" | "warning" | "danger" | undefined}  
        variant={'bordered'}
      >
        {props.text}
      </Button>
    )
  },
  ModalButtonAction: (props) => {
    return (
      <Button 
        {...props} 
        color={props.color as "default" | "primary" | "secondary" | "success" | "warning" | "danger" | undefined} 
        variant={'solid'}
      >
        {props.text}
      </Button>)
  },
  Button: ({ text, ...props }) => {
    return (
      <Button 
        {...props} 
        color={props.color as "default" | "primary" | "secondary" | "success" | "warning" | "danger" | undefined}  
        variant={props.variant as "flat" | "bordered" | "solid" | "light" | "faded" | "shadow" | "ghost" | undefined}
      >
        {text}
      </Button>)
  },
  Input: ({ invalid, error, disabled, onChange, ...props }) => {
    return (
      <Input 
        {...props}
        onValueChange={onChange}
        errorMessage={error?.message}
        isInvalid={invalid}
        isDisabled={disabled}
        />
    )
  },
  Select: ({ options, invalid, error, isMulti, isSearch, disabled, onChange, value, ...props }) => {
    return (
      !isSearch ?
      <Select 
        {...props}
        selectedKeys={isMulti ? value : [value]}
        onSelectionChange={onChange}
        selectionMode={isMulti ? 'multiple' : 'single'}
        errorMessage={error?.message}
        isInvalid={invalid}
        isDisabled={disabled}
        >
        {options.map((option) => (
          <SelectItem key={option.id}>{option.name}</SelectItem>
        ))}
      </Select> :
      <Autocomplete 
        {...props} 
        selectedKey={value}
        onSelectionChange={onChange}
        errorMessage={error?.message}
        isInvalid={invalid}
        isDisabled={disabled}
        >
        {options.map((item) => (
          <AutocompleteItem key={item.id}>{item.name}</AutocompleteItem>
        ))}
      </Autocomplete>
    )
  },
  Textarea: ({ invalid, error, disabled, ...props }) => {
    return (
      <Textarea 
        {...props}
        errorMessage={error?.message}
        isInvalid={invalid}
        isDisabled={disabled}
        />
    )
  },
  Toggle: ({ value, onChange, label, invalid, ...props }) => {
    return(
      <Switch {...props} isSelected={value} onValueChange={onChange}>
        {label}
      </Switch>
    )
  }
}
```

En el provider principal de tu aplicación Next.js, importa tus componentes del modal (definidos anteriormente) y envuelve tu aplicación con el componente `ComponentState` para que `dynamic-modal` funcione adecuadamente. Aquí un ejemplo:

```jsx
import { ComponentState } from 'dynamic-modal'
import { ModalComponents } from "@data/modal-component/modal-component"

function Provider({ children }: Readonly<{ children: ReactNode }>) {
  return (
    <ComponentState components={ModalComponents}>
      <Component {...pageProps} />
    </ComponentState>
  )
}

export default Provider

```
En el layout principal crea el `portal` para el modal (este componente utiliza react portal)

```jsx
//imports...

export default function RootLayout ({
  children
}: Readonly<{
  children: ReactNode
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Provider>
          {children}
        </Provider>
        <div id='modal-portal'/>
      </body>
    </html>
  )
}
```

## Setup for Next.js 13 o mas antiguo
Edita el archivo llamado `_document.tsx` y crea el `portal` para el modal (este componente utiliza react portal)

```jsx
import { Html, Head, Main, NextScript } from 'next/document'

export default function Document () {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <div id='modal-portal'/>
        <NextScript />
      </body>
    </Html>
  )
}
```

## Uso
Para controlar la apertura y cierre del modal, obtén el custom hook `useModalHandler` y llama a `openModal` cuando necesites mostrar el modal.

```jsx
import { useModalHandler, DynamicModal } from 'dynamic-modal'
import { Button } from '@nextui-org/react'
//Crea el modal, importa y usa
import testModal from '@modal-config/test'

function ExampleComponent() {
  const { openModal, modalProps } = useModalHandler()

  return (
    <>
      <Button
        onClick={() => {
          openModal(testModal.default({}, (data) => {
            console.log('modal data', data)
          }))
        }}
      >
        Open modal
      </Button>

      <DynamicModal {...modalProps} />
    </>
  )
}

export default ExampleComponent

```

## Ejemplos
La carpeta `examples` en el repositorio contiene distintos modos de configuración para ayudarte a personalizar tu modal.

