WFDASHDRIVER-APP/components/BottomSheet.tsx

71 lines
2.4 KiB
TypeScript
Raw Normal View History

'use client';
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { X } from 'lucide-react';
interface BottomSheetProps {
title: string;
subtitle?: string;
onClose: () => void;
children: React.ReactNode;
}
// Full-width panel that slides up from the bottom of the screen.
//
// Replaces the small popovers on the compact rows: those were 256px
// wide and pinned to a button in the middle of the row, so on a phone
// they ran off the left edge. Rendered into <body> through a portal so
// no parent's stacking context or overflow can clip it, and it sits
// above the BottomNav.
export default function BottomSheet({ title, subtitle, onClose, children }: BottomSheetProps) {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [onClose]);
if (!mounted) return null;
return createPortal(
<div
data-bottom-sheet
className="fixed inset-0 z-[90] flex items-end bg-black/35 animate-fade-in"
onClick={onClose}
>
<div
role="dialog"
aria-modal="true"
aria-label={title}
onClick={(e) => e.stopPropagation()}
className="w-full max-h-[80vh] overflow-y-auto bg-white rounded-t-2xl px-4 pt-2 pb-[calc(1.25rem+env(safe-area-inset-bottom))] shadow-[0_-8px_24px_rgba(0,0,0,0.12)]"
>
<div className="w-10 h-1 rounded-full bg-[rgba(38,37,30,0.2)] mx-auto mb-3" aria-hidden="true" />
<div className="flex items-start justify-between gap-3 mb-3">
<div className="min-w-0">
<p className="text-[13px] text-[rgba(38,37,30,0.6)]">{title}</p>
{subtitle && (
<p className="text-[18px] font-semibold text-[#26251e] break-words leading-snug">{subtitle}</p>
)}
</div>
<button
type="button"
onClick={onClose}
aria-label="Close"
className="w-10 h-10 flex-shrink-0 rounded-lg border border-[rgba(38,37,30,0.15)] flex items-center justify-center active:bg-[rgba(38,37,30,0.06)] touch-manipulation"
>
<X size={18} aria-hidden="true" />
</button>
</div>
{children}
</div>
</div>,
document.body,
);
}