|
| 1 | +import * as React from 'react'; |
| 2 | +import { Portal as PortalPrimitive, type PortalProps } from '@radix-ui/react-portal'; |
| 3 | +import { Presence } from '@radix-ui/react-presence'; |
| 4 | +import type * as Radix from '@radix-ui/react-primitive'; |
| 5 | +import { Primitive } from '@radix-ui/react-primitive'; |
| 6 | + |
| 7 | +import { cn } from '../../utils/cn'; |
| 8 | +import { composeEventHandlers } from '../../utils/compose-event-handler'; |
| 9 | +import { withAttr } from '../../utils/withAttr'; |
| 10 | +import { useSidebarContext } from './sidebar.context'; |
| 11 | + |
| 12 | +type SidebarOverlayImplElement = React.ElementRef<typeof Primitive.div>; |
| 13 | +type PrimitiveDivProps = Radix.ComponentPropsWithoutRef<typeof Primitive.div>; |
| 14 | +interface SidebarOverlayImplProps extends PrimitiveDivProps {} |
| 15 | + |
| 16 | +const SidebarOverlayImpl = React.forwardRef<SidebarOverlayImplElement, SidebarOverlayImplProps>( |
| 17 | + (props: SidebarOverlayImplProps, forwardedRef) => { |
| 18 | + const { open, setOpen } = useSidebarContext(); |
| 19 | + return ( |
| 20 | + <Primitive.div |
| 21 | + role="sidebar-overlay" |
| 22 | + data-state={withAttr(open)} |
| 23 | + {...props} |
| 24 | + ref={forwardedRef} |
| 25 | + style={{ pointerEvents: open ? 'auto' : 'none', ...props.style }} |
| 26 | + onClick={composeEventHandlers(props.onClick, (e: any) => { |
| 27 | + if (e.target['ariaHidden'] === 'true') return; |
| 28 | + setOpen(false); |
| 29 | + })} |
| 30 | + /> |
| 31 | + ); |
| 32 | + } |
| 33 | +); |
| 34 | + |
| 35 | +const OVERLAY_NAME = 'SidebarOverlay'; |
| 36 | + |
| 37 | +type SidebarOverlayElement = SidebarOverlayImplElement; |
| 38 | +export interface SidebarOverlayProps extends SidebarOverlayImplProps { |
| 39 | + /** |
| 40 | + * Specify a container element to portal the content into. |
| 41 | + */ |
| 42 | + container?: PortalProps['container']; |
| 43 | + /** |
| 44 | + * Used to force mounting when more control is needed. Useful when |
| 45 | + * controlling animation with React animation libraries. |
| 46 | + */ |
| 47 | + forceMount?: true; |
| 48 | +} |
| 49 | + |
| 50 | +export const SidebarOverlay = React.forwardRef<SidebarOverlayElement, SidebarOverlayProps>( |
| 51 | + (props: SidebarOverlayProps, forwardedRef) => { |
| 52 | + const { forceMount, className, container, ...etc } = props; |
| 53 | + const { open, isMobile } = useSidebarContext(); |
| 54 | + if (!isMobile) return null; |
| 55 | + |
| 56 | + return ( |
| 57 | + <SidebarOverlayImpl |
| 58 | + className={cn( |
| 59 | + 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 bg-background/70 fixed inset-0 z-40 backdrop-blur-sm transition-all', |
| 60 | + { |
| 61 | + 'invisible opacity-0': !open, |
| 62 | + 'visible opacity-100': open, |
| 63 | + }, |
| 64 | + className |
| 65 | + )} |
| 66 | + {...etc} |
| 67 | + ref={forwardedRef} |
| 68 | + /> |
| 69 | + ); |
| 70 | + } |
| 71 | +); |
| 72 | + |
| 73 | +SidebarOverlay.displayName = OVERLAY_NAME; |
0 commit comments