Skip to content

Commit

Permalink
Add modal footer and fix height with overflow (#1455)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitayutanov authored Nov 23, 2023
1 parent ef56620 commit 2aca796
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 8 deletions.
4 changes: 2 additions & 2 deletions utils/gear-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion utils/gear-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gear-js/ui",
"version": "0.5.19",
"version": "0.5.20",
"description": "React UI components used across Gear applications",
"author": "Gear Technologies",
"license": "GPL-3.0",
Expand Down
42 changes: 41 additions & 1 deletion utils/gear-ui/src/components/Modal/Modal.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@
@use '../../assets/styles/mixins' as *;
@use '../../assets/styles/headings' as *;

@mixin customScroll {
// TODO: temp solution specifically for modal,
// take a closer look to simplebar-react
overflow-y: auto;

// firefox
scrollbar-width: thin;
scrollbar-color: #fff rgba(#fff, 0.1);

// chrome
&::-webkit-scrollbar {
width: 19px;
}

&::-webkit-scrollbar-track,
&::-webkit-scrollbar-thumb {
background-clip: padding-box;
border-style: solid;
border-color: transparent;
}

&::-webkit-scrollbar-track {
border-width: 12px 9px;
background-color: rgba(#fff, 0.1);
}

&::-webkit-scrollbar-thumb {
border-width: 12px 8px;
background-color: #fff;
border-radius: 1px;
}
}

.overlay {
position: fixed;
top: 0;
Expand Down Expand Up @@ -33,7 +66,6 @@
.header {
// border on .header and .body instead of .modal,
// cuz otherwise .modal should have overflow: hidden
// and we can't cuz modal can have tooltip inside
display: flex;
justify-content: space-between;
padding: 17px 32px 22px;
Expand All @@ -45,10 +77,18 @@
}

.body {
@include customScroll;
padding: 32px;
background-color: #29292b;
border-left: $borderModal;
border-right: $borderModal;
}

.footer {
padding: 24px 32px;
background-color: #222225;
border-left: $borderModal;
border-right: $borderModal;
border-bottom: $borderModal;
border-radius: 0 0 $borderRadiusMedium $borderRadiusMedium;
}
Expand Down
37 changes: 33 additions & 4 deletions utils/gear-ui/src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import clsx from 'clsx';
import { ReactNode, useEffect, useState, MouseEvent } from 'react';
import { ReactNode, useEffect, useState, MouseEvent, useCallback } from 'react';
import { createPortal } from 'react-dom';
import { Button } from '../Button/Button';
import { ReactComponent as xSVG } from './images/x.svg';
Expand All @@ -9,12 +9,34 @@ type Props = {
heading: string;
close: () => void;
children?: ReactNode;
footer?: ReactNode;
className?: string;
size?: 'normal' | 'large';
};

const Modal = ({ heading, close, children, className, size = 'normal' }: Props) => {
function useHeight() {
const [height, setHeight] = useState(0);

const ref = useCallback((node: HTMLDivElement | null) => {
if (node) setHeight(node.getBoundingClientRect().height);
}, []);

return [height, ref] as const;
}

function useMaxHeight() {
const [headerHeight, headerRef] = useHeight();
const [footerHeight, footerRef] = useHeight();

const padding = 32;
const bodyStyle = { maxHeight: `calc(100vh - ${headerHeight + footerHeight + 2 * padding}px)` };

return { bodyStyle, headerRef, footerRef };
}

const Modal = ({ heading, close, children, footer, className, size = 'normal' }: Props) => {
const [root, setRoot] = useState<HTMLDivElement>();
const { bodyStyle, headerRef, footerRef } = useMaxHeight();

const modalClassName = clsx(styles.modal, styles[size]);
const bodyClassName = clsx(styles.body, className);
Expand All @@ -37,15 +59,22 @@ const Modal = ({ heading, close, children, className, size = 'normal' }: Props)
const component = (
<div className={styles.overlay} onClick={handleOverlayClick} data-testid="overlay">
<div className={modalClassName} data-testid="modal">
<header className={styles.header}>
<header className={styles.header} ref={headerRef}>
<h3 className={styles.heading}>{heading}</h3>
<Button icon={xSVG} color="transparent" onClick={close} />
</header>

{children && (
<div className={bodyClassName} data-testid="body">
<div className={bodyClassName} data-testid="body" style={bodyStyle}>
{children}
</div>
)}

{footer && (
<footer className={styles.footer} ref={footerRef}>
{footer}
</footer>
)}
</div>
</div>
);
Expand Down

0 comments on commit 2aca796

Please sign in to comment.