Files
natreex d4765e6576 Add foundational components and logic for migration, UI design, and input handling
- Introduced foundational UI components (`Badge`, `LockCard`, `SectionHeader`, `AvatarIcon`, etc.) for flexible layouts and consistent design.
- Added migration support with the `MigrationModal` component and backend integration for exporting/importing data between Electron and Tauri.
- Extended form components with `TextAreaInput`, `OrderInput`, `ToggleField`, and `ToolbarSelect` for improved input handling.
- Updated `ScribeShell` with migration popup logic to prompt users for data migration.
- Integrated `AlertStack` for better alert handling and notification management.
- Enhanced Rust/Tauri services with migration command implementations.
- Added translations and styles for new components.
2026-04-05 12:52:54 -04:00

103 lines
3.5 KiB
TypeScript

'use client';
import React, {ReactNode, useEffect, useState} from 'react';
import {createPortal} from 'react-dom';
import {LucideIcon, X} from "lucide-react";
import Button from "@/components/ui/Button";
import IconButton from "@/components/ui/IconButton";
type ModalSize = 'sm' | 'md' | 'lg';
interface ModalProps {
title: string;
icon?: LucideIcon;
children: ReactNode;
size?: ModalSize;
onClose: () => void;
onConfirm?: () => void;
confirmText?: string;
cancelText?: string;
footer?: ReactNode;
actions?: ReactNode;
enableOverflow?: boolean;
}
const sizeClasses: Record<ModalSize, string> = {
sm: 'md:w-3/4 xl:w-1/4 lg:w-2/4 sm:w-11/12',
md: 'md:w-3/4 xl:w-2/5 lg:w-2/4 sm:w-11/12',
lg: 'md:w-3/4 xl:w-3/5 lg:w-3/4 sm:w-11/12',
};
export default function Modal(
{
title,
icon: Icon,
children,
size = 'md',
onClose,
onConfirm,
confirmText = 'Confirmer',
cancelText = 'Annuler',
footer,
actions,
enableOverflow = true,
}: ModalProps) {
const [mounted, setMounted] = useState<boolean>(false);
useEffect((): (() => void) => {
setMounted(true);
document.body.style.overflow = 'hidden';
return (): void => {
setMounted(false);
document.body.style.overflow = 'auto';
};
}, []);
const hasFooter: boolean = !!footer || !!onConfirm;
const modalContent: React.JSX.Element = (
<div
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-darkest-background/60 backdrop-blur-md animate-fadeIn">
<div
className={`relative bg-tertiary text-text-primary rounded-xl max-h-[90vh] overflow-hidden flex flex-col ${sizeClasses[size]}`}>
<div className="flex justify-between items-center px-6 py-4">
<h2 className="flex items-center gap-3 font-['ADLaM_Display'] text-xl tracking-wide">
{Icon && <Icon className="w-6 h-6" strokeWidth={1.75}/>}
{title}
</h2>
<div className="flex items-center gap-2">
{actions}
<IconButton icon={X} variant="light" onClick={onClose}/>
</div>
</div>
<div
className={`flex-1 min-h-0 bg-darkest-background rounded-xl mx-2 flex flex-col overflow-hidden ${!hasFooter ? 'mb-2' : ''}`}>
<div
className={`flex-1 min-h-0 ${enableOverflow ? 'overflow-auto custom-scrollbar' : 'overflow-hidden'}`}>
<div className="p-5 space-y-6">
{children}
</div>
</div>
</div>
{hasFooter && (
<div className="flex justify-end gap-3 px-6 py-4">
{footer ? footer : (
<>
<Button variant="secondary" onClick={onClose}>
{cancelText}
</Button>
<Button variant="primary" onClick={onConfirm}>
{confirmText}
</Button>
</>
)}
</div>
)}
</div>
</div>
);
if (!mounted) return null;
return createPortal(modalContent, document.body);
}