| import {createContext, type ReactNode, useContext, useState} from 'react'; |
|
|
| type AsideType = 'search' | 'cart' | 'mobile' | 'closed'; |
| type AsideContextValue = { |
| type: AsideType; |
| open: (mode: AsideType) => void; |
| close: () => void; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function Aside({ |
| children, |
| heading, |
| type, |
| }: { |
| children?: React.ReactNode; |
| type: AsideType; |
| heading: React.ReactNode; |
| }) { |
| const {type: activeType, close} = useAside(); |
| const expanded = type === activeType; |
|
|
| return ( |
| <div |
| aria-modal |
| className={`overlay ${expanded ? 'expanded' : ''}`} |
| role="dialog" |
| > |
| <button className="close-outside" onClick={close} /> |
| <aside> |
| <header> |
| <h3>{heading}</h3> |
| <button className="close reset" onClick={close}> |
| × |
| </button> |
| </header> |
| <main>{children}</main> |
| </aside> |
| </div> |
| ); |
| } |
|
|
| const AsideContext = createContext<AsideContextValue | null>(null); |
|
|
| Aside.Provider = function AsideProvider({children}: {children: ReactNode}) { |
| const [type, setType] = useState<AsideType>('closed'); |
|
|
| return ( |
| <AsideContext.Provider |
| value={{ |
| type, |
| open: setType, |
| close: () => setType('closed'), |
| }} |
| > |
| {children} |
| </AsideContext.Provider> |
| ); |
| }; |
|
|
| export function useAside() { |
| const aside = useContext(AsideContext); |
| if (!aside) { |
| throw new Error('useAside must be used within an AsideProvider'); |
| } |
| return aside; |
| } |
|
|