Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

290 lines
9.7 KiB
TypeScript
Raw Normal View History

2025-07-23 16:41:27 +01:00
import React, { useMemo } from "react";
2025-07-31 14:46:14 +01:00
import { Stack, Text, Group, Divider, UnstyledButton, useMantineTheme, useMantineColorScheme } from "@mantine/core";
2025-07-23 12:01:40 +01:00
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
2025-07-23 11:25:55 +01:00
import { useTranslation } from "react-i18next";
2025-07-23 16:41:27 +01:00
import { useMultipleEndpointsEnabled } from "../../../hooks/useEndpointConfig";
2025-07-31 13:47:48 +01:00
import { isImageFormat, isWebFormat } from "../../../utils/convertUtils";
2025-07-30 20:03:11 +01:00
import { useFileSelectionActions } from "../../../contexts/FileSelectionContext";
import { useFileContext } from "../../../contexts/FileContext";
2025-07-31 13:47:48 +01:00
import { detectFileExtension } from "../../../utils/fileUtils";
2025-07-23 12:01:40 +01:00
import GroupedFormatDropdown from "./GroupedFormatDropdown";
2025-07-23 16:55:13 +01:00
import ConvertToImageSettings from "./ConvertToImageSettings";
import ConvertFromImageSettings from "./ConvertFromImageSettings";
2025-07-31 14:46:14 +01:00
import ConvertFromWebSettings from "./ConvertFromWebSettings";
import ConvertFromEmailSettings from "./ConvertFromEmailSettings";
2025-07-31 15:23:38 +01:00
import ConvertToPdfaSettings from "./ConvertToPdfaSettings";
2025-07-23 11:25:55 +01:00
import { ConvertParameters } from "../../../hooks/tools/convert/useConvertParameters";
import {
FROM_FORMAT_OPTIONS,
2025-07-23 16:55:13 +01:00
EXTENSION_TO_ENDPOINT,
2025-07-23 11:25:55 +01:00
COLOR_TYPES,
2025-07-30 14:24:23 +01:00
OUTPUT_OPTIONS,
FIT_OPTIONS
2025-07-23 11:25:55 +01:00
} from "../../../constants/convertConstants";
interface ConvertSettingsProps {
parameters: ConvertParameters;
onParameterChange: (key: keyof ConvertParameters, value: any) => void;
getAvailableToExtensions: (fromExtension: string) => Array<{value: string, label: string, group: string}>;
2025-07-30 20:03:11 +01:00
selectedFiles: File[];
2025-07-23 11:25:55 +01:00
disabled?: boolean;
}
const ConvertSettings = ({
parameters,
onParameterChange,
getAvailableToExtensions,
2025-07-30 20:03:11 +01:00
selectedFiles,
2025-07-23 11:25:55 +01:00
disabled = false
}: ConvertSettingsProps) => {
const { t } = useTranslation();
2025-07-23 12:01:40 +01:00
const theme = useMantineTheme();
const { colorScheme } = useMantineColorScheme();
2025-07-30 20:03:11 +01:00
const { setSelectedFiles } = useFileSelectionActions();
const { setSelectedFiles: setContextSelectedFiles } = useFileContext();
2025-07-23 11:25:55 +01:00
2025-07-23 16:41:27 +01:00
const allEndpoints = useMemo(() => {
const endpoints = new Set<string>();
Object.values(EXTENSION_TO_ENDPOINT).forEach(toEndpoints => {
Object.values(toEndpoints).forEach(endpoint => {
endpoints.add(endpoint);
});
});
return Array.from(endpoints);
}, []);
const { endpointStatus } = useMultipleEndpointsEnabled(allEndpoints);
const isConversionAvailable = (fromExt: string, toExt: string): boolean => {
const endpointKey = EXTENSION_TO_ENDPOINT[fromExt]?.[toExt];
if (!endpointKey) return false;
return endpointStatus[endpointKey] === true;
};
// Enhanced FROM options with endpoint availability
const enhancedFromOptions = useMemo(() => {
2025-07-25 11:36:57 +01:00
return FROM_FORMAT_OPTIONS.map(option => {
// Check if this source format has any available conversions
const availableConversions = getAvailableToExtensions(option.value) || [];
const hasAvailableConversions = availableConversions.some(targetOption =>
isConversionAvailable(option.value, targetOption.value)
);
return {
...option,
enabled: hasAvailableConversions
};
});
}, [getAvailableToExtensions, endpointStatus]);
2025-07-23 16:41:27 +01:00
// Enhanced TO options with endpoint availability
const enhancedToOptions = useMemo(() => {
if (!parameters.fromExtension) return [];
const availableOptions = getAvailableToExtensions(parameters.fromExtension) || [];
return availableOptions.map(option => ({
...option,
enabled: isConversionAvailable(parameters.fromExtension, option.value)
}));
2025-07-25 11:36:57 +01:00
}, [parameters.fromExtension, getAvailableToExtensions, endpointStatus]);
2025-07-23 16:41:27 +01:00
2025-07-23 14:30:53 +01:00
const handleFromExtensionChange = (value: string) => {
onParameterChange('fromExtension', value);
2025-07-30 14:24:23 +01:00
// Auto-select target if only one option available
const availableToOptions = getAvailableToExtensions(value);
const autoTarget = availableToOptions.length === 1 ? availableToOptions[0].value : '';
onParameterChange('toExtension', autoTarget);
2025-07-23 14:30:53 +01:00
onParameterChange('imageOptions', {
colorType: COLOR_TYPES.COLOR,
dpi: 300,
singleOrMultiple: OUTPUT_OPTIONS.MULTIPLE,
2025-07-30 14:24:23 +01:00
fitOption: FIT_OPTIONS.MAINTAIN_ASPECT,
autoRotate: true,
combineImages: true,
2025-07-23 14:30:53 +01:00
});
2025-07-31 14:46:14 +01:00
onParameterChange('emailOptions', {
includeAttachments: true,
maxAttachmentSizeMB: 10,
downloadHtml: false,
includeAllRecipients: false,
});
2025-07-31 15:23:38 +01:00
onParameterChange('pdfaOptions', {
outputFormat: 'pdfa-1',
});
2025-07-30 14:24:23 +01:00
onParameterChange('isSmartDetection', false);
onParameterChange('smartDetectionType', 'none');
2025-07-30 20:03:11 +01:00
if (selectedFiles.length > 0 && value !== 'any') {
const matchingFiles = selectedFiles.filter(file => {
const extension = file.name.split('.').pop()?.toLowerCase() || '';
if (value === 'image') {
return isImageFormat(extension);
}
return extension === value;
});
if (matchingFiles.length !== selectedFiles.length) {
setSelectedFiles(matchingFiles);
const matchingFileIds = matchingFiles.map(file => (file as any).id || file.name);
setContextSelectedFiles(matchingFileIds);
}
}
2025-07-23 11:25:55 +01:00
};
2025-07-23 12:01:40 +01:00
const handleToExtensionChange = (value: string) => {
onParameterChange('toExtension', value);
onParameterChange('imageOptions', {
colorType: COLOR_TYPES.COLOR,
dpi: 300,
singleOrMultiple: OUTPUT_OPTIONS.MULTIPLE,
2025-07-30 14:24:23 +01:00
fitOption: FIT_OPTIONS.MAINTAIN_ASPECT,
autoRotate: true,
combineImages: true,
2025-07-23 12:01:40 +01:00
});
2025-07-31 14:46:14 +01:00
onParameterChange('emailOptions', {
includeAttachments: true,
maxAttachmentSizeMB: 10,
downloadHtml: false,
includeAllRecipients: false,
});
2025-07-31 15:23:38 +01:00
onParameterChange('pdfaOptions', {
outputFormat: 'pdfa-1',
});
2025-07-23 11:25:55 +01:00
};
2025-07-23 12:01:40 +01:00
2025-07-23 11:25:55 +01:00
return (
<Stack gap="md">
2025-07-30 14:24:23 +01:00
2025-07-23 11:25:55 +01:00
{/* Format Selection */}
<Stack gap="sm">
<Text size="sm" fw={500}>
{t("convert.convertFrom", "Convert from")}:
</Text>
2025-07-23 14:30:53 +01:00
<GroupedFormatDropdown
2025-07-28 13:58:43 +01:00
name="convert-from-dropdown"
data-testid="from-format-dropdown"
2025-07-23 11:25:55 +01:00
value={parameters.fromExtension}
2025-07-23 16:57:28 +01:00
placeholder={t("convert.sourceFormatPlaceholder", "Source format")}
2025-07-23 16:41:27 +01:00
options={enhancedFromOptions}
2025-07-23 11:25:55 +01:00
onChange={handleFromExtensionChange}
2025-07-30 20:03:11 +01:00
disabled={disabled}
minWidth="21.875rem"
2025-07-23 11:25:55 +01:00
/>
</Stack>
<Stack gap="sm">
<Text size="sm" fw={500}>
{t("convert.convertTo", "Convert to")}:
</Text>
2025-07-23 12:01:40 +01:00
{!parameters.fromExtension ? (
<UnstyledButton
style={{
padding: '0.5rem 0.75rem',
border: `0.0625rem solid ${theme.colors.gray[4]}`,
2025-07-23 12:01:40 +01:00
borderRadius: theme.radius.sm,
backgroundColor: colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
color: colorScheme === 'dark' ? theme.colors.dark[2] : theme.colors.gray[6],
cursor: 'not-allowed'
}}
>
<Group justify="space-between">
2025-07-31 17:35:10 +01:00
<Text size="sm">{t("convert.selectSourceFormatFirst", "Select a source format first")}</Text>
2025-07-23 12:01:40 +01:00
<KeyboardArrowDownIcon
style={{
fontSize: '1rem',
2025-07-23 12:01:40 +01:00
color: colorScheme === 'dark' ? theme.colors.dark[2] : theme.colors.gray[6]
}}
/>
</Group>
</UnstyledButton>
) : (
<GroupedFormatDropdown
2025-07-28 13:58:43 +01:00
name="convert-to-dropdown"
data-testid="to-format-dropdown"
2025-07-23 12:01:40 +01:00
value={parameters.toExtension}
2025-07-28 13:58:43 +01:00
placeholder={t("convert.targetFormatPlaceholder", "Target format")}
2025-07-23 16:41:27 +01:00
options={enhancedToOptions}
2025-07-23 12:01:40 +01:00
onChange={handleToExtensionChange}
disabled={disabled}
minWidth="21.875rem"
2025-07-23 12:01:40 +01:00
/>
)}
2025-07-23 11:25:55 +01:00
</Stack>
{/* Format-specific options */}
2025-07-25 11:36:57 +01:00
{isImageFormat(parameters.toExtension) && (
2025-07-23 11:25:55 +01:00
<>
<Divider />
2025-07-23 16:55:13 +01:00
<ConvertToImageSettings
parameters={parameters}
onParameterChange={onParameterChange}
disabled={disabled}
/>
2025-07-23 11:25:55 +01:00
</>
)}
{/* Color options for image to PDF conversion */}
2025-07-30 14:24:23 +01:00
{(isImageFormat(parameters.fromExtension) && parameters.toExtension === 'pdf') ||
(parameters.isSmartDetection && parameters.smartDetectionType === 'images') ? (
2025-07-23 11:25:55 +01:00
<>
<Divider />
2025-07-23 16:55:13 +01:00
<ConvertFromImageSettings
parameters={parameters}
onParameterChange={onParameterChange}
disabled={disabled}
/>
2025-07-23 11:25:55 +01:00
</>
2025-07-30 14:24:23 +01:00
) : null}
2025-07-25 11:36:57 +01:00
2025-07-31 14:46:14 +01:00
{/* Web to PDF options */}
2025-07-31 13:47:48 +01:00
{((isWebFormat(parameters.fromExtension) && parameters.toExtension === 'pdf') ||
2025-07-31 14:46:14 +01:00
(parameters.isSmartDetection && parameters.smartDetectionType === 'web')) ? (
2025-07-31 13:47:48 +01:00
<>
<Divider />
2025-07-31 14:46:14 +01:00
<ConvertFromWebSettings
parameters={parameters}
onParameterChange={onParameterChange}
disabled={disabled}
/>
2025-07-31 13:47:48 +01:00
</>
2025-07-31 14:46:14 +01:00
) : null}
2025-07-31 13:47:48 +01:00
2025-07-31 14:46:14 +01:00
{/* Email to PDF options */}
2025-07-25 11:36:57 +01:00
{parameters.fromExtension === 'eml' && parameters.toExtension === 'pdf' && (
<>
<Divider />
2025-07-31 14:46:14 +01:00
<ConvertFromEmailSettings
parameters={parameters}
onParameterChange={onParameterChange}
disabled={disabled}
/>
2025-07-25 11:36:57 +01:00
</>
)}
2025-07-28 13:58:43 +01:00
2025-07-31 15:23:38 +01:00
{/* PDF to PDF/A options */}
{parameters.fromExtension === 'pdf' && parameters.toExtension === 'pdfa' && (
<>
<Divider />
<ConvertToPdfaSettings
parameters={parameters}
onParameterChange={onParameterChange}
selectedFiles={selectedFiles}
disabled={disabled}
/>
</>
)}
2025-07-23 11:25:55 +01:00
</Stack>
);
};
export default ConvertSettings;