| 123456789101112131415161718192021222324252627282930313233343536373839404142 |
- import { useEffect, type ReactNode } from 'react'
- import { useTranslation } from 'react-i18next'
- type ImageLightboxProps = {
- image: string | null
- alt?: string
- onClose: () => void
- action?: ReactNode
- }
- export function ImageLightbox({ image, alt = '', onClose, action }: ImageLightboxProps) {
- const { t } = useTranslation()
- const translated = (key: string, fallback: string, values?: Record<string, string>) => {
- const value = t(key, values)
- return value === key ? fallback : value
- }
- useEffect(() => {
- if (!image) return undefined
- const handleKeyDown = (event: KeyboardEvent) => {
- if (event.key === 'Escape') onClose()
- }
- document.addEventListener('keydown', handleKeyDown)
- const previousOverflow = document.body.style.overflow
- document.body.style.overflow = 'hidden'
- return () => {
- document.removeEventListener('keydown', handleKeyDown)
- document.body.style.overflow = previousOverflow
- }
- }, [image, onClose])
- if (!image) return null
- return (
- <div className="aitori-image-lightbox" role="dialog" aria-modal="true" aria-label={alt || translated('common.imagePreview', '图片预览')} onClick={onClose}>
- <button type="button" aria-label={translated('common.closeImagePreview', '关闭图片预览')} onClick={onClose}><i className="feather icon-feather-x"></i></button>
- <div className={`aitori-image-lightbox-content${action ? ' has-action' : ''}`} onClick={(event) => event.stopPropagation()}>
- <img src={image} alt={alt ? translated('common.enlargedImage', `${alt} 放大预览`, { name: alt }) : translated('common.enlargedImageFallback', '图片放大预览')} />
- {action ? <div className="aitori-image-lightbox-action">{action}</div> : null}
- </div>
- </div>
- )
- }
|