From d55d588a15b852bc4952d89832a3efdf62663f7e Mon Sep 17 00:00:00 2001 From: Jake Laderman Date: Tue, 20 Aug 2024 22:57:40 -0400 Subject: [PATCH] fix: modal regressions (#636) --- src/components/IconFrame.tsx | 9 ++--- src/components/Modal.tsx | 34 ++++++++++++---- src/components/ModalWrapper.tsx | 69 ++++++++++++++------------------- 3 files changed, 60 insertions(+), 52 deletions(-) diff --git a/src/components/IconFrame.tsx b/src/components/IconFrame.tsx index b23d6a78..79dfdbe6 100644 --- a/src/components/IconFrame.tsx +++ b/src/components/IconFrame.tsx @@ -98,7 +98,7 @@ type IconFrameProps = { type?: Type selected?: boolean background?: SemanticColorKey -} +} & Omit, 'size'> const IconFrameSC = styled(Flex)<{ $type: Type @@ -148,10 +148,7 @@ const IconFrameSC = styled(Flex)<{ ...($type === 'floating' ? { boxShadow: theme.boxShadows.slight } : {}), })) -const IconFrame = forwardRef< - HTMLDivElement, - IconFrameProps & ComponentProps ->( +const IconFrame = forwardRef( ( { icon, @@ -166,7 +163,7 @@ const IconFrame = forwardRef< background, as, ...props - }, + }: IconFrameProps, ref ) => { icon = cloneElement(icon, { size: sizeToIconSize[size] }) diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 83db4891..dff8dec7 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -8,7 +8,10 @@ import { useEffect, } from 'react' -import styled, { type StyledComponentPropsWithRef } from 'styled-components' +import styled, { + type StyledComponentPropsWithRef, + useTheme, +} from 'styled-components' import { type ColorKey, type Nullable, type SeverityExt } from '../types' @@ -28,7 +31,7 @@ export const SEVERITIES = [ 'success', 'danger', ] as const satisfies Readonly -const SIZES = ['medium', 'large', 'custom'] as const +const SIZES = ['medium', 'large', 'custom', 'auto'] as const type ModalSeverity = Extract @@ -37,6 +40,7 @@ type ModalSize = (typeof SIZES)[number] type ModalPropsType = ModalWrapperProps & { onClose?: Nullable<() => void> form?: boolean + scrollable?: boolean size?: ModalSize header?: ReactNode actions?: ReactNode @@ -68,24 +72,32 @@ const severityToIcon = { const sizeToWidth = { medium: 480, large: 608, + auto: 'auto', custom: undefined as undefined, -} as const satisfies Record +} as const satisfies Partial> const ModalSC = styled(Card)<{ $width: number $maxWidth: number }>(({ theme, $width, $maxWidth }) => ({ position: 'relative', + display: 'flex', + flexDirection: 'column', + height: '100%', width: $width, maxWidth: $maxWidth, backgroundColor: theme.colors['fill-one'], })) const ModalContentSC = styled.div<{ + $scrollable: boolean $hasActions: boolean -}>(({ theme, $hasActions }) => ({ +}>(({ theme, $scrollable, $hasActions }) => ({ position: 'relative', zIndex: 0, + display: 'flex', + flexDirection: 'column', + overflow: $scrollable ? 'auto' : 'hidden', margin: theme.spacing.large, marginBottom: $hasActions ? 0 : theme.spacing.large, ...theme.partials.text.body1, @@ -144,6 +156,7 @@ function ModalRef( }: ModalPropsType, ref: Ref ) { + const theme = useTheme() const HeaderIcon = severityToIcon[severity ?? 'default'] const iconColorKey = severityToIconColorKey[severity ?? 'default'] @@ -160,21 +173,28 @@ function ModalRef( [onClose] ) + const maxWidth = + size === 'auto' + ? `min(1000px, 100vw - ${theme.spacing.xlarge * 2}px)` + : sizeToWidth[size] + return ( - + {!!header && ( {HeaderIcon && ( diff --git a/src/components/ModalWrapper.tsx b/src/components/ModalWrapper.tsx index 2b56a2c8..8a078276 100644 --- a/src/components/ModalWrapper.tsx +++ b/src/components/ModalWrapper.tsx @@ -1,7 +1,7 @@ // styling here mostly just for the overlay and animations import * as Dialog from '@radix-ui/react-dialog' -import { type ComponentProps, type ReactNode, forwardRef } from 'react' +import { type ReactNode, forwardRef } from 'react' import styled, { useTheme } from 'styled-components' const ANIMATION_SPEED = '150ms' @@ -9,18 +9,11 @@ const ANIMATION_SPEED = '150ms' export type ModalWrapperProps = { open: boolean onOpenChange?: (open: boolean) => void - scrollable?: boolean children?: ReactNode -} & ComponentProps<'div'> +} & Dialog.DialogContentProps function ModalWrapperRef( - { - open, - onOpenChange, - scrollable = true, - children, - ...props - }: ModalWrapperProps, + { open, onOpenChange, children, ...props }: ModalWrapperProps, ref: any ) { const theme = useTheme() @@ -32,10 +25,9 @@ function ModalWrapperRef( onOpenChange={onOpenChange} > - + e.stopPropagation()}> {children} @@ -46,36 +38,35 @@ function ModalWrapperRef( ) } -const ContentSC = styled(Dialog.Content)<{ $scrollable?: boolean }>( - ({ $scrollable }) => ({ - overflowY: $scrollable ? 'auto' : 'hidden', - maxHeight: '100%', - '@keyframes popIn': { - from: { transform: 'scale(0.8)' }, - to: { transform: 'scale(1)' }, - }, - '@keyframes popOut': { - from: { transform: 'scale(1)' }, - to: { transform: 'scale(0.9)' }, - }, - '&[data-state="open"]': { - animation: `popIn ${ANIMATION_SPEED} ease-out`, - }, - '&[data-state="closed"]': { - animation: `popOut ${ANIMATION_SPEED} ease-out`, - }, - }) -) +const ContentSC = styled(Dialog.Content)({ + display: 'flex', + flexDirection: 'column', + overflow: 'auto', + '@keyframes popIn': { + from: { transform: 'scale(0.8)' }, + to: { transform: 'scale(1)' }, + }, + '@keyframes popOut': { + from: { transform: 'scale(1)' }, + to: { transform: 'scale(0.9)' }, + }, + '&[data-state="open"]': { + animation: `popIn ${ANIMATION_SPEED} ease-out`, + }, + '&[data-state="closed"]': { + animation: `popOut ${ANIMATION_SPEED} ease-out`, + }, +}) + const OverlaySC = styled(Dialog.Overlay)(({ theme }) => ({ - background: theme.colors['modal-backdrop'], + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', position: 'fixed', + inset: 0, + background: theme.colors['modal-backdrop'], padding: theme.spacing.xlarge, - top: 0, - left: 0, - right: 0, - bottom: 0, - display: 'grid', - placeItems: 'center', zIndex: theme.zIndexes.modal, '@keyframes fadeIn': { from: { opacity: 0 },