mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 06:09:23 +00:00
Fix split display on zoom
This commit is contained in:
parent
5025e06289
commit
d8f34769aa
@ -8,10 +8,6 @@ import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
<<<<<<< HEAD
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
=======
|
||||
>>>>>>> origin
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -33,10 +29,6 @@ public class KeyPairCleanupService {
|
||||
private final KeyPersistenceService keyPersistenceService;
|
||||
private final ApplicationProperties.Security.Jwt jwtProperties;
|
||||
|
||||
<<<<<<< HEAD
|
||||
@Autowired
|
||||
=======
|
||||
>>>>>>> origin
|
||||
public KeyPairCleanupService(
|
||||
KeyPersistenceService keyPersistenceService,
|
||||
ApplicationProperties applicationProperties) {
|
||||
|
@ -1,17 +1,11 @@
|
||||
package stirling.software.proprietary.security.service;
|
||||
|
||||
import java.io.IOException;
|
||||
<<<<<<< HEAD
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
=======
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
>>>>>>> origin
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
@ -28,10 +22,6 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
<<<<<<< HEAD
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
=======
|
||||
>>>>>>> origin
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
@ -54,24 +44,10 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
public static final String KEY_SUFFIX = ".key";
|
||||
|
||||
private final ApplicationProperties.Security.Jwt jwtProperties;
|
||||
<<<<<<< HEAD
|
||||
private final CacheManager cacheManager;
|
||||
=======
|
||||
>>>>>>> origin
|
||||
private final Cache verifyingKeyCache;
|
||||
|
||||
private volatile JwtVerificationKey activeKey;
|
||||
|
||||
<<<<<<< HEAD
|
||||
@Autowired
|
||||
public KeyPersistenceService(
|
||||
ApplicationProperties applicationProperties, CacheManager cacheManager) {
|
||||
this.jwtProperties = applicationProperties.getSecurity().getJwt();
|
||||
this.cacheManager = cacheManager;
|
||||
this.verifyingKeyCache = cacheManager.getCache("verifyingKeys");
|
||||
}
|
||||
|
||||
=======
|
||||
public KeyPersistenceService(
|
||||
ApplicationProperties applicationProperties, CacheManager cacheManager) {
|
||||
this.jwtProperties = applicationProperties.getSecurity().getJwt();
|
||||
@ -106,7 +82,6 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
>>>>>>> origin
|
||||
@PostConstruct
|
||||
public void initializeKeystore() {
|
||||
if (!isKeystoreEnabled()) {
|
||||
@ -114,10 +89,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
}
|
||||
|
||||
try {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
moveKeysToBackup();
|
||||
>>>>>>> origin
|
||||
ensurePrivateKeyDirectoryExists();
|
||||
loadKeyPair();
|
||||
} catch (Exception e) {
|
||||
@ -214,11 +186,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
nativeCache.asMap().size());
|
||||
|
||||
return nativeCache.asMap().values().stream()
|
||||
<<<<<<< HEAD
|
||||
.filter(value -> value instanceof JwtVerificationKey)
|
||||
=======
|
||||
.filter(JwtVerificationKey.class::isInstance)
|
||||
>>>>>>> origin
|
||||
.map(value -> (JwtVerificationKey) value)
|
||||
.filter(
|
||||
key -> {
|
||||
@ -292,10 +260,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
@Override
|
||||
>>>>>>> origin
|
||||
public PublicKey decodePublicKey(String encodedKey)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
byte[] keyBytes = Base64.getDecoder().decode(encodedKey);
|
||||
|
@ -3,6 +3,7 @@ import { Box } from '@mantine/core';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||
import styles from './PageEditor.module.css';
|
||||
import { GRID_CONSTANTS } from './constants';
|
||||
|
||||
interface DragDropItem {
|
||||
id: string;
|
||||
@ -33,10 +34,7 @@ const DragDropGrid = <T extends DragDropItem>({
|
||||
|
||||
// Responsive grid configuration
|
||||
const [itemsPerRow, setItemsPerRow] = useState(4);
|
||||
const ITEM_WIDTH = 320; // 20rem (page width)
|
||||
const ITEM_GAP = 24; // 1.5rem gap between items
|
||||
const ITEM_HEIGHT = 340; // 20rem + gap
|
||||
const OVERSCAN = items.length > 1000 ? 8 : 4; // More overscan for large documents
|
||||
const OVERSCAN = items.length > 1000 ? GRID_CONSTANTS.OVERSCAN_LARGE : GRID_CONSTANTS.OVERSCAN_SMALL;
|
||||
|
||||
// Calculate items per row based on container width
|
||||
const calculateItemsPerRow = useCallback(() => {
|
||||
@ -45,6 +43,11 @@ const DragDropGrid = <T extends DragDropItem>({
|
||||
const containerWidth = containerRef.current.offsetWidth;
|
||||
if (containerWidth === 0) return 4; // Container not measured yet
|
||||
|
||||
// Convert rem to pixels for calculation
|
||||
const remToPx = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||
const ITEM_WIDTH = parseFloat(GRID_CONSTANTS.ITEM_WIDTH) * remToPx;
|
||||
const ITEM_GAP = parseFloat(GRID_CONSTANTS.ITEM_GAP) * remToPx;
|
||||
|
||||
// Calculate how many items fit: (width - gap) / (itemWidth + gap)
|
||||
const availableWidth = containerWidth - ITEM_GAP; // Account for first gap
|
||||
const itemWithGap = ITEM_WIDTH + ITEM_GAP;
|
||||
@ -82,7 +85,10 @@ const DragDropGrid = <T extends DragDropItem>({
|
||||
const rowVirtualizer = useVirtualizer({
|
||||
count: Math.ceil(items.length / itemsPerRow),
|
||||
getScrollElement: () => containerRef.current?.closest('[data-scrolling-container]') as Element,
|
||||
estimateSize: () => ITEM_HEIGHT,
|
||||
estimateSize: () => {
|
||||
const remToPx = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||
return parseFloat(GRID_CONSTANTS.ITEM_HEIGHT) * remToPx;
|
||||
},
|
||||
overscan: OVERSCAN,
|
||||
});
|
||||
|
||||
@ -124,7 +130,7 @@ const DragDropGrid = <T extends DragDropItem>({
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '1.5rem',
|
||||
gap: GRID_CONSTANTS.ITEM_GAP,
|
||||
justifyContent: 'flex-start',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
BulkPageBreakCommand,
|
||||
UndoManager
|
||||
} from './commands/pageCommands';
|
||||
import { GRID_CONSTANTS } from './constants';
|
||||
import { usePageDocument } from './hooks/usePageDocument';
|
||||
import { usePageEditorState } from './hooks/usePageEditorState';
|
||||
|
||||
@ -103,6 +104,9 @@ const PageEditor = ({
|
||||
|
||||
// Grid container ref for positioning split indicators
|
||||
const gridContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// State to trigger re-renders when container size changes
|
||||
const [containerDimensions, setContainerDimensions] = useState({ width: 0, height: 0 });
|
||||
|
||||
// Undo/Redo state
|
||||
const [canUndo, setCanUndo] = useState(false);
|
||||
@ -121,6 +125,28 @@ const PageEditor = ({
|
||||
updateUndoRedoState();
|
||||
}, [updateUndoRedoState]);
|
||||
|
||||
// Watch for container size changes to update split line positions
|
||||
useEffect(() => {
|
||||
const container = gridContainerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (entry) {
|
||||
setContainerDimensions({
|
||||
width: entry.contentRect.width,
|
||||
height: entry.contentRect.height
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
resizeObserver.observe(container);
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
// Interface functions for parent component
|
||||
const displayDocument = editedDocument || mergedPdfDocument;
|
||||
@ -576,18 +602,25 @@ const PageEditor = ({
|
||||
}}
|
||||
>
|
||||
{Array.from(splitPositions).map((position) => {
|
||||
// Calculate the split line position based on grid layout
|
||||
const ITEM_WIDTH = 320; // 20rem
|
||||
const ITEM_HEIGHT = 340; // 20rem + gap
|
||||
const ITEM_GAP = 24; // 1.5rem
|
||||
const ITEMS_PER_ROW = 4; // Default, could be dynamic
|
||||
|
||||
const row = Math.floor(position / ITEMS_PER_ROW);
|
||||
const col = position % ITEMS_PER_ROW;
|
||||
|
||||
// Position after the current item
|
||||
const leftPosition = (col + 1) * (ITEM_WIDTH + ITEM_GAP) - ITEM_GAP / 2;
|
||||
const topPosition = row * ITEM_HEIGHT + 100; // Offset for header controls
|
||||
// Match DragDropGrid's layout calculations exactly
|
||||
const containerWidth = containerDimensions.width;
|
||||
const remToPx = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||
const ITEM_WIDTH = parseFloat(GRID_CONSTANTS.ITEM_WIDTH) * remToPx;
|
||||
const ITEM_HEIGHT = parseFloat(GRID_CONSTANTS.ITEM_HEIGHT) * remToPx;
|
||||
const ITEM_GAP = parseFloat(GRID_CONSTANTS.ITEM_GAP) * remToPx;
|
||||
|
||||
// Calculate items per row using DragDropGrid's logic
|
||||
const availableWidth = containerWidth - ITEM_GAP; // Account for first gap
|
||||
const itemWithGap = ITEM_WIDTH + ITEM_GAP;
|
||||
const itemsPerRow = Math.max(1, Math.floor(availableWidth / itemWithGap));
|
||||
|
||||
// Calculate position within the grid (same as DragDropGrid)
|
||||
const row = Math.floor(position / itemsPerRow);
|
||||
const col = position % itemsPerRow;
|
||||
|
||||
// Position split line between pages (after the current page)
|
||||
const leftPosition = col * itemWithGap + ITEM_WIDTH + (ITEM_GAP / 2);
|
||||
const topPosition = row * ITEM_HEIGHT;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -597,7 +630,7 @@ const PageEditor = ({
|
||||
left: leftPosition,
|
||||
top: topPosition,
|
||||
width: '1px',
|
||||
height: '20rem', // Match item height
|
||||
height: GRID_CONSTANTS.ITEM_WIDTH, // Match item height
|
||||
borderLeft: '1px dashed #3b82f6'
|
||||
}}
|
||||
/>
|
||||
|
8
frontend/src/components/pageEditor/constants.ts
Normal file
8
frontend/src/components/pageEditor/constants.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// Shared constants for PageEditor grid layout
|
||||
export const GRID_CONSTANTS = {
|
||||
ITEM_WIDTH: '20rem', // page width
|
||||
ITEM_HEIGHT: '21.5rem', // 20rem + 1.5rem gap
|
||||
ITEM_GAP: '1.5rem', // gap between items
|
||||
OVERSCAN_SMALL: 4, // Overscan for normal documents
|
||||
OVERSCAN_LARGE: 8, // Overscan for large documents (>1000 pages)
|
||||
} as const;
|
Loading…
x
Reference in New Issue
Block a user