Refactor buttons into shared component

This commit is contained in:
James Brunton 2025-09-09 09:52:45 +01:00
parent e0b41adbdc
commit e7cfb7045b
5 changed files with 105 additions and 76 deletions

View File

@ -1906,6 +1906,11 @@
"title": "Compress", "title": "Compress",
"desc": "Compress PDFs to reduce their file size.", "desc": "Compress PDFs to reduce their file size.",
"header": "Compress PDF", "header": "Compress PDF",
"method": {
"title": "Compression Method",
"quality": "Quality",
"filesize": "File Size"
},
"credit": "This service uses qpdf for PDF Compress/Optimisation.", "credit": "This service uses qpdf for PDF Compress/Optimisation.",
"grayscale": { "grayscale": {
"label": "Apply Grayscale for Compression" "label": "Apply Grayscale for Compression"

View File

@ -0,0 +1,49 @@
import { Button } from "@mantine/core";
export interface ButtonOption<T> {
value: T;
label: string;
disabled?: boolean;
}
interface ButtonSelectorProps<T> {
value: T | undefined;
onChange: (value: T) => void;
options: ButtonOption<T>[];
disabled?: boolean;
fullWidth?: boolean;
}
const ButtonSelector = <T extends string>({
value,
onChange,
options,
disabled = false,
fullWidth = true,
}: ButtonSelectorProps<T>) => {
return (
<div style={{ display: 'flex', gap: '4px' }}>
{options.map((option) => (
<Button
key={option.value}
variant={value === option.value ? 'filled' : 'outline'}
color={value === option.value ? 'blue' : 'var(--text-muted)'}
onClick={() => onChange(option.value)}
disabled={disabled || option.disabled}
style={{
flex: fullWidth ? 1 : undefined,
height: 'auto',
minHeight: '40px',
fontSize: '11px'
}}
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
{option.label}
</div>
</Button>
))}
</div>
);
};
export default ButtonSelector;

View File

@ -1,5 +1,6 @@
import { Button, Stack } from "@mantine/core"; import { Stack } from "@mantine/core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ButtonSelector from "../../shared/ButtonSelector";
interface WatermarkTypeSettingsProps { interface WatermarkTypeSettingsProps {
watermarkType?: 'text' | 'image'; watermarkType?: 'text' | 'image';
@ -10,32 +11,25 @@ interface WatermarkTypeSettingsProps {
const WatermarkTypeSettings = ({ watermarkType, onWatermarkTypeChange, disabled = false }: WatermarkTypeSettingsProps) => { const WatermarkTypeSettings = ({ watermarkType, onWatermarkTypeChange, disabled = false }: WatermarkTypeSettingsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const options = [
{
value: 'text' as const,
label: t('watermark.watermarkType.text', 'Text')
},
{
value: 'image' as const,
label: t('watermark.watermarkType.image', 'Image')
}
];
return ( return (
<Stack gap="sm"> <Stack gap="sm">
<div style={{ display: 'flex', gap: '4px' }}> <ButtonSelector
<Button value={watermarkType}
variant={watermarkType === 'text' ? 'filled' : 'outline'} onChange={onWatermarkTypeChange}
color={watermarkType === 'text' ? 'blue' : 'var(--text-muted)'} options={options}
onClick={() => onWatermarkTypeChange('text')}
disabled={disabled} disabled={disabled}
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }} />
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
{t('watermark.watermarkType.text', 'Text')}
</div>
</Button>
<Button
variant={watermarkType === 'image' ? 'filled' : 'outline'}
color={watermarkType === 'image' ? 'blue' : 'var(--text-muted)'}
onClick={() => onWatermarkTypeChange('image')}
disabled={disabled}
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
{t('watermark.watermarkType.image', 'Image')}
</div>
</Button>
</div>
</Stack> </Stack>
); );
}; };

View File

@ -1,7 +1,8 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Button, Stack, Text, NumberInput, Select, Divider } from "@mantine/core"; import { Stack, Text, NumberInput, Select, Divider } from "@mantine/core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { CompressParameters } from "../../../hooks/tools/compress/useCompressParameters"; import { CompressParameters } from "../../../hooks/tools/compress/useCompressParameters";
import ButtonSelector from "../../shared/ButtonSelector";
interface CompressSettingsProps { interface CompressSettingsProps {
parameters: CompressParameters; parameters: CompressParameters;
@ -19,31 +20,16 @@ const CompressSettings = ({ parameters, onParameterChange, disabled = false }: C
<Divider ml='-md'></Divider> <Divider ml='-md'></Divider>
{/* Compression Method */} {/* Compression Method */}
<Stack gap="sm"> <Stack gap="sm">
<Text size="sm" fw={500}>Compression Method</Text> <Text size="sm" fw={500}>{t('compress.method.title', 'Compression Method')}</Text>
<div style={{ display: 'flex', gap: '4px' }}> <ButtonSelector
<Button value={parameters.compressionMethod}
variant={parameters.compressionMethod === 'quality' ? 'filled' : 'outline'} onChange={(value) => onParameterChange('compressionMethod', value)}
color={parameters.compressionMethod === 'quality' ? 'blue' : 'var(--text-muted)'} options={[
onClick={() => onParameterChange('compressionMethod', 'quality')} { value: 'quality', label: t('compress.method.quality', 'Quality') },
{ value: 'filesize', label: t('compress.method.filesize', 'File Size') },
]}
disabled={disabled} disabled={disabled}
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }} />
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
Quality
</div>
</Button>
<Button
variant={parameters.compressionMethod === 'filesize' ? 'filled' : 'outline'}
color={parameters.compressionMethod === 'filesize' ? 'blue' : 'var(--text-muted)'}
onClick={() => onParameterChange('compressionMethod', 'filesize')}
disabled={disabled}
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
File Size
</div>
</Button>
</div>
</Stack> </Stack>
{/* Quality Adjustment */} {/* Quality Adjustment */}

View File

@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button, Stack, Text } from '@mantine/core'; import { Stack, Text } from '@mantine/core';
import { RedactMode } from '../../../hooks/tools/redact/useRedactParameters'; import { RedactMode } from '../../../hooks/tools/redact/useRedactParameters';
import ButtonSelector from '../../shared/ButtonSelector';
interface RedactModeSelectorProps { interface RedactModeSelectorProps {
mode: RedactMode; mode: RedactMode;
@ -11,36 +12,30 @@ interface RedactModeSelectorProps {
export default function RedactModeSelector({ mode, onModeChange, disabled }: RedactModeSelectorProps) { export default function RedactModeSelector({ mode, onModeChange, disabled }: RedactModeSelectorProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const options = [
{
value: 'automatic' as const,
label: t('redact.modeSelector.automatic', 'Automatic')
},
{
value: 'manual' as const,
label: t('redact.modeSelector.manual', 'Manual'),
disabled: true // Keep manual mode disabled until implemented
}
];
return ( return (
<Stack gap="sm"> <Stack gap="sm">
<Text size="sm" fw={600}> <Text size="sm" fw={600}>
{t('redact.modeSelector.mode', 'Mode')} {t('redact.modeSelector.mode', 'Mode')}
</Text> </Text>
<div style={{ display: 'flex', gap: '4px' }}> <ButtonSelector
<Button value={mode}
variant={mode === 'automatic' ? 'filled' : 'outline'} onChange={onModeChange}
color={mode === 'automatic' ? 'blue' : 'var(--text-muted)'} options={options}
onClick={() => onModeChange('automatic')}
disabled={disabled} disabled={disabled}
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }} />
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
{t('redact.modeSelector.automatic', 'Automatic')}
</div>
</Button>
<Button
variant={mode === 'manual' ? 'filled' : 'outline'}
color={mode === 'manual' ? 'blue' : 'var(--text-muted)'}
onClick={() => onModeChange('manual')}
disabled={disabled || true} // Keep manual disabled until implemented
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
>
<div style={{ textAlign: 'center', lineHeight: '1.1', fontSize: '11px' }}>
{t('redact.modeSelector.manual', 'Manual')}
</div>
</Button>
</div>
</Stack> </Stack>
); );
} }