fix: файлы в поле отображаются горизонтально с переносом по ширине
- FileFieldRow: список файлов в одну строку, перенос на следующую только при нехватке места - 'Добавить файл' идёт в той же строке после файлов - Добавлена лёгкая подложка bg-muted/40 вокруг каждого файла для визуального разделения
This commit is contained in:
@@ -533,72 +533,74 @@ function FileFieldRow({
|
|||||||
<Upload className="w-3.5 h-3.5" />
|
<Upload className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0 flex flex-wrap items-center gap-x-2 gap-y-0.5">
|
||||||
<strong>{field.name}:</strong>{' '}
|
<strong>{field.name}:</strong>{' '}
|
||||||
{field.maxFileCount != null && (
|
{field.maxFileCount != null && (
|
||||||
<span className="text-xs text-muted-foreground ml-1">({files.length}/{field.maxFileCount})</span>
|
<span className="text-xs text-muted-foreground">({files.length}/{field.maxFileCount})</span>
|
||||||
)}
|
)}
|
||||||
{isUploading ? (
|
{isUploading ? (
|
||||||
<span className="text-muted-foreground text-sm">Загрузка...</span>
|
<span className="text-muted-foreground text-sm">Загрузка...</span>
|
||||||
) : files.length > 0 ? (
|
) : files.length > 0 ? (
|
||||||
<div className="flex flex-col gap-0.5 mt-0.5">
|
<>
|
||||||
{files.map((fileInfo, idx) => {
|
<div className="inline-flex flex-wrap items-center gap-x-2 gap-y-0.5">
|
||||||
const vType = detectViewerType(fileInfo.url, fileInfo.name);
|
{files.map((fileInfo, idx) => {
|
||||||
const canPreview = vType === 'pdf' || vType === 'word' || vType === 'excel' || vType === 'powerpoint' || vType === 'video' || vType === 'audio' || vType === 'image';
|
const vType = detectViewerType(fileInfo.url, fileInfo.name);
|
||||||
return (
|
const canPreview = vType === 'pdf' || vType === 'word' || vType === 'excel' || vType === 'powerpoint' || vType === 'video' || vType === 'audio' || vType === 'image';
|
||||||
<span key={idx} className="inline-flex items-center gap-1 group/file">
|
return (
|
||||||
{canPreview ? (
|
<span key={idx} className="inline-flex items-center gap-1 group/file bg-muted/40 hover:bg-muted/60 rounded px-1.5 py-0.5">
|
||||||
|
{canPreview ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setViewerFile({ url: fileInfo.url, name: fileInfo.name })}
|
||||||
|
className="inline-flex items-center gap-1 text-sm text-primary hover:underline"
|
||||||
|
data-testid={`file-link-${field.code}-${idx}`}
|
||||||
|
>
|
||||||
|
<FileText className="w-3 h-3" />
|
||||||
|
{fileInfo.name}
|
||||||
|
{fileInfo.size !== undefined && (
|
||||||
|
<span className="text-muted-foreground text-xs">({formatFileSize(fileInfo.size)})</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<a
|
||||||
|
href={addFileToken(fileInfo.url)}
|
||||||
|
onClick={(e) => { e.preventDefault(); openFilePresigned(fileInfo.url); }}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-1 text-sm text-primary hover:underline"
|
||||||
|
data-testid={`file-link-${field.code}-${idx}`}
|
||||||
|
>
|
||||||
|
<FileText className="w-3 h-3" />
|
||||||
|
{fileInfo.name}
|
||||||
|
{fileInfo.size !== undefined && (
|
||||||
|
<span className="text-muted-foreground text-xs">({formatFileSize(fileInfo.size)})</span>
|
||||||
|
)}
|
||||||
|
<Download className="w-3 h-3 text-muted-foreground" />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
onClick={() => handleRemoveFile(idx)}
|
||||||
onClick={() => setViewerFile({ url: fileInfo.url, name: fileInfo.name })}
|
className="opacity-0 group-hover/file:opacity-100 transition-opacity text-muted-foreground hover:text-destructive"
|
||||||
className="inline-flex items-center gap-1 text-sm text-primary hover:underline"
|
title="Удалить файл"
|
||||||
data-testid={`file-link-${field.code}-${idx}`}
|
data-testid={`file-clear-${field.code}-${idx}`}
|
||||||
>
|
>
|
||||||
<FileText className="w-3 h-3" />
|
<X className="w-3 h-3" />
|
||||||
{fileInfo.name}
|
|
||||||
{fileInfo.size !== undefined && (
|
|
||||||
<span className="text-muted-foreground text-xs ml-1">({formatFileSize(fileInfo.size)})</span>
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
) : (
|
</span>
|
||||||
<a
|
);
|
||||||
href={addFileToken(fileInfo.url)}
|
})}
|
||||||
onClick={(e) => { e.preventDefault(); openFilePresigned(fileInfo.url); }}
|
</div>
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="inline-flex items-center gap-1 text-sm text-primary hover:underline"
|
|
||||||
data-testid={`file-link-${field.code}-${idx}`}
|
|
||||||
>
|
|
||||||
<FileText className="w-3 h-3" />
|
|
||||||
{fileInfo.name}
|
|
||||||
{fileInfo.size !== undefined && (
|
|
||||||
<span className="text-muted-foreground text-xs ml-1">({formatFileSize(fileInfo.size)})</span>
|
|
||||||
)}
|
|
||||||
<Download className="w-3 h-3 text-muted-foreground" />
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
onClick={() => handleRemoveFile(idx)}
|
|
||||||
className="opacity-0 group-hover/file:opacity-100 transition-opacity ml-1 text-muted-foreground hover:text-destructive"
|
|
||||||
title="Удалить файл"
|
|
||||||
data-testid={`file-clear-${field.code}-${idx}`}
|
|
||||||
>
|
|
||||||
<X className="w-3 h-3" />
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{(field.maxFileCount == null || files.length < field.maxFileCount) && (
|
{(field.maxFileCount == null || files.length < field.maxFileCount) && (
|
||||||
<button
|
<button
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
disabled={isUploading}
|
disabled={isUploading}
|
||||||
className="inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground mt-0.5 w-fit disabled:opacity-30"
|
className="inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground disabled:opacity-30"
|
||||||
>
|
>
|
||||||
<Upload className="w-3 h-3" />
|
<Upload className="w-3 h-3" />
|
||||||
Добавить файл
|
Добавить файл
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-muted-foreground text-sm" data-testid={`file-empty-${field.code}`}>Нет файла</span>
|
<span className="text-muted-foreground text-sm" data-testid={`file-empty-${field.code}`}>Нет файла</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user