mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 14:19:24 +00:00
working with bad ui
This commit is contained in:
parent
85b612bf22
commit
7083ad9207
@ -1761,13 +1761,24 @@
|
|||||||
"selected": "Selected: {{filename}}"
|
"selected": "Selected: {{filename}}"
|
||||||
},
|
},
|
||||||
"fontSize": "Font Size",
|
"fontSize": "Font Size",
|
||||||
"position": "Position",
|
"alphabet": "Font/Language",
|
||||||
|
"color": "Watermark Color",
|
||||||
"rotation": "Rotation (degrees)",
|
"rotation": "Rotation (degrees)",
|
||||||
"opacity": "Opacity (%)",
|
"opacity": "Opacity (%)",
|
||||||
"spacing": {
|
"spacing": {
|
||||||
"width": "Width Spacing",
|
"width": "Width Spacing",
|
||||||
"height": "Height Spacing"
|
"height": "Height Spacing"
|
||||||
}
|
},
|
||||||
|
"convertToImage": "Convert result to image-based PDF",
|
||||||
|
"convertToImageDesc": "Creates a PDF with images instead of text (more secure but larger file size)"
|
||||||
|
},
|
||||||
|
"alphabet": {
|
||||||
|
"roman": "Roman/Latin",
|
||||||
|
"arabic": "Arabic",
|
||||||
|
"japanese": "Japanese",
|
||||||
|
"korean": "Korean",
|
||||||
|
"chinese": "Chinese",
|
||||||
|
"thai": "Thai"
|
||||||
},
|
},
|
||||||
"positions": {
|
"positions": {
|
||||||
"topLeft": "Top Left",
|
"topLeft": "Top Left",
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
import React, { useRef } from "react";
|
||||||
|
import { Stack, Text, TextInput, FileButton, Button, NumberInput } from "@mantine/core";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface AddWatermarkParameters {
|
||||||
|
watermarkType?: 'text' | 'image';
|
||||||
|
watermarkText: string;
|
||||||
|
watermarkImage?: File;
|
||||||
|
fontSize: number;
|
||||||
|
rotation: number;
|
||||||
|
opacity: number;
|
||||||
|
widthSpacer: number;
|
||||||
|
heightSpacer: number;
|
||||||
|
position: string;
|
||||||
|
overrideX?: number;
|
||||||
|
overrideY?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WatermarkContentSettingsProps {
|
||||||
|
parameters: AddWatermarkParameters;
|
||||||
|
onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WatermarkContentSettings = ({ parameters, onParameterChange, disabled = false }: WatermarkContentSettingsProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const resetRef = useRef<() => void>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="md">
|
||||||
|
{/* Text Watermark Settings */}
|
||||||
|
{parameters.watermarkType === 'text' && (
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.text.label', 'Watermark Text')}</Text>
|
||||||
|
<TextInput
|
||||||
|
placeholder={t('watermark.settings.text.placeholder', 'Enter watermark text')}
|
||||||
|
value={parameters.watermarkText}
|
||||||
|
onChange={(e) => onParameterChange('watermarkText', e.target.value)}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.fontSize', 'Font Size')}</Text>
|
||||||
|
<NumberInput
|
||||||
|
value={parameters.fontSize}
|
||||||
|
onChange={(value) => onParameterChange('fontSize', value || 12)}
|
||||||
|
min={8}
|
||||||
|
max={72}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Image Watermark Settings */}
|
||||||
|
{parameters.watermarkType === 'image' && (
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.image.label', 'Watermark Image')}</Text>
|
||||||
|
<FileButton
|
||||||
|
resetRef={resetRef}
|
||||||
|
onChange={(file) => onParameterChange('watermarkImage', file)}
|
||||||
|
accept="image/*"
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{(props) => (
|
||||||
|
<Button {...props} variant="outline" fullWidth>
|
||||||
|
{parameters.watermarkImage ? parameters.watermarkImage.name : t('watermark.settings.image.choose', 'Choose Image')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</FileButton>
|
||||||
|
{parameters.watermarkImage && (
|
||||||
|
<Text size="xs" c="dimmed">
|
||||||
|
{t('watermark.settings.image.selected', 'Selected: {{filename}}', { filename: parameters.watermarkImage.name })}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WatermarkContentSettings;
|
@ -0,0 +1,117 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Stack, Text, NumberInput, Select, ColorInput, Checkbox } from "@mantine/core";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface AddWatermarkParameters {
|
||||||
|
watermarkType?: 'text' | 'image';
|
||||||
|
watermarkText: string;
|
||||||
|
watermarkImage?: File;
|
||||||
|
fontSize: number;
|
||||||
|
rotation: number;
|
||||||
|
opacity: number;
|
||||||
|
widthSpacer: number;
|
||||||
|
heightSpacer: number;
|
||||||
|
alphabet: string;
|
||||||
|
customColor: string;
|
||||||
|
convertPDFToImage: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WatermarkStyleSettingsProps {
|
||||||
|
parameters: AddWatermarkParameters;
|
||||||
|
onParameterChange: (key: keyof AddWatermarkParameters, value: any) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WatermarkStyleSettings = ({ parameters, onParameterChange, disabled = false }: WatermarkStyleSettingsProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const alphabetOptions = [
|
||||||
|
{ value: 'roman', label: t('watermark.alphabet.roman', 'Roman/Latin') },
|
||||||
|
{ value: 'arabic', label: t('watermark.alphabet.arabic', 'Arabic') },
|
||||||
|
{ value: 'japanese', label: t('watermark.alphabet.japanese', 'Japanese') },
|
||||||
|
{ value: 'korean', label: t('watermark.alphabet.korean', 'Korean') },
|
||||||
|
{ value: 'chinese', label: t('watermark.alphabet.chinese', 'Chinese') },
|
||||||
|
{ value: 'thai', label: t('watermark.alphabet.thai', 'Thai') }
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="md">
|
||||||
|
{/* Text-specific settings */}
|
||||||
|
{parameters.watermarkType === 'text' && (
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.alphabet', 'Font/Language')}</Text>
|
||||||
|
<Select
|
||||||
|
value={parameters.alphabet}
|
||||||
|
onChange={(value) => value && onParameterChange('alphabet', value)}
|
||||||
|
data={alphabetOptions}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.color', 'Watermark Color')}</Text>
|
||||||
|
<ColorInput
|
||||||
|
value={parameters.customColor}
|
||||||
|
onChange={(value) => onParameterChange('customColor', value)}
|
||||||
|
disabled={disabled}
|
||||||
|
format="hex"
|
||||||
|
swatches={['#d3d3d3', '#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff']}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Appearance Settings */}
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.rotation', 'Rotation (degrees)')}</Text>
|
||||||
|
<NumberInput
|
||||||
|
value={parameters.rotation}
|
||||||
|
onChange={(value) => onParameterChange('rotation', value || 0)}
|
||||||
|
min={-360}
|
||||||
|
max={360}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.opacity', 'Opacity (%)')}</Text>
|
||||||
|
<NumberInput
|
||||||
|
value={parameters.opacity}
|
||||||
|
onChange={(value) => onParameterChange('opacity', value || 50)}
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{/* Spacing Settings */}
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.spacing.width', 'Width Spacing')}</Text>
|
||||||
|
<NumberInput
|
||||||
|
value={parameters.widthSpacer}
|
||||||
|
onChange={(value) => onParameterChange('widthSpacer', value || 50)}
|
||||||
|
min={0}
|
||||||
|
max={200}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.spacing.height', 'Height Spacing')}</Text>
|
||||||
|
<NumberInput
|
||||||
|
value={parameters.heightSpacer}
|
||||||
|
onChange={(value) => onParameterChange('heightSpacer', value || 50)}
|
||||||
|
min={0}
|
||||||
|
max={200}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{/* Output Options */}
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Checkbox
|
||||||
|
label={t('watermark.settings.convertToImage', 'Convert result to image-based PDF')}
|
||||||
|
description={t('watermark.settings.convertToImageDesc', 'Creates a PDF with images instead of text (more secure but larger file size)')}
|
||||||
|
checked={parameters.convertPDFToImage}
|
||||||
|
onChange={(event) => onParameterChange('convertPDFToImage', event.currentTarget.checked)}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WatermarkStyleSettings;
|
@ -0,0 +1,45 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Button, Stack, Text } from "@mantine/core";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface WatermarkTypeSettingsProps {
|
||||||
|
watermarkType?: 'text' | 'image';
|
||||||
|
onWatermarkTypeChange: (type: 'text' | 'image') => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WatermarkTypeSettings = ({ watermarkType, onWatermarkTypeChange, disabled = false }: WatermarkTypeSettingsProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Text size="sm" fw={500}>{t('watermark.settings.type', 'Watermark Type')}</Text>
|
||||||
|
<div style={{ display: 'flex', gap: '4px' }}>
|
||||||
|
<Button
|
||||||
|
variant={watermarkType === 'text' ? 'filled' : 'outline'}
|
||||||
|
color={watermarkType === 'text' ? 'blue' : 'gray'}
|
||||||
|
onClick={() => onWatermarkTypeChange('text')}
|
||||||
|
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' : 'gray'}
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WatermarkTypeSettings;
|
@ -7,25 +7,27 @@ const buildFormData = (parameters: AddWatermarkParameters, file: File): FormData
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("fileInput", file);
|
formData.append("fileInput", file);
|
||||||
|
|
||||||
|
// Required: watermarkType as string
|
||||||
|
formData.append("watermarkType", parameters.watermarkType || "text");
|
||||||
|
|
||||||
|
// Add watermark content based on type
|
||||||
if (parameters.watermarkType === 'text') {
|
if (parameters.watermarkType === 'text') {
|
||||||
formData.append("watermarkText", parameters.watermarkText);
|
formData.append("watermarkText", parameters.watermarkText);
|
||||||
} else if (parameters.watermarkImage) {
|
} else if (parameters.watermarkType === 'image' && parameters.watermarkImage) {
|
||||||
formData.append("watermarkImage", parameters.watermarkImage);
|
formData.append("watermarkImage", parameters.watermarkImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Required parameters with correct formatting
|
||||||
formData.append("fontSize", parameters.fontSize.toString());
|
formData.append("fontSize", parameters.fontSize.toString());
|
||||||
formData.append("rotation", parameters.rotation.toString());
|
formData.append("rotation", parameters.rotation.toString());
|
||||||
formData.append("opacity", (parameters.opacity / 100).toString()); // Convert percentage to decimal
|
formData.append("opacity", (parameters.opacity / 100).toString()); // Convert percentage to decimal
|
||||||
formData.append("widthSpacer", parameters.widthSpacer.toString());
|
formData.append("widthSpacer", parameters.widthSpacer.toString());
|
||||||
formData.append("heightSpacer", parameters.heightSpacer.toString());
|
formData.append("heightSpacer", parameters.heightSpacer.toString());
|
||||||
formData.append("position", parameters.position);
|
|
||||||
|
|
||||||
if (parameters.overrideX !== undefined) {
|
// Backend-expected parameters from user input
|
||||||
formData.append("overrideX", parameters.overrideX.toString());
|
formData.append("alphabet", parameters.alphabet);
|
||||||
}
|
formData.append("customColor", parameters.customColor);
|
||||||
if (parameters.overrideY !== undefined) {
|
formData.append("convertPDFToImage", parameters.convertPDFToImage.toString());
|
||||||
formData.append("overrideY", parameters.overrideY.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return formData;
|
return formData;
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
|
||||||
export interface AddWatermarkParameters {
|
export interface AddWatermarkParameters {
|
||||||
watermarkType: 'text' | 'image';
|
watermarkType?: 'text' | 'image';
|
||||||
watermarkText: string;
|
watermarkText: string;
|
||||||
watermarkImage?: File;
|
watermarkImage?: File;
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
@ -9,20 +9,22 @@ export interface AddWatermarkParameters {
|
|||||||
opacity: number;
|
opacity: number;
|
||||||
widthSpacer: number;
|
widthSpacer: number;
|
||||||
heightSpacer: number;
|
heightSpacer: number;
|
||||||
position: string;
|
alphabet: string;
|
||||||
overrideX?: number;
|
customColor: string;
|
||||||
overrideY?: number;
|
convertPDFToImage: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultParameters: AddWatermarkParameters = {
|
const defaultParameters: AddWatermarkParameters = {
|
||||||
watermarkType: 'text',
|
watermarkType: undefined,
|
||||||
watermarkText: '',
|
watermarkText: '',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
opacity: 50,
|
opacity: 50,
|
||||||
widthSpacer: 50,
|
widthSpacer: 50,
|
||||||
heightSpacer: 50,
|
heightSpacer: 50,
|
||||||
position: 'center'
|
alphabet: 'roman',
|
||||||
|
customColor: '#d3d3d3',
|
||||||
|
convertPDFToImage: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAddWatermarkParameters = () => {
|
export const useAddWatermarkParameters = () => {
|
||||||
@ -40,6 +42,9 @@ export const useAddWatermarkParameters = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const validateParameters = useCallback((): boolean => {
|
const validateParameters = useCallback((): boolean => {
|
||||||
|
if (!parameters.watermarkType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (parameters.watermarkType === 'text') {
|
if (parameters.watermarkType === 'text') {
|
||||||
return parameters.watermarkText.trim().length > 0;
|
return parameters.watermarkText.trim().length > 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useMemo } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { Button, Stack, Text } from "@mantine/core";
|
import { Button, Stack, Text } from "@mantine/core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import DownloadIcon from "@mui/icons-material/Download";
|
import DownloadIcon from "@mui/icons-material/Download";
|
||||||
@ -12,7 +12,9 @@ import ErrorNotification from "../components/tools/shared/ErrorNotification";
|
|||||||
import FileStatusIndicator from "../components/tools/shared/FileStatusIndicator";
|
import FileStatusIndicator from "../components/tools/shared/FileStatusIndicator";
|
||||||
import ResultsPreview from "../components/tools/shared/ResultsPreview";
|
import ResultsPreview from "../components/tools/shared/ResultsPreview";
|
||||||
|
|
||||||
import AddWatermarkSettings from "../components/tools/addWatermark/AddWatermarkSettings";
|
import WatermarkTypeSettings from "../components/tools/addWatermark/WatermarkTypeSettings";
|
||||||
|
import WatermarkContentSettings from "../components/tools/addWatermark/WatermarkContentSettings";
|
||||||
|
import WatermarkStyleSettings from "../components/tools/addWatermark/WatermarkStyleSettings";
|
||||||
|
|
||||||
import { useAddWatermarkParameters } from "../hooks/tools/addWatermark/useAddWatermarkParameters";
|
import { useAddWatermarkParameters } from "../hooks/tools/addWatermark/useAddWatermarkParameters";
|
||||||
import { useAddWatermarkOperation } from "../hooks/tools/addWatermark/useAddWatermarkOperation";
|
import { useAddWatermarkOperation } from "../hooks/tools/addWatermark/useAddWatermarkOperation";
|
||||||
@ -65,7 +67,65 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =>
|
|||||||
const hasFiles = selectedFiles.length > 0;
|
const hasFiles = selectedFiles.length > 0;
|
||||||
const hasResults = watermarkOperation.files.length > 0 || watermarkOperation.downloadUrl !== null;
|
const hasResults = watermarkOperation.files.length > 0 || watermarkOperation.downloadUrl !== null;
|
||||||
const filesCollapsed = hasFiles;
|
const filesCollapsed = hasFiles;
|
||||||
const settingsCollapsed = hasResults;
|
|
||||||
|
// Step completion logic
|
||||||
|
const typeStepCompleted = hasFiles && !!watermarkParams.parameters.watermarkType;
|
||||||
|
const contentStepCompleted = typeStepCompleted && (
|
||||||
|
(watermarkParams.parameters.watermarkType === 'text' && watermarkParams.parameters.watermarkText.trim().length > 0) ||
|
||||||
|
(watermarkParams.parameters.watermarkType === 'image' && watermarkParams.parameters.watermarkImage !== undefined)
|
||||||
|
);
|
||||||
|
const styleStepCompleted = contentStepCompleted; // Style step has defaults, so completed when content is done
|
||||||
|
|
||||||
|
// Track which steps have been manually opened
|
||||||
|
const [manuallyOpenedSteps, setManuallyOpenedSteps] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
|
// Auto-collapse logic with manual override
|
||||||
|
const typeStepCollapsed = typeStepCompleted && !hasResults && !manuallyOpenedSteps.has('type');
|
||||||
|
const contentStepCollapsed = contentStepCompleted && !hasResults && !manuallyOpenedSteps.has('content');
|
||||||
|
const styleStepCollapsed = !manuallyOpenedSteps.has('style'); // Style starts collapsed, only opens when clicked
|
||||||
|
|
||||||
|
// Click handlers to manage step visibility and reset results
|
||||||
|
const handleTypeStepClick = () => {
|
||||||
|
setManuallyOpenedSteps(prev => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
if (newSet.has('type')) {
|
||||||
|
newSet.delete('type'); // Close if already open
|
||||||
|
} else {
|
||||||
|
newSet.add('type'); // Open if closed
|
||||||
|
}
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
|
watermarkOperation.resetResults();
|
||||||
|
onPreviewFile?.(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleContentStepClick = () => {
|
||||||
|
setManuallyOpenedSteps(prev => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
if (newSet.has('content')) {
|
||||||
|
newSet.delete('content'); // Close if already open
|
||||||
|
} else {
|
||||||
|
newSet.add('content'); // Open if closed
|
||||||
|
}
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
|
watermarkOperation.resetResults();
|
||||||
|
onPreviewFile?.(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStyleStepClick = () => {
|
||||||
|
setManuallyOpenedSteps(prev => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
if (newSet.has('style')) {
|
||||||
|
newSet.delete('style'); // Close if already open
|
||||||
|
} else {
|
||||||
|
newSet.add('style'); // Open if closed
|
||||||
|
}
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
|
watermarkOperation.resetResults();
|
||||||
|
onPreviewFile?.(null);
|
||||||
|
};
|
||||||
|
|
||||||
const previewResults = useMemo(() =>
|
const previewResults = useMemo(() =>
|
||||||
watermarkOperation.files?.map((file, index) => ({
|
watermarkOperation.files?.map((file, index) => ({
|
||||||
@ -96,22 +156,62 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =>
|
|||||||
/>
|
/>
|
||||||
</ToolStep>
|
</ToolStep>
|
||||||
|
|
||||||
{/* Settings Step */}
|
{/* Watermark Type Step */}
|
||||||
<ToolStep
|
<ToolStep
|
||||||
title="Settings"
|
title="Watermark Type"
|
||||||
isVisible={hasFiles}
|
isVisible={hasFiles}
|
||||||
isCollapsed={settingsCollapsed}
|
isCollapsed={typeStepCollapsed}
|
||||||
isCompleted={settingsCollapsed}
|
isCompleted={typeStepCompleted}
|
||||||
onCollapsedClick={settingsCollapsed ? handleSettingsReset : undefined}
|
onCollapsedClick={handleTypeStepClick}
|
||||||
completedMessage={settingsCollapsed ? "Watermark added" : undefined}
|
completedMessage={typeStepCompleted ?
|
||||||
|
`Type: ${watermarkParams.parameters.watermarkType === 'text' ? 'Text' : 'Image'}` : undefined}
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<WatermarkTypeSettings
|
||||||
<AddWatermarkSettings
|
watermarkType={watermarkParams.parameters.watermarkType}
|
||||||
|
onWatermarkTypeChange={(type) => watermarkParams.updateParameter('watermarkType', type)}
|
||||||
|
disabled={endpointLoading}
|
||||||
|
/>
|
||||||
|
</ToolStep>
|
||||||
|
|
||||||
|
{/* Content Step */}
|
||||||
|
<ToolStep
|
||||||
|
title={watermarkParams.parameters.watermarkType === 'text' ? "Text Content" : "Image Content"}
|
||||||
|
isVisible={typeStepCompleted}
|
||||||
|
isCollapsed={contentStepCollapsed}
|
||||||
|
isCompleted={contentStepCompleted}
|
||||||
|
onCollapsedClick={handleContentStepClick}
|
||||||
|
completedMessage={contentStepCompleted ?
|
||||||
|
(watermarkParams.parameters.watermarkType === 'text'
|
||||||
|
? `Text: "${watermarkParams.parameters.watermarkText}"`
|
||||||
|
: `Image: ${watermarkParams.parameters.watermarkImage?.name}`) : undefined}
|
||||||
|
>
|
||||||
|
<WatermarkContentSettings
|
||||||
parameters={watermarkParams.parameters}
|
parameters={watermarkParams.parameters}
|
||||||
onParameterChange={watermarkParams.updateParameter}
|
onParameterChange={watermarkParams.updateParameter}
|
||||||
disabled={endpointLoading}
|
disabled={endpointLoading}
|
||||||
/>
|
/>
|
||||||
|
</ToolStep>
|
||||||
|
|
||||||
|
{/* Style Step */}
|
||||||
|
<ToolStep
|
||||||
|
title="Style & Position (Optional)"
|
||||||
|
isVisible={contentStepCompleted}
|
||||||
|
isCollapsed={styleStepCollapsed}
|
||||||
|
isCompleted={styleStepCompleted}
|
||||||
|
onCollapsedClick={handleStyleStepClick}
|
||||||
|
completedMessage={styleStepCompleted ?
|
||||||
|
`Opacity: ${watermarkParams.parameters.opacity}%, Rotation: ${watermarkParams.parameters.rotation}°` : undefined}
|
||||||
|
>
|
||||||
|
<WatermarkStyleSettings
|
||||||
|
parameters={watermarkParams.parameters}
|
||||||
|
onParameterChange={watermarkParams.updateParameter}
|
||||||
|
disabled={endpointLoading}
|
||||||
|
/>
|
||||||
|
</ToolStep>
|
||||||
|
|
||||||
|
{/* Apply Button - Outside of settings steps */}
|
||||||
|
{styleStepCompleted && !hasResults && (
|
||||||
|
<Stack gap="sm" p="md">
|
||||||
<OperationButton
|
<OperationButton
|
||||||
onClick={handleAddWatermark}
|
onClick={handleAddWatermark}
|
||||||
isLoading={watermarkOperation.isLoading}
|
isLoading={watermarkOperation.isLoading}
|
||||||
@ -120,7 +220,7 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =>
|
|||||||
submitText="Add Watermark and Review"
|
submitText="Add Watermark and Review"
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</ToolStep>
|
)}
|
||||||
|
|
||||||
{/* Results Step */}
|
{/* Results Step */}
|
||||||
<ToolStep
|
<ToolStep
|
||||||
|
Loading…
x
Reference in New Issue
Block a user