change for host

This commit is contained in:
2025-11-26 18:40:03 +01:00
parent baacb7cfac
commit abfe6c347a
6 changed files with 1835 additions and 26 deletions

View File

@@ -25,44 +25,73 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
}) => {
const [order, setOrder] = useState<number[]>([]);
const [draggingIndex, setDraggingIndex] = useState<number | null>(null);
const [dropIndex, setDropIndex] = useState<number | null>(null); // slot 0..order.length
useEffect(() => {
if (pageCount > 0) {
setOrder(Array.from({ length: pageCount }, (_, i) => i));
} else {
setOrder([]);
setDraggingIndex(null);
setDropIndex(null);
}
}, [pageCount]);
const handleDragStart = (index: number) => (e: React.DragEvent) => {
setDraggingIndex(index);
setDropIndex(index); // initial assumption: before itself
e.dataTransfer.effectAllowed = 'move';
};
const handleDragOver = (index: number) => (e: React.DragEvent) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
};
const handleDrop = (index: number) => (e: React.DragEvent) => {
e.preventDefault();
if (draggingIndex === null || draggingIndex === index) return;
setOrder((prev) => {
const updated = [...prev];
const [moved] = updated.splice(draggingIndex, 1);
updated.splice(index, 0, moved);
return updated;
});
setDraggingIndex(null);
// Firefox needs some data
e.dataTransfer.setData('text/plain', String(index));
};
const handleDragEnd = () => {
setDraggingIndex(null);
setDropIndex(null);
};
const handleCardDragOver = (cardIndex: number) => (e: React.DragEvent) => {
if (draggingIndex == null) return;
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();
const x = e.clientX - rect.left;
// left half => slot BEFORE this card
// right half => slot AFTER this card
const slot = x < rect.width / 2 ? cardIndex : cardIndex + 1;
setDropIndex(slot);
};
const handleEndSlotDragOver = (e: React.DragEvent) => {
if (draggingIndex == null) return;
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
setDropIndex(order.length); // slot at the very end
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
if (draggingIndex == null || dropIndex == null) return;
setOrder((prev) => {
const updated = [...prev];
const [moved] = updated.splice(draggingIndex, 1);
const adjustedSlot = dropIndex > draggingIndex ? dropIndex - 1 : dropIndex;
updated.splice(adjustedSlot, 0, moved);
return updated;
});
setDraggingIndex(null);
setDropIndex(null);
};
const handleDelete = (visualIndex: number) => () => {
setOrder((prev) => prev.filter((_, idx) => idx !== visualIndex));
setDraggingIndex(null);
setDropIndex(null);
};
const handleRotateClick = (pageIndex: number) => () => {
@@ -83,12 +112,21 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
);
}
const showLeftLine = (cardIndex: number) =>
dropIndex !== null && dropIndex === cardIndex && draggingIndex !== null;
const showRightLine = (cardIndex: number) =>
dropIndex !== null && dropIndex === cardIndex + 1 && draggingIndex !== null;
const showEndLine = () =>
dropIndex !== null && dropIndex === order.length && draggingIndex !== null;
return (
<div className="card">
<h2>Reorder / delete / rotate</h2>
<p>
Drag pages to reorder them. Use rotate and delete controls below each
thumbnail. All changes stay in memory until you export a new PDF.
Drag pages to reorder them. A vertical blue line shows where the page
will be inserted. Use rotate and delete controls below each thumbnail.
</p>
<div
@@ -96,8 +134,10 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
display: 'flex',
flexWrap: 'wrap',
gap: '0.5rem',
alignItems: 'flex-start',
marginBottom: '0.75rem',
}}
onDrop={handleDrop}
>
{order.map((pageIndex, visualIndex) => {
const thumb = thumbnails?.[pageIndex];
@@ -109,10 +149,10 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
key={`${pageIndex}-${visualIndex}`}
draggable
onDragStart={handleDragStart(visualIndex)}
onDragOver={handleDragOver(visualIndex)}
onDrop={handleDrop(visualIndex)}
onDragEnd={handleDragEnd}
onDragOver={handleCardDragOver(visualIndex)}
style={{
position: 'relative',
width: '130px',
padding: '0.4rem',
borderRadius: '0.5rem',
@@ -125,6 +165,36 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
cursor: 'grab',
}}
>
{/* left drop indicator */}
{showLeftLine(visualIndex) && (
<div
style={{
position: 'absolute',
left: '-4px',
top: '4px',
bottom: '4px',
width: '3px',
borderRadius: '999px',
background: '#2563eb',
}}
/>
)}
{/* right drop indicator */}
{showRightLine(visualIndex) && (
<div
style={{
position: 'absolute',
right: '-4px',
top: '4px',
bottom: '4px',
width: '3px',
borderRadius: '999px',
background: '#2563eb',
}}
/>
)}
{thumb ? (
<img
src={thumb}
@@ -194,6 +264,32 @@ const ReorderPanel: React.FC<ReorderPanelProps> = ({
</div>
);
})}
{/* invisible end slot to allow dropping after the last card */}
<div
onDragOver={handleEndSlotDragOver}
onDrop={handleDrop}
style={{
width: '20px',
height: '120px',
position: 'relative',
alignSelf: 'stretch',
}}
>
{showEndLine() && (
<div
style={{
position: 'absolute',
left: '8px',
top: '4px',
bottom: '4px',
width: '3px',
borderRadius: '999px',
background: '#2563eb',
}}
/>
)}
</div>
</div>
<div className="button-row">