Modal
Basic usage
import Button from '@semcore/ui/button';
import Modal from '@semcore/ui/modal';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
const [visible, setVisible] = React.useState(false);
const handleOpen = React.useCallback(() => setVisible(true), []);
const handleClose = React.useCallback(() => setVisible(false), []);
return (
<React.Fragment>
<Button onClick={handleOpen}>Open modal</Button>
<Modal visible={visible} onClose={handleClose}>
<Modal.Title>Do you want to save your changes?</Modal.Title>
<Text size={200} mb={4} tag='p'>
Your changes will be lost if you don't save them.
</Text>
<Button use='primary' theme='success' size='l' onClick={handleClose}>
Save changes
</Button>
<Button size='l' ml={2} onClick={handleClose}>
Don't save
</Button>
</Modal>
</React.Fragment>
);
};
export default Demo;
Modal bigger than viewport
Sometimes the amount of content overfills the window's visibility, but you don't need to worry about it, because the component will be adjusted and the scroll will appear.
import { Flex } from '@semcore/ui/base-components';
import Button from '@semcore/ui/button';
import Modal from '@semcore/ui/modal';
import { Text } from '@semcore/ui/typography';
import React, { useState } from 'react';
const loremString = `As Gregor Samsa awoke one morning from uneasy dreams, he found himself transformed in his bed into a gigantic insect. He lay on his hard, armor-like back, and when he lifted his head a little, he could see his brown, domed belly divided into stiff, arched segments.
`;
const Demo = () => {
const [visible, setVisible] = useState(false);
const openModal = () => setVisible(true);
const closeModal = () => setVisible(false);
return (
<>
<Button onClick={openModal}>Open modal</Button>
<Modal visible={visible} onClose={closeModal} w={500}>
<Flex direction='column'>
{Array(6)
.fill(0)
.map((_, index) => (
<Text key={index} size={300} tag='p'>
{loremString}
</Text>
))}
<Flex justifyContent='center' mt={8}>
<Button use='primary' theme='success' size='l' onClick={closeModal}>
Got it!
</Button>
</Flex>
</Flex>
</Modal>
</>
);
};
export default Demo;
Changing alignment
By default, the modal window is centered. However, in some cases, when the content height inside the window changes dynamically and causes the modal window to "jump," it may be necessary to adjust the window alignment. This can be achieved by applying the desired margin on the respective side.
import Button from '@semcore/ui/button';
import Modal from '@semcore/ui/modal';
import React from 'react';
const DEFAULT_TEXT =
'As Gregor Samsa awoke one morning from uneasy dreams, he found himself transformed in his bed into a gigantic insect. He lay on his hard, armor-like back, and when he lifted his head a little, he could see his brown, domed belly divided into stiff, arched segments.';
const Demo = () => {
const [visible, setVisible] = React.useState(false);
const [text, setText] = React.useState(DEFAULT_TEXT);
React.useEffect(() => {
const timer = window.setInterval(() => {
if (text.length > 5000) {
setText(DEFAULT_TEXT);
} else {
setText(text + text);
}
}, 1000);
return () => {
window.clearInterval(timer);
};
}, [text]);
return (
<React.Fragment>
<Button onClick={() => setVisible(true)}>Open modal</Button>
<Modal mt={0} w={500} visible={visible} onClose={() => setVisible(false)}>
{text}
</Modal>
</React.Fragment>
);
};
export default Demo;
Modal over another modal
While it's generally not recommended, there are instances where it may be necessary to open a modal window within another modal window. In such cases, it's important to nest the modal windows properly to ensure correct background visibility and keyboard control.
import Button from '@semcore/ui/button';
import Modal from '@semcore/ui/modal';
import { Text } from '@semcore/ui/typography';
import React from 'react';
const Demo = () => {
const [visible, setVisible] = React.useState(false);
const [secondVisible, setSecondVisible] = React.useState(false);
const handleOpen = () => setVisible(true);
const handleClose = () => setVisible(false);
const handleSecondOpen = () => setSecondVisible(true);
const handleSecondClose = () => setSecondVisible(false);
return (
<>
<Button onClick={handleOpen}>Open modal</Button>
<Modal visible={visible} onClose={handleClose} w='400px'>
<Modal.Title>Open one more window</Modal.Title>
<Text size={300} mb={3} tag='p'>
Use this example for the cases when you need to open one more window inside the other modal window.
</Text>
<Button size='l' use='primary' onClick={handleSecondOpen}>
Open modal
</Button>
</Modal>
<Modal visible={secondVisible} onClose={handleSecondClose} w='360px'>
<Modal.Title>Modal window inside a modal window</Modal.Title>
<Text size={300} mb={3} tag='p'>
Use this example for the cases when you need to open one more window inside the other modal window.
</Text>
<Button size='l' use='primary' theme='brand' onClick={handleSecondClose}>
Close
</Button>
</Modal>
</>
);
};
export default Demo;
Advanced usage
To access the background or the close Close icon, you will need to expand the modal window and recreate the same component sequence.
In most cases, it's expected that you won't require this functionality. There is no need for a custom Close icon. Instead, consider the following solutions:
- If you need to send analytics upon clicking the close icon, you can use the
onCloseprop in the Modal component. - To ensure the spinner overlaps the close icon, refer to the example provided above.
import Button from '@semcore/ui/button';
import Modal from '@semcore/ui/modal';
import { Text } from '@semcore/ui/typography';
import React, { useState } from 'react';
const overlayStyles = { background: 'oklch(from var(--intergalactic-brand-primary) l c h / 0.5)' };
const Demo = () => {
const [visible, setVisible] = useState(false);
return (
<>
<Button onClick={() => setVisible(true)}>Open modal</Button>
<Modal visible={visible} closable={false} onClose={() => setVisible(false)}>
<Modal.Overlay style={overlayStyles}>
<Modal.Window wMax='400px' px={5} py={5}>
<Modal.Close />
<Text size={400} mb={2} tag='h2'>Customized modal window</Text>
<Text size={300} mb={4} tag='p'>
This is a customized modal window with a custom overlay and window styles. The example also uses native tags for the heading and text.
</Text>
<Button size='l' use='primary' theme='brand' onClick={() => setVisible(false)}>
Close
</Button>
</Modal.Window>
</Modal.Overlay>
</Modal>
</>
);
};
export default Demo;
Modal in iframe
Whenever possible, opt for using pages instead of modal windows. Modal windows within an iframe won't overlay the entire viewport; instead, they will only cover a portion of the iframe area. Additionally, they won't appear at the center of the viewport but rather at the center of the iframe, resulting in an awkward visual experience.
One alternative solution is to use a FullscreenModal. This type of modal will cover the entire iframe and resemble a page rather than a dialog.
TIP
Avoid using fullscreen modals for simple confirmation dialogs or warnings.
When using a simple modal dialog, you can apply a quick fix to improve its appearance by setting margin-top: 0 or <Modal mt={0}/>.