mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
# Description of Changes - Added the OverlayPDF tool --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
179 lines
6.8 KiB
TypeScript
179 lines
6.8 KiB
TypeScript
import { Stack, Text, Group, Select, SegmentedControl, NumberInput, Button, ActionIcon, Divider } from '@mantine/core';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { type OverlayPdfsParameters, type OverlayMode } from '../../../hooks/tools/overlayPdfs/useOverlayPdfsParameters';
|
|
import LocalIcon from '../../shared/LocalIcon';
|
|
import { useFilesModalContext } from '../../../contexts/FilesModalContext';
|
|
import styles from './OverlayPdfsSettings.module.css';
|
|
|
|
interface OverlayPdfsSettingsProps {
|
|
parameters: OverlayPdfsParameters;
|
|
onParameterChange: <K extends keyof OverlayPdfsParameters>(key: K, value: OverlayPdfsParameters[K]) => void;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export default function OverlayPdfsSettings({ parameters, onParameterChange, disabled = false }: OverlayPdfsSettingsProps) {
|
|
const { t } = useTranslation();
|
|
const { openFilesModal } = useFilesModalContext();
|
|
|
|
const handleOverlayFilesChange = (files: File[]) => {
|
|
onParameterChange('overlayFiles', files);
|
|
// Reset counts to match number of files if in FixedRepeatOverlay
|
|
if (parameters.overlayMode === 'FixedRepeatOverlay') {
|
|
const nextCounts = files.map((_, i) => parameters.counts[i] && parameters.counts[i] > 0 ? parameters.counts[i] : 1);
|
|
onParameterChange('counts', nextCounts);
|
|
}
|
|
};
|
|
|
|
const handleModeChange = (mode: OverlayMode) => {
|
|
onParameterChange('overlayMode', mode);
|
|
if (mode !== 'FixedRepeatOverlay') {
|
|
onParameterChange('counts', []);
|
|
} else if (parameters.overlayFiles?.length > 0) {
|
|
onParameterChange('counts', parameters.overlayFiles.map((_, i) => parameters.counts[i] && parameters.counts[i] > 0 ? parameters.counts[i] : 1));
|
|
}
|
|
};
|
|
|
|
const handleOpenOverlayFilesModal = () => {
|
|
if (disabled) return;
|
|
openFilesModal({
|
|
customHandler: (files: File[]) => {
|
|
handleOverlayFilesChange([...(parameters.overlayFiles || []), ...files]);
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<Stack gap="md">
|
|
|
|
<Stack gap="xs">
|
|
<Text size="sm" fw={500}>{t('overlay-pdfs.mode.label', 'Overlay Mode')}</Text>
|
|
<Select
|
|
data={[
|
|
{ value: 'SequentialOverlay', label: t('overlay-pdfs.mode.sequential', 'Sequential Overlay') },
|
|
{ value: 'InterleavedOverlay', label: t('overlay-pdfs.mode.interleaved', 'Interleaved Overlay') },
|
|
{ value: 'FixedRepeatOverlay', label: t('overlay-pdfs.mode.fixedRepeat', 'Fixed Repeat Overlay') },
|
|
]}
|
|
value={parameters.overlayMode}
|
|
onChange={(v) => handleModeChange((v || 'SequentialOverlay') as OverlayMode)}
|
|
disabled={disabled}
|
|
/>
|
|
</Stack>
|
|
|
|
<Divider />
|
|
|
|
<Stack gap="xs">
|
|
<Text size="sm" fw={500}>{t('overlay-pdfs.position.label', 'Overlay Position')}</Text>
|
|
<SegmentedControl
|
|
value={String(parameters.overlayPosition)}
|
|
onChange={(v) => onParameterChange('overlayPosition', (v === '1' ? 1 : 0) as 0 | 1)}
|
|
data={[
|
|
{ label: t('overlay-pdfs.position.foreground', 'Foreground'), value: '0' },
|
|
{ label: t('overlay-pdfs.position.background', 'Background'), value: '1' },
|
|
]}
|
|
disabled={disabled}
|
|
/>
|
|
</Stack>
|
|
|
|
{parameters.overlayMode === 'FixedRepeatOverlay' && (
|
|
<>
|
|
<Divider />
|
|
<Stack gap="xs">
|
|
<Text size="sm" fw={500}>{t('overlay-pdfs.counts.label', 'Overlay Counts')}</Text>
|
|
{parameters.overlayFiles?.length > 0 ? (
|
|
<Stack gap="xs">
|
|
{parameters.overlayFiles.map((_, index) => (
|
|
<Group key={index} gap="xs" wrap="nowrap">
|
|
<Text size="sm" className={styles.countLabel}>
|
|
{t('overlay-pdfs.counts.item', 'Count for file')} {index + 1}
|
|
</Text>
|
|
<NumberInput
|
|
min={1}
|
|
step={1}
|
|
value={parameters.counts[index] ?? 1}
|
|
onChange={(value) => {
|
|
const next = [...(parameters.counts || [])];
|
|
next[index] = Number(value) || 1;
|
|
onParameterChange('counts', next);
|
|
}}
|
|
disabled={disabled}
|
|
/>
|
|
</Group>
|
|
))}
|
|
</Stack>
|
|
) : (
|
|
<Text size="sm" c="dimmed">
|
|
{t('overlay-pdfs.counts.noFiles', 'Add overlay files to configure counts')}
|
|
</Text>
|
|
)}
|
|
</Stack>
|
|
</>
|
|
)}
|
|
|
|
<Divider />
|
|
|
|
<Stack gap="xs">
|
|
<Text size="sm" fw={500}>{t('overlay-pdfs.overlayFiles.label', 'Overlay Files')}</Text>
|
|
<Button
|
|
size="xs"
|
|
color="blue"
|
|
onClick={handleOpenOverlayFilesModal}
|
|
disabled={disabled}
|
|
leftSection={<LocalIcon icon="add" width="14" height="14" />}
|
|
fullWidth
|
|
>
|
|
{parameters.overlayFiles?.length > 0
|
|
? t('overlay-pdfs.overlayFiles.addMore', 'Add more PDFs...')
|
|
: t('overlay-pdfs.overlayFiles.placeholder', 'Choose PDF(s)...')}
|
|
</Button>
|
|
|
|
{parameters.overlayFiles?.length > 0 && (() => {
|
|
return (
|
|
<div className={styles.fileListContainer}>
|
|
<Stack gap="xs">
|
|
{parameters.overlayFiles.map((file, index) => (
|
|
<Group
|
|
key={index}
|
|
justify="space-between"
|
|
p="xs"
|
|
className={styles.fileItem}
|
|
>
|
|
<Group gap="xs" className={styles.fileGroup}>
|
|
<div className={styles.fileNameContainer}>
|
|
<div
|
|
className={styles.fileName}
|
|
title={file.name}
|
|
>
|
|
{file.name}
|
|
</div>
|
|
</div>
|
|
<Text size="xs" c="dimmed" className={styles.fileSize}>
|
|
({(file.size / 1024).toFixed(1)} KB)
|
|
</Text>
|
|
</Group>
|
|
<ActionIcon
|
|
size="sm"
|
|
variant="subtle"
|
|
color="red"
|
|
className={styles.removeButton}
|
|
onClick={() => {
|
|
const next = (parameters.overlayFiles || []).filter((_, i) => i !== index);
|
|
handleOverlayFilesChange(next);
|
|
}}
|
|
disabled={disabled}
|
|
>
|
|
<LocalIcon icon="close-rounded" width="14" height="14" />
|
|
</ActionIcon>
|
|
</Group>
|
|
))}
|
|
</Stack>
|
|
</div>
|
|
);
|
|
})()}
|
|
</Stack>
|
|
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
|