195 lines
6.5 KiB
TypeScript
Raw Normal View History

2025-04-02 03:46:42 +00:00
import { Box } from '@mui/material';
import React, { useState } from 'react';
import * as Yup from 'yup';
import ToolImageInput from '@components/input/ToolImageInput';
import ToolFileResult from '@components/result/ToolFileResult';
import { GetGroupsType } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import SimpleRadio from '@components/options/SimpleRadio';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
2025-04-02 04:05:00 +00:00
import { processImage } from './service';
import { InitialValuesType } from './types';
2025-07-12 23:02:35 -07:00
import { useTranslation } from 'react-i18next';
2025-04-02 03:46:42 +00:00
2025-04-02 04:05:00 +00:00
const initialValues: InitialValuesType = {
2025-04-02 03:46:42 +00:00
resizeMethod: 'pixels' as 'pixels' | 'percentage',
dimensionType: 'width' as 'width' | 'height',
width: '800',
height: '600',
percentage: '50',
maintainAspectRatio: true
};
const validationSchema = Yup.object({
width: Yup.number().when('resizeMethod', {
is: 'pixels',
then: (schema) =>
schema.min(1, 'Width must be at least 1px').required('Width is required')
}),
height: Yup.number().when('resizeMethod', {
is: 'pixels',
then: (schema) =>
schema
.min(1, 'Height must be at least 1px')
.required('Height is required')
}),
percentage: Yup.number().when('resizeMethod', {
is: 'percentage',
then: (schema) =>
schema
.min(1, 'Percentage must be at least 1%')
.max(1000, 'Percentage must be at most 1000%')
.required('Percentage is required')
})
});
export default function ResizeImage({ title }: ToolComponentProps) {
2025-07-12 23:02:35 -07:00
const { t } = useTranslation();
2025-04-02 03:46:42 +00:00
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
2025-04-02 04:05:00 +00:00
const compute = async (optionsValues: InitialValuesType, input: any) => {
2025-04-02 03:46:42 +00:00
if (!input) return;
2025-04-02 04:05:00 +00:00
setResult(await processImage(input, optionsValues));
2025-04-02 03:46:42 +00:00
};
const getGroups: GetGroupsType<InitialValuesType> = ({
values,
updateField
}) => [
{
2025-07-12 23:02:35 -07:00
title: t('image.resize.resizeMethod'),
2025-04-02 03:46:42 +00:00
component: (
<Box>
<SimpleRadio
onClick={() => updateField('resizeMethod', 'pixels')}
checked={values.resizeMethod === 'pixels'}
2025-07-12 23:02:35 -07:00
description={t('image.resize.resizeByPixelsDescription')}
title={t('image.resize.resizeByPixels')}
2025-04-02 03:46:42 +00:00
/>
<SimpleRadio
onClick={() => updateField('resizeMethod', 'percentage')}
checked={values.resizeMethod === 'percentage'}
2025-07-12 23:02:35 -07:00
description={t('image.resize.resizeByPercentageDescription')}
title={t('image.resize.resizeByPercentage')}
2025-04-02 03:46:42 +00:00
/>
</Box>
)
},
...(values.resizeMethod === 'pixels'
? [
{
2025-07-12 23:02:35 -07:00
title: t('image.resize.dimensionType'),
2025-04-02 03:46:42 +00:00
component: (
<Box>
<CheckboxWithDesc
checked={values.maintainAspectRatio}
onChange={(value) =>
updateField('maintainAspectRatio', value)
}
2025-07-12 23:02:35 -07:00
description={t('image.resize.maintainAspectRatioDescription')}
title={t('image.resize.maintainAspectRatio')}
2025-04-02 03:46:42 +00:00
/>
{values.maintainAspectRatio && (
<Box>
<SimpleRadio
onClick={() => updateField('dimensionType', 'width')}
checked={values.dimensionType === 'width'}
2025-07-12 23:02:35 -07:00
description={t('image.resize.setWidthDescription')}
title={t('image.resize.setWidth')}
2025-04-02 03:46:42 +00:00
/>
<SimpleRadio
onClick={() => updateField('dimensionType', 'height')}
checked={values.dimensionType === 'height'}
2025-07-12 23:02:35 -07:00
description={t('image.resize.setHeightDescription')}
title={t('image.resize.setHeight')}
2025-04-02 03:46:42 +00:00
/>
</Box>
)}
<TextFieldWithDesc
value={values.width}
onOwnChange={(val) => updateField('width', val)}
2025-07-12 23:02:35 -07:00
description={t('image.resize.widthDescription')}
2025-04-02 03:46:42 +00:00
disabled={
values.maintainAspectRatio &&
values.dimensionType === 'height'
}
inputProps={{
'data-testid': 'width-input',
type: 'number',
min: 1
}}
/>
<TextFieldWithDesc
value={values.height}
onOwnChange={(val) => updateField('height', val)}
2025-07-12 23:02:35 -07:00
description={t('image.resize.heightDescription')}
2025-04-02 03:46:42 +00:00
disabled={
values.maintainAspectRatio &&
values.dimensionType === 'width'
}
inputProps={{
'data-testid': 'height-input',
type: 'number',
min: 1
}}
/>
</Box>
)
}
]
: [
{
2025-07-12 23:02:35 -07:00
title: t('image.resize.percentage'),
2025-04-02 03:46:42 +00:00
component: (
<Box>
<TextFieldWithDesc
value={values.percentage}
onOwnChange={(val) => updateField('percentage', val)}
2025-07-12 23:02:35 -07:00
description={t('image.resize.percentageDescription')}
2025-04-02 03:46:42 +00:00
inputProps={{
'data-testid': 'percentage-input',
type: 'number',
min: 1,
max: 1000
}}
/>
</Box>
)
}
])
];
return (
<ToolContent
title={title}
initialValues={initialValues}
getGroups={getGroups}
compute={compute}
input={input}
validationSchema={validationSchema}
inputComponent={
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/jpeg', 'image/png', 'image/svg+xml', 'image/gif']}
2025-07-12 23:02:35 -07:00
title={t('image.resize.inputTitle')}
2025-04-02 03:46:42 +00:00
/>
}
resultComponent={
<ToolFileResult
2025-07-12 23:02:35 -07:00
title={t('image.resize.resultTitle')}
2025-04-02 03:46:42 +00:00
value={result}
extension={input?.name.split('.').pop() || 'png'}
/>
}
toolInfo={{
2025-07-12 23:02:35 -07:00
title: t('image.resize.toolInfo.title'),
description: t('image.resize.toolInfo.description')
2025-04-02 03:46:42 +00:00
}}
/>
);
}