Modals manager

Modals manager

다중 단계 modals의 상태를 처리할 수 있는 중앙 집중식 modals 관리자예요. @mantine/modals 패키지로 제공되며 MIT 라이선스로 배포돼요.

출처: 문서

본문

설치

yarn add @mantine/modals

ModalsProvider 설정

앱을 ModalsProvider 컴포넌트로 감싸요:

import { MantineProvider } from '@mantine/core';
import { ModalsProvider } from '@mantine/modals';

function Demo() {
  return (
    <MantineProvider>
      <ModalsProvider>
        {/* Your app here */}
      </ModalsProvider>
    </MantineProvider>
  );
}

Confirm modal

@mantine/modals 패키지에는 확인(confirmations)에 사용할 수 있는 특별한 modal이 포함돼 있어요. 이 컴포넌트는 confirm과 cancel 버튼을 포함하고, 동작에 대한 추가 정보를 표시하는 children을 지원해요. openConfirmModal 함수로 confirm modal을 열어요:

import { Button, Text } from '@mantine/core';
import { modals } from '@mantine/modals';

function Demo() {
  const openModal = () => modals.openConfirmModal({
    title: 'Please confirm your action',
    children: (
      <Text size="sm">
        This action is so important that you are required to confirm it with a modal. Please click
        one of these buttons to proceed.
      </Text>
    ),
    labels: { confirm: 'Confirm', cancel: 'Cancel' },
    onCancel: () => console.log('Cancel'),
    onConfirm: () => console.log('Confirmed'),
  });

  return <Button onClick={openModal}>Open confirm modal</Button>;
}

openConfirmModal 함수는 다음 속성을 가진 인자 하나를 받아요:

  • modalId – modal id, 기본은 임의의 id, 프로그래매틱하게 modal을 닫는 데 사용할 수 있어요

  • children – actions 앞에 표시되는 추가 modal 콘텐츠

  • onCancel – cancel 버튼이 클릭될 때 호출돼요

  • onConfirm – confirm 버튼이 클릭될 때 호출돼요

  • closeOnConfirm – confirm 버튼이 클릭될 때 modal을 닫을지, 기본 true

  • closeOnCancel – cancel 버튼이 클릭될 때 modal을 닫을지, 기본 true

  • cancelProps – cancel 버튼 props

  • confirmProps – confirm 버튼 props

  • groupProps – 버튼 Group props

  • labels – cancel과 confirm 버튼 라벨, ModalsProvider에서 정의할 수 있어요

이 속성들을 사용해 현재 컨텍스트 요구사항에 맞게 confirm modal을 커스터마이즈할 수 있어요:

import { Button, Text } from '@mantine/core';
import { modals } from '@mantine/modals';

function Demo() {
  const openDeleteModal = () =>
    modals.openConfirmModal({
      title: 'Delete your profile',
      centered: true,
      children: (
        <Text size="sm">
          Are you sure you want to delete your profile? This action is destructive and you will have
          to contact support to restore your data.
        </Text>
      ),
      labels: { confirm: 'Delete account', cancel: "No don't delete it" },
      confirmProps: { color: 'red' },
      onCancel: () => console.log('Cancel'),
      onConfirm: () => console.log('Confirmed'),
    });

  return <Button onClick={openDeleteModal}>Delete account</Button>;
}

confirm modals의 공유 라벨을 설정하려면 ModalsProvider에 labels를 설정해요:

import { ModalsProvider } from '@mantine/modals';

function Demo() {
  return (
    <ModalsProvider labels={{ confirm: 'Submit', cancel: 'Cancel' }}>
      {/* Your app here */}
    </ModalsProvider>
  );
}

Context modals

ModalsProvider 컨텍스트에 원하는 만큼 modals를 정의할 수 있어요:

import { Button, Text } from '@mantine/core';
import { ContextModalProps, ModalsProvider } from '@mantine/modals';

const TestModal = ({
  context,
  id,
  innerProps,
}: ContextModalProps<{ modalBody: string }>) => (
  <>
    <Text>{innerProps.modalBody}</Text>
    <Button onClick={() => context.closeModal(id)}>Close modal</Button>
  </>
);

function Demo() {
  return (
    <ModalsProvider
      modals={{
        demonstration: TestModal,
      }}
    >
      {/* Your app here */}
    </ModalsProvider>
  );
}

그런 다음 modals.openContextModal 함수로 이 modals 중 하나를 열어요. modals.openContextModal 함수는 modal key(ModalsProvider에 정의된 것과 일치해야 함)와 modal props, 2개의 인자를 받아요:

import { Button } from '@mantine/core';
import { modals } from '@mantine/modals';

function Demo() {
  return (
    <Button
      onClick={() =>
        modals.openContextModal({
          modal: 'demonstration',
          title: 'Test modal from context',
          innerProps: {
            modalBody:
              'This modal was defined in ModalsProvider, you can open it anywhere in you app with useModals hook',
          },
        })
      }
    >
      Open demonstration context modal
    </Button>
  );
}

타입 안전한 context modals

기본적으로 innerProps와 modal은 타입 안전하지 않아요. TypeScript 모듈 선언으로 타입 안전성을 추가할 수 있어요.

const TestModal = ({
  context,
  id,
  innerProps,
}: ContextModalProps<{ modalBody: string }>) => (
  <>
    <Text>{innerProps.modalBody}</Text>
    <Button onClick={() => context.closeModal(id)}>Close modal</Button>
  </>
);
const modals = {
  demonstration: TestModal,
  /* ...other modals */
};
declare module '@mantine/modals' {
  export interface MantineModalsOverride {
    modals: typeof modals;
  }
}
function Demo() {
  return (
    <ModalsProvider modals={modals}>
      {/* Your app here */}
    </ModalsProvider>
  );
}

타입 안전한 context modals는 openContextModal에 올바른 타입을 사용하도록 강제해요:

import { closeModal, openContextModal } from '@mantine/modals';

openContextModal({
  modal: 'demonstration',
  title: 'Test modal from context',
  innerProps: {
    modalBody:
      'This modal was defined in ModalsProvider, you can open it anywhere in your app with useModals hook',
  },
});
closeModal('demonstration');

Content modals

modals.open 함수로 어떤 콘텐츠든 가진 modal을 열 수 있어요:

import { TextInput, Button } from '@mantine/core';
import { modals } from '@mantine/modals';

function Demo() {
  return (
    <Button
      onClick={() => {
        modals.open({
          title: 'Subscribe to newsletter',
          children: (
            <>
              <TextInput placeholder="Your email" />
              <Button onClick={() => modals.closeAll()} mt="md" fullWidth>
                Submit
              </Button>
            </>
          ),
        });
      }}
    >
      Open content modal
    </Button>
  );
}

여러 열린 modals

여러 레이어의 modals를 열 수 있어요. 열리는 모든 modal은 modals 큐의 첫 번째 요소로 추가돼요. 열려 있는 모든 modals를 닫으려면 modals.closeAll() 함수를 호출해요:

import { Button, Text } from '@mantine/core';
import { modals } from '@mantine/modals';

function Demo() {
  return (
    <Button
      onClick={() =>
        modals.openConfirmModal({
          title: 'Please confirm your action',
          closeOnConfirm: false,
          labels: { confirm: 'Next modal', cancel: 'Close modal' },
          children: (
            <Text size="sm">
              This action is so important that you are required to confirm it with a modal. Please
              click one of these buttons to proceed.
            </Text>
          ),
          onConfirm: () =>
            modals.openConfirmModal({
              title: 'This is modal at second layer',
              labels: { confirm: 'Close modal', cancel: 'Back' },
              closeOnConfirm: false,
              children: (
                <Text size="sm">
                  When this modal is closed modals state will revert to first modal
                </Text>
              ),
              onConfirm: modals.closeAll,
            }),
        })
      }
    >
      Open multiple steps modal
    </Button>
  );
}

모든 modals.x 함수의 인자에 추가하면 Modal 컴포넌트에 props를 전달할 수 있어요. radius, size, withCloseButton props를 설정하는 예시:

import { Button, Text } from '@mantine/core';
import { modals } from '@mantine/modals';

function Demo() {
  const openModal = () => modals.openConfirmModal({
    title: 'Please confirm your action',
    size: 'sm',
    withCloseButton: false,
    children: (
      <Text size="sm">
        This action is so important that you are required to confirm it with a modal. Please click
        one of these buttons to proceed.
      </Text>
    ),
    labels: { confirm: 'Confirm', cancel: 'Cancel' },
    onCancel: () => console.log('Cancel'),
    onConfirm: () => console.log('Confirmed'),
  });

  return <Button onClick={openModal}>Open confirm modal</Button>;
}

동적 콘텐츠와 modals manager

modals manager를 사용하면 standard와 context modals를 연 후 콘텐츠와 속성을 동적으로 업데이트할 수 있어요.

일반 modals를 업데이트하려면 modals.updateModal 함수를 사용해요:

import { Button } from '@mantine/core';
import { modals } from '@mantine/modals';

function Demo() {
  return (
    <Button
      onClick={() => {
        const modalId = modals.open({
          title: 'Initial Modal Title',
          children: <Text>This text will update in 2 seconds.</Text>,
        });

        setTimeout(() => {
          modals.updateModal({
            modalId,
            title: 'Updated Modal Title',
            children: (
              <Text>
                This is the updated content of the modal.
              </Text>
            ),
          });
        }, 2000);
      }}
    >
      Open updating modal
    </Button>
  );
}

Context modals도 modals.updateContextModal을 사용해 동적으로 업데이트할 수 있어요:

import { Button, Text, Stack, Center, Loader } from '@mantine/core';
import { modals, ContextModalProps, ModalsProvider } from '@mantine/modals';
import { CheckIcon } from '@phosphor-icons/react';

const TestModal = ({
  context,
  id,
  innerProps,
}: ContextModalProps<{ modalBody: string; loading: boolean }>) => (
  <>
    <Text size="sm">{innerProps.modalBody}</Text>

    <Center my="lg">
      {innerProps.loading ? <Loader size={32} /> : <CheckIcon size={32} />}
    </Center>

    <Button onClick={() => context.closeModal(id)}>Close modal</Button>
  </>
);

function Demo() {
  return (
    <ModalsProvider modals={{ asyncDemonstration: TestModal }}>
      <Button
        onClick={() => {
          const modalId = modals.openContextModal({
            modal: 'asyncDemonstration',
            title: 'Processing...',
            closeOnEscape: false,
            closeOnClickOutside: false,
            closeButtonProps:{ disabled:true },
            innerProps: {
              modalBody:
                'You cannot close this modal until 2 seconds have passed.',
              loading: true,
            },
          });

          setTimeout(() => {
            modals.updateContextModal({
              modalId,
              title: "Processing Complete!",
              closeOnEscape: true,
              closeOnClickOutside: true,
              closeButtonProps:{ disabled: false },
              innerProps: {
                modalBody:
                  'You can now close the modal.',
                loading: false,
              },
            })
          }, 2000);
        }}
      >
        Open updating context modal
      </Button>
    </ModalsProvider>
  );
}

더 알아보기 (Learn more)