Last Meeting Changes - Document page change according to scroll
This commit is contained in:
parent
c8bba1d34f
commit
78f895bd2c
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { MdKeyboardArrowUp, MdMoreVert } from 'react-icons/md';
|
||||
import { DocumentType } from './SignDocument';
|
||||
import { DroppedItem } from '../../utlity/types';
|
||||
|
|
@ -9,25 +9,35 @@ interface DocumentPagesInterface {
|
|||
setDropDownTogglePage: React.Dispatch<React.SetStateAction<string>>;
|
||||
dropDownTogglePage: string;
|
||||
droppedItems: Record<string, DroppedItem[]> | null;
|
||||
isPages: boolean;
|
||||
setIsPages: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
activePage: string | null; // NEW
|
||||
onPageSelect: (key: string) => void;
|
||||
}
|
||||
|
||||
const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownTogglePage, droppedItems }: DocumentPagesInterface) => {
|
||||
const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownTogglePage, droppedItems, isPages, setIsPages, activePage, onPageSelect }: DocumentPagesInterface) => {
|
||||
|
||||
const [activePage, setActivePage] = useState<string | null>(
|
||||
selectedDocuments?.[0] ? `${selectedDocuments[0].documentId}_0` : null
|
||||
)
|
||||
// const [activePage, setActivePage] = useState<string | null>(
|
||||
// selectedDocuments?.[0] ? `${selectedDocuments[0].documentId}_0` : null
|
||||
// )
|
||||
|
||||
const [documentPagesTagsMapping, setDocumentPagesTagsMapping] = useState<Record<string, Record<number, [number, string]>>>({})
|
||||
|
||||
useEffect(() => {
|
||||
const documentId = selectedDocuments?.[0] ? `${selectedDocuments[0].documentId}_0` : null
|
||||
if (documentId === null) return;
|
||||
// useEffect(() => {
|
||||
// const documentId = selectedDocuments?.[0] ? `${selectedDocuments[0].documentId}_0` : null
|
||||
// if (documentId === null) return;
|
||||
|
||||
const key = 'pageDropDown' + 0;
|
||||
setDropDownTogglePage(dropDownTogglePage === key ? '' : key);
|
||||
setActivePage(`${documentId}_0`);
|
||||
window.location.hash = documentId + "0";
|
||||
}, [selectedDocuments])
|
||||
// const key = 'pageDropDown' + 0;
|
||||
// setDropDownTogglePage(dropDownTogglePage === key ? '' : key);
|
||||
// setActivePage(`${documentId}_0`);
|
||||
// window.location.hash = documentId + "0";
|
||||
// }, [selectedDocuments])
|
||||
|
||||
|
||||
// const goToPage = (docId: string, pageIndex: number) => {
|
||||
// const key = `${docId}_${pageIndex}`;
|
||||
// onPageSelect(key);
|
||||
// };
|
||||
|
||||
const [openDocs, setOpenDocs] = useState<Record<string, boolean>>(
|
||||
() =>
|
||||
|
|
@ -54,11 +64,18 @@ const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownToggl
|
|||
}, [selectedDocuments])
|
||||
|
||||
const goToPage = (documentId: string, pageIndex: number) => {
|
||||
console.log("boom Active Page:", activePage);
|
||||
const key = `${documentId}_${pageIndex}`;
|
||||
setActivePage(key);
|
||||
window.location.hash = documentId + "" + pageIndex;
|
||||
onPageSelect(key);
|
||||
}
|
||||
|
||||
const thumbnailRefs = useRef<{ [key: string]: HTMLDivElement | null }>({}); // NEW
|
||||
|
||||
useEffect(() => {
|
||||
if (!activePage) return;
|
||||
const el = thumbnailRefs.current[activePage];
|
||||
el?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}, [activePage]);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
|
|
@ -87,7 +104,9 @@ const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownToggl
|
|||
// const isOpen = dropDownTogglePage === 'pageDropDown' + indx;
|
||||
|
||||
return (
|
||||
<div key={indx} className="border-b border-slate-200">
|
||||
<div
|
||||
key={indx}
|
||||
className="border-b border-slate-200">
|
||||
|
||||
{/* Document Header */}
|
||||
<div
|
||||
|
|
@ -118,6 +137,7 @@ const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownToggl
|
|||
return (
|
||||
<div
|
||||
key={pageIndex}
|
||||
ref={(el) => { thumbnailRefs.current[key] = el; }} // NEW
|
||||
onClick={() => goToPage(document.documentId, pageIndex)}
|
||||
className="flex flex-col items-center py-2 px-1 rounded-md cursor-pointer hover:bg-slate-50 transition-colors"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -172,6 +172,8 @@ enum RightSideNav {
|
|||
PAGES = "pages"
|
||||
}
|
||||
|
||||
let selectedDoc: { [key: string]: string | null } = { key: null }
|
||||
|
||||
const SignDocument = () => {
|
||||
|
||||
const windowWidth = window.innerWidth;
|
||||
|
|
@ -179,6 +181,7 @@ const SignDocument = () => {
|
|||
const dispatch = useDispatch();
|
||||
|
||||
const imageRefs = useRef<{ [key: string]: HTMLImageElement | null }>({});
|
||||
const pageRefs = useRef<{ [key: string]: HTMLDivElement | null }>({});
|
||||
|
||||
// Signer data - START
|
||||
const [selectedDocuments, setSelectedDocuments] = useState<DocumentType[] | null>(null);
|
||||
|
|
@ -187,6 +190,8 @@ const SignDocument = () => {
|
|||
|
||||
const [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null);
|
||||
const [isPages, setIsPages] = useState<boolean>(true);
|
||||
const [activePage, setActivePage] = useState<string | null>(null);
|
||||
const isProgrammaticScrollRef = useRef(false);
|
||||
|
||||
const [recipient, setRecipient] = useState<RecipientType | null>(null);
|
||||
const [tagJson, setTagJson] = useState<DroppedItem[] | []>([]);
|
||||
|
|
@ -252,6 +257,56 @@ const SignDocument = () => {
|
|||
};
|
||||
// Handle Confirm Sign Modal - END
|
||||
|
||||
// Sync activePage with scroll position via IntersectionObserver
|
||||
useEffect(() => {
|
||||
const scrollRoot = document.getElementById('doc-con');
|
||||
if (!scrollRoot) return;
|
||||
|
||||
// Track intersection ratio per page so we can pick the most-visible one
|
||||
// when multiple pages are partially visible at once.
|
||||
const ratios: Record<string, number> = {};
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
// Ignore intersection changes caused by our own programmatic
|
||||
// scroll (e.g. clicking a thumbnail) to avoid feedback loops.
|
||||
if (isProgrammaticScrollRef.current) return;
|
||||
|
||||
entries.forEach((entry) => {
|
||||
const key = entry.target.getAttribute('data-page-key');
|
||||
if (!key) return;
|
||||
ratios[key] = entry.isIntersecting ? entry.intersectionRatio : 0;
|
||||
});
|
||||
|
||||
// Pick the key with the highest intersection ratio
|
||||
let bestKey: string | null = null;
|
||||
let bestRatio = 0;
|
||||
for (const [key, ratio] of Object.entries(ratios)) {
|
||||
if (ratio > bestRatio) {
|
||||
bestRatio = ratio;
|
||||
bestKey = key;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestKey && bestRatio > 0) {
|
||||
setActivePage((prev) => (prev === bestKey ? prev : bestKey)); // dedupe, avoid re-render
|
||||
}
|
||||
},
|
||||
{
|
||||
root: scrollRoot,
|
||||
// Multiple thresholds so ratio updates fire smoothly while
|
||||
// scrolling, without needing scroll-event listeners at all.
|
||||
threshold: [0, 0.25, 0.5, 0.75, 1],
|
||||
rootMargin: '0px',
|
||||
}
|
||||
);
|
||||
|
||||
Object.values(pageRefs.current).forEach((el) => {
|
||||
if (el) observer.observe(el);
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [selectedDocuments]);
|
||||
|
||||
// To center the container on every zoom scale change - START
|
||||
useEffect(() => {
|
||||
|
|
@ -272,30 +327,30 @@ const SignDocument = () => {
|
|||
// To center the container on every zoom scale change - END
|
||||
|
||||
// Handle Tags and Image Size - START
|
||||
useEffect(() => {
|
||||
const observers: ResizeObserver[] = [];
|
||||
// useEffect(() => {
|
||||
// const observers: ResizeObserver[] = [];
|
||||
|
||||
Object.keys(imageRefs.current).forEach((key) => {
|
||||
const el = imageRefs.current[key];
|
||||
if (!el) return;
|
||||
// Object.keys(imageRefs.current).forEach((key) => {
|
||||
// const el = imageRefs.current[key];
|
||||
// if (!el) return;
|
||||
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const { width, height } = entry.contentRect;
|
||||
setImageSizes((prev) => ({
|
||||
...prev,
|
||||
[key]: { width, height },
|
||||
}));
|
||||
}
|
||||
});
|
||||
// const observer = new ResizeObserver((entries) => {
|
||||
// for (const entry of entries) {
|
||||
// const { width, height } = entry.contentRect;
|
||||
// setImageSizes((prev) => ({
|
||||
// ...prev,
|
||||
// [key]: { width, height },
|
||||
// }));
|
||||
// }
|
||||
// });
|
||||
|
||||
observer.observe(el);
|
||||
observers.push(observer);
|
||||
});
|
||||
// observer.observe(el);
|
||||
// observers.push(observer);
|
||||
// });
|
||||
|
||||
return () => observers.forEach((o) => o.disconnect());
|
||||
// return () => observers.forEach((o) => o.disconnect());
|
||||
|
||||
}, [selectedDocuments]); // re-run when documents load
|
||||
// }, [selectedDocuments]); // re-run when documents load
|
||||
// Handle Tags anf Image Size = END
|
||||
|
||||
|
||||
|
|
@ -345,6 +400,16 @@ const SignDocument = () => {
|
|||
setSelectedTag({ key, index })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDocuments && selectedDocuments.length > 0 && selectedDocuments[0].pages?.length > 0) {
|
||||
const firstDoc = selectedDocuments[0];
|
||||
const firstPage = firstDoc.pages[0];
|
||||
if (!selectedDoc.key) {
|
||||
selectedDoc.key = `${firstDoc.documentId}_${firstPage.index}`;
|
||||
}
|
||||
}
|
||||
}, [selectedDocuments]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(showLoader("Loading ..."));
|
||||
|
|
@ -958,6 +1023,10 @@ const SignDocument = () => {
|
|||
// }
|
||||
// }
|
||||
|
||||
const handleDocumentFocus = (key: string) => {
|
||||
selectedDoc.key = key;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`w-full overflow-hidden relative ${(globalLoader.isLoading) ? 'pointer-events-none overflow-hidden' : ''}`}>
|
||||
|
|
@ -1200,7 +1269,10 @@ const SignDocument = () => {
|
|||
return (
|
||||
<div
|
||||
key={indx}
|
||||
onFocus={() => handleDocumentFocus(key)}
|
||||
id={doc.documentId + indx}
|
||||
data-page-key={key}
|
||||
ref={(el) => { pageRefs.current[key] = el; }}
|
||||
onDrop={(e) => handleDrop(e, doc.documentId, doc.documentId, page.index)}
|
||||
onDragOver={(e) => handleDragOver(e)}
|
||||
className='drop-page-in shadow-md relative overflow-hidden'
|
||||
|
|
@ -1383,10 +1455,24 @@ const SignDocument = () => {
|
|||
{!transparentTags && isFields && recipientId && provideTags && recipient?.type != "CC" && <FieldList color={"#8f87f1"} recipientId={recipientId} recipientIndx={0} acntId={"1"} />}
|
||||
|
||||
<DocumentPages
|
||||
isPages={isPages}
|
||||
setIsPages={setIsPages}
|
||||
|
||||
droppedItems={droppedItems}
|
||||
|
||||
selectedDocuments={selectedDocuments}
|
||||
setDropDownTogglePage={setDropDownTogglePage}
|
||||
dropDownTogglePage={dropDownTogglePage}
|
||||
droppedItems={droppedItems}
|
||||
|
||||
activePage={activePage}
|
||||
onPageSelect={(key: string) => { // NEW — replaces hash-based nav
|
||||
isProgrammaticScrollRef.current = true;
|
||||
setActivePage(key);
|
||||
pageRefs.current[key]?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
// Release the guard after the smooth scroll has had time to settle,
|
||||
// so the IntersectionObserver resumes driving activePage afterward.
|
||||
window.setTimeout(() => { isProgrammaticScrollRef.current = false; }, 600);
|
||||
}}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ const DocumentPages: React.FC<{
|
|||
|
||||
const thumbnailRefs = useRef<{ [key: string]: HTMLDivElement | null }>({}); // NEW
|
||||
|
||||
useEffect(() => { // NEW
|
||||
useEffect(() => {
|
||||
if (!activePage) return;
|
||||
const el = thumbnailRefs.current[activePage];
|
||||
el?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
|
|
@ -101,9 +101,6 @@ const DocumentPages: React.FC<{
|
|||
// // }, [droppedItems, selectedRecipientIndx])
|
||||
// }, [droppedItems])
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue