65 lines
1.8 KiB
TypeScript
65 lines
1.8 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import * as Dialog from '@radix-ui/react-dialog';
|
|
import { clsx } from 'clsx';
|
|
import { X } from 'lucide-react';
|
|
|
|
export interface ModalProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
title: string;
|
|
description?: string;
|
|
size?: 'sm' | 'md' | 'lg' | 'full';
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
const sizes: Record<string, string> = {
|
|
sm: 'max-w-sm',
|
|
md: 'max-w-lg',
|
|
lg: 'max-w-2xl',
|
|
full: 'max-w-[90vw]',
|
|
};
|
|
|
|
export function Modal({
|
|
open,
|
|
onOpenChange,
|
|
title,
|
|
description,
|
|
size = 'md',
|
|
children,
|
|
}: ModalProps) {
|
|
return (
|
|
<Dialog.Root open={open} onOpenChange={onOpenChange}>
|
|
<Dialog.Portal>
|
|
<Dialog.Overlay className="fixed inset-0 z-[9998] bg-black/60 backdrop-blur-sm" />
|
|
<Dialog.Content
|
|
className={clsx(
|
|
'fixed left-1/2 top-1/2 z-[9999] -translate-x-1/2 -translate-y-1/2',
|
|
'w-full rounded-xl border p-6 shadow-xl',
|
|
'bg-[var(--bl-bg-elevated,#12151c)] border-[var(--bl-border,#2a2a4a)] text-[var(--bl-text-primary,#fff)]',
|
|
'focus:outline-none',
|
|
sizes[size]
|
|
)}
|
|
>
|
|
<Dialog.Title className="text-lg font-semibold">{title}</Dialog.Title>
|
|
{description && (
|
|
<Dialog.Description className="mt-1 text-sm text-[var(--bl-text-secondary,#a0a0b0)]">
|
|
{description}
|
|
</Dialog.Description>
|
|
)}
|
|
<div className="mt-4">{children}</div>
|
|
<Dialog.Close asChild>
|
|
<button
|
|
className="absolute right-4 top-4 text-[var(--bl-text-tertiary,#666)] hover:text-[var(--bl-text-primary,#fff)]"
|
|
aria-label="Close dialog"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</button>
|
|
</Dialog.Close>
|
|
</Dialog.Content>
|
|
</Dialog.Portal>
|
|
</Dialog.Root>
|
|
);
|
|
}
|