feat(ui): F1+F2 — component primitives + rebuilt app shell
F1: shadcn-style primitive set in components/ui (Button, Card, Badge, Input, Switch, Separator, Skeleton, Tooltip, DropdownMenu, Dialog, Sheet, Tabs, Table, Toast+useToast, ThemeToggle, PageHeader/StatCard/EmptyState) on Radix + cva. F2: Layout rebuilt on the new system — sidebar with primary nav (lucide icons) + favorites/saved-searches/mailbox/smart-folder sections, sticky topbar with search, sync status, theme toggle, digest toggle, and an account dropdown. All prior behavior preserved (auth, sync, collapse/section persistence, "/" search focus). App wrapped in ToastProvider + TooltipProvider. Build green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { forwardRef } from 'react';
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { X } from 'lucide-react';
|
||||
import { cn } from '../../lib/utils.js';
|
||||
|
||||
// A side drawer built on Radix Dialog. Used for the rule editor, filters, etc.
|
||||
const Sheet = DialogPrimitive.Root;
|
||||
const SheetTrigger = DialogPrimitive.Trigger;
|
||||
const SheetClose = DialogPrimitive.Close;
|
||||
|
||||
const sideClasses = {
|
||||
right: 'inset-y-0 right-0 h-full w-full max-w-md border-l animate-slide-in-right',
|
||||
left: 'inset-y-0 left-0 h-full w-full max-w-md border-r',
|
||||
};
|
||||
|
||||
const SheetContent = forwardRef(function SheetContent({ className, children, side = 'right', ...props }, ref) {
|
||||
return (
|
||||
<DialogPrimitive.Portal>
|
||||
<DialogPrimitive.Overlay className="fixed inset-0 z-50 bg-foreground/40 backdrop-blur-sm animate-fade-in" />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed z-50 flex flex-col gap-4 bg-card p-6 shadow-md border-border',
|
||||
sideClasses[side],
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm text-muted-foreground opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPrimitive.Portal>
|
||||
);
|
||||
});
|
||||
|
||||
function SheetHeader({ className, ...props }) {
|
||||
return <div className={cn('flex flex-col gap-1.5', className)} {...props} />;
|
||||
}
|
||||
|
||||
function SheetFooter({ className, ...props }) {
|
||||
return <div className={cn('mt-auto flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)} {...props} />;
|
||||
}
|
||||
|
||||
const SheetTitle = forwardRef(function SheetTitle({ className, ...props }, ref) {
|
||||
return <DialogPrimitive.Title ref={ref} className={cn('text-lg font-semibold', className)} {...props} />;
|
||||
});
|
||||
|
||||
const SheetDescription = forwardRef(function SheetDescription({ className, ...props }, ref) {
|
||||
return <DialogPrimitive.Description ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />;
|
||||
});
|
||||
|
||||
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription };
|
||||
Reference in New Issue
Block a user