Shared:Components:ModalDialog: fix errors
This commit is contained in:
parent
d4225849c1
commit
53460eb49c
@ -32,7 +32,7 @@ const Template = ({ ...args }: ModalDialogProps) => {
|
||||
<>
|
||||
<Button
|
||||
label="Show"
|
||||
primary={true}
|
||||
primary
|
||||
size={ButtonSize.medium}
|
||||
onClick={openModal}
|
||||
/>
|
||||
@ -53,7 +53,7 @@ const Template = ({ ...args }: ModalDialogProps) => {
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label="Send"
|
||||
primary={true}
|
||||
primary
|
||||
scale
|
||||
size={ButtonSize.normal}
|
||||
onClick={() => {
|
||||
@ -88,6 +88,6 @@ export const Default: Story = {
|
||||
tablet: ModalDialogType.aside,
|
||||
mobile: ModalDialogType.aside,
|
||||
},
|
||||
children: <></>,
|
||||
children: <>123</>,
|
||||
},
|
||||
};
|
||||
|
@ -65,14 +65,20 @@ const Content = styled.div.attrs((props: { modalSwipeOffset?: number }) => ({
|
||||
visible?: boolean;
|
||||
}>`
|
||||
height: auto;
|
||||
max-height: ${(props) =>
|
||||
props.autoMaxHeight ? "auto" : props.isLarge ? "400px" : "280px"};
|
||||
width: ${(props) =>
|
||||
props.autoMaxWidth ? "auto" : props.isLarge ? "520px" : "400px"};
|
||||
max-height: ${props.autoMaxHeight
|
||||
? "auto"
|
||||
: props.isLarge
|
||||
? "400px"
|
||||
: "280px"};
|
||||
width: ${props.autoMaxWidth
|
||||
? "auto"
|
||||
: props.isLarge
|
||||
? "520px"
|
||||
: "400px"};
|
||||
|
||||
border-radius: 6px;
|
||||
@media ${mobile} {
|
||||
transform: translateY(${(props) => (props.visible ? "0" : "100%")});
|
||||
transform: translateY(${props.visible ? "0" : "100%"});
|
||||
transition: transform 0.3s ease-in-out;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@ -89,30 +95,25 @@ const Content = styled.div.attrs((props: { modalSwipeOffset?: number }) => ({
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css<{ visible?: boolean }>`
|
||||
left: 0;
|
||||
transform: translateX(
|
||||
${(props) => (props.visible ? "0" : "-100%")}
|
||||
);
|
||||
`
|
||||
: css<{ visible?: boolean }>`
|
||||
right: 0;
|
||||
transform: translateX(
|
||||
${(props) => (props.visible ? "0" : "100%")}
|
||||
);
|
||||
`}
|
||||
${props.theme.interfaceDirection === "rtl"
|
||||
? css<{ visible?: boolean }>`
|
||||
left: 0;
|
||||
transform: translateX(${props.visible ? "0" : "-100%"});
|
||||
`
|
||||
: css<{ visible?: boolean }>`
|
||||
right: 0;
|
||||
transform: translateX(${props.visible ? "0" : "100%"});
|
||||
`}
|
||||
|
||||
transition: transform 0.3s ease-in-out;
|
||||
|
||||
@media ${mobile} {
|
||||
transform: translateY(${(props) => (props.visible ? "0" : "100%")});
|
||||
transform: translateY(${props.visible ? "0" : "100%"});
|
||||
height: calc(100% - 64px);
|
||||
width: 100%;
|
||||
left: 0;
|
||||
|
||||
top: ${(props) => (props.embedded ? "0" : "auto")};
|
||||
top: ${props.embedded ? "0" : "auto"};
|
||||
right: 0;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
@ -164,10 +165,9 @@ const StyledBody = styled(Box)<{
|
||||
${(props) =>
|
||||
props.isScrollLocked &&
|
||||
css`
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? `margin-left: 0 !important;`
|
||||
: `margin-right: 0 !important;`}
|
||||
${props.theme.interfaceDirection === "rtl"
|
||||
? `margin-left: 0 !important;`
|
||||
: `margin-right: 0 !important;`}
|
||||
|
||||
overflow: hidden !important;
|
||||
`}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { isSafari, isTablet } from "react-device-detect";
|
||||
import throttle from "lodash/throttle";
|
||||
|
||||
@ -54,17 +55,18 @@ const ModalDialog = ({
|
||||
embedded,
|
||||
withForm,
|
||||
}: ModalDialogProps) => {
|
||||
const onCloseEvent = () => {
|
||||
const onCloseEvent = React.useCallback(() => {
|
||||
if (embedded) return;
|
||||
if (isCloseable) onClose?.();
|
||||
};
|
||||
}, [embedded, isCloseable, onClose]);
|
||||
|
||||
const [currentDisplayType, setCurrentDisplayType] = useState(
|
||||
getCurrentDisplayType(displayType, displayTypeDetailed),
|
||||
);
|
||||
const [modalSwipeOffset, setModalSwipeOffset] = useState(0);
|
||||
|
||||
const returnWindowPositionAfterKeyboard = () => {
|
||||
isSafari && isTablet && window.scrollY !== 0 && window.scrollTo(0, 0);
|
||||
if (isSafari && isTablet && window.scrollY !== 0) window.scrollTo(0, 0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -97,7 +99,7 @@ const ModalDialog = ({
|
||||
window.removeEventListener("touchmove", onSwipe);
|
||||
window.addEventListener("touchend", onSwipeEnd);
|
||||
};
|
||||
}, []);
|
||||
}, [displayType, displayTypeDetailed, onClose, onCloseEvent, visible]);
|
||||
|
||||
const [header, body, footer, container] = parseChildren(
|
||||
children,
|
||||
@ -119,11 +121,11 @@ const ModalDialog = ({
|
||||
currentDisplayType={currentDisplayType}
|
||||
withBodyScroll={withBodyScroll}
|
||||
isScrollLocked={isScrollLocked}
|
||||
isLarge={isLarge}
|
||||
isLarge={isLarge || false}
|
||||
zIndex={zIndex}
|
||||
autoMaxHeight={autoMaxHeight}
|
||||
autoMaxWidth={autoMaxWidth}
|
||||
withFooterBorder={withFooterBorder}
|
||||
withFooterBorder={withFooterBorder || false}
|
||||
onClose={onCloseEvent}
|
||||
isLoading={isLoading}
|
||||
header={header}
|
||||
|
@ -31,18 +31,18 @@ export interface ModalDialogProps {
|
||||
isCloseable?: boolean;
|
||||
|
||||
/** **`MODAL-ONLY`**
|
||||
Sets `width: 520px` and `max-height: 400px`*/
|
||||
Sets `width: 520px` and `max-height: 400px` */
|
||||
isLarge?: boolean;
|
||||
|
||||
/** **`MODAL-ONLY`**
|
||||
Sets `max-width: auto`*/
|
||||
Sets `max-width: auto` */
|
||||
autoMaxWidth?: boolean;
|
||||
/** **`MODAL-ONLY`**
|
||||
Sets `max-height: auto`*/
|
||||
Sets `max-height: auto` */
|
||||
autoMaxHeight?: boolean;
|
||||
|
||||
/** **`MODAL-ONLY`**
|
||||
Displays border betweeen body and footer`*/
|
||||
Displays border betweeen body and footer` */
|
||||
withFooterBorder?: boolean;
|
||||
|
||||
/** **`ASIDE-ONLY`**
|
||||
@ -106,3 +106,30 @@ export interface ModalDialogBackdropProps {
|
||||
visible?: boolean;
|
||||
modalSwipeOffset?: number;
|
||||
}
|
||||
|
||||
export interface ModalSubComponentsProps {
|
||||
id?: string;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
currentDisplayType: ModalDialogType;
|
||||
withBodyScroll?: boolean;
|
||||
isScrollLocked?: boolean;
|
||||
isLarge: boolean;
|
||||
zIndex?: number;
|
||||
autoMaxHeight?: boolean;
|
||||
autoMaxWidth?: boolean;
|
||||
onClose: () => void;
|
||||
isLoading?: boolean;
|
||||
header?: React.ReactNode;
|
||||
body?: React.ReactNode;
|
||||
footer?: React.ReactNode;
|
||||
container?: React.ReactNode;
|
||||
visible?: boolean;
|
||||
withFooterBorder: boolean;
|
||||
modalSwipeOffset?: number;
|
||||
containerVisible?: boolean;
|
||||
isDoubleFooterLine?: boolean;
|
||||
isCloseable?: boolean;
|
||||
embedded?: boolean;
|
||||
withForm?: boolean;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { ReactElement } from "react";
|
||||
import React from "react";
|
||||
|
||||
import { isMobile, isTablet, isTouchDevice } from "../../utils";
|
||||
import { ModalDialogType } from "./ModalDialog.enums";
|
||||
@ -27,7 +27,7 @@ export const handleTouchStart = (e: TouchEvent) => {
|
||||
export const handleTouchMove = (e: TouchEvent, onClose?: () => void) => {
|
||||
if (!y1) return 0;
|
||||
|
||||
let y2 = e.touches[0].clientY;
|
||||
const y2 = e.touches[0].clientY;
|
||||
if (y2 - y1 > 120) onClose?.();
|
||||
|
||||
return y1 - y2;
|
||||
|
@ -28,37 +28,33 @@ const StyledCloseButtonWrapper = styled.div<{
|
||||
? css`
|
||||
top: 18px;
|
||||
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? `left: -30px;`
|
||||
: `right: -30px;`}
|
||||
${props.theme.interfaceDirection === "rtl"
|
||||
? `left: -30px;`
|
||||
: `right: -30px;`}
|
||||
|
||||
@media ${mobile} {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? `left: 10px;`
|
||||
: `right: 10px;`}
|
||||
${props.theme.interfaceDirection === "rtl"
|
||||
? `left: 10px;`
|
||||
: `right: 10px;`}
|
||||
top: -27px;
|
||||
}
|
||||
`
|
||||
: css`
|
||||
top: 18px;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? `right: -27px;`
|
||||
: `left: -27px;`}
|
||||
${props.theme.interfaceDirection === "rtl"
|
||||
? `right: -27px;`
|
||||
: `left: -27px;`}
|
||||
@media ${mobile} {
|
||||
top: -27px;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
right: auto;
|
||||
left: 10px;
|
||||
`
|
||||
: css`
|
||||
left: auto;
|
||||
right: 10px;
|
||||
`}
|
||||
${props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
right: auto;
|
||||
left: 10px;
|
||||
`
|
||||
: css`
|
||||
left: auto;
|
||||
right: 10px;
|
||||
`}
|
||||
}
|
||||
`}
|
||||
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
import { CloseButton } from "./CloseButton";
|
||||
import { ModalBackdrop } from "./ModalBackdrop";
|
||||
import { FormWrapper } from "./FormWrapper";
|
||||
import { ModalSubComponentsProps } from "../ModalDialog.types";
|
||||
|
||||
const Modal = ({
|
||||
id,
|
||||
@ -44,13 +45,57 @@ const Modal = ({
|
||||
isCloseable,
|
||||
embedded,
|
||||
withForm,
|
||||
}: any) => {
|
||||
const [windowHeight, setWindowHeight] = React.useState(window.innerHeight);
|
||||
}: ModalSubComponentsProps) => {
|
||||
const [windowHeight] = React.useState(window.innerHeight);
|
||||
|
||||
const visualPageTop = React.useRef(0);
|
||||
const diffRef = React.useRef(0);
|
||||
const contentRef = React.useRef<null | HTMLDivElement>(null);
|
||||
|
||||
const onResize = React.useCallback(
|
||||
(e: Event) => {
|
||||
if (!contentRef.current || !window.visualViewport) return;
|
||||
|
||||
const target = e.target as VisualViewport;
|
||||
|
||||
if (currentDisplayType === "modal") {
|
||||
const diff = windowHeight - target.height - target.pageTop;
|
||||
|
||||
visualPageTop.current = target.pageTop;
|
||||
|
||||
contentRef.current.style.bottom = `${diff}px`;
|
||||
|
||||
return;
|
||||
}
|
||||
if (e?.type === "resize") {
|
||||
const diff = windowHeight - target.height - target.pageTop;
|
||||
|
||||
visualPageTop.current = target.pageTop;
|
||||
|
||||
contentRef.current.style.bottom = `${diff}px`;
|
||||
|
||||
contentRef.current.style.height = `${
|
||||
target.height - 64 + target.pageTop
|
||||
}px`;
|
||||
|
||||
contentRef.current.style.position = "fixed";
|
||||
|
||||
diffRef.current = diff;
|
||||
} else if (e?.type === "scroll") {
|
||||
const diff = window.visualViewport.pageTop ? 0 : visualPageTop.current;
|
||||
|
||||
contentRef.current.style.bottom = `${diffRef.current + diff}px`;
|
||||
|
||||
contentRef.current.style.height = `${
|
||||
window.visualViewport.height - 64 + diff
|
||||
}px`;
|
||||
|
||||
contentRef.current.style.position = "fixed";
|
||||
}
|
||||
},
|
||||
[currentDisplayType, windowHeight],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isMobileOnly && isIOS && window.visualViewport) {
|
||||
window.visualViewport.addEventListener("resize", onResize);
|
||||
@ -62,55 +107,33 @@ const Modal = ({
|
||||
window.visualViewport.removeEventListener("scroll", onResize);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}, [onResize]);
|
||||
|
||||
const onResize = (e: any) => {
|
||||
if (!contentRef.current || !window.visualViewport) return;
|
||||
const headerComponent = React.isValidElement(header)
|
||||
? header.props.children
|
||||
: null;
|
||||
const bodyComponent = React.isValidElement(body) ? body.props.children : null;
|
||||
const footerComponent = React.isValidElement(footer)
|
||||
? footer.props.children
|
||||
: null;
|
||||
const containerComponent = React.isValidElement(container)
|
||||
? container.props.children
|
||||
: null;
|
||||
|
||||
if (currentDisplayType === "modal") {
|
||||
let diff = windowHeight - e.target.height - e.target.pageTop;
|
||||
|
||||
visualPageTop.current = e.target.pageTop;
|
||||
|
||||
contentRef.current.style.bottom = `${diff}px`;
|
||||
|
||||
return;
|
||||
}
|
||||
if (e?.type === "resize") {
|
||||
let diff = windowHeight - e.target.height - e.target.pageTop;
|
||||
|
||||
visualPageTop.current = e.target.pageTop;
|
||||
|
||||
contentRef.current.style.bottom = `${diff}px`;
|
||||
|
||||
contentRef.current.style.height = `${
|
||||
e.target.height - 64 + e.target.pageTop
|
||||
}px`;
|
||||
|
||||
contentRef.current.style.position = "fixed";
|
||||
|
||||
diffRef.current = diff;
|
||||
} else if (e?.type === "scroll") {
|
||||
const diff = window.visualViewport.pageTop ? 0 : visualPageTop.current;
|
||||
|
||||
contentRef.current.style.bottom = `${diffRef.current + diff}px`;
|
||||
|
||||
contentRef.current.style.height = `${
|
||||
window.visualViewport.height - 64 + diff
|
||||
}px`;
|
||||
|
||||
contentRef.current.style.position = "fixed";
|
||||
}
|
||||
const validateOnMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = e.target as HTMLDivElement;
|
||||
if (target.id === "modal-onMouseDown-close") onClose?.();
|
||||
};
|
||||
|
||||
const headerComponent = header ? header.props.children : null;
|
||||
const bodyComponent = body ? body.props.children : null;
|
||||
const footerComponent = footer ? footer.props.children : null;
|
||||
const containerComponent = container ? container.props.children : null;
|
||||
|
||||
const validateOnMouseDown = (e: any) => {
|
||||
if (e.target.id === "modal-onMouseDown-close") onClose();
|
||||
};
|
||||
const headerProps = React.isValidElement(header)
|
||||
? (header?.props as { className?: string })
|
||||
: { className: "" };
|
||||
const bodyProps = React.isValidElement(body)
|
||||
? (body?.props as { className?: string })
|
||||
: { className: "" };
|
||||
const footerProps = React.isValidElement(footer)
|
||||
? (footer?.props as { className?: string })
|
||||
: { className: "" };
|
||||
|
||||
return (
|
||||
<StyledModal
|
||||
@ -120,19 +143,19 @@ const Modal = ({
|
||||
>
|
||||
<ModalBackdrop
|
||||
className={visible ? "modal-backdrop-active backdrop-active" : ""}
|
||||
visible={true}
|
||||
visible
|
||||
zIndex={zIndex}
|
||||
modalSwipeOffset={modalSwipeOffset}
|
||||
>
|
||||
<Dialog
|
||||
id="modal-onMouseDown-close"
|
||||
className={
|
||||
classNames(
|
||||
classNames([
|
||||
className,
|
||||
"modalOnCloseBacdrop",
|
||||
"not-selectable",
|
||||
"dialog",
|
||||
) || ""
|
||||
]) || ""
|
||||
}
|
||||
style={style}
|
||||
onMouseDown={validateOnMouseDown}
|
||||
@ -167,77 +190,71 @@ const Modal = ({
|
||||
withFooterBorder={withFooterBorder}
|
||||
/>
|
||||
)
|
||||
) : container &&
|
||||
containerVisible &&
|
||||
currentDisplayType !== "modal" ? (
|
||||
{ containerComponent }
|
||||
) : (
|
||||
<>
|
||||
{container &&
|
||||
containerVisible &&
|
||||
currentDisplayType !== "modal" ? (
|
||||
<>{containerComponent}</>
|
||||
) : (
|
||||
<FormWrapper withForm={withForm}>
|
||||
{header && (
|
||||
<StyledHeader
|
||||
id="modal-header-swipe"
|
||||
className={classNames(
|
||||
"modal-header",
|
||||
header.props.className,
|
||||
)}
|
||||
currentDisplayType={currentDisplayType}
|
||||
{...header.props}
|
||||
>
|
||||
<Heading
|
||||
level={1}
|
||||
className={"heading"}
|
||||
size={HeadingSize.medium}
|
||||
truncate={true}
|
||||
>
|
||||
{headerComponent}
|
||||
</Heading>
|
||||
</StyledHeader>
|
||||
)}
|
||||
{body && (
|
||||
<StyledBody
|
||||
className={classNames(
|
||||
"modal-body",
|
||||
body.props.className,
|
||||
)}
|
||||
withBodyScroll={withBodyScroll}
|
||||
isScrollLocked={isScrollLocked}
|
||||
hasFooter={1 && footer}
|
||||
currentDisplayType={currentDisplayType}
|
||||
{...body.props}
|
||||
embedded={embedded}
|
||||
>
|
||||
{currentDisplayType === "aside" && withBodyScroll ? (
|
||||
<Scrollbar
|
||||
stype={ScrollbarType.mediumBlack}
|
||||
id="modal-scroll"
|
||||
className="modal-scroll"
|
||||
>
|
||||
{bodyComponent}
|
||||
</Scrollbar>
|
||||
) : (
|
||||
bodyComponent
|
||||
)}
|
||||
</StyledBody>
|
||||
)}
|
||||
{footer && (
|
||||
<StyledFooter
|
||||
className={classNames(
|
||||
"modal-footer",
|
||||
footer.props.className,
|
||||
)}
|
||||
withFooterBorder={withFooterBorder}
|
||||
currentDisplayType={currentDisplayType}
|
||||
isDoubleFooterLine={isDoubleFooterLine}
|
||||
{...footer.props}
|
||||
>
|
||||
{footerComponent}
|
||||
</StyledFooter>
|
||||
)}
|
||||
</FormWrapper>
|
||||
<FormWrapper withForm={withForm || false}>
|
||||
{header && (
|
||||
<StyledHeader
|
||||
id="modal-header-swipe"
|
||||
className={
|
||||
classNames(["modal-header", headerProps.className]) ||
|
||||
"modal-header"
|
||||
}
|
||||
{...headerProps}
|
||||
>
|
||||
<Heading
|
||||
level={1}
|
||||
className="heading"
|
||||
size={HeadingSize.medium}
|
||||
truncate
|
||||
>
|
||||
{headerComponent}
|
||||
</Heading>
|
||||
</StyledHeader>
|
||||
)}
|
||||
</>
|
||||
{body && (
|
||||
<StyledBody
|
||||
className={
|
||||
classNames(["modal-body", bodyProps.className]) ||
|
||||
"modal-body"
|
||||
}
|
||||
withBodyScroll={withBodyScroll}
|
||||
isScrollLocked={isScrollLocked}
|
||||
hasFooter={!!footer}
|
||||
currentDisplayType={currentDisplayType}
|
||||
{...bodyProps}
|
||||
// embedded={embedded}
|
||||
>
|
||||
{currentDisplayType === "aside" && withBodyScroll ? (
|
||||
<Scrollbar
|
||||
stype={ScrollbarType.mediumBlack}
|
||||
id="modal-scroll"
|
||||
className="modal-scroll"
|
||||
>
|
||||
{bodyComponent}
|
||||
</Scrollbar>
|
||||
) : (
|
||||
bodyComponent
|
||||
)}
|
||||
</StyledBody>
|
||||
)}
|
||||
{footer && (
|
||||
<StyledFooter
|
||||
className={
|
||||
classNames(["modal-footer", footerProps.className]) ||
|
||||
"modal-footer"
|
||||
}
|
||||
withFooterBorder={withFooterBorder}
|
||||
isDoubleFooterLine={isDoubleFooterLine}
|
||||
{...footerProps}
|
||||
>
|
||||
{footerComponent}
|
||||
</StyledFooter>
|
||||
)}
|
||||
</FormWrapper>
|
||||
)}
|
||||
</Content>
|
||||
</Dialog>
|
||||
|
Loading…
Reference in New Issue
Block a user