fix(ui): improve pipeline status dialog layout and styling

- Switch from AlertDialog to Dialog component for better modal behavior
- Adjust dialog positioning and alignment controls
- Remove custom close button to avoid duplication
- Add proper spacing between alignment buttons and close button
- Simplify history container height with min/max height
- Reduce overlay opacity for better visibility
This commit is contained in:
yangdx
2025-03-26 16:24:38 +08:00
parent 4adfcdc8fe
commit 03934b1385
2 changed files with 54 additions and 96 deletions

View File

@@ -1,16 +1,15 @@
import { useState, useEffect, useRef, useCallback } from 'react' import { useState, useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { toast } from 'sonner' import { toast } from 'sonner'
import { X, AlignLeft, AlignCenter, AlignRight } from 'lucide-react' import { AlignLeft, AlignCenter, AlignRight } from 'lucide-react'
import { import {
AlertDialog, Dialog,
AlertDialogContent, DialogContent,
AlertDialogHeader, DialogHeader,
AlertDialogTitle, DialogTitle,
AlertDialogDescription, DialogDescription
AlertDialogOverlay, } from '@/components/ui/Dialog'
} from '@/components/ui/AlertDialog'
import Button from '@/components/ui/Button' import Button from '@/components/ui/Button'
import { getPipelineStatus, PipelineStatusResponse } from '@/api/lightrag' import { getPipelineStatus, PipelineStatusResponse } from '@/api/lightrag'
import { errorMessage } from '@/lib/utils' import { errorMessage } from '@/lib/utils'
@@ -31,44 +30,15 @@ export default function PipelineStatusDialog({
const [status, setStatus] = useState<PipelineStatusResponse | null>(null) const [status, setStatus] = useState<PipelineStatusResponse | null>(null)
const [position, setPosition] = useState<DialogPosition>('center') const [position, setPosition] = useState<DialogPosition>('center')
const [isUserScrolled, setIsUserScrolled] = useState(false) const [isUserScrolled, setIsUserScrolled] = useState(false)
const [historyHeight, setHistoryHeight] = useState('20em')
const historyRef = useRef<HTMLDivElement>(null) const historyRef = useRef<HTMLDivElement>(null)
const resizeObserverRef = useRef<ResizeObserver | null>(null)
// Calculate history height based on window height
const updateHistoryHeight = useCallback(() => {
const minHeight = 7.5 // 5 lines * 1.5em line height
const windowHeight = window.innerHeight
const pixelsPerEm = parseFloat(getComputedStyle(document.documentElement).fontSize)
const maxHeightInEm = Math.max(Math.floor((windowHeight * 0.4) / pixelsPerEm), minHeight)
setHistoryHeight(`${maxHeightInEm}em`)
}, [])
// Reset position when dialog opens // Reset position when dialog opens
useEffect(() => { useEffect(() => {
if (open) { if (open) {
setPosition('center') setPosition('center')
setIsUserScrolled(false) setIsUserScrolled(false)
updateHistoryHeight()
} }
}, [open, updateHistoryHeight]) }, [open])
// Setup resize observer
useEffect(() => {
if (!open) return
resizeObserverRef.current = new ResizeObserver((entries) => {
if (entries[0]) {
updateHistoryHeight()
}
})
resizeObserverRef.current.observe(document.body)
return () => {
resizeObserverRef.current?.disconnect()
}
}, [open, updateHistoryHeight])
// Handle scroll position // Handle scroll position
useEffect(() => { useEffect(() => {
@@ -112,74 +82,63 @@ export default function PipelineStatusDialog({
}, [open, t]) }, [open, t])
return ( return (
<AlertDialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<AlertDialogOverlay className="bg-black/30" /> <DialogContent
<AlertDialogContent
className={cn( className={cn(
'sm:max-w-[600px] transition-all duration-200', 'sm:max-w-[600px] transition-all duration-200 fixed',
position === 'left' && '!left-4 !translate-x-0', position === 'left' && '!left-[25%] !translate-x-[-50%] !mx-4',
position === 'center' && '!left-1/2 !-translate-x-1/2', position === 'center' && '!left-1/2 !-translate-x-1/2',
position === 'right' && '!right-4 !left-auto !translate-x-0' position === 'right' && '!left-[75%] !translate-x-[-50%] !mx-4'
)} )}
> >
<AlertDialogDescription className="sr-only"> <DialogDescription className="sr-only">
{status?.job_name {status?.job_name
? `${t('documentPanel.pipelineStatus.jobName')}: ${status.job_name}, ${t('documentPanel.pipelineStatus.progress')}: ${status.cur_batch}/${status.batchs}` ? `${t('documentPanel.pipelineStatus.jobName')}: ${status.job_name}, ${t('documentPanel.pipelineStatus.progress')}: ${status.cur_batch}/${status.batchs}`
: t('documentPanel.pipelineStatus.noActiveJob') : t('documentPanel.pipelineStatus.noActiveJob')
} }
</AlertDialogDescription> </DialogDescription>
<AlertDialogHeader className="flex flex-row items-center justify-between"> <DialogHeader className="flex flex-row items-center">
<AlertDialogTitle> <DialogTitle className="flex-1">
{t('documentPanel.pipelineStatus.title')} {t('documentPanel.pipelineStatus.title')}
</AlertDialogTitle> </DialogTitle>
{/* Position control buttons and close button */} {/* Position control buttons */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 mr-8">
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
className={cn(
'h-6 w-6',
position === 'left' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
)}
onClick={() => setPosition('left')}
>
<AlignLeft className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className={cn(
'h-6 w-6',
position === 'center' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
)}
onClick={() => setPosition('center')}
>
<AlignCenter className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className={cn(
'h-6 w-6',
position === 'right' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
)}
onClick={() => setPosition('right')}
>
<AlignRight className="h-4 w-4" />
</Button>
</div>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-6 w-6" className={cn(
onClick={() => onOpenChange(false)} 'h-6 w-6',
position === 'left' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
)}
onClick={() => setPosition('left')}
> >
<X className="h-4 w-4" /> <AlignLeft className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className={cn(
'h-6 w-6',
position === 'center' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
)}
onClick={() => setPosition('center')}
>
<AlignCenter className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className={cn(
'h-6 w-6',
position === 'right' && 'bg-zinc-200 text-zinc-800 hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-600'
)}
onClick={() => setPosition('right')}
>
<AlignRight className="h-4 w-4" />
</Button> </Button>
</div> </div>
</AlertDialogHeader> </DialogHeader>
{/* Status Content */} {/* Status Content */}
<div className="space-y-4 pt-4"> <div className="space-y-4 pt-4">
@@ -218,8 +177,7 @@ export default function PipelineStatusDialog({
<div <div
ref={historyRef} ref={historyRef}
onScroll={handleScroll} onScroll={handleScroll}
className="font-mono text-sm rounded-md bg-zinc-800 text-zinc-100 p-3 overflow-y-auto" className="font-mono text-sm rounded-md bg-zinc-800 text-zinc-100 p-3 overflow-y-auto min-h-[7.5em] max-h-[40vh]"
style={{ height: historyHeight }}
> >
{status?.history_messages?.map((msg, idx) => ( {status?.history_messages?.map((msg, idx) => (
<div key={idx}>{msg}</div> <div key={idx}>{msg}</div>
@@ -227,7 +185,7 @@ export default function PipelineStatusDialog({
</div> </div>
</div> </div>
</div> </div>
</AlertDialogContent> </DialogContent>
</AlertDialog> </Dialog>
) )
} }

View File

@@ -19,7 +19,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
ref={ref} ref={ref}
className={cn( className={cn(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/30',
className className
)} )}
{...props} {...props}