dashboard-nanobot/frontend/src/components/lucent/LucentDrawer.tsx

99 lines
3.1 KiB
TypeScript

import { useEffect, type CSSProperties, type ReactNode } from 'react';
import { Maximize2, Minimize2, X } from 'lucide-react';
import { LucentIconButton } from './LucentIconButton';
export type LucentDrawerSize = 'default' | 'expand';
interface LucentDrawerProps {
open: boolean;
title: string;
description?: string;
size?: LucentDrawerSize;
onClose: () => void;
onToggleSize?: () => void;
children: ReactNode;
footer?: ReactNode;
headerActions?: ReactNode;
topOffset?: number;
panelClassName?: string;
bodyClassName?: string;
closeLabel?: string;
expandLabel?: string;
collapseLabel?: string;
}
export function LucentDrawer({
open,
title,
description,
size = 'default',
onClose,
onToggleSize,
children,
footer,
headerActions,
topOffset = 0,
panelClassName = '',
bodyClassName = '',
closeLabel = 'Close panel',
expandLabel = 'Expand panel',
collapseLabel = 'Collapse panel',
}: LucentDrawerProps) {
useEffect(() => {
if (!open) return;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') onClose();
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [onClose, open]);
return (
<>
<div
className={`lucent-drawer-mask ${open ? 'is-open' : ''} ${size === 'expand' ? 'is-expand' : ''}`}
onClick={open ? onClose : undefined}
aria-hidden="true"
/>
<aside
className={`lucent-drawer ${open ? 'is-open' : ''} is-${size}`}
style={{ '--lucent-drawer-top': `${topOffset}px` } as CSSProperties}
aria-hidden={!open}
>
<div className={`lucent-drawer-panel card ${panelClassName}`.trim()} onClick={(event) => event.stopPropagation()}>
<div className="lucent-drawer-header">
<div className="lucent-drawer-header-copy">
<div className="section-mini-title">{title}</div>
{description ? <div className="field-label">{description}</div> : null}
</div>
<div className="lucent-drawer-header-actions">
{headerActions}
{onToggleSize ? (
<LucentIconButton
className="btn btn-secondary btn-sm icon-btn"
onClick={onToggleSize}
tooltip={size === 'expand' ? collapseLabel : expandLabel}
aria-label={size === 'expand' ? collapseLabel : expandLabel}
>
{size === 'expand' ? <Minimize2 size={14} /> : <Maximize2 size={14} />}
</LucentIconButton>
) : null}
<LucentIconButton
className="btn btn-secondary btn-sm icon-btn"
onClick={onClose}
tooltip={closeLabel}
aria-label={closeLabel}
>
<X size={14} />
</LucentIconButton>
</div>
</div>
<div className={`lucent-drawer-body ${bodyClassName}`.trim()}>{children}</div>
{footer ? <div className="lucent-drawer-footer">{footer}</div> : null}
</div>
</aside>
</>
);
}