Fixed SignInUp Modal misalignment for devices smaller than 400px width (#6386)
Hi @Bonapara, Issue #6385 I encountered an issue with the Modal component where its width was fixed at 400px. While the container housing the Modal adjusted its size based on the screen width, the Modal itself remained at 400px regardless of the screen size. I have implemented a change to address this problem. Could you please review the changes and let me know your thoughts? Thank you! https://github.com/user-attachments/assets/8358aacb-d6c3-440e-895e-7abc4f8a3534 --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@ -1,46 +1,183 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import {
|
||||
ModalLayout,
|
||||
ModalLayoutProps,
|
||||
} from '@/ui/layout/modal/components/ModalLayout';
|
||||
import { ModalHotkeyScope } from '@/ui/layout/modal/components/types/ModalHotkeyScope';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
|
||||
import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
|
||||
import { ModalHotkeyScope } from './types/ModalHotkeyScope';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
type ModalProps = ModalLayoutProps & {
|
||||
isOpen?: boolean;
|
||||
const StyledModalDiv = styled(motion.div)<{
|
||||
size?: ModalSize;
|
||||
padding?: ModalPadding;
|
||||
isMobile: boolean;
|
||||
}>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: ${({ theme }) => theme.background.primary};
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
border-radius: ${({ theme, isMobile }) => {
|
||||
if (isMobile) return `0`;
|
||||
return theme.border.radius.md;
|
||||
}};
|
||||
overflow: hidden;
|
||||
z-index: 10000; // should be higher than Backdrop's z-index
|
||||
|
||||
width: ${({ isMobile, size, theme }) => {
|
||||
if (isMobile) return theme.modal.size.fullscreen;
|
||||
switch (size) {
|
||||
case 'small':
|
||||
return theme.modal.size.sm;
|
||||
case 'medium':
|
||||
return theme.modal.size.md;
|
||||
case 'large':
|
||||
return theme.modal.size.lg;
|
||||
default:
|
||||
return 'auto';
|
||||
}
|
||||
}};
|
||||
|
||||
padding: ${({ padding, theme }) => {
|
||||
switch (padding) {
|
||||
case 'none':
|
||||
return theme.spacing(0);
|
||||
case 'small':
|
||||
return theme.spacing(2);
|
||||
case 'medium':
|
||||
return theme.spacing(4);
|
||||
case 'large':
|
||||
return theme.spacing(6);
|
||||
default:
|
||||
return 'auto';
|
||||
}
|
||||
}};
|
||||
height: ${({ isMobile, theme }) =>
|
||||
isMobile ? theme.modal.size.fullscreen : 'auto'};
|
||||
max-height: ${({ isMobile }) => (isMobile ? 'none' : '90dvh')};
|
||||
`;
|
||||
|
||||
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: 1 1 0%;
|
||||
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 StyledBackDrop = styled(motion.div)<{
|
||||
modalVariant: ModalVariants;
|
||||
}>`
|
||||
align-items: center;
|
||||
background: ${({ theme, modalVariant }) =>
|
||||
modalVariant === 'primary'
|
||||
? theme.background.overlayPrimary
|
||||
: theme.background.overlaySecondary};
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
type ModalHeaderProps = React.PropsWithChildren & {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const ModalHeader = ({ children, className }: ModalHeaderProps) => (
|
||||
<StyledHeader className={className}>{children}</StyledHeader>
|
||||
);
|
||||
|
||||
type ModalContentProps = React.PropsWithChildren & {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const ModalContent = ({ children, className }: ModalContentProps) => (
|
||||
<StyledContent className={className}>{children}</StyledContent>
|
||||
);
|
||||
|
||||
type ModalFooterProps = React.PropsWithChildren & {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const ModalFooter = ({ children, className }: ModalFooterProps) => (
|
||||
<StyledFooter className={className}>{children}</StyledFooter>
|
||||
);
|
||||
|
||||
export type ModalSize = 'small' | 'medium' | 'large';
|
||||
export type ModalPadding = 'none' | 'small' | 'medium' | 'large';
|
||||
export type ModalVariants = 'primary' | 'secondary';
|
||||
|
||||
export type ModalProps = React.PropsWithChildren & {
|
||||
size?: ModalSize;
|
||||
padding?: ModalPadding;
|
||||
className?: string;
|
||||
hotkeyScope?: ModalHotkeyScope;
|
||||
onClose?: () => void;
|
||||
onEnter?: () => void;
|
||||
modalVariant?: ModalVariants;
|
||||
} & (
|
||||
| { isClosable: true; onClose: () => void }
|
||||
| { isClosable?: false; onClose?: never }
|
||||
);
|
||||
|
||||
const modalAnimation = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: { opacity: 1 },
|
||||
exit: { opacity: 0 },
|
||||
};
|
||||
|
||||
export const Modal = ({
|
||||
isOpen = false,
|
||||
children,
|
||||
onClose,
|
||||
hotkeyScope = ModalHotkeyScope.Default,
|
||||
onEnter,
|
||||
size = 'medium',
|
||||
padding = 'medium',
|
||||
className,
|
||||
hotkeyScope = ModalHotkeyScope.Default,
|
||||
onEnter,
|
||||
isClosable = false,
|
||||
onClose,
|
||||
modalVariant = 'primary',
|
||||
}: ModalProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {
|
||||
goBackToPreviousHotkeyScope,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Escape],
|
||||
() => {
|
||||
onClose?.();
|
||||
},
|
||||
useEffect(() => {
|
||||
setHotkeyScopeAndMemorizePreviousScope(hotkeyScope);
|
||||
return () => {
|
||||
goBackToPreviousHotkeyScope();
|
||||
};
|
||||
}, [
|
||||
hotkeyScope,
|
||||
[onClose],
|
||||
);
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
]);
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Enter],
|
||||
@ -50,41 +187,53 @@ export const Modal = ({
|
||||
hotkeyScope,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(hotkeyScope);
|
||||
} else {
|
||||
goBackToPreviousHotkeyScope();
|
||||
}
|
||||
}, [
|
||||
goBackToPreviousHotkeyScope,
|
||||
useScopedHotkeys(
|
||||
[Key.Escape],
|
||||
() => {
|
||||
if (isClosable && onClose !== undefined) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
hotkeyScope,
|
||||
isOpen,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
]);
|
||||
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
);
|
||||
|
||||
useListenClickOutsideV2({
|
||||
refs: [modalRef],
|
||||
listenerId: 'MODAL_CLICK_OUTSIDE_LISTENER_ID',
|
||||
callback: () => onClose?.(),
|
||||
callback: () => {
|
||||
if (isClosable && onClose !== undefined) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return isOpen ? (
|
||||
<ModalLayout
|
||||
className={className}
|
||||
modalRef={modalRef}
|
||||
size={size}
|
||||
padding={padding}
|
||||
const stopEventPropagation = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledBackDrop
|
||||
onMouseDown={stopEventPropagation}
|
||||
modalVariant={modalVariant}
|
||||
>
|
||||
{children}
|
||||
</ModalLayout>
|
||||
) : (
|
||||
<></>
|
||||
<StyledModalDiv
|
||||
ref={modalRef}
|
||||
size={size}
|
||||
padding={padding}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
exit="exit"
|
||||
layout
|
||||
variants={modalAnimation}
|
||||
className={className}
|
||||
isMobile={isMobile}
|
||||
>
|
||||
{children}
|
||||
</StyledModalDiv>
|
||||
</StyledBackDrop>
|
||||
);
|
||||
};
|
||||
|
||||
Modal.Header = ModalLayout.Header;
|
||||
Modal.Content = ModalLayout.Content;
|
||||
Modal.Footer = ModalLayout.Footer;
|
||||
Modal.Header = ModalHeader;
|
||||
Modal.Content = ModalContent;
|
||||
Modal.Footer = ModalFooter;
|
||||
|
||||
Reference in New Issue
Block a user