Files
twenty/front/src/modules/ui/modal/components/Modal.tsx
T
Jérémy M 56cada6335 feat: wip import csv [part 1] (#1033)
* feat: wip import csv

* feat: start implementing twenty UI

* feat: new radio button component

* feat: use new radio button component and fix scroll issue

* fix: max height modal

* feat: wip try to customize react-data-grid to match design

* feat: wip match columns

* feat: wip match column selection

* feat: match column

* feat: clean heading component & try to fix scroll in last step

* feat: validation step

* fix: small cleaning and remove unused component

* feat: clean folder architecture

* feat: remove translations

* feat: remove chackra theme

* feat: remove unused libraries

* feat: use option button to open spreadsheet & fix stories

* Fix lint and fix imports

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2023-08-15 15:12:47 -07:00

136 lines
3.0 KiB
TypeScript

import React, { useRef } from 'react';
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import {
ClickOutsideMode,
useListenClickOutside,
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
const ModalDiv = styled(motion.div)`
display: flex;
flex-direction: column;
background: ${({ theme }) => theme.background.primary};
border-radius: ${({ theme }) => theme.border.radius.md};
overflow: hidden;
max-height: 90vh;
z-index: 10000; // should be higher than Backdrop's z-index
`;
const StyledHeader = styled.div`
align-items: center;
display: flex;
flex-direction: row;
height: 60px;
overflow: hidden;
padding: ${({ theme }) => theme.spacing(5)};
`;
const StyledContent = styled.div`
display: flex;
flex: 1;
flex-direction: column;
overflow-y: auto;
padding: ${({ theme }) => theme.spacing(10)};
`;
const StyledFooter = styled.div`
align-items: center;
display: flex;
flex-direction: row;
height: 60px;
overflow: hidden;
padding: ${({ theme }) => theme.spacing(5)};
`;
const BackDrop = styled(motion.div)`
align-items: center;
background: ${({ theme }) => theme.background.overlay};
display: flex;
height: 100%;
justify-content: center;
left: 0;
position: fixed;
top: 0;
width: 100%;
z-index: 9999;
`;
/**
* Modal components
*/
type ModalHeaderProps = React.PropsWithChildren & React.ComponentProps<'div'>;
function ModalHeader({ children, ...restProps }: ModalHeaderProps) {
return <StyledHeader {...restProps}>{children}</StyledHeader>;
}
type ModalContentProps = React.PropsWithChildren & React.ComponentProps<'div'>;
function ModalContent({ children, ...restProps }: ModalContentProps) {
return <StyledContent {...restProps}>{children}</StyledContent>;
}
type ModalFooterProps = React.PropsWithChildren & React.ComponentProps<'div'>;
function ModalFooter({ children, ...restProps }: ModalFooterProps) {
return <StyledFooter {...restProps}>{children}</StyledFooter>;
}
/**
* Modal
*/
type ModalProps = React.PropsWithChildren &
React.ComponentProps<'div'> & {
isOpen?: boolean;
onOutsideClick?: () => void;
};
const modalVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
exit: { opacity: 0 },
};
export function Modal({
isOpen = false,
children,
onOutsideClick,
...restProps
}: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null);
useListenClickOutside({
refs: [modalRef],
callback: () => onOutsideClick?.(),
mode: ClickOutsideMode.absolute,
});
if (!isOpen) {
return null;
}
return (
<BackDrop>
<ModalDiv
// framer-motion seems to have typing problems with refs
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
ref={modalRef}
initial="hidden"
animate="visible"
exit="exit"
layout
variants={modalVariants}
{...restProps}
>
{children}
</ModalDiv>
</BackDrop>
);
}
Modal.Header = ModalHeader;
Modal.Content = ModalContent;
Modal.Footer = ModalFooter;