Skip to content

Commit

Permalink
feat(FullscreenModal): add component
Browse files Browse the repository at this point in the history
  • Loading branch information
ajkl2533 committed Dec 15, 2020
1 parent 7955a28 commit 9a59804
Show file tree
Hide file tree
Showing 37 changed files with 869 additions and 3 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
'@storybook/addon-backgrounds',
'@storybook/addon-docs',
'@storybook/addon-controls',
'storycap'
],
typescript: {
check: false,
Expand Down
7 changes: 6 additions & 1 deletion .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { withScreenshot } from 'storycap'

import { DSProvider, createIconLibrary } from '../src/theme';
import colors from '../src/theme/colors';
Expand Down Expand Up @@ -32,6 +33,10 @@ export const parameters = {
controls: { expanded: true, hideNoControlsWarning: true },
docs: { source: { type: 'dynamic' } },
actions: { argTypesRegex: '^on.*' },
// storycap settings
screenshot: {
fullPage: true,
},
};

createIconLibrary();
Expand All @@ -42,4 +47,4 @@ const wrapper = (storyFn) => (
</>
);

export const decorators = [wrapper]
export const decorators = [ withScreenshot, wrapper ];
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@types/react": "^16.9.38",
"@types/styled-components": "^5.1.0",
"csstype": "^3.0.0-beta.4",
"lodash.debounce": "^4.0.8",
"polished": "^3.6.5",
"prop-types": "^15.7.2",
"ramda": "^0.27.0",
Expand Down
5 changes: 5 additions & 0 deletions src/components/FullscreenModal/Content/Content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

const Content: React.FC = ({ children }) => <div>{children}</div>;

export default Content;
12 changes: 12 additions & 0 deletions src/components/FullscreenModal/Context/Context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { noop } from 'ramda-adjunct';

import { ContextProps } from './Context.types';

const Context = React.createContext<ContextProps>({
size: 'lg',
modalRef: null,
handleClose: noop,
});

export default Context;
7 changes: 7 additions & 0 deletions src/components/FullscreenModal/Context/Context.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Sizes } from '../FullscreenModal.types';

export interface ContextProps {
size: Sizes;
modalRef: React.MutableRefObject<HTMLElement>;
handleClose: () => void;
}
68 changes: 68 additions & 0 deletions src/components/FullscreenModal/Footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { useContext, useRef } from 'react';
import styled from 'styled-components';

import { getColor, pxToRem } from '../../../utils/helpers';
import { FlexContainer } from '../../FlexContainer';
import { ScrollToTop } from '../ScrollToTop';
import { FullscreenModalProps } from '../FullscreenModal.types';
import ModalContext from '../Context/Context';
import { useStickyFooter } from './useStickyFooter';

const BaseStickyFooter = styled.footer`
position: fixed;
bottom: 0;
left: 0;
width: 100%;
padding: ${pxToRem(10, 0)};
background-color: ${getColor('graphite5H')};
border-top: 1px solid ${getColor('graphiteHB')};
`;
const BaseFooter = styled.footer`
background-color: ${getColor('graphite5H')};
border-top: 1px solid ${getColor('graphiteHB')};
padding-top: ${pxToRem(24)};
margin-top: ${pxToRem(40)};
`;

const FooterInnerContainer = styled.div<Pick<FullscreenModalProps, 'size'>>`
width: ${({ size, theme }) => pxToRem(theme.modals.size[size])};
margin: 0 auto;
`;

const Footer: React.FC = ({ children }) => {
const { size, modalRef } = useContext(ModalContext);
const modalFooterRef = useRef(null);

const { isFixed, shouldShowScrollToTopButton } = useStickyFooter(
modalRef,
modalFooterRef,
);

const scrollToTop = () => {
modalRef.current.scrollTo(0, 0);
};

return (
<>
{isFixed && (
<BaseStickyFooter>
<FooterInnerContainer size={size}>{children}</FooterInnerContainer>
</BaseStickyFooter>
)}
<BaseFooter ref={modalFooterRef}>
{children}
{shouldShowScrollToTopButton && (
<FlexContainer
alignItems="center"
justifyContent="center"
margin={{ vertical: 1.2 }}
>
<ScrollToTop onClick={scrollToTop} />
</FlexContainer>
)}
</BaseFooter>
</>
);
};

export default Footer;
75 changes: 75 additions & 0 deletions src/components/FullscreenModal/Footer/useStickyFooter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useCallback, useEffect, useState } from 'react';
import debounce from 'lodash.debounce';

export const useStickyFooter = (
modalRef: React.MutableRefObject<HTMLElement>,
modalFooterRef: React.MutableRefObject<HTMLElement>,
): { isFixed: boolean; shouldShowScrollToTopButton: boolean } => {
const [isFixed, setIsFixed] = useState(false);
const [
shouldShowScrollToTopButton,
setShouldShowScrollToTopButton,
] = useState(false);
const showScrollToTopButton = useCallback(() => {
if (modalRef.current === null) return;
const isScrollable =
modalRef.current.scrollHeight > modalRef.current.offsetHeight;
setShouldShowScrollToTopButton(isScrollable);
setIsFixed(false);
}, [modalRef, setShouldShowScrollToTopButton]);

useEffect(() => {
showScrollToTopButton();
window.addEventListener('resize', debounce(showScrollToTopButton, 200));
}, [modalRef, showScrollToTopButton]);

const isInView = useCallback(() => {
if (modalRef.current === null || modalFooterRef.current === null) return;
const scrollOffset =
modalRef.current.scrollTop + modalRef.current.offsetHeight;
const contentHeight =
modalRef.current.scrollHeight - modalFooterRef.current.scrollHeight;

if (isFixed && scrollOffset >= contentHeight) {
setIsFixed(false);
} else if (
!isFixed &&
scrollOffset < contentHeight - contentHeight * 0.001
) {
setIsFixed(true);
}
}, [isFixed, setIsFixed, modalRef, modalFooterRef]);
const [observer] = useState(new MutationObserver(() => isInView()));

useEffect(() => {
if (
modalRef.current !== null &&
modalFooterRef.current !== null &&
shouldShowScrollToTopButton
) {
observer.observe(modalRef.current, {
childList: true,
subtree: true,
attributes: true,
});

isInView();
modalRef.current.addEventListener('scroll', debounce(isInView, 100));
window.addEventListener('resize', debounce(isInView, 200));
}
return () => {
observer.disconnect();
};
}, [
observer,
modalRef,
modalFooterRef,
shouldShowScrollToTopButton,
isInView,
]);

return {
isFixed,
shouldShowScrollToTopButton,
};
};
5 changes: 5 additions & 0 deletions src/components/FullscreenModal/FullscreenModal.enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const FullscreenModalSizes = {
lg: 'lg',
md: 'md',
sm: 'sm',
} as const;
Loading

0 comments on commit 9a59804

Please sign in to comment.