mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
Feature/v2/add text (#4951)
Refactor sign to separate out add text and add image functions. Implement add text as standalone tool
This commit is contained in:
@@ -11,6 +11,7 @@ interface SavedSignaturesSectionProps {
|
||||
onUseSignature: (signature: SavedSignature) => void;
|
||||
onDeleteSignature: (signature: SavedSignature) => void;
|
||||
onRenameSignature: (id: string, label: string) => void;
|
||||
translationScope?: string;
|
||||
}
|
||||
|
||||
const typeBadgeColor: Record<SavedSignatureType, string> = {
|
||||
@@ -26,11 +27,18 @@ export const SavedSignaturesSection = ({
|
||||
onUseSignature,
|
||||
onDeleteSignature,
|
||||
onRenameSignature,
|
||||
translationScope = 'sign',
|
||||
}: SavedSignaturesSectionProps) => {
|
||||
const { t } = useTranslation();
|
||||
const translate = useCallback(
|
||||
(key: string, defaultValue: string, options?: Record<string, unknown>) =>
|
||||
t(`${translationScope}.${key}`, { defaultValue, ...options }),
|
||||
[t, translationScope]
|
||||
);
|
||||
const [labelDrafts, setLabelDrafts] = useState<Record<string, string>>({});
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const activeSignature = signatures[activeIndex];
|
||||
const activeSignatureRef = useRef<SavedSignature | null>(activeSignature ?? null);
|
||||
const appliedSignatureIdRef = useRef<string | null>(null);
|
||||
const onUseSignatureRef = useRef(onUseSignature);
|
||||
|
||||
@@ -38,6 +46,10 @@ export const SavedSignaturesSection = ({
|
||||
onUseSignatureRef.current = onUseSignature;
|
||||
}, [onUseSignature]);
|
||||
|
||||
useEffect(() => {
|
||||
activeSignatureRef.current = activeSignature ?? null;
|
||||
}, [activeSignature]);
|
||||
|
||||
useEffect(() => {
|
||||
setLabelDrafts(prev => {
|
||||
const nextDrafts: Record<string, string> = {};
|
||||
@@ -132,10 +144,10 @@ export const SavedSignaturesSection = ({
|
||||
const emptyState = (
|
||||
<Card withBorder>
|
||||
<Stack gap="xs">
|
||||
<Text fw={500}>{t('sign.saved.emptyTitle', 'No saved signatures yet')}</Text>
|
||||
<Text fw={500}>{translate('saved.emptyTitle', 'No saved signatures yet')}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t(
|
||||
'sign.saved.emptyDescription',
|
||||
{translate(
|
||||
'saved.emptyDescription',
|
||||
'Draw, upload, or type a signature above, then use "Save to library" to keep up to {{max}} favourites ready to use.',
|
||||
{ max: MAX_SAVED_SIGNATURES }
|
||||
)}
|
||||
@@ -147,11 +159,11 @@ export const SavedSignaturesSection = ({
|
||||
const typeLabel = (type: SavedSignatureType) => {
|
||||
switch (type) {
|
||||
case 'canvas':
|
||||
return t('sign.saved.type.canvas', 'Drawing');
|
||||
return translate('saved.type.canvas', 'Drawing');
|
||||
case 'image':
|
||||
return t('sign.saved.type.image', 'Upload');
|
||||
return translate('saved.type.image', 'Upload');
|
||||
case 'text':
|
||||
return t('sign.saved.type.text', 'Text');
|
||||
return translate('saved.type.text', 'Text');
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
@@ -182,36 +194,37 @@ export const SavedSignaturesSection = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeSignature || disabled) {
|
||||
const signature = activeSignatureRef.current;
|
||||
if (!signature || disabled) {
|
||||
appliedSignatureIdRef.current = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (appliedSignatureIdRef.current === activeSignature.id) {
|
||||
if (appliedSignatureIdRef.current === signature.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
appliedSignatureIdRef.current = activeSignature.id;
|
||||
onUseSignatureRef.current(activeSignature);
|
||||
}, [activeSignature, disabled]);
|
||||
appliedSignatureIdRef.current = signature.id;
|
||||
onUseSignatureRef.current(signature);
|
||||
}, [activeSignature?.id, disabled]);
|
||||
|
||||
return (
|
||||
<Stack gap="sm">
|
||||
<Group justify="space-between" align="flex-start">
|
||||
<Stack gap={0}>
|
||||
<Text fw={600} size="md">
|
||||
{t('sign.saved.heading', 'Saved signatures')}
|
||||
{translate('saved.heading', 'Saved signatures')}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('sign.saved.description', 'Reuse saved signatures at any time.')}
|
||||
{translate('saved.description', 'Reuse saved signatures at any time.')}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
|
||||
{isAtCapacity && (
|
||||
<Alert color="yellow" title={t('sign.saved.limitTitle', 'Limit reached')}>
|
||||
<Alert color="yellow" title={translate('saved.limitTitle', 'Limit reached')}>
|
||||
<Text size="sm">
|
||||
{t('sign.saved.limitDescription', 'Remove a saved signature before adding new ones (max {{max}}).', {
|
||||
{translate('saved.limitDescription', 'Remove a saved signature before adding new ones (max {{max}}).', {
|
||||
max: MAX_SAVED_SIGNATURES,
|
||||
})}
|
||||
</Text>
|
||||
@@ -224,7 +237,7 @@ export const SavedSignaturesSection = ({
|
||||
<Stack gap="xs">
|
||||
<Group justify="space-between" align="center">
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('sign.saved.carouselPosition', '{{current}} of {{total}}', {
|
||||
{translate('saved.carouselPosition', '{{current}} of {{total}}', {
|
||||
current: activeIndex + 1,
|
||||
total: signatures.length,
|
||||
})}
|
||||
@@ -232,7 +245,7 @@ export const SavedSignaturesSection = ({
|
||||
<Group gap={4}>
|
||||
<ActionIcon
|
||||
variant="light"
|
||||
aria-label={t('sign.saved.prev', 'Previous')}
|
||||
aria-label={translate('saved.prev', 'Previous')}
|
||||
onClick={() => handleNavigate('prev')}
|
||||
disabled={disabled || activeIndex === 0}
|
||||
>
|
||||
@@ -240,7 +253,7 @@ export const SavedSignaturesSection = ({
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
variant="light"
|
||||
aria-label={t('sign.saved.next', 'Next')}
|
||||
aria-label={translate('saved.next', 'Next')}
|
||||
onClick={() => handleNavigate('next')}
|
||||
disabled={disabled || activeIndex >= signatures.length - 1}
|
||||
>
|
||||
@@ -256,11 +269,11 @@ export const SavedSignaturesSection = ({
|
||||
<Badge color={typeBadgeColor[activeSignature.type]} variant="light">
|
||||
{typeLabel(activeSignature.type)}
|
||||
</Badge>
|
||||
<Tooltip label={t('sign.saved.delete', 'Remove')}>
|
||||
<Tooltip label={translate('saved.delete', 'Remove')}>
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="red"
|
||||
aria-label={t('sign.saved.delete', 'Remove')}
|
||||
aria-label={translate('saved.delete', 'Remove')}
|
||||
onClick={() => onDeleteSignature(activeSignature)}
|
||||
disabled={disabled}
|
||||
>
|
||||
@@ -272,7 +285,7 @@ export const SavedSignaturesSection = ({
|
||||
{renderPreview(activeSignature)}
|
||||
|
||||
<TextInput
|
||||
label={t('sign.saved.label', 'Label')}
|
||||
label={translate('saved.label', 'Label')}
|
||||
value={labelDrafts[activeSignature.id] ?? activeSignature.label}
|
||||
onChange={event => handleLabelChange(event, activeSignature)}
|
||||
onBlur={() => handleLabelBlur(activeSignature)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
||||
import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Stack, Button, Text, Alert, SegmentedControl, Divider, ActionIcon, Tooltip, Group, Box } from '@mantine/core';
|
||||
import { SignParameters } from "@app/hooks/tools/sign/useSignParameters";
|
||||
@@ -39,9 +39,14 @@ interface SignSettingsProps {
|
||||
onUndo?: () => void;
|
||||
onRedo?: () => void;
|
||||
onSave?: () => void;
|
||||
translationScope?: string;
|
||||
allowedSignatureSources?: SignatureSource[];
|
||||
defaultSignatureSource?: SignatureSource;
|
||||
}
|
||||
|
||||
type SignatureSource = 'canvas' | 'image' | 'text' | 'saved';
|
||||
export type SignatureSource = 'canvas' | 'image' | 'text' | 'saved';
|
||||
|
||||
const DEFAULT_SIGNATURE_SOURCES: SignatureSource[] = ['canvas', 'image', 'text', 'saved'];
|
||||
|
||||
const SignSettings = ({
|
||||
parameters,
|
||||
@@ -52,13 +57,26 @@ const SignSettings = ({
|
||||
onUpdateDrawSettings,
|
||||
onUndo,
|
||||
onRedo,
|
||||
onSave
|
||||
onSave,
|
||||
translationScope = 'sign',
|
||||
allowedSignatureSources = DEFAULT_SIGNATURE_SOURCES,
|
||||
defaultSignatureSource,
|
||||
}: SignSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { isPlacementMode, signaturesApplied, historyApiRef } = useSignature();
|
||||
const { activeFileIndex } = useViewer();
|
||||
const [historyAvailability, setHistoryAvailability] = useState({ canUndo: false, canRedo: false });
|
||||
const historyApiInstance = historyApiRef.current;
|
||||
const translate = useCallback(
|
||||
(key: string, defaultValue: string, options?: Record<string, unknown>) =>
|
||||
t(`${translationScope}.${key}`, { defaultValue, ...options }),
|
||||
[t, translationScope]
|
||||
);
|
||||
const effectiveDefaultSource =
|
||||
(defaultSignatureSource && allowedSignatureSources.includes(defaultSignatureSource)
|
||||
? defaultSignatureSource
|
||||
: allowedSignatureSources[0]) ?? 'text';
|
||||
const canUseSavedLibrary = allowedSignatureSources.includes('saved');
|
||||
|
||||
// State for drawing
|
||||
const [selectedColor, setSelectedColor] = useState('#000000');
|
||||
@@ -82,7 +100,13 @@ const SignSettings = ({
|
||||
updateSignatureLabel,
|
||||
byTypeCounts,
|
||||
} = useSavedSignatures();
|
||||
const [signatureSource, setSignatureSource] = useState<SignatureSource>(parameters.signatureType);
|
||||
const [signatureSource, setSignatureSource] = useState<SignatureSource>(() => {
|
||||
const paramSource = parameters.signatureType as SignatureSource;
|
||||
if (allowedSignatureSources.includes(paramSource)) {
|
||||
return paramSource;
|
||||
}
|
||||
return effectiveDefaultSource;
|
||||
});
|
||||
const [lastSavedSignatureKeys, setLastSavedSignatureKeys] = useState<Record<SavedSignatureType, string | null>>({
|
||||
canvas: null,
|
||||
image: null,
|
||||
@@ -103,17 +127,17 @@ const SignSettings = ({
|
||||
const getDefaultSavedLabel = useCallback(
|
||||
(type: SavedSignatureType) => {
|
||||
const nextIndex = (byTypeCounts[type] ?? 0) + 1;
|
||||
let baseLabel = t('sign.saved.defaultLabel', 'Signature');
|
||||
let baseLabel = translate('saved.defaultLabel', 'Signature');
|
||||
if (type === 'canvas') {
|
||||
baseLabel = t('sign.saved.defaultCanvasLabel', 'Drawing signature');
|
||||
baseLabel = translate('saved.defaultCanvasLabel', 'Drawing signature');
|
||||
} else if (type === 'image') {
|
||||
baseLabel = t('sign.saved.defaultImageLabel', 'Uploaded signature');
|
||||
baseLabel = translate('saved.defaultImageLabel', 'Uploaded signature');
|
||||
} else if (type === 'text') {
|
||||
baseLabel = t('sign.saved.defaultTextLabel', 'Typed signature');
|
||||
baseLabel = translate('saved.defaultTextLabel', 'Typed signature');
|
||||
}
|
||||
return `${baseLabel} ${nextIndex}`;
|
||||
},
|
||||
[byTypeCounts, t]
|
||||
[byTypeCounts, t, translate]
|
||||
);
|
||||
|
||||
const signatureKeysByType = useMemo(() => {
|
||||
@@ -263,7 +287,10 @@ const SignSettings = ({
|
||||
);
|
||||
|
||||
const renderSaveButton = (type: SavedSignatureType, isReady: boolean, onClick: () => void) => {
|
||||
const label = t('sign.saved.saveButton', 'Save signature');
|
||||
if (!canUseSavedLibrary) {
|
||||
return null;
|
||||
}
|
||||
const label = translate('saved.saveButton', 'Save signature');
|
||||
const currentKey = signatureKeysByType[type];
|
||||
const lastSavedKey = lastSavedSignatureKeys[type];
|
||||
const hasChanges = Boolean(currentKey && currentKey !== lastSavedKey);
|
||||
@@ -271,11 +298,11 @@ const SignSettings = ({
|
||||
|
||||
let tooltipMessage: string | undefined;
|
||||
if (!isReady) {
|
||||
tooltipMessage = t('sign.saved.saveUnavailable', 'Create a signature first to save it.');
|
||||
tooltipMessage = translate('saved.saveUnavailable', 'Create a signature first to save it.');
|
||||
} else if (isSaved) {
|
||||
tooltipMessage = t('sign.saved.noChanges', 'Current signature is already saved.');
|
||||
tooltipMessage = translate('saved.noChanges', 'Current signature is already saved.');
|
||||
} else if (isSavedSignatureLimitReached) {
|
||||
tooltipMessage = t('sign.saved.limitDescription', 'Remove a saved signature before adding new ones (max {{max}}).', {
|
||||
tooltipMessage = translate('saved.limitDescription', 'Remove a saved signature before adding new ones (max {{max}}).', {
|
||||
max: MAX_SAVED_SIGNATURES,
|
||||
});
|
||||
}
|
||||
@@ -289,7 +316,7 @@ const SignSettings = ({
|
||||
disabled={!isReady || disabled || isSavedSignatureLimitReached || !hasChanges}
|
||||
leftSection={<LocalIcon icon="material-symbols:save-rounded" width={16} height={16} />}
|
||||
>
|
||||
{isSaved ? t('sign.saved.status.saved', 'Saved') : label}
|
||||
{isSaved ? translate('saved.status.saved', 'Saved') : label}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -304,14 +331,33 @@ const SignSettings = ({
|
||||
return button;
|
||||
};
|
||||
|
||||
const renderSaveButtonRow = (type: SavedSignatureType, isReady: boolean, onClick: () => void) => {
|
||||
const button = renderSaveButton(type, isReady, onClick);
|
||||
if (!button) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Box style={{ alignSelf: 'flex-start', marginTop: '0.4rem' }}>
|
||||
{button}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (signatureSource === 'saved' && !canUseSavedLibrary) {
|
||||
setSignatureSource(effectiveDefaultSource);
|
||||
return;
|
||||
}
|
||||
if (signatureSource === 'saved') {
|
||||
return;
|
||||
}
|
||||
if (signatureSource !== parameters.signatureType) {
|
||||
setSignatureSource(parameters.signatureType);
|
||||
const nextSource = allowedSignatureSources.includes(parameters.signatureType as SignatureSource)
|
||||
? (parameters.signatureType as SignatureSource)
|
||||
: effectiveDefaultSource;
|
||||
if (signatureSource !== nextSource) {
|
||||
setSignatureSource(nextSource);
|
||||
}
|
||||
}, [parameters.signatureType, signatureSource]);
|
||||
}, [parameters.signatureType, signatureSource, allowedSignatureSources, effectiveDefaultSource, canUseSavedLibrary]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!disabled) {
|
||||
@@ -376,26 +422,47 @@ const SignSettings = ({
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
// Reset pause state and directly activate placement
|
||||
setPlacementManuallyPaused(false);
|
||||
lastAppliedPlacementKey.current = null;
|
||||
setImageSignatureData(result);
|
||||
|
||||
// Directly activate placement on image upload
|
||||
if (typeof window !== 'undefined') {
|
||||
window.setTimeout(() => onActivateSignaturePlacement?.(), PLACEMENT_ACTIVATION_DELAY);
|
||||
} else {
|
||||
onActivateSignaturePlacement?.();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error reading file:', error);
|
||||
}
|
||||
} else if (!file) {
|
||||
setImageSignatureData(undefined);
|
||||
onDeactivateSignature?.();
|
||||
setImageSignatureData(undefined);
|
||||
onDeactivateSignature?.();
|
||||
}
|
||||
};
|
||||
|
||||
// Handle signature data changes
|
||||
const handleCanvasSignatureChange = (data: string | null) => {
|
||||
const handleCanvasSignatureChange = useCallback((data: string | null) => {
|
||||
const nextValue = data ?? undefined;
|
||||
setCanvasSignatureData(prev => {
|
||||
if (prev === nextValue) {
|
||||
return prev;
|
||||
setCanvasSignatureData(prevData => {
|
||||
// Reset pause state and trigger placement for signature changes
|
||||
// (onDrawingComplete handles initial activation)
|
||||
if (prevData && prevData !== nextValue && nextValue) {
|
||||
setPlacementManuallyPaused(false);
|
||||
lastAppliedPlacementKey.current = null;
|
||||
// Directly activate placement on signature change
|
||||
if (typeof window !== 'undefined') {
|
||||
window.setTimeout(() => onActivateSignaturePlacement?.(), PLACEMENT_ACTIVATION_DELAY);
|
||||
} else {
|
||||
onActivateSignaturePlacement?.();
|
||||
}
|
||||
}
|
||||
return nextValue;
|
||||
});
|
||||
};
|
||||
}, [onActivateSignaturePlacement]);
|
||||
|
||||
const hasCanvasSignature = useMemo(() => Boolean(canvasSignatureData), [canvasSignatureData]);
|
||||
const hasImageSignature = useMemo(() => Boolean(imageSignatureData), [imageSignatureData]);
|
||||
@@ -588,9 +655,7 @@ const SignSettings = ({
|
||||
const timer = window.setTimeout(() => {
|
||||
onActivateSignaturePlacement?.();
|
||||
}, PLACEMENT_ACTIVATION_DELAY);
|
||||
return () => {
|
||||
window.clearTimeout(timer);
|
||||
};
|
||||
return () => window.clearTimeout(timer);
|
||||
}
|
||||
|
||||
onActivateSignaturePlacement?.();
|
||||
@@ -601,6 +666,7 @@ const SignSettings = ({
|
||||
isPlacementManuallyPaused,
|
||||
onActivateSignaturePlacement,
|
||||
onDeactivateSignature,
|
||||
placementSignatureKey,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -627,9 +693,7 @@ const SignSettings = ({
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const timer = window.setTimeout(trigger, PLACEMENT_ACTIVATION_DELAY);
|
||||
return () => {
|
||||
window.clearTimeout(timer);
|
||||
};
|
||||
return () => window.clearTimeout(timer);
|
||||
}
|
||||
|
||||
trigger();
|
||||
@@ -652,16 +716,29 @@ const SignSettings = ({
|
||||
const timer = window.setTimeout(() => {
|
||||
onActivateSignaturePlacement?.();
|
||||
}, FILE_SWITCH_ACTIVATION_DELAY);
|
||||
return () => {
|
||||
window.clearTimeout(timer);
|
||||
};
|
||||
return () => window.clearTimeout(timer);
|
||||
}
|
||||
|
||||
onActivateSignaturePlacement?.();
|
||||
}, [activeFileIndex, shouldEnablePlacement, signaturesApplied, onActivateSignaturePlacement]);
|
||||
|
||||
const sourceLabels: Record<SignatureSource, string> = {
|
||||
canvas: translate('type.canvas', 'Draw'),
|
||||
image: translate('type.image', 'Upload'),
|
||||
text: translate('type.text', 'Type'),
|
||||
saved: translate('type.saved', 'Saved'),
|
||||
};
|
||||
|
||||
const sourceOptions = allowedSignatureSources.map(source => ({
|
||||
label: sourceLabels[source],
|
||||
value: source,
|
||||
}));
|
||||
|
||||
const renderSignatureBuilder = () => {
|
||||
if (signatureSource === 'saved') {
|
||||
if (!canUseSavedLibrary) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<SavedSignaturesSection
|
||||
signatures={savedSignatures}
|
||||
@@ -670,6 +747,7 @@ const SignSettings = ({
|
||||
onUseSignature={handleUseSavedSignature}
|
||||
onDeleteSignature={handleDeleteSavedSignature}
|
||||
onRenameSignature={handleRenameSavedSignature}
|
||||
translationScope={translationScope}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -691,9 +769,7 @@ const SignSettings = ({
|
||||
disabled={disabled}
|
||||
initialSignatureData={canvasSignatureData}
|
||||
/>
|
||||
<Box style={{ alignSelf: 'flex-start', marginTop: '0.4rem' }}>
|
||||
{renderSaveButton('canvas', hasCanvasSignature, handleSaveCanvasSignature)}
|
||||
</Box>
|
||||
{renderSaveButtonRow('canvas', hasCanvasSignature, handleSaveCanvasSignature)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -705,9 +781,7 @@ const SignSettings = ({
|
||||
onImageChange={handleImageChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Box style={{ alignSelf: 'flex-start', marginTop: '0.4rem' }}>
|
||||
{renderSaveButton('image', hasImageSignature, handleSaveImageSignature)}
|
||||
</Box>
|
||||
{renderSaveButtonRow('image', hasImageSignature, handleSaveImageSignature)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -724,35 +798,43 @@ const SignSettings = ({
|
||||
textColor={parameters.textColor || '#000000'}
|
||||
onTextColorChange={(color) => onParameterChange('textColor', color)}
|
||||
disabled={disabled}
|
||||
onAnyChange={() => {
|
||||
setPlacementManuallyPaused(false);
|
||||
lastAppliedPlacementKey.current = null;
|
||||
// Directly activate placement on text changes
|
||||
if (typeof window !== 'undefined') {
|
||||
window.setTimeout(() => onActivateSignaturePlacement?.(), PLACEMENT_ACTIVATION_DELAY);
|
||||
} else {
|
||||
onActivateSignaturePlacement?.();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Box style={{ alignSelf: 'flex-start', marginTop: '0.4rem' }}>
|
||||
{renderSaveButton('text', hasTextSignature, handleSaveTextSignature)}
|
||||
</Box>
|
||||
{renderSaveButtonRow('text', hasTextSignature, handleSaveTextSignature)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const placementInstructions = () => {
|
||||
if (signatureSource === 'saved') {
|
||||
return t(
|
||||
'sign.instructions.saved',
|
||||
return translate(
|
||||
'instructions.saved',
|
||||
'Select a saved signature above, then click anywhere on the PDF to place it.'
|
||||
);
|
||||
}
|
||||
if (parameters.signatureType === 'canvas') {
|
||||
return t(
|
||||
'sign.instructions.canvas',
|
||||
return translate(
|
||||
'instructions.canvas',
|
||||
'After drawing your signature and closing the canvas, click anywhere on the PDF to place it.'
|
||||
);
|
||||
}
|
||||
if (parameters.signatureType === 'image') {
|
||||
return t(
|
||||
'sign.instructions.image',
|
||||
return translate(
|
||||
'instructions.image',
|
||||
'After uploading your signature image, click anywhere on the PDF to place it.'
|
||||
);
|
||||
}
|
||||
return t(
|
||||
'sign.instructions.text',
|
||||
return translate(
|
||||
'instructions.text',
|
||||
'After entering your name above, click anywhere on the PDF to place your signature.'
|
||||
);
|
||||
};
|
||||
@@ -761,16 +843,16 @@ const SignSettings = ({
|
||||
? {
|
||||
color: isPlacementMode ? 'blue' : 'teal',
|
||||
title: isPlacementMode
|
||||
? t('sign.instructions.title', 'How to add your signature')
|
||||
: t('sign.instructions.paused', 'Placement paused'),
|
||||
? translate('instructions.title', 'How to add your signature')
|
||||
: translate('instructions.paused', 'Placement paused'),
|
||||
message: isPlacementMode
|
||||
? placementInstructions()
|
||||
: t('sign.instructions.resumeHint', 'Resume placement to click and add your signature.'),
|
||||
: translate('instructions.resumeHint', 'Resume placement to click and add your signature.'),
|
||||
}
|
||||
: {
|
||||
color: 'yellow',
|
||||
title: t('sign.instructions.title', 'How to add your signature'),
|
||||
message: t('sign.instructions.noSignature', 'Create a signature above to enable placement tools.'),
|
||||
title: translate('instructions.title', 'How to add your signature'),
|
||||
message: translate('instructions.noSignature', 'Create a signature above to enable placement tools.'),
|
||||
};
|
||||
|
||||
const handlePausePlacement = () => {
|
||||
@@ -806,11 +888,11 @@ const SignSettings = ({
|
||||
onActivateSignaturePlacement || onDeactivateSignature
|
||||
? isPlacementMode
|
||||
? (
|
||||
<Tooltip label={t('sign.mode.pause', 'Pause placement')}>
|
||||
<Tooltip label={translate('mode.pause', 'Pause placement')}>
|
||||
<ActionIcon
|
||||
variant="default"
|
||||
size="lg"
|
||||
aria-label={t('sign.mode.pause', 'Pause placement')}
|
||||
aria-label={translate('mode.pause', 'Pause placement')}
|
||||
onClick={handlePausePlacement}
|
||||
disabled={disabled || !onDeactivateSignature}
|
||||
style={{
|
||||
@@ -823,17 +905,17 @@ const SignSettings = ({
|
||||
>
|
||||
<LocalIcon icon="material-symbols:pause-rounded" width={20} height={20} />
|
||||
<Text component="span" size="sm" fw={500}>
|
||||
{t('sign.mode.pause', 'Pause placement')}
|
||||
{translate('mode.pause', 'Pause placement')}
|
||||
</Text>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)
|
||||
: (
|
||||
<Tooltip label={t('sign.mode.resume', 'Resume placement')}>
|
||||
<Tooltip label={translate('mode.resume', 'Resume placement')}>
|
||||
<ActionIcon
|
||||
variant="default"
|
||||
size="lg"
|
||||
aria-label={t('sign.mode.resume', 'Resume placement')}
|
||||
aria-label={translate('mode.resume', 'Resume placement')}
|
||||
onClick={handleResumePlacement}
|
||||
disabled={disabled || !isCurrentTypeReady || !onActivateSignaturePlacement}
|
||||
style={{
|
||||
@@ -846,7 +928,7 @@ const SignSettings = ({
|
||||
>
|
||||
<LocalIcon icon="material-symbols:play-arrow-rounded" width={20} height={20} />
|
||||
<Text component="span" size="sm" fw={500}>
|
||||
{t('sign.mode.resume', 'Resume placement')}
|
||||
{translate('mode.resume', 'Resume placement')}
|
||||
</Text>
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
@@ -857,19 +939,16 @@ const SignSettings = ({
|
||||
<Stack>
|
||||
<Stack gap="sm">
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('sign.step.createDesc', 'Choose how you want to create the signature')}
|
||||
{translate('step.createDesc', 'Choose how you want to create the signature')}
|
||||
</Text>
|
||||
<SegmentedControl
|
||||
value={signatureSource}
|
||||
fullWidth
|
||||
onChange={(value) => handleSignatureSourceChange(value as SignatureSource)}
|
||||
data={[
|
||||
{ label: t('sign.type.canvas', 'Draw'), value: 'canvas' },
|
||||
{ label: t('sign.type.image', 'Upload'), value: 'image' },
|
||||
{ label: t('sign.type.text', 'Type'), value: 'text' },
|
||||
{ label: t('sign.type.saved', 'Saved'), value: 'saved' },
|
||||
]}
|
||||
/>
|
||||
{sourceOptions.length > 1 && (
|
||||
<SegmentedControl
|
||||
value={signatureSource}
|
||||
fullWidth
|
||||
onChange={(value) => handleSignatureSourceChange(value as SignatureSource)}
|
||||
data={sourceOptions}
|
||||
/>
|
||||
)}
|
||||
{renderSignatureBuilder()}
|
||||
</Stack>
|
||||
|
||||
@@ -877,10 +956,10 @@ const SignSettings = ({
|
||||
|
||||
<Stack gap="sm">
|
||||
<Text fw={600} size="md">
|
||||
{t('sign.step.place', 'Place & save')}
|
||||
{translate('step.place', 'Place & save')}
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('sign.step.placeDesc', 'Position the signature on your PDF')}
|
||||
{translate('step.placeDesc', 'Position the signature on your PDF')}
|
||||
</Text>
|
||||
|
||||
<Group gap="xs" wrap="nowrap" align="center">
|
||||
@@ -911,7 +990,7 @@ const SignSettings = ({
|
||||
onClose={() => setIsColorPickerOpen(false)}
|
||||
selectedColor={selectedColor}
|
||||
onColorChange={setSelectedColor}
|
||||
title={t('sign.canvas.colorPickerTitle', 'Choose stroke colour')}
|
||||
title={translate('canvas.colorPickerTitle', 'Choose stroke colour')}
|
||||
/>
|
||||
|
||||
{onSave && (
|
||||
@@ -921,7 +1000,7 @@ const SignSettings = ({
|
||||
variant="filled"
|
||||
fullWidth
|
||||
>
|
||||
{t('sign.applySignatures', 'Apply Signatures')}
|
||||
{translate('applySignatures', 'Apply Signatures')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user