Bläddra i källkod

create generic abstract modal component

Mgrdich 2 år sedan
förälder
incheckning
c19a2f3571

+ 39 - 0
kafka-ui-react-app/src/components/common/Backdrop/Backdrop.tsx

@@ -0,0 +1,39 @@
+import React, { MouseEventHandler, SyntheticEvent } from 'react';
+import styled from 'styled-components';
+
+import Styles from './backdrop.module.scss';
+
+interface BackdropProps {
+  onClick?: (e: SyntheticEvent<HTMLElement>) => void;
+  open?: boolean;
+}
+
+const BackdropStyled = styled.div`
+  display: flex;
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background: rgba(#000000, 0.5);
+`;
+
+const Backdrop: React.FC<BackdropProps> = ({ open, onClick }) => {
+  const handleClick: MouseEventHandler<HTMLElement> = (e) => {
+    e.stopPropagation();
+    if (e.target !== e.currentTarget) {
+      return;
+    }
+    onClick?.(e);
+  };
+
+  return open ? (
+    // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
+    <BackdropStyled className={Styles.backdrop} onClick={handleClick} />
+  ) : null;
+};
+
+export default Backdrop;

+ 41 - 0
kafka-ui-react-app/src/components/common/Modal/Modal.tsx

@@ -0,0 +1,41 @@
+import React from 'react';
+import Overlay, { OverlayProps } from 'components/common/Overlay/Overlay';
+import CloseIcon from 'components/common/Icons/CloseIcon';
+
+import {
+  CloseIconWrapper,
+  ModalSizes,
+  Wrapper,
+  Header,
+} from './ModalStyled.styled';
+
+interface ModalProps extends OverlayProps {
+  size?: ModalSizes;
+  header?: string;
+  isCloseIcon?: boolean;
+  transparent?: boolean;
+}
+
+export const Modal: React.FC<React.PropsWithChildren<ModalProps>> = ({
+  children,
+  open,
+  portal = true,
+  header,
+  isCloseIcon,
+  onClose,
+  transparent = false,
+}) => {
+  return (
+    <Overlay backdrop onClose={onClose} open={open} portal={portal}>
+      <Wrapper transparent={transparent}>
+        {isCloseIcon && (
+          <CloseIconWrapper>
+            <CloseIcon />
+          </CloseIconWrapper>
+        )}
+        {header && <Header>{header}</Header>}
+        {children}
+      </Wrapper>
+    </Overlay>
+  );
+};

+ 35 - 0
kafka-ui-react-app/src/components/common/Modal/ModalStyled.styled.ts

@@ -0,0 +1,35 @@
+import styled, { css } from 'styled-components';
+
+export type ModalSizes = 'XS' | 'S' | 'M' | 'L';
+
+export const Wrapper = styled.div<{ transparent: boolean }>(
+  ({ theme, transparent }) => css`
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    width: 90vw;
+    max-width: 560px;
+    max-height: 90vh;
+    margin: 5vh auto;
+    background-color: ${transparent
+      ? 'transparent'
+      : theme.modal.backgroundColor};
+    padding: 20px;
+    border-radius: 8px;
+    overflow: hidden;
+  `
+);
+
+export const CloseIconWrapper = styled.span`
+  position: absolute;
+  right: 16px;
+  top: 16px;
+  cursor: pointer;
+  font-size: 24px;
+`;
+
+export const Header = styled.span`
+  font-weight: 700;
+  font-size: 20px;
+  text-align: center;
+`;

+ 52 - 0
kafka-ui-react-app/src/components/common/Overlay/Overlay.tsx

@@ -0,0 +1,52 @@
+import React, { SyntheticEvent } from 'react';
+import styled from 'styled-components';
+import Backdrop from 'components/common/Backdrop/Backdrop';
+import Portal from 'components/common/Portal/Portal';
+
+export interface OverlayProps {
+  backdrop?: boolean;
+  open?: boolean;
+  portal?: boolean;
+  onClose?: (e: SyntheticEvent<HTMLElement>) => void;
+}
+
+const Wrapper = styled.div`
+  position: fixed;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  z-index: 1100;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  pointer-events: none;
+
+  > * {
+    pointer-events: auto;
+  }
+`;
+
+const Overlay: React.FC<React.PropsWithChildren<OverlayProps>> = ({
+  children,
+  backdrop,
+  open,
+  portal,
+  onClose,
+}) => {
+  if (!open) {
+    return null;
+  }
+
+  return (
+    <Portal conditional={portal}>
+      <Wrapper>
+        {backdrop && <Backdrop onClick={onClose} open={open} />}
+        {children}
+      </Wrapper>
+    </Portal>
+  );
+};
+
+export default Overlay;

+ 22 - 0
kafka-ui-react-app/src/components/common/Portal/Portal.tsx

@@ -0,0 +1,22 @@
+import React, { ReactElement } from 'react';
+import { createPortal } from 'react-dom';
+
+interface PortalProps {
+  conditional?: boolean;
+  target?: HTMLElement;
+}
+
+const Portal: React.FC<React.PropsWithChildren<PortalProps>> = ({
+  children,
+  conditional = true,
+  target,
+}) => {
+  if (!conditional) {
+    return children as ReactElement;
+  }
+  const node = target ?? document.body;
+
+  return createPortal(children, node);
+};
+
+export default Portal;