import React, { useState, FunctionComponent, createContext, useContext, useEffect, useLayoutEffect, useRef, memo, NamedExoticComponent, ComponentProps, } from 'react' import classNames from 'classnames' const TrayContext = createContext({}) const TrayControlsBase: FunctionComponent<{ children: (data: any) => any }> = ({ children }) => { const [open, setOpen] = useState(false) return ( setOpen(false), isOpen: open, }} > {children({ close: () => setOpen(false), open: () => setOpen(true), })} ) } export const TrayControls = memo(TrayControlsBase) const TrayBase: FunctionComponent<{ padding: boolean position: 'left' | 'right' | 'top' | 'bottom' | 'center' isOpen?: boolean isRelativePosition?: boolean close?: () => void backdrop: boolean ariaLabel: string ariaDescId: string role?: 'dialog' | 'alertdialog' className?: string isInfoOnly?: boolean }> = (props) => { const ctx = useContext(TrayContext) const closeButtonRef = useRef(null) const { children, padding, position, close, isOpen, isRelativePosition = false, backdrop, className, isInfoOnly, ariaDescId, ariaLabel, role = 'dialog', } = { ...ctx, ...props, } useLayoutEffect(() => { if (!isOpen || !isInfoOnly || !closeButtonRef.current) return closeButtonRef.current.focus() }, [isOpen, isInfoOnly]) useEffect(() => { if (!isOpen) return const handleKeyDown = (e: KeyboardEvent) => e.which === 27 /* Escape Key */ && isOpen && close && close() window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [close, isOpen]) // Only evaluate when isOpen changes return (
{backdrop && ( ) } TrayBase.defaultProps = { padding: true, position: 'left', backdrop: true, } export const Tray: NamedExoticComponent> = memo(TrayBase)