diff --git a/src/components/modal/README.md b/src/components/modal/README.md index 6b6ff94..b8ea51a 100644 --- a/src/components/modal/README.md +++ b/src/components/modal/README.md @@ -9,7 +9,6 @@ Default width - 480px, height 100% ### Props : - **title**: _string_, optional, default = "" -- **headerNode**: _node_, optional, default = null - **children**: _node_, optional, default = null - **footerNode**: _node_, optional, default = null - **okButton**: _object_, optional, default = null @@ -17,6 +16,7 @@ Default width - 480px, height 100% - **className**: _string_, optional, default = "" - **size**: _string_, optional, default = "default" - **onClose**: _function_, optional, default = () => {} +- **description**: _node_, optional, default = null ### Variants diff --git a/src/components/modal/modal.module.scss b/src/components/modal/modal.module.scss index 1a0040b..162f53b 100644 --- a/src/components/modal/modal.module.scss +++ b/src/components/modal/modal.module.scss @@ -1,3 +1,6 @@ +@import 'src/assets/styles/variables/typography'; +@import 'src/assets/styles/mixins/font-scale'; + $WIDTH-SMALL: 320px; $WIDTH-DEFAULT: 480px; $WIDTH-LARGE: 720px; @@ -45,7 +48,7 @@ $WIDTH-LARGE: 720px; transform: translate(-50%); display: inline-block; margin-bottom: 10px; - padding: 32px 48px; + padding: 32px 40px; box-sizing: border-box; background-color: var(--rp-ui-base-bg-100); text-align: left; @@ -56,6 +59,16 @@ $WIDTH-LARGE: 720px; max-height: 90%; } +.description { + display: inline-block; + width: 100%; + padding-bottom: 24px; + font-family: var(--rp-ui-base-font-family); + font-weight: $fw-regular; + @include font-scale(); + color: var(--rp-ui-base-almost-black); +} + .size-default { @include modalWidth($WIDTH-DEFAULT); } diff --git a/src/components/modal/modal.stories.tsx b/src/components/modal/modal.stories.tsx index 6bbbadc..6544c13 100644 --- a/src/components/modal/modal.stories.tsx +++ b/src/components/modal/modal.stories.tsx @@ -1,5 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Modal } from './modal'; +import { FC, useState } from 'react'; +import { Button } from '@components/button'; const meta: Meta = { title: 'Modal', @@ -32,6 +34,49 @@ export const Default: Story = { args: {}, }; +export const WithoutFooter: Story = { + args: { + withoutFooter: true, + description: 'title description', + }, +}; + +export const WithSteps: Story = { + render: (args) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [step, setStep] = useState(1); + + const CustomFooter: FC<{ closeHandler: () => void }> = ({ closeHandler }) => { + return ( +
+ + + + +
+ ); + }; + + return ( + + content for step {step} + + ); + }, +}; + export const Small: Story = { args: { size: 'small', @@ -53,9 +98,44 @@ export const OverlayLightCyan: Story = { export const Scrollable: Story = { args: { scrollable: true, + description: 'title description', + children: ( + <> +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure + dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit + anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute + irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia + deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing + elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat + nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia + deserunt mollit anim id est laborum. +

+ + ), + }, +}; + +export const ScrollableWithoutFooter: Story = { + args: { + scrollable: true, + withoutFooter: true, children: ( <> -

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in diff --git a/src/components/modal/modal.tsx b/src/components/modal/modal.tsx index 7539a8e..1f26f22 100644 --- a/src/components/modal/modal.tsx +++ b/src/components/modal/modal.tsx @@ -13,14 +13,16 @@ import styles from './modal.module.scss'; const cx = classNames.bind(styles); const MODAL_MAX_RATIO = 0.9; -const MODAL_HEADER_AND_FOOTER_HEIGHT = 176; +const MODAL_HEADER_HEIGHT = 32 + 24; +const MODAL_HEADER_WITH_DESCRIPTION_HEIGHT = 32 + 8; +const MODAL_FOOTER_HEIGHT = 36 + 16; +const MODAL_LAYOUT_PADDING = 32 * 2; type ModalOverlay = 'default' | 'light-cyan'; interface ModalProps { - onClose: () => void; + onClose?: () => void; title?: ReactNode; - headerNode?: ReactNode; children?: ReactNode; footerNode?: ReactNode; className?: string; @@ -31,12 +33,14 @@ interface ModalProps { okButton?: ButtonProps; cancelButton?: ButtonProps; scrollable?: boolean; + withoutFooter?: boolean; + CustomFooter?: FC<{ closeHandler: () => void }>; + description?: ReactNode; } // TODO: Fix issue with modal positioning export const Modal: FC = ({ title, - headerNode, children, footerNode, okButton, @@ -48,6 +52,9 @@ export const Modal: FC = ({ zIndex = 2, allowCloseOutside = true, scrollable = false, + withoutFooter = false, + CustomFooter = null, + description = null, }) => { const [isShown, setShown] = useState(false); const [modalHeight, setModalHeight] = useState(0); @@ -57,7 +64,20 @@ export const Modal: FC = ({ const windowHeight = windowSize.height; const modalMaxHeight = windowHeight * MODAL_MAX_RATIO; const modalMargin = (windowHeight - modalHeight) / 2; - const contentMaxHeight = modalMaxHeight - MODAL_HEADER_AND_FOOTER_HEIGHT; + const getContentMaxHeight = () => { + let contentMaxHeight = modalMaxHeight - MODAL_LAYOUT_PADDING; + if (!withoutFooter) { + contentMaxHeight = contentMaxHeight - MODAL_FOOTER_HEIGHT; + } + + if (description) { + contentMaxHeight = contentMaxHeight - MODAL_HEADER_WITH_DESCRIPTION_HEIGHT; + } else { + contentMaxHeight = contentMaxHeight - MODAL_HEADER_HEIGHT; + } + + return contentMaxHeight; + }; const closeModal = () => { setShown(false); @@ -81,10 +101,6 @@ export const Modal: FC = ({ useEffect(() => { setShown(true); - if (modalRef && modalRef.current) { - modalRef.current.focus(); - } - document.addEventListener('keydown', onKeydown, false); return () => document.removeEventListener('keydown', onKeydown, false); @@ -105,22 +121,32 @@ export const Modal: FC = ({ animate={{ opacity: 1, marginTop: modalMargin }} exit={{ opacity: 0, marginTop: -modalMargin }} transition={{ duration: 0.3 }} + onAnimationStart={() => modalRef.current?.focus()} > - + {scrollable ? ( - + + {description && {description}} {children} ) : ( - {children} + <> + {description && {description}} + {children} + )} - + {!withoutFooter && + (CustomFooter ? ( + + ) : ( + + ))} )} diff --git a/src/components/modal/modalContent/modalContent.module.scss b/src/components/modal/modalContent/modalContent.module.scss index a5cfb50..eff3288 100644 --- a/src/components/modal/modalContent/modalContent.module.scss +++ b/src/components/modal/modalContent/modalContent.module.scss @@ -3,11 +3,11 @@ .modal-content { width: calc(100% - 34px); - margin-top: 13px; font-family: var(--rp-ui-base-font-family); font-weight: $fw-regular; @include font-scale(); color: var(--rp-ui-color-text); white-space: pre-line; word-break: break-word; + padding: 0 0 16px; } diff --git a/src/components/modal/modalHeader/modalHeader.module.scss b/src/components/modal/modalHeader/modalHeader.module.scss index d28256c..694625f 100644 --- a/src/components/modal/modalHeader/modalHeader.module.scss +++ b/src/components/modal/modalHeader/modalHeader.module.scss @@ -8,12 +8,13 @@ @include font-scale(x4-medium); color: var(--rp-ui-color-text); padding-bottom: 24px; + + &.width-description { + padding-bottom: 8px; + } } .modal-header-content { - display: flex; - justify-content: stretch; - align-items: center; width: 100%; padding-right: 30px; box-sizing: border-box; diff --git a/src/components/modal/modalHeader/modalHeader.tsx b/src/components/modal/modalHeader/modalHeader.tsx index fac0fd0..6e26b56 100644 --- a/src/components/modal/modalHeader/modalHeader.tsx +++ b/src/components/modal/modalHeader/modalHeader.tsx @@ -7,15 +7,14 @@ const cx = classNames.bind(styles); interface ModalHeaderProps { title?: ReactNode; - headerNode?: ReactNode; onClose: () => void; + withDescription?: boolean; } -export const ModalHeader: FC = ({ title, onClose, headerNode }) => ( -

+export const ModalHeader: FC = ({ title, onClose, withDescription = false }) => ( +
{title && {title}} - {headerNode && headerNode}