"use client" ;
import { useState, useCallback } from "react" ;
import { Table, Loader2 } from "lucide-react" ;
import { Button } from "@/components/ui/button" ;
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog" ;
import { Label } from "@/components/ui/label" ;
import { Switch } from "@/components/ui/switch" ;
import { cn } from "@/lib/utils" ;
const MAX_ROWS = 8 ;
const MAX_COLS = 8 ;
export const title = "React Dialog Block Insert Table" ;
export default function DialogTableInsert () {
const [ open , setOpen ] = useState ( false );
const [ hoveredRow , setHoveredRow ] = useState ( 0 );
const [ hoveredCol , setHoveredCol ] = useState ( 0 );
const [ selectedRows , setSelectedRows ] = useState ( 3 );
const [ selectedCols , setSelectedCols ] = useState ( 3 );
const [ hasHeader , setHasHeader ] = useState ( true );
const [ isInserting , setIsInserting ] = useState ( false );
const handleCellHover = useCallback (( row : number , col : number ) => {
setHoveredRow (row);
setHoveredCol (col);
}, []);
const handleCellClick = useCallback (( row : number , col : number ) => {
setSelectedRows (row);
setSelectedCols (col);
}, []);
const handleInsert = async () => {
setIsInserting ( true );
await new Promise (( resolve ) => setTimeout (resolve, 800 ));
setIsInserting ( false );
setOpen ( false );
setSelectedRows ( 3 );
setSelectedCols ( 3 );
setHasHeader ( true );
};
const displayRows = hoveredRow > 0 ? hoveredRow : selectedRows;
const displayCols = hoveredCol > 0 ? hoveredCol : selectedCols;
return (
< Dialog open = {open} onOpenChange = {setOpen}>
< div className = "flex min-h-[350px] items-center justify-center" >
< DialogTrigger asChild >
< Button variant = "outline" >
< Table className = "mr-2 h-4 w-4" />
Insert Table
</ Button >
</ DialogTrigger >
</ div >
< DialogContent className = "sm:max-w-sm" >
< DialogHeader >
< DialogTitle >Insert Table</ DialogTitle >
< DialogDescription >
Select size by hovering over the grid.
</ DialogDescription >
</ DialogHeader >
< div className = "space-y-4 py-4" >
{ /* Size Display */ }
< p className = "text-center text-sm text-muted-foreground" >
{displayRows} × {displayCols}
</ p >
{ /* Grid Selector */ }
< div
className = "flex justify-center"
onMouseLeave = {() => {
setHoveredRow ( 0 );
setHoveredCol ( 0 );
}}
>
< div className = "inline-grid gap-0.5" >
{Array. from ({ length: MAX_ROWS }, ( _ , rowIndex ) => (
< div key = {rowIndex} className = "flex gap-0.5" >
{Array. from ({ length: MAX_COLS }, ( _ , colIndex ) => {
const row = rowIndex + 1 ;
const col = colIndex + 1 ;
const isHovered = hoveredRow > 0 && hoveredCol > 0 && row <= hoveredRow && col <= hoveredCol;
const isSelected = row <= selectedRows && col <= selectedCols;
const showHighlight = hoveredRow > 0 ? isHovered : isSelected;
return (
< button
key = {colIndex}
className = { cn (
"h-5 w-5 rounded-sm border transition-colors" ,
showHighlight
? "bg-primary border-primary"
: "border-border hover:border-muted-foreground"
)}
onMouseEnter = {() => handleCellHover (row, col)}
onClick = {() => handleCellClick (row, col)}
/>
);
})}
</ div >
))}
</ div >
</ div >
{ /* Header Option */ }
< div className = "flex items-center justify-between" >
< Label htmlFor = "header" className = "text-sm" >Include header row</ Label >
< Switch
id = "header"
checked = {hasHeader}
onCheckedChange = {setHasHeader}
/>
</ div >
</ div >
< DialogFooter >
< Button variant = "outline" onClick = {() => setOpen ( false )} disabled = {isInserting}>
Cancel
</ Button >
< Button onClick = {handleInsert} disabled = {isInserting}>
{isInserting ? (
<>
< Loader2 className = "mr-2 h-4 w-4 animate-spin" />
Inserting...
</>
) : (
"Insert"
)}
</ Button >
</ DialogFooter >
</ DialogContent >
</ Dialog >
);
}