@navinc/base-react-components
Version:
Nav's Pattern Library
168 lines (165 loc) • 6.58 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { createContext, useContext, useState, useRef, useEffect, useCallback } from 'react';
import styled, { keyframes } from 'styled-components';
import { useDebouncedCallback } from 'use-debounce';
import Header from './header.js';
import Icon from './icon.js';
const moveOffScreen = keyframes `
from { right: 0%; }
to { right: -100%; }
`;
const moveOnScreen = keyframes `
from { right: -100%; }
to { right: 0%; }
`;
const suddenIn = keyframes `
from { width: 100%; opacity: 0; }
to { width: 100%; opacity: 1; }
`;
const suddenOut = keyframes `
0% { width: 100%; opacity: 1; }
99.9% { width: 100%; }
100% { width: 0%; opacity: 0; }
`;
const Backdrop = styled.div `
align-items: flex-start;
animation: 0.2s ${({ theme }) => theme.materialTransitionTiming} both ${({ isOpen }) => isOpen ? suddenIn : suddenOut} ;
background-color: hsla(0, 0%, 0%, .2)};
box-sizing: border-box;
display: flex;
height: 100%;
justify-content: flex-end;
overflow: hidden;
pointer-events: ${({ isOpen }) => (isOpen ? 'initial' : 'none')};
position: fixed;
right: 0;
& * {
box-sizing: border-box;
}
`;
const drawerHeightVariations = {
dynamic: `
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
height: auto;
margin-top: 100px;
max-height: calc(100vh - 100px);
min-height: 80px;
`,
full: `
align-content: flex-start;
height: 100vh;
`,
};
const contentHeightVariations = {
dynamic: 'max-height: calc(100vh - 160px);',
full: 'max-height: calc(100vh - 65px);',
};
const CloseButton = styled(Icon).attrs(() => ({ name: 'actions/close' })) `
cursor: pointer;
`;
const Content = styled.div `
overflow: scroll;
${({ heightVariation }) => { var _a; return (_a = contentHeightVariations[heightVariation]) !== null && _a !== void 0 ? _a : contentHeightVariations.dynamic; }}
`;
const Drawer = styled.div `
animation: 0.2s ${({ theme }) => theme.materialTransitionTiming} both
${({ isOpen }) => (isOpen ? moveOnScreen : moveOffScreen)};
background-color: ${({ variation = 'default', theme }) => {
var _a;
const colorVariation = {
blue: theme.pastelBlue100,
brown: theme.timberwolf100,
default: theme.white,
};
return (_a = colorVariation[variation]) !== null && _a !== void 0 ? _a : colorVariation.default;
}};
display: grid;
grid-template-columns: 1fr min-content;
gap: 16px;
border-right: solid 1px ${({ theme }) => theme.border};
max-width: calc(100vw - 55px);
overflow: hidden;
padding: 16px;
transform: translateX(
${({ isOpen, moveStartX, translateX }) => (isOpen ? Math.max(translateX - moveStartX, 0) : 485)}px
);
transition: transform 0.2s ${({ theme }) => theme.materialTransitionTiming};
${({ heightVariation }) => { var _a; return (_a = drawerHeightVariations[heightVariation]) !== null && _a !== void 0 ? _a : drawerHeightVariations.dynamic; }}
width: 485px;
$ > ${Content} {
grid-columns: 1 / -1;
}
@media (${({ theme }) => theme.forLargerThanPhone}) {
padding: 24px 24px 32px;
}
`;
const InfoDrawerContext = createContext({
content: null,
title: null,
setInfoDrawer: () => { },
closeHandlersRef: { current: [] },
});
export const InfoDrawerProvider = ({ children }) => {
const [content, setContent] = useState(null);
const [title, setTitle] = useState(null);
const [isOpen, setIsOpen] = useState(false);
const closeHandlersRef = useRef([]);
const close = useCallback(() => {
setIsOpen(false);
closeHandlersRef.current.forEach((closeHandler) => closeHandler());
}, []);
const setInfoDrawer = useCallback((config = {}) => {
const cfg = Object.assign({ title,
content,
isOpen }, config);
if (isOpen && !config.isOpen) {
close();
}
else {
setIsOpen(cfg.isOpen);
}
setTitle(cfg.title);
setContent(cfg.content);
}, [close, content, isOpen, title]);
return (_jsx(InfoDrawerContext.Provider, Object.assign({ value: { content, isOpen, setIsOpen, title, closeHandlersRef, close, setInfoDrawer } }, { children: children }), void 0));
};
export const useInfoDrawer = ({ onClose } = {}) => {
const { content, title, setInfoDrawer, closeHandlersRef, isOpen } = useContext(InfoDrawerContext);
if (onClose && !closeHandlersRef.current.includes(onClose)) {
closeHandlersRef.current = closeHandlersRef.current.concat(onClose);
}
// When the component unmounts, we want to clean up our onClose handler.
useEffect(() => () => {
closeHandlersRef.current = closeHandlersRef.current.filter((closeHandler) => closeHandlersRef !== onClose);
}, [closeHandlersRef, onClose]);
return {
content,
title,
isOpen,
setInfoDrawer,
};
};
export const InfoDrawer = ({ variation, heightVariation = 'dynamic', className }) => {
const { content, title, isOpen, setInfoDrawer } = useInfoDrawer();
const [moveStartX, setMoveStartX] = useState(0);
const [translateX, setTranslateX] = useState(0);
const onTouchMove = useDebouncedCallback((e) => {
onTouchMove.cancel();
e.persist();
setTranslateX(e.touches[0].clientX);
}, 2, { maxWait: 5 });
return (_jsx(Backdrop, Object.assign({ isOpen: isOpen, className: className, "data-testid": "info-drawer:backdrop", onTouchStart: (e) => setMoveStartX(e.touches[0].clientX), onTouchMove: onTouchMove, onTouchEnd: () => {
onTouchMove.cancel();
translateX - moveStartX > 180 && setInfoDrawer({ isOpen: false });
setMoveStartX(0);
setTranslateX(0);
}, onClick: (e) => {
e.persist();
if (e.target.dataset.testid === 'info-drawer:backdrop') {
setInfoDrawer({ isOpen: false });
}
} }, { children: _jsxs(Drawer, Object.assign({ moveStartX: moveStartX, translateX: translateX, isOpen: isOpen, variation: variation, heightVariation: heightVariation, "data-testid": "info-drawer:drawer" }, { children: [_jsx(Header, Object.assign({ size: "sm" }, { children: title }), void 0), _jsx(CloseButton, { onClick: () => setInfoDrawer({ isOpen: false }), "data-testid": "info-drawer:close-button" }, void 0), _jsx(Content, Object.assign({ heightVariation: heightVariation }, { children: content }), void 0)] }), void 0) }), void 0));
};
export default styled(InfoDrawer) ``;
//# sourceMappingURL=info-drawer.js.map