mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-24 04:26:14 +00:00
Refactor buttons into shared component
This commit is contained in:
parent
e0b41adbdc
commit
e7cfb7045b
@ -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"
|
||||||
|
49
frontend/src/components/shared/ButtonSelector.tsx
Normal file
49
frontend/src/components/shared/ButtonSelector.tsx
Normal 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;
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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') },
|
||||||
disabled={disabled}
|
{ value: 'filesize', label: t('compress.method.filesize', 'File Size') },
|
||||||
style={{ flex: 1, height: 'auto', minHeight: '40px', fontSize: '11px' }}
|
]}
|
||||||
>
|
disabled={disabled}
|
||||||
<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 */}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user