Modal

A modal is a secondary window that communicates or provides an action inside the same process.

installyarn add @clayui/modal
versionNPM Version
useimport Modal from '@clayui/modal';

You may want to compose your content and customize with your use cases or add your own components while still keeping the logic of a modal.

import {Provider} from '@clayui/core';
import Modal, {useModal} from '@clayui/modal';
import Button from '@clayui/button';
import React from 'react';

import '@clayui/css/lib/css/atlas.css';

export default function App() {
	const {observer, onOpenChange, open} = useModal();

	return (
		<Provider spritemap="/public/icons.svg">
			<div className="p-4">
				<>
					{open && (
						<Modal observer={observer} size="lg" status="info">
							<Modal.Header>Documents</Modal.Header>
							<Modal.Body>
								<p>Do you want to save your documents?</p>
							</Modal.Body>
							<Modal.Footer
								last={
									<Button.Group spaced>
										<Button
											displayType="secondary"
											onClick={() => onOpenChange(false)}
										>
											Cancel
										</Button>
										<Button
											onClick={() => onOpenChange(false)}
										>
											Save changes
										</Button>
									</Button.Group>
								}
							/>
						</Modal>
					)}
					<Button onClick={() => onOpenChange(true)}>
						Open modal
					</Button>
				</>
			</div>
		</Provider>
	);
}

Usage

Modal can be controlled using the useModal hook without the need for additional state, it handles the animations for you.

const Dialog = () => {
	const {observer, onOpenChange, open} = useModal();

	return (
		<>
			{open && (
				<Modal observer={observer}>
					<Modal.Header>Title</Modal.Header>
					<Modal.Body>Body</Modal.Body>
					<Modal.Footer
						last={
							<ClayButton onClick={() => onOpenChange(false)}>
								Close
							</ClayButton>
						}
					/>
				</Modal>
			)}
			<ClayButton onClick={() => onOpenChange(true)}>
				Open modal
			</ClayButton>
		</>
	);
};

useModal hook

The useModal hook is required to create a Modal component in Clay, it is responsible for communicating with Modal and controlling states to make animations, this is necessary because Modal needs animation when opening and closing so we need to delay calls of the onClose so you can safely control Modal’s visibility or do other things.

useModal returns a signature with observer and onClose:

  • observer - Observer is the communication channel that connects Modal with useModal .
  • onOpenChange - Callback to change open state.
  • open - Flag to indicate the open state of the modal.
const {observer, onOpenChange, open} = useModal();

To avoid future problems, do not use the information or change the values ​​of observer, it is considered a bad practice.

Modal.Header offers two different APIs for use by toggling the prop withTitle. By default(withTitle={true}), Modal.Header behaves like a high-level component. If you want to use the lower-level components in Header, all you need to do is set withTitle={false}.

Here is an example of both APIs:

import {Provider} from '@clayui/core';
import Modal, {useModal} from '@clayui/modal';
import Button from '@clayui/button';
import Icon from '@clayui/icon';
import React from 'react';

import '@clayui/css/lib/css/atlas.css';

export default function App() {
	const {observer, onOpenChange, open} = useModal();

	return (
		<Provider spritemap="/public/icons.svg">
			<div className="p-4">
				<>
					<Modal.Header>{'Modal Title'}</Modal.Header>

					<br />

					<Modal.Header withTitle={false}>
						<Modal.ItemGroup>
							<Modal.Item>
								<Modal.TitleSection>
									<Modal.Title>
										<Modal.TitleIndicator>
											<Icon symbol="info-circle" />
										</Modal.TitleIndicator>
										{'Modal Title'}
									</Modal.Title>
								</Modal.TitleSection>
							</Modal.Item>
							<Modal.Item shrink>
								<Modal.SubtitleSection>
									<Modal.Subtitle>
										{'Modal Subtitle'}
									</Modal.Subtitle>
								</Modal.SubtitleSection>
							</Modal.Item>
						</Modal.ItemGroup>

						<Button
							aria-label="close"
							className="close"
							displayType="unstyled"
							onClick={() => {}}
						>
							<Icon symbol="times" />
						</Button>
					</Modal.Header>
				</>
			</div>
		</Provider>
	);
}

Provider

When your application uses a lot of modals, you can use <ClayModalProvider /> to have only one open Modal on the screen and the component centered on your application, you can invoke the modal from the context through the API. It is not recommended to have multiple modals open at the same time.

Add in the project root:

<ClayModalProvider spritemap={spritemap}>
	<MyApp />
</ClayModalProvider>

In any part of your application you can invoke modal.

import {Context} from '@clayui/modal';
import ClayButton from '@clayui/button';

const MyApp = () => {
	const [state, dispatch] = useContext(Context);

	return (
		<ClayButton
			displayType="primary"
			onClick={() =>
				dispatch({
					payload: {
						body: <h1>{'Hello world!'}</h1>,
						footer: [
							,
							,
							<ClayButton key={3} onClick={state.onClose}>
								{'Primary'}
							</ClayButton>,
						],
						header: 'Title',
						size: 'lg',
					},
					type: 1,
				})
			}
		>
			{'Open modal'}
		</ClayButton>
	);
};

Provider contains two different states that you can control:

  • Close - CLOSE
  • Open - OPEN

The context signature is similar to a useReducer, contains state and dispatch.

function Example() {
	const [state, dispatch] = useContext(Context);

	return (
		<Button
			onClick={() => dispatch({payload: {header: 'Title'}, type: 'OPEN'})}
		>
			Open Modal
		</Button>
	);
}

state returns all values that are passed to dispatch payload and you can pass body, footer, header, size, status and url to payload.