/v1.0

MultiSelectModal

A full-screen modal that combines an Autocomplete search bar with a DynamicList to let users build a collection of selected items. Items can be added by searching and removed individually. When savePath is provided, the floating save button posts the final selection to the API. Changes are tracked by comparing the current list to its initial state, so the save button is only enabled when the selection has actually changed. The modal can also be used in read-only mode.

Props

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | isOpen | boolean | Yes | — | Controls whether the modal is visible. | | onClose | (reason: 'cancel' \| 'success' \| 'error' \| 'custom') => void | Yes | — | Called when the modal closes. Receives a reason string. | | title | string | Yes | — | Title displayed in the modal header. | | searchPath | string | Yes | — | API path for the autocomplete search requests. | | subtitle | string | No | — | Subtitle displayed in the modal header. | | data | any[] \| null | No | — | Initial array of selected items. | | backdropStyle | React.CSSProperties | No | — | Inline styles for the modal backdrop. | | windowStyle | React.CSSProperties | No | — | Inline styles merged into the modal window. | | closeButtonStyle | React.CSSProperties | No | — | Styles for the close button. | | closeIcon | string | No | — | Icon name for the close button. | | closeIconPaths | any[] | No | — | Custom icon paths for the close button. | | closeIconSize | number | No | — | Icon size for the close button. | | zIndex | number | No | 99999 | CSS z-index of the modal overlay. | | id | string | No | — | HTML id for the modal element. | | fullScreen | boolean | No | — | When true, the modal occupies the full viewport. | | apiBaseUrl | string | No | — | Base URL for the HTTP client. | | useAuthToken | boolean | No | — | When true, uses the authenticated HTTP client. | | noContentText | string | No | 'No content available' | Text shown when the list is empty. | | noContentIcon | string | No | 'records' | Icon shown when the list is empty. | | startSlots | Slot[] | No | [] | Slots rendered at the start of each list item. | | endSlots | Slot[] | No | [] | Slots rendered at the end of each list item (a remove button is always appended unless readonly). | | searchPlaceholder | string | No | "Buscar..." | Placeholder text for the search input. | | searchItemLabelKey | string | No | — | Key used as the label in search result items. | | searchItemDescriptionKey | string | No | — | Key used as the description in search result items. | | searchItemImageKey | string | No | — | Key used as the image in search result items. | | fetchPath | string | No | — | API path to load the initial list of selected items. | | savePath | string | No | — | API path to POST/PUT/PATCH the final selection. | | saveMethod | 'POST' \| 'PUT' \| 'PATCH' \| 'DELETE' | No | — | HTTP method for the save request. | | payloadKey | string | No | — | Key under which the items array is sent in the payload. Defaults to 'data'. | | searchBarWidth | number \| string | No | 400 | Width of the search bar. | | listWidth | number \| string | No | 500 | Max width of the items list. | | readonly | boolean | No | — | When true, hides the search bar and remove buttons. | | onSaveSuccess | (data: any) => void | No | — | Called after a successful save API request. | | onSaveError | (error: any) => void | No | — | Called after a failed save API request. |

Usage

Basic

import React from 'react';
import MultiSelectModal from '@/components/modals/MultiSelectModal';

export default function Example() {
  const [open, setOpen] = React.useState(false);
  const [members, setMembers] = React.useState([]);

  return (
    <>
      <button onClick={() => setOpen(true)}>Manage Members</button>
      <MultiSelectModal
        isOpen={open}
        onClose={(reason) => {
          console.log('Closed:', reason);
          setOpen(false);
        }}
        title="Team Members"
        searchPath="/v1/users/search"
        searchItemLabelKey="name"
        searchItemDescriptionKey="email"
        savePath="/v1/team/members"
        saveMethod="PUT"
        payloadKey="members"
        data={members}
        useAuthToken
        onSaveSuccess={() => console.log('Saved!')}
      />
    </>
  );
}