mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-18 09:29:24 +00:00
Split up AdvancedSelectionPanel and allow users to delete all selected pages
This commit is contained in:
parent
504c2d277b
commit
f742d5cb1b
@ -171,7 +171,8 @@ const PageEditor = ({
|
|||||||
},
|
},
|
||||||
() => splitPositions,
|
() => splitPositions,
|
||||||
setSplitPositions,
|
setSplitPositions,
|
||||||
() => getPageNumbersFromIds(selectedPageIds)
|
() => getPageNumbersFromIds(selectedPageIds),
|
||||||
|
closePdf
|
||||||
);
|
);
|
||||||
undoManagerRef.current.executeCommand(deleteCommand);
|
undoManagerRef.current.executeCommand(deleteCommand);
|
||||||
}
|
}
|
||||||
@ -228,7 +229,8 @@ const PageEditor = ({
|
|||||||
},
|
},
|
||||||
() => splitPositions,
|
() => splitPositions,
|
||||||
setSplitPositions,
|
setSplitPositions,
|
||||||
() => selectedPageNumbers
|
() => selectedPageNumbers,
|
||||||
|
closePdf
|
||||||
);
|
);
|
||||||
undoManagerRef.current.executeCommand(deleteCommand);
|
undoManagerRef.current.executeCommand(deleteCommand);
|
||||||
}, [selectedPageIds, displayDocument, splitPositions, getPageNumbersFromIds, getPageIdsFromNumbers]);
|
}, [selectedPageIds, displayDocument, splitPositions, getPageNumbersFromIds, getPageIdsFromNumbers]);
|
||||||
@ -246,7 +248,8 @@ const PageEditor = ({
|
|||||||
},
|
},
|
||||||
() => splitPositions,
|
() => splitPositions,
|
||||||
setSplitPositions,
|
setSplitPositions,
|
||||||
() => getPageNumbersFromIds(selectedPageIds)
|
() => getPageNumbersFromIds(selectedPageIds),
|
||||||
|
closePdf
|
||||||
);
|
);
|
||||||
undoManagerRef.current.executeCommand(deleteCommand);
|
undoManagerRef.current.executeCommand(deleteCommand);
|
||||||
}, [displayDocument, splitPositions, selectedPageIds, getPageNumbersFromIds]);
|
}, [displayDocument, splitPositions, selectedPageIds, getPageNumbersFromIds]);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button, Text, NumberInput, Group, Flex } from '@mantine/core';
|
import { Flex } from '@mantine/core';
|
||||||
import classes from './BulkSelectionPanel.module.css';
|
import classes from './BulkSelectionPanel.module.css';
|
||||||
import {
|
import {
|
||||||
appendExpression,
|
appendExpression,
|
||||||
@ -9,6 +9,8 @@ import {
|
|||||||
everyNthExpression,
|
everyNthExpression,
|
||||||
rangeExpression,
|
rangeExpression,
|
||||||
} from './BulkSelection';
|
} from './BulkSelection';
|
||||||
|
import SelectPages from './SelectPages';
|
||||||
|
import OperatorsSection from './OperatorsSection';
|
||||||
|
|
||||||
interface AdvancedSelectionPanelProps {
|
interface AdvancedSelectionPanelProps {
|
||||||
csvInput: string;
|
csvInput: string;
|
||||||
@ -25,16 +27,32 @@ const AdvancedSelectionPanel = ({
|
|||||||
maxPages,
|
maxPages,
|
||||||
advancedOpened,
|
advancedOpened,
|
||||||
}: AdvancedSelectionPanelProps) => {
|
}: AdvancedSelectionPanelProps) => {
|
||||||
// Visibility now controlled by parent
|
|
||||||
const [firstNValue, setFirstNValue] = useState<number | ''>('');
|
|
||||||
const [lastNValue, setLastNValue] = useState<number | ''>('');
|
|
||||||
const [everyNthValue, setEveryNthValue] = useState<number | ''>('');
|
|
||||||
const [rangeStart, setRangeStart] = useState<number | ''>('');
|
|
||||||
const [rangeEnd, setRangeEnd] = useState<number | ''>('');
|
const [rangeEnd, setRangeEnd] = useState<number | ''>('');
|
||||||
const [firstNError, setFirstNError] = useState<string | null>(null);
|
|
||||||
const [lastNError, setLastNError] = useState<string | null>(null);
|
|
||||||
const [rangeError, setRangeError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
|
const handleRangeEndChange = (val: string | number) => {
|
||||||
|
const next = typeof val === 'number' ? val : '';
|
||||||
|
setRangeEnd(next);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Named validation functions
|
||||||
|
const validatePositiveNumber = (value: number): string | null => {
|
||||||
|
return value <= 0 ? 'Enter a positive number' : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateRangeStart = (start: number): string | null => {
|
||||||
|
if (start <= 0) return 'Values must be positive';
|
||||||
|
if (typeof rangeEnd === 'number' && start > rangeEnd) {
|
||||||
|
return 'From must be less than or equal to To';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateRangeEnd = (end: number): string | null => {
|
||||||
|
if (end <= 0) return 'Values must be positive';
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Named callback functions
|
||||||
const applyExpression = (expr: string) => {
|
const applyExpression = (expr: string) => {
|
||||||
const nextInput = appendExpression(csvInput, expr);
|
const nextInput = appendExpression(csvInput, expr);
|
||||||
setCsvInput(nextInput);
|
setCsvInput(nextInput);
|
||||||
@ -46,6 +64,28 @@ const AdvancedSelectionPanel = ({
|
|||||||
setCsvInput(next);
|
setCsvInput(next);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFirstNApply = (value: number) => {
|
||||||
|
const expr = firstNExpression(value, maxPages);
|
||||||
|
if (expr) applyExpression(expr);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLastNApply = (value: number) => {
|
||||||
|
const expr = lastNExpression(value, maxPages);
|
||||||
|
if (expr) applyExpression(expr);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEveryNthApply = (value: number) => {
|
||||||
|
const expr = everyNthExpression(value);
|
||||||
|
if (expr) applyExpression(expr);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRangeApply = (start: number) => {
|
||||||
|
if (typeof rangeEnd !== 'number') return;
|
||||||
|
const expr = rangeExpression(start, rangeEnd, maxPages);
|
||||||
|
if (expr) applyExpression(expr);
|
||||||
|
setRangeEnd('');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Advanced section */}
|
{/* Advanced section */}
|
||||||
@ -54,211 +94,47 @@ const AdvancedSelectionPanel = ({
|
|||||||
<div className={classes.advancedContent}>
|
<div className={classes.advancedContent}>
|
||||||
{/* Cards row */}
|
{/* Cards row */}
|
||||||
<Flex direction="row" mb="xs" wrap="wrap">
|
<Flex direction="row" mb="xs" wrap="wrap">
|
||||||
{/* First N Pages - Card Style */}
|
<SelectPages
|
||||||
<div className={classes.advancedCard}>
|
title="First N Pages"
|
||||||
<Text size="sm" fw={600} c="var(--text-secondary)" mb="xs">First N Pages</Text>
|
placeholder="Number of pages"
|
||||||
{firstNError && (<Text size="xs" c="var(--text-brand-accent)" mb="xs">{firstNError}</Text>)}
|
onApply={handleFirstNApply}
|
||||||
<div className={classes.inputGroup}>
|
maxPages={maxPages}
|
||||||
<Group gap="sm" align="flex-end" wrap="nowrap">
|
validationFn={validatePositiveNumber}
|
||||||
<NumberInput
|
/>
|
||||||
size="sm"
|
|
||||||
value={firstNValue}
|
|
||||||
onChange={(val) => {
|
|
||||||
const next = typeof val === 'number' ? val : '';
|
|
||||||
setFirstNValue(next);
|
|
||||||
if (next === '') setFirstNError(null);
|
|
||||||
else if (typeof next === 'number' && next <= 0) setFirstNError('Enter a positive number');
|
|
||||||
else setFirstNError(null);
|
|
||||||
}}
|
|
||||||
min={1}
|
|
||||||
placeholder="Number of pages"
|
|
||||||
className={classes.fullWidthInput}
|
|
||||||
error={Boolean(firstNError)}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
className={classes.applyButton}
|
|
||||||
onClick={() => {
|
|
||||||
if (!firstNValue || typeof firstNValue !== 'number') return;
|
|
||||||
const expr = firstNExpression(firstNValue, maxPages);
|
|
||||||
if (expr) applyExpression(expr);
|
|
||||||
setFirstNValue('');
|
|
||||||
}}
|
|
||||||
disabled={Boolean(firstNError) || firstNValue === ''}
|
|
||||||
>
|
|
||||||
Apply
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Range - Card Style */}
|
<SelectPages
|
||||||
<div className={classes.advancedCard}>
|
title="Range"
|
||||||
<Text size="sm" fw={600} c="var(--text-secondary)" mb="xs">Range</Text>
|
placeholder="From"
|
||||||
{rangeError && (<Text size="xs" c="var(--text-brand-accent)" mb="xs">{rangeError}</Text>)}
|
onApply={handleRangeApply}
|
||||||
<div className={classes.inputGroup}>
|
maxPages={maxPages}
|
||||||
<Group gap="sm" align="flex-end" wrap="nowrap">
|
validationFn={validateRangeStart}
|
||||||
<div style={{ flex: 1 }}>
|
isRange={true}
|
||||||
<NumberInput
|
rangeEndValue={rangeEnd}
|
||||||
size="sm"
|
onRangeEndChange={handleRangeEndChange}
|
||||||
value={rangeStart}
|
rangeEndPlaceholder="To"
|
||||||
onChange={(val) => {
|
/>
|
||||||
const next = typeof val === 'number' ? val : '';
|
|
||||||
setRangeStart(next);
|
|
||||||
const s = typeof next === 'number' ? next : null;
|
|
||||||
const e = typeof rangeEnd === 'number' ? rangeEnd : null;
|
|
||||||
if (s !== null && s <= 0) setRangeError('Values must be positive');
|
|
||||||
else if (s !== null && e !== null && s > e) setRangeError('From must be less than or equal to To');
|
|
||||||
else setRangeError(null);
|
|
||||||
}}
|
|
||||||
min={1}
|
|
||||||
placeholder="From"
|
|
||||||
error={Boolean(rangeError)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<NumberInput
|
|
||||||
size="sm"
|
|
||||||
value={rangeEnd}
|
|
||||||
onChange={(val) => {
|
|
||||||
const next = typeof val === 'number' ? val : '';
|
|
||||||
setRangeEnd(next);
|
|
||||||
const e = typeof next === 'number' ? next : null;
|
|
||||||
const s = typeof rangeStart === 'number' ? rangeStart : null;
|
|
||||||
if (e !== null && e <= 0) setRangeError('Values must be positive');
|
|
||||||
else if (s !== null && e !== null && s > e) setRangeError('From must be less than or equal to To');
|
|
||||||
else setRangeError(null);
|
|
||||||
}}
|
|
||||||
min={1}
|
|
||||||
placeholder="To"
|
|
||||||
error={Boolean(rangeError)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
className={classes.applyButton}
|
|
||||||
onClick={() => {
|
|
||||||
if (
|
|
||||||
rangeStart === '' || rangeEnd === '' ||
|
|
||||||
typeof rangeStart !== 'number' || typeof rangeEnd !== 'number'
|
|
||||||
) return;
|
|
||||||
const expr = rangeExpression(rangeStart, rangeEnd, maxPages);
|
|
||||||
if (expr) applyExpression(expr);
|
|
||||||
setRangeStart('');
|
|
||||||
setRangeEnd('');
|
|
||||||
}}
|
|
||||||
disabled={Boolean(rangeError) || rangeStart === '' || rangeEnd === ''}
|
|
||||||
>
|
|
||||||
Apply
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Last N Pages - Card Style */}
|
<SelectPages
|
||||||
<div className={classes.advancedCard}>
|
title="Last N Pages"
|
||||||
<Text size="sm" fw={600} c="var(--text-secondary)" mb="xs">Last N Pages</Text>
|
placeholder="Number of pages"
|
||||||
{lastNError && (<Text size="xs" c="var(--text-brand-accent)" mb="xs">{lastNError}</Text>)}
|
onApply={handleLastNApply}
|
||||||
<div className={classes.inputGroup}>
|
maxPages={maxPages}
|
||||||
<Group gap="sm" align="flex-end" wrap="nowrap">
|
validationFn={validatePositiveNumber}
|
||||||
<NumberInput
|
/>
|
||||||
size="sm"
|
|
||||||
value={lastNValue}
|
|
||||||
onChange={(val) => {
|
|
||||||
const next = typeof val === 'number' ? val : '';
|
|
||||||
setLastNValue(next);
|
|
||||||
if (next === '') setLastNError(null);
|
|
||||||
else if (typeof next === 'number' && next <= 0) setLastNError('Enter a positive number');
|
|
||||||
else setLastNError(null);
|
|
||||||
}}
|
|
||||||
min={1}
|
|
||||||
placeholder="Number of pages"
|
|
||||||
className={classes.fullWidthInput}
|
|
||||||
error={Boolean(lastNError)}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
className={classes.applyButton}
|
|
||||||
onClick={() => {
|
|
||||||
if (!lastNValue || typeof lastNValue !== 'number') return;
|
|
||||||
const expr = lastNExpression(lastNValue, maxPages);
|
|
||||||
if (expr) applyExpression(expr);
|
|
||||||
setLastNValue('');
|
|
||||||
}}
|
|
||||||
disabled={Boolean(lastNError) || lastNValue === ''}
|
|
||||||
>
|
|
||||||
Apply
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Every Nth Page - Card Style */}
|
<SelectPages
|
||||||
<div className={classes.advancedCard}>
|
title="Every Nth Page"
|
||||||
<Text size="sm" fw={600} c="var(--text-secondary)" mb="xs">Every Nth Page</Text>
|
placeholder="Step size"
|
||||||
<div className={classes.inputGroup}>
|
onApply={handleEveryNthApply}
|
||||||
<Group gap="sm" align="flex-end" wrap="nowrap">
|
maxPages={maxPages}
|
||||||
<NumberInput
|
/>
|
||||||
size="sm"
|
|
||||||
value={everyNthValue}
|
|
||||||
onChange={(val) => setEveryNthValue(typeof val === 'number' ? val : '')}
|
|
||||||
min={1}
|
|
||||||
placeholder="Step size"
|
|
||||||
className={classes.fullWidthInput}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
className={classes.applyButton}
|
|
||||||
onClick={() => {
|
|
||||||
if (!everyNthValue || typeof everyNthValue !== 'number') return;
|
|
||||||
const expr = everyNthExpression(everyNthValue);
|
|
||||||
if (expr) applyExpression(expr);
|
|
||||||
setEveryNthValue('');
|
|
||||||
}}
|
|
||||||
disabled={!everyNthValue}
|
|
||||||
>
|
|
||||||
Apply
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* Operators row at bottom */}
|
{/* Operators row at bottom */}
|
||||||
<div>
|
<OperatorsSection
|
||||||
<Text size="xs" c="var(--text-muted)" fw={500} mb="xs">Add Operators:</Text>
|
csvInput={csvInput}
|
||||||
<Group gap="sm" wrap="nowrap">
|
onInsertOperator={insertOperator}
|
||||||
<Button
|
/>
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
className={classes.operatorChip}
|
|
||||||
onClick={() => insertOperator('and')}
|
|
||||||
disabled={!csvInput.trim()}
|
|
||||||
title="Combine selections (both conditions must be true)"
|
|
||||||
>
|
|
||||||
<Text size="xs" fw={500}>and</Text>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
className={classes.operatorChip}
|
|
||||||
onClick={() => insertOperator('or')}
|
|
||||||
disabled={!csvInput.trim()}
|
|
||||||
title="Add to selection (either condition can be true)"
|
|
||||||
>
|
|
||||||
<Text size="xs" fw={500}>or</Text>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
className={classes.operatorChip}
|
|
||||||
onClick={() => insertOperator('not')}
|
|
||||||
disabled={!csvInput.trim()}
|
|
||||||
title="Exclude from selection"
|
|
||||||
>
|
|
||||||
<Text size="xs" fw={500}>not</Text>
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
import { Button, Text, Group } from '@mantine/core';
|
||||||
|
import classes from './BulkSelectionPanel.module.css';
|
||||||
|
|
||||||
|
interface OperatorsSectionProps {
|
||||||
|
csvInput: string;
|
||||||
|
onInsertOperator: (op: 'and' | 'or' | 'not') => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OperatorsSection = ({ csvInput, onInsertOperator }: OperatorsSectionProps) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Text size="xs" c="var(--text-muted)" fw={500} mb="xs">Add Operators:</Text>
|
||||||
|
<Group gap="sm" wrap="nowrap">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className={classes.operatorChip}
|
||||||
|
onClick={() => onInsertOperator('and')}
|
||||||
|
disabled={!csvInput.trim()}
|
||||||
|
title="Combine selections (both conditions must be true)"
|
||||||
|
>
|
||||||
|
<Text size="xs" fw={500}>and</Text>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className={classes.operatorChip}
|
||||||
|
onClick={() => onInsertOperator('or')}
|
||||||
|
disabled={!csvInput.trim()}
|
||||||
|
title="Add to selection (either condition can be true)"
|
||||||
|
>
|
||||||
|
<Text size="xs" fw={500}>or</Text>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className={classes.operatorChip}
|
||||||
|
onClick={() => onInsertOperator('not')}
|
||||||
|
disabled={!csvInput.trim()}
|
||||||
|
title="Exclude from selection"
|
||||||
|
>
|
||||||
|
<Text size="xs" fw={500}>not</Text>
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OperatorsSection;
|
@ -0,0 +1,105 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Button, Text, NumberInput, Group } from '@mantine/core';
|
||||||
|
import classes from './BulkSelectionPanel.module.css';
|
||||||
|
|
||||||
|
interface SelectPagesProps {
|
||||||
|
title: string;
|
||||||
|
placeholder: string;
|
||||||
|
onApply: (value: number) => void;
|
||||||
|
maxPages: number;
|
||||||
|
validationFn?: (value: number) => string | null;
|
||||||
|
isRange?: boolean;
|
||||||
|
rangeEndValue?: number | '';
|
||||||
|
onRangeEndChange?: (value: string | number) => void;
|
||||||
|
rangeEndPlaceholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelectPages = ({
|
||||||
|
title,
|
||||||
|
placeholder,
|
||||||
|
onApply,
|
||||||
|
maxPages,
|
||||||
|
validationFn,
|
||||||
|
isRange = false,
|
||||||
|
rangeEndValue,
|
||||||
|
onRangeEndChange,
|
||||||
|
rangeEndPlaceholder,
|
||||||
|
}: SelectPagesProps) => {
|
||||||
|
const [value, setValue] = useState<number | ''>('');
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleValueChange = (val: string | number) => {
|
||||||
|
const next = typeof val === 'number' ? val : '';
|
||||||
|
setValue(next);
|
||||||
|
|
||||||
|
if (validationFn && typeof next === 'number') {
|
||||||
|
setError(validationFn(next));
|
||||||
|
} else {
|
||||||
|
setError(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleApply = () => {
|
||||||
|
if (value === '' || typeof value !== 'number') return;
|
||||||
|
onApply(value);
|
||||||
|
setValue('');
|
||||||
|
setError(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDisabled = Boolean(error) || value === '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.advancedCard}>
|
||||||
|
<Text size="sm" fw={600} c="var(--text-secondary)" mb="xs">{title}</Text>
|
||||||
|
{error && (<Text size="xs" c="var(--text-brand-accent)" mb="xs">{error}</Text>)}
|
||||||
|
<div className={classes.inputGroup}>
|
||||||
|
<Group gap="sm" align="flex-end" wrap="nowrap">
|
||||||
|
{isRange ? (
|
||||||
|
<>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<NumberInput
|
||||||
|
size="sm"
|
||||||
|
value={value}
|
||||||
|
onChange={handleValueChange}
|
||||||
|
min={1}
|
||||||
|
placeholder={placeholder}
|
||||||
|
error={Boolean(error)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<NumberInput
|
||||||
|
size="sm"
|
||||||
|
value={rangeEndValue}
|
||||||
|
onChange={onRangeEndChange}
|
||||||
|
min={1}
|
||||||
|
placeholder={rangeEndPlaceholder}
|
||||||
|
error={Boolean(error)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<NumberInput
|
||||||
|
size="sm"
|
||||||
|
value={value}
|
||||||
|
onChange={handleValueChange}
|
||||||
|
min={1}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={classes.fullWidthInput}
|
||||||
|
error={Boolean(error)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
className={classes.applyButton}
|
||||||
|
onClick={handleApply}
|
||||||
|
disabled={isDisabled}
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectPages;
|
@ -59,6 +59,7 @@ export class DeletePagesCommand extends DOMCommand {
|
|||||||
private originalSelectedPages: number[] = [];
|
private originalSelectedPages: number[] = [];
|
||||||
private hasExecuted: boolean = false;
|
private hasExecuted: boolean = false;
|
||||||
private pageIdsToDelete: string[] = [];
|
private pageIdsToDelete: string[] = [];
|
||||||
|
private onAllPagesDeleted?: () => void;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private pagesToDelete: number[],
|
private pagesToDelete: number[],
|
||||||
@ -67,9 +68,11 @@ export class DeletePagesCommand extends DOMCommand {
|
|||||||
private setSelectedPages: (pages: number[]) => void,
|
private setSelectedPages: (pages: number[]) => void,
|
||||||
private getSplitPositions: () => Set<number>,
|
private getSplitPositions: () => Set<number>,
|
||||||
private setSplitPositions: (positions: Set<number>) => void,
|
private setSplitPositions: (positions: Set<number>) => void,
|
||||||
private getSelectedPages: () => number[]
|
private getSelectedPages: () => number[],
|
||||||
|
onAllPagesDeleted?: () => void
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
this.onAllPagesDeleted = onAllPagesDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(): void {
|
execute(): void {
|
||||||
@ -99,7 +102,13 @@ export class DeletePagesCommand extends DOMCommand {
|
|||||||
!this.pageIdsToDelete.includes(page.id)
|
!this.pageIdsToDelete.includes(page.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (remainingPages.length === 0) return; // Safety check
|
if (remainingPages.length === 0) {
|
||||||
|
// If all pages would be deleted, clear selection/splits and close PDF
|
||||||
|
this.setSelectedPages([]);
|
||||||
|
this.setSplitPositions(new Set());
|
||||||
|
this.onAllPagesDeleted?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Renumber remaining pages
|
// Renumber remaining pages
|
||||||
remainingPages.forEach((page, index) => {
|
remainingPages.forEach((page, index) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user