UNPKG

@navinc/base-react-components

Version:
168 lines (165 loc) 6.58 kB
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