add extension dropdown for image convert

This commit is contained in:
smanylov
2026-02-17 17:58:32 +07:00
parent fad2a525ea
commit 62b91f0ce9
6 changed files with 180 additions and 19 deletions
+3 -3
View File
@@ -14,7 +14,7 @@ interface initMessageBody {
token?: string
}
export async function fileUpload(messageBody: initMessageBody, needConvert: boolean | undefined) {
export async function fileUpload(messageBody: initMessageBody, convertTo: string | null | undefined) {
const token = await getSessionData('token');
if (!token) {
return;
@@ -23,8 +23,8 @@ export async function fileUpload(messageBody: initMessageBody, needConvert: bool
message.action = 'init';
message.token = token;
// нужно будет добавить свойство needConvert в тело запроса.
// console.log(needConvert);
// нужно будет добавить свойство convertTo в тело запроса он может прийти со значением reject тогда конвертировать не нужно.
// console.log(convertTo);
try {
const response = await fetch(`${API_BASE_URL}/api/v1/data`, {
@@ -0,0 +1,115 @@
import React, { ReactNode, useState, useRef } from 'react';
import { useClickOutside } from '@/app/hooks/useClickOutside';
import { SelectedFile } from './UploadSectionFile';
import { useTranslations } from 'next-intl';
interface DropDownItem {
file: SelectedFile;
convertTo: string;
}
interface DropDownListProps {
children: ReactNode;
value: string | null | undefined;
callBack: (value: DropDownItem) => void;
translatedValue?: string | null | undefined;
selectedFiles: SelectedFile[];
}
interface LiElementProps extends React.LiHTMLAttributes<HTMLLIElement> {
'data-file'?: string;
'data-convert-to'?: string;
}
export default function DropDownListForImageConvert({
children,
value,
callBack,
translatedValue,
selectedFiles
}: DropDownListProps) {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const t = useTranslations('Global')
useClickOutside(dropdownRef,
() => {
setIsOpen(false);
},
isOpen
);
const getDisplayValue = (): string => {
if (translatedValue) return translatedValue;
return value ? value : t('do-not-convert');
};
const findFileByName = (fileName: string): SelectedFile | undefined => {
return selectedFiles.find(file => file.name === fileName);
};
const enhancedChildren = React.Children.map(children, (child, index) => {
if (React.isValidElement<LiElementProps>(child)) {
const fileName = child.props['data-file'];
const convertTo = child.props['data-convert-to'];
const isCurrent = value === convertTo;
return React.cloneElement(child, {
onClick: () => {
if (fileName && convertTo) {
const file = findFileByName(fileName);
if (file) {
callBack({ file, convertTo });
} else {
console.error(`Файл с именем ${fileName} не найден`);
}
setIsOpen(false);
}
},
className: `dropdown-item ${isCurrent ? "current" : ""}`,
key: index,
});
}
return child;
});
return (
<div className="dropdown-wrapper" ref={dropdownRef}>
<button
onClick={() => {
setIsOpen(!isOpen);
}}
className="dropdown-button"
>
<span className="flex items-center">
<span className="mr-2">
{getDisplayValue()}
</span>
</span>
<svg
className={`w-5 h-5 ml-2 transition-transform ${isOpen ? 'rotate-180' : ''}`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</button>
{isOpen && (
<div className="dropdown-list">
<ul
className=""
role="listbox"
>
{enhancedChildren}
</ul>
</div>
)}
</div>
);
}
+35 -14
View File
@@ -6,8 +6,9 @@ import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/a
import { useTranslations } from 'next-intl';
import { useNavigationBlocker } from '@/app/hooks/useNavigationBlocker';
import { useQueryClient } from '@tanstack/react-query';
import DropDownListForImageConvert from '@/app/components/DropDownListForImageConvert';
interface SelectedFile {
export interface SelectedFile {
file: File;
preview: string | undefined;
name: string;
@@ -20,7 +21,7 @@ interface SelectedFile {
progress: number;
status: 'pending' | 'uploading' | 'completed' | 'error' | 'cancelled';
error?: string;
needConvert?: boolean;
convertTo?: string | null;
}
interface FileUploadInitResponse {
@@ -239,7 +240,7 @@ export default function UploadSectionFile({
file_size: fileInfo.file.size
};
const response = await fileUpload(initMessageBody, fileInfo.needConvert) as FileUploadInitResponse;
const response = await fileUpload(initMessageBody, fileInfo.convertTo) as FileUploadInitResponse;
if (!response?.upload_id) {
throw new Error('Failed to get upload_id');
@@ -393,12 +394,15 @@ export default function UploadSectionFile({
}
});
function converButtonHandler(file: SelectedFile) {
function converButtonHandler(value: {
convertTo: string;
file: SelectedFile
}): void {
setSelectedFiles(selectedFiles.map(selectedFile => {
if (selectedFile.name === file.name) {
if (selectedFile.name === value.file.name) {
return {
...selectedFile,
needConvert: !selectedFile.needConvert
convertTo: value.convertTo
};
}
return selectedFile;
@@ -544,14 +548,31 @@ export default function UploadSectionFile({
<div className="selected-file-right-block">
{
(fileType === 'image' && file.status === 'pending') && (
<button
className={`btn-convert ${file.needConvert ? 'active' : ''}`}
onClick={() => {
converButtonHandler(file);
}}
>
{t('convert-image')} jpg
</button>
<>
<DropDownListForImageConvert
value={file.convertTo}
translatedValue={file.convertTo === 'reject' ? t('do-not-convert') : file.convertTo}
callBack={converButtonHandler}
selectedFiles={selectedFiles}
>
<li data-file={file.name} data-convert-to="reject">
{t('do-not-convert')}
</li>
{
allowedExtensions && allowedExtensions.map(extension => {
if (extension === 'gif') {
return null
}
return (
<li key={extension} data-file={file.name} data-convert-to={extension}>
{t('convert-to')} {extension}
</li>
)
})
}
</DropDownListForImageConvert>
</>
)
}
<div>
+23
View File
@@ -473,6 +473,29 @@
transform: translateY(-2px);
box-shadow: 0 8px 25px v.$shadow-1;
}
.dropdown-wrapper {
.dropdown-button {
border: 2px solid v.$p-color;
color: v.$p-color;
min-width: 200px;
}
.dropdown-list {
border: 1px solid v.$p-color;
color: v.$p-color;
li {
&:first-child {
border-radius: 12px 12px 0 0;
}
&:last-child {
border-radius: 0 0 12px 12px;
}
}
}
}
}
.selected-file-action-panel {
+2 -1
View File
@@ -226,7 +226,8 @@
"all-content": "All content",
"monitoring": "Monitoring",
"efficiency": "Efficiency",
"convert-image": "Convert image to"
"convert-to": "Convert to",
"do-not-convert": "Do not convert"
},
"Login-register-form": {
"and": "and",
+2 -1
View File
@@ -226,7 +226,8 @@
"all-content": "Весь контент",
"monitoring": "Мониторинг",
"efficiency": "Эффективность",
"convert-image": "Конвертировать изображение в"
"convert-to": "Конвертировать в",
"do-not-convert": "Не конвертировать"
},
"Login-register-form": {
"and": "и",