zoom fix + tag placement fix for process step two + expand and collapse tag

This commit is contained in:
Bhagwan Singh 2026-05-26 12:39:22 +05:30
parent 6efe737e50
commit 68c45678a3
11 changed files with 733 additions and 588 deletions

View File

@ -142,6 +142,22 @@ const fitObj = [
label: "200%",
value: "200"
},
{
label: "250%",
value: "250"
},
{
label: "300%",
value: "300"
},
{
label: "350%",
value: "350"
},
{
label: "400%",
value: "400"
},
]
enum RightSideNav {
@ -150,6 +166,7 @@ enum RightSideNav {
}
const SignDocument = () => {
const windowWidth = window.innerWidth;
const { recipientId } = useParams();
const dispatch = useDispatch();
@ -158,6 +175,9 @@ const SignDocument = () => {
// Signer data - START
const [selectedDocuments, setSelectedDocuments] = useState<DocumentType[] | null>(null);
const [imageSizes, setImageSizes] = useState<Record<string, { width: number; height: number }>>({});
const [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null);
const [isPages, setIsPages] = useState<boolean>(true);
@ -208,6 +228,55 @@ const SignDocument = () => {
const [provideTags, setProvideTags] = useState(false)
const [showCompletionScreen, setShowCompletionScreen] = useState(false)
const [zoomScale, setZoomScale] = useState<number>(100)
const scaleFactor: number = (zoomScale ?? 100) / 100;
// To center the container on every zoom scale change - START
useEffect(() => {
const container = document.getElementById("doc-con");
if (!container) return;
const prevScrollRatio = container.scrollTop / (container.scrollHeight || 1);
const newScrollTop = prevScrollRatio * container.scrollHeight;
const newScrollLeft = (container.scrollWidth - container.clientWidth) / 2;
container.scrollTo({
top: newScrollTop,
left: newScrollLeft,
behavior: "instant" as ScrollBehavior
});
}, [zoomScale]);
// To center the container on every zoom scale change - END
// Handle Tags and Image Size - START
useEffect(() => {
const observers: ResizeObserver[] = [];
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 },
}));
}
});
observer.observe(el);
observers.push(observer);
});
return () => observers.forEach((o) => o.disconnect());
}, [selectedDocuments]); // re-run when documents load
// Handle Tags anf Image Size = END
const [saved, setSaved] = useState<SaveType>({
signatureId: null,
saveType: null,
@ -229,25 +298,31 @@ const SignDocument = () => {
const [signConfObj, setSignConfObj] = useState<SignConfTypeObj | {}>({});
const handleFitSelect = (indx: number) => {
setSelFitIndx(indx);
let value = fitObj[indx].value;
if (value === "FIT") value = "100";
setZoomScale?.(Number(value));
}
const zoom = (percentage: number, contentClass: string) => {
const content = document.querySelector(`.${contentClass}`);
if (content) {
(content as HTMLElement).style.zoom = `${percentage}%`;
}
}
// const zoom = (percentage: number, contentClass: string) => {
// const content = document.querySelector(`.${contentClass}`);
// if (content) {
// (content as HTMLElement).style.zoom = `${percentage}%`;
// }
// }
// useEffect(() => {
// let indx = fitObj[selFitIndx].value
// if (indx == "FIT") {
// indx = "100";
// }
// zoom(Number(indx), 'zoom-con')
// }, [selFitIndx])
const handleTagClick = (key: string, index: number) => {
setSelectedTag({ key, index })
}
useEffect(() => {
let indx = fitObj[selFitIndx].value
if (indx == "FIT") {
indx = "100";
}
zoom(Number(indx), 'zoom-con')
}, [selFitIndx])
useEffect(() => {
dispatch(showLoader("Loading ..."));
@ -894,7 +969,8 @@ const SignDocument = () => {
{fitObj.map((val, indx) => {
return (<div
key={indx}
onClick={() => setSelFitIndx(indx)}
// onClick={() => setSelFitIndx(indx)}
onClick={() => handleFitSelect(indx)}
className='flex z-50 items-center justify-between gap-2 w-[70px] p-2 text-xs cursor-default hover:bg-slate-100'>
<div
@ -1087,6 +1163,22 @@ const SignDocument = () => {
id="doc-con"
className={`zoom-con relative ${provideTags ? (isFields && selectedTag != null ? "col-span-8" : "col-span-10") : (isFields ? "col-span-12" : "col-span-10")} sm:px-4 bg-slate-100 h-full w-full overflow-auto scroll-smooth transition-all duration-200 pb-[4rem]`}
>
<div style={{
width: `${100 * scaleFactor}%`, // ← outer wrapper grows to make room
height: `${100 * scaleFactor}%`,
}}>
<div className="zoom-con"
style={{
transform: `scale(${scaleFactor})`,
transformOrigin: 'top left', // ← change to top left
width: `${100 / scaleFactor}%`,
height: scaleFactor < 1
? `${100 / scaleFactor}%` // zoomed out — expand to fill
: 'auto', // zoomed in — let content grow
}}>
{
selectedDocuments && selectedDocuments.length > 0 && selectedDocuments.map((doc) => {
return (
@ -1119,6 +1211,16 @@ const SignDocument = () => {
// style={{ "width": page.width, "height": page.height, "maxWidth": "100%", "maxHeight": "100%" }}
className={`w-[${page.width / 2}px] h-[${page.height / 2}px] sm:w-[${page.width}px] sm:h-[${page.height}px]`}
ref={(el) => (imageRefs.current[page.index] = el)}
onLoad={(e) => {
const el = e.currentTarget;
setImageSizes((prev) => ({
...prev,
[page.index]: {
width: el.offsetWidth,
height: el.offsetHeight,
},
}));
}}
draggable="false"
// onLoad={(imgElem) => {
// setPageScales((prev) => ({
@ -1137,10 +1239,11 @@ const SignDocument = () => {
{!hideTags && <>
<TagsCon
zoomScale={zoomScale}
pageWidth={page.width}
pageHeight={page.height}
renderedWidth={imageRefs.current[page.index]?.offsetWidth || 0}
renderedHeight={imageRefs.current[page.index]?.offsetHeight || 0}
renderedWidth={imageSizes[page.index]?.width || imageRefs.current[page.index]?.offsetWidth || 0}
renderedHeight={imageSizes[page.index]?.height || imageRefs.current[page.index]?.offsetHeight || 0}
currDocTags={currDocTags}
pageIdkey={key}
provideTags={provideTags}
@ -1221,6 +1324,8 @@ const SignDocument = () => {
</div>}
</div>
</div>
</div>
{provideTags && selectedTag && selectedTag != null && "key" in selectedTag && "index" in selectedTag &&
<div className={`col-span-2 ${provideTags ? "" : "order-first"} h-full ${!isFields ? "bg-white" : isFields && provideTags ? "bg-white" : ""} overflow-y-auto`}>
@ -1247,6 +1352,7 @@ const SignDocument = () => {
<div style={{ display: `${openSign ? 'block' : 'none'}` }}>
{recipient &&
<ClientSignCon
zoomScale={zoomScale}
recipient={recipient}
recipientId={recipientId as string}
setOpenSign={setOpenSign} saved={saved}

View File

@ -14,6 +14,7 @@ import axios from 'axios';
interface TagsConProps {
zoomScale: number,
pageWidth: number,
pageHeight: number,
renderedWidth: number,
@ -43,6 +44,7 @@ interface TagsConProps {
}
const TagsCon: React.FC<TagsConProps> = ({
zoomScale,
pageWidth,
pageHeight,
renderedWidth,
@ -71,60 +73,76 @@ const TagsCon: React.FC<TagsConProps> = ({
handleTagNavClick
}) => {
const scaleFactor: number = (zoomScale ?? 100) / 100;
const [dateValues, setDateValues] = useState<{ [key: string]: string }>({});
const [isDateOpen, setIsDateOpen] = useState<string | null>(null);
const handleTagMouseDown = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
setSelectedTag({ key, index })
setSelectedTag({ key, index });
event.preventDefault();
const scale = scaleFactor ?? 1; // zoom factor
let targetContainer = event?.currentTarget?.parentElement?.parentElement;
let targetElement = event?.currentTarget;
let targetElement = event?.currentTarget?.parentElement;
if (!targetContainer || !targetElement) return;
const containerRect = targetContainer.getBoundingClientRect();
const elementRect = targetElement.getBoundingClientRect();
// Calculate the initial offset (where the mouse was clicked inside the element)
const offsetX = event.clientX - elementRect.left;
const offsetY = event.clientY - elementRect.top;
const scaleX = renderedWidth && pageWidth ? renderedWidth / pageWidth : 1;
const scaleY = renderedHeight && pageHeight ? renderedHeight / pageHeight : 1;
const conScrollWidth = targetContainer?.scrollWidth;
const conScrollHeight = targetContainer?.scrollHeight;
// Click offset in screen pixels
const clickOffsetX = event.clientX - elementRect.left;
const clickOffsetY = event.clientY - elementRect.top;
let isGroupAttr = event.currentTarget.hasAttribute("group");
let groupId = null;
if (isGroupAttr) {
groupId = event.currentTarget.getAttribute("group");
}
if (isGroupAttr) groupId = event.currentTarget.getAttribute("group");
let groupElements: NodeListOf<HTMLElement> | null = null
let groupElements: NodeListOf<HTMLElement> | null = null;
if (isGroupAttr && groupId) {
groupElements = document.querySelectorAll(`div[group="${groupId}"]`);
}
const handleMouseMove = (event: MouseEvent) => {
// Get new X and Y positions relative to the container
let left = event.clientX - containerRect.left - offsetX;
let top = event.clientY - containerRect.top - offsetY;
const currentContainerRect = targetContainer!.getBoundingClientRect();
// Mouse position relative to container in screen pixels
const mouseX = event.clientX - currentContainerRect.left;
const mouseY = event.clientY - currentContainerRect.top;
// **Restrict movement within container bounds**
left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height));
// Convert to rendered pixel space (undo zoom)
const renderedLeft = (mouseX - clickOffsetX) / scale;
const renderedTop = (mouseY - clickOffsetY) / scale;
// Convert to original coord space for storage
const originalLeft = renderedLeft / scaleX;
const originalTop = renderedTop / scaleY;
if (groupElements != null && groupElements.length) handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY);
if (groupElements != null && groupElements.length) {
handleGroupBorderPosition(groupElements, currentContainerRect, clickOffsetX, clickOffsetY);
}
// Apply the movement to the element
// Update state in original coord space
setDroppedItems(
droppedItems.map((item, i) =>
i == index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item
i === index
? {
...item,
dimensions: {
...item.dimensions,
left: Math.max(0, originalLeft),
top: Math.max(0, originalTop),
},
update: true
}
: item
)
);
};
const handleMouseUp = () => {
@ -132,7 +150,6 @@ const TagsCon: React.FC<TagsConProps> = ({
document.removeEventListener("pointerup", handleMouseUp);
};
// Add event listeners
document.addEventListener("pointermove", handleMouseMove);
document.addEventListener("pointerup", handleMouseUp);
};
@ -308,9 +325,6 @@ const TagsCon: React.FC<TagsConProps> = ({
}
}
console.log(signConfObj, saved, "Line 305")
return (
<>
{currDocTags.map((item, index) => {

View File

@ -42,6 +42,9 @@ export const droppedTagsDefaultField: DroppedTagsDefaultFieldType = {
}
interface DocumentContainerProps {
isPages: boolean;
setIsPages: React.Dispatch<React.SetStateAction<boolean>>;
zoomScale: number;
transactionSaving?: boolean;
setTransactionSaving?: React.Dispatch<React.SetStateAction<boolean>>;
isSend?: boolean | null;
@ -62,10 +65,11 @@ export interface DocumentContainerRef {
handleTransactionProcess: (isSave?: boolean, isNavigate?: boolean, navigateTo?: string) => void;
}
const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProps>(
({ transactionSaving, setTransactionSaving, isSend, isPreview = false, aId = null, tId = null, selectedRecipientIndx = 0 }, ref) => {
({ isPages, zoomScale, transactionSaving, setTransactionSaving, isSend, isPreview = false, aId = null, tId = null, selectedRecipientIndx = 0, setIsPages }, ref) => {
const scaleFactor: number = (zoomScale ?? 100) / 100;
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
const dispatch = useDispatch();
const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>();
@ -93,12 +97,42 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
const tagUndoRef = useRef<DroppedItem[] | []>([]);
const imageRefs = useRef<{ [key: string]: HTMLImageElement | null }>({});
const [imageSizes, setImageSizes] = useState<Record<string, { width: number; height: number }>>({});
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
// handle Tag - Document Pos and size for Responsiveness - START
const [imageSizes, setImageSizes] = useState<Record<string, { width: number; height: number }>>({});
useEffect(() => {
const observers: ResizeObserver[] = [];
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 },
}));
}
});
observer.observe(el);
observers.push(observer);
});
return () => observers.forEach((o) => o.disconnect());
}, [selectedDocuments]);
// handle Tag - Document Pos and size for Responsiveness - START
// Redux state for dropped items
const transactionKey = `${accountId}_${transactionId}`;
const droppedItems = useSelector((state: RootState) => state.documentTags.items[transactionKey] || {});
@ -358,8 +392,11 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
clientY = event.clientY;
}
let x = clientX - dropZone.left - (dragElemData.width / 2);
let y = clientY - dropZone.top - (dragElemData.height / 2);
// let x = (clientX - dropZone.left) - (dragElemData.width / 2);
// let y = (clientY - dropZone.top) - (dragElemData.height / 2);
let x = (clientX - dropZone.left) / scaleFactor - (dragElemData.width / 2);
let y = (clientY - dropZone.top) / scaleFactor - (dragElemData.height / 2);
// Handling Drop Over Right
// if (dropZone.width < x + dragElemData.width) {
@ -378,8 +415,11 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
// y = 0;
// }
x = Math.max(0, Math.min(x, dropZone.width - dragElemData.width));
y = Math.max(0, Math.min(y, dropZone.height - dragElemData.height));
// x = Math.max(0, Math.min(x, dropZone.width - dragElemData.width));
// y = Math.max(0, Math.min(y, dropZone.height - dragElemData.height));
x = Math.max(0, Math.min(x, originalWidth - dragElemData.width));
y = Math.max(0, Math.min(y, originalHeight - dragElemData.height));
const key = `${docId}_${pageIndex}`;
@ -561,8 +601,21 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
return (
<>
{/* Center Doc Container - START */}
<div id="doc-con" className={`zoom-con col-span-8 bg-slate-100 h-full w-full overflow-auto scroll-smooth`}>
<div id="doc-con"
className={`sm:col-span-8 bg-slate-100 h-full w-full overflow-auto scroll-smooth`}>
<div style={{
width: `${100 * scaleFactor}%`, // ← outer wrapper grows to make room
height: `${100 * scaleFactor}%`,
}}>
<div className="zoom-con"
style={{
transform: `scale(${scaleFactor})`,
transformOrigin: 'top left', // ← change to top left
width: `${100 / scaleFactor}%`,
height: scaleFactor < 1
? `${100 / scaleFactor}%` // zoomed out — expand to fill
: 'auto', // zoomed in — let content grow
}}>
{
selectedDocuments.length > 0 ? selectedDocuments.map((doc, indxDoc) => {
return (
@ -620,6 +673,7 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
/>
<DroppedItemsTagsCon
scaleFactor={scaleFactor}
pageWidth={page.width}
pageHeight={page.height}
renderedWidth={imageSizes[key]?.width || imageRefs.current[key]?.offsetWidth || 0}
@ -667,8 +721,8 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
)
}) : <Loader />
}
</div>
</div>
</div>
@ -695,6 +749,8 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
</>
{selectedDocuments && selectedDocuments.length > 0 && <DocumentPages
isPages={isPages}
setIsPages={setIsPages}
droppedItems={droppedItems}
/>}

View File

@ -22,6 +22,7 @@ import { DroppedItem } from '../../../utlity/types';
import { setDroppedItems as setDroppedItemsAction } from '../../../store/slices/documentTagsSlice';
interface DroppedItemsTagsConProps {
scaleFactor: number,
pageWidth: number,
pageHeight: number,
renderedWidth: number,
@ -41,6 +42,7 @@ interface DroppedItemsTagsConProps {
// 👶 Child component with forwardRef
const DroppedItemsTagsCon = forwardRef(({
scaleFactor,
pageWidth,
pageHeight,
renderedWidth,
@ -163,109 +165,85 @@ const DroppedItemsTagsCon = forwardRef(({
handleRemoveTag
}));
// HANDLE TAG MOUSE DOWN - START
// Updated handleTagMouseDown function with proper group border positioning
const handleTagMouseDown = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
event.preventDefault();
const scale = scaleFactor ?? 1;
const scaleX = renderedWidth && pageWidth ? renderedWidth / pageWidth : 1;
const scaleY = renderedHeight && pageHeight ? renderedHeight / pageHeight : 1;
console.log("=== DRAG DEBUG ===");
console.log("scaleFactor (zoom):", scale);
console.log("renderedWidth:", renderedWidth, "pageWidth:", pageWidth);
console.log("renderedHeight:", renderedHeight, "pageHeight:", pageHeight);
console.log("scaleX (render ratio):", scaleX);
console.log("scaleY (render ratio):", scaleY);
console.log("combined scaleX:", scaleX * scale);
console.log("combined scaleY:", scaleY * scale);
let targetContainer = event?.currentTarget?.parentElement?.parentElement;
let targetElement = event?.currentTarget?.parentElement;
if (!targetContainer || !targetElement) return;
const containerRect = targetContainer.getBoundingClientRect();
// Get container rect in screen space
// const containerRect = targetContainer.getBoundingClientRect();
const elementRect = targetElement.getBoundingClientRect();
let clientX = event.clientX
let clientY = event.clientY
const containerRect = targetContainer.getBoundingClientRect();
if ((event as any).changedTouches?.[0]) {
clientX = (event as any).changedTouches[0].pageX;
clientY = (event as any).changedTouches[0].pageY;
}
console.log("containerRect:", containerRect.left, containerRect.top, containerRect.width, containerRect.height);
console.log("elementRect:", elementRect.left, elementRect.top, elementRect.width, elementRect.height);
console.log("mouse at:", event.clientX, event.clientY);
// Calculate the initial offset (where the mouse was clicked inside the element)
const offsetY = event.clientY - elementRect.top;
const offsetX = event.clientX - elementRect.left;
// Current tag position in original coord space (from state)
const currentItem = droppedItems[key][index];
const tagOriginalLeft = currentItem.dimensions.left;
const tagOriginalTop = currentItem.dimensions.top;
positionRef.current = { left: event.clientX - containerRect.left - offsetX, top: event.clientY - containerRect.top - offsetY };
// Tag's top-left in screen space
const tagScreenLeft = containerRect.left + tagOriginalLeft * scaleX * scale;
const tagScreenTop = containerRect.top + tagOriginalTop * scaleY * scale;
const conScrollWidth = targetContainer?.scrollWidth;
const conScrollHeight = targetContainer?.scrollHeight;
// Click offset within tag in screen pixels
const clickOffsetX = event.clientX - tagScreenLeft;
const clickOffsetY = event.clientY - tagScreenTop;
let isGroupAttr = event.currentTarget.hasAttribute("group");
let groupId = null;
if (isGroupAttr) {
groupId = event.currentTarget.getAttribute("group");
}
if (isGroupAttr) groupId = event.currentTarget.getAttribute("group");
let groupElements: NodeListOf<HTMLElement> | null = null;
if (isGroupAttr && groupId) {
groupElements = document.querySelectorAll(`div[group="${groupId}"]`);
// Show initial border position during drag start
handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY);
handleGroupBorderPosition(groupElements, containerRect, clickOffsetX, clickOffsetY);
}
// let timeOut = null
const handleMouseMove = (event: MouseEvent) => {
const currentContainerRect = targetContainer!.getBoundingClientRect();
// AUTO SCROLL ON DRAG - START
// const scrollContainer = document.getElementById("doc-con");
// Mouse relative to container in screen pixels
const mouseScreenX = event.clientX - currentContainerRect.left;
const mouseScreenY = event.clientY - currentContainerRect.top;
// const rect = scrollContainer.getBoundingClientRect();
// Subtract click offset to get tag's top-left in screen pixels
const tagScreenX = mouseScreenX - clickOffsetX;
const tagScreenY = mouseScreenY - clickOffsetY;
// const threshold = 80; // distance from edge
// const scrollSpeed = 15;
// Convert screen pixels to original coord space
// screen = original * scaleX * scale → original = screen / scaleX / scale
const originalLeft = tagScreenX / scale / scaleX;
const originalTop = tagScreenY / scale / scaleY;
// // mouse near bottom
// if (event.clientY > rect.bottom - threshold) {
// scrollContainer.scrollTop += scrollSpeed;
// }
positionRef.current = { left: originalLeft, top: originalTop };
// // mouse near top
// else if (event.clientY < rect.top + threshold) {
// scrollContainer.scrollTop -= scrollSpeed;
// }
// AUTO SCROLL ON DRAG - END
// Set style in rendered pixel space (original * scaleX)
// The parent transform:scale handles the rest
targetElement!.style.left = `${originalLeft * scaleX}px`;
targetElement!.style.top = `${originalTop * scaleY}px`;
// Get new X and Y positions relative to the container
let clientX = event.clientX
let clientY = event.clientY
if ((event as any).changedTouches?.[0]) {
clientX = (event as any).changedTouches[0].pageX;
clientY = (event as any).changedTouches[0].pageY;
}
let left = clientX - containerRect.left - offsetX;
let top = clientY - containerRect.top - offsetY;
// Restrict movement within container bounds
// left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
// top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height));
// Save in ref (fast, no re-render)
positionRef.current = { left, top };
// Update DOM directly
targetElement!.style.top = `${top}px`;
targetElement!.style.left = `${left}px`;
// Apply the movement to the element first
// setDroppedItems(prevState => ({
// ...prevState,
// [key]: Array.isArray(prevState[key])
// ? prevState[key].map((item, i) =>
// i == index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item
// )
// : [],
// }));
// Update group border position during drag if it's a group element
if (groupElements != null && groupElements.length) {
// Use a small timeout to ensure DOM is updated before recalculating borders
requestAnimationFrame(() => {
handleGroupBorderPosition(groupElements!, containerRect, offsetX, offsetY);
handleGroupBorderPosition(groupElements!, currentContainerRect, clickOffsetX, clickOffsetY);
});
}
};
@ -284,33 +262,24 @@ const DroppedItemsTagsCon = forwardRef(({
documentId: ""
}
// N: Adding pointer event to none get page not tag itselef from elementFromPoint
targetElement.style.pointerEvents = "none";
const el = document.elementFromPoint(
e.clientX,
e.clientY
) as HTMLElement | null;
const hoveredContainer = el?.closest(".drop-page-in")
const el = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null;
const hoveredContainer = el?.closest(".drop-page-in");
targetElement.style.pointerEvents = "auto";
// N: Adding pointer event to auto changing back the changes
if (hoveredContainer) {
const imgElem: HTMLElement | null = hoveredContainer.querySelector("[data-doc-img]");
hoveredDocId = imgElem ? imgElem.getAttribute("id") || "" : ""
hoveredDocId = imgElem ? imgElem.getAttribute("id") || "" : "";
hoveredDocId = hoveredDocId.split("-");
hoveredPageIndex = hoveredDocId[hoveredDocId.length - 1];
hoveredDocId.pop()
hoveredDocId.pop();
hoveredDocId = hoveredDocId.join("-");
}
if (hoveredContainer === null || hoveredDocId != currentDocumentId) { // NOT WORKING STILL MOVING THE TAG OUTSIDE THE COTEND DOCUMENT PAGE OVERFLOWING
targetElement!.style.top = `${droppedItems[key][index].dimensions.top}px`;
targetElement!.style.left = `${droppedItems[key][index].dimensions.left}px`;
if (hoveredContainer === null || hoveredDocId != currentDocumentId) {
// Revert
targetElement!.style.left = `${tagOriginalLeft * scaleX}px`;
targetElement!.style.top = `${tagOriginalTop * scaleY}px`;
document.removeEventListener("pointermove", handleMouseMove);
document.removeEventListener("pointerup", handleMouseUp);
@ -318,18 +287,11 @@ const DroppedItemsTagsCon = forwardRef(({
}
if (key in droppedItems && index in droppedItems[key]) {
// const tag = droppedItems[key][index];
const documentId = droppedItems[key][index]["documentId"];
const pageIndex = droppedItems[key][index]["pageIndex"];
// const targetElemTag = e.target.closest("[data-doc-id]")
tagDocumentUpdate.pageIndex = pageIndex
tagDocumentUpdate.documentId = documentId
// if (targetElemTag) {
// let hoveredDocId = targetElemTag.getAttribute("data-doc-id");
// if (hoveredDocId) {
tagDocumentUpdate.pageIndex = pageIndex;
tagDocumentUpdate.documentId = documentId;
if (hoveredDocId == documentId) {
if (hoveredPageIndex != pageIndex) {
@ -341,150 +303,66 @@ const DroppedItemsTagsCon = forwardRef(({
tagDocumentUpdate.updatingDocument = true;
tagDocumentUpdate.updatingPage = true;
tagDocumentUpdate.pageIndex = hoveredPageIndex;
tagDocumentUpdate.documentId = hoveredDocId
tagDocumentUpdate.documentId = hoveredDocId;
}
// }
// }
}
if (groupElements != null && groupElements.length && groupId) {
// Multiple approaches to ensure the positioning works:
// 1. Immediate update
setTimeout(() => {
handleIGroupBorderPosition(groupId);
}, 10);
// 2. Secondary update to ensure it's properly positioned
setTimeout(() => {
handleIGroupBorderPosition(groupId);
}, 100);
// 3. Final update using requestAnimationFrame for DOM sync
requestAnimationFrame(() => {
setTimeout(() => {
handleIGroupBorderPosition(groupId);
}, 50);
});
setTimeout(() => { handleIGroupBorderPosition(groupId); }, 10);
setTimeout(() => { handleIGroupBorderPosition(groupId); }, 100);
requestAnimationFrame(() => { setTimeout(() => { handleIGroupBorderPosition(groupId); }, 50); });
}
// const tempDroppedItems = (droppedItems: any) => ({
// ...droppedItems,
// [key]: Array.isArray(droppedItems[key])
// ? droppedItems[key].map((item, i) =>
// i == index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item
// )
// : [],
// })
// setDroppedItems(prevState => ({
// ...prevState,
// [key]: Array.isArray(prevState[key])
// ? prevState[key].map((item, i) =>
// i == index ? { ...item, documentId: tagDocumentUpdate.documentId, pageIndex: tagDocumentUpdate.pageIndex, dimensions: { ...item.dimensions, left, top }, update: true } : item
// )
// : [],
// }));
setDroppedItems(prevState => {
// check documentId match
const isSameDocument =
currentDocumentId == tagDocumentUpdate.documentId;
const isSameDocument = currentDocumentId == tagDocumentUpdate.documentId;
if (!isSameDocument) return prevState;
if (!isSameDocument) return prevState
// check pageIndex match
const isSamePage = currentPageIndex == tagDocumentUpdate.pageIndex;
const scaleX = renderedWidth && pageWidth ? renderedWidth / pageWidth : 1;
const scaleY = renderedHeight && pageHeight ? renderedHeight / pageHeight : 1;
// same document + same page
if (isSameDocument && isSamePage) {
left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height));
const normalizedLeft = left / scaleX;
const normalizedTop = top / scaleY;
const clampedLeft = Math.max(0, Math.min(left, pageWidth - currentItem.dimensions.width));
const clampedTop = Math.max(0, Math.min(top, pageHeight - currentItem.dimensions.height));
return {
...prevState,
[key]: Array.isArray(prevState[key])
? prevState[key].map((item, i) =>
i === index
? {
i === index ? {
...item,
documentId:
tagDocumentUpdate.documentId,
pageIndex:
tagDocumentUpdate.pageIndex,
dimensions: {
...item.dimensions,
left: normalizedLeft,
top: normalizedTop,
},
update: true,
}
: item
)
: [],
};
}
// left = left - containerRect.left
const newContainerRect = hoveredContainer?.getBoundingClientRect() as DOMRect;
left = e.clientX - newContainerRect.left - offsetX;
top = e.clientY - newContainerRect.top - offsetY;
left = Math.max(
0,
Math.min(left, newContainerRect.width - elementRect.width)
);
top = Math.max(
0,
Math.min(top, newContainerRect.height - elementRect.height)
);
// move to another page/document
const targetKey = `${tagDocumentUpdate.documentId}_${tagDocumentUpdate.pageIndex}`;
const currentItems = Array.isArray(prevState[key])
? [...prevState[key]]
: [];
const movedItem = currentItems[index];
if (!movedItem) return prevState;
// remove from old array
currentItems.splice(index, 1);
// updated item
const updatedItem = {
...movedItem,
documentId: tagDocumentUpdate.documentId,
pageIndex: tagDocumentUpdate.pageIndex,
dimensions: {
...movedItem.dimensions,
left,
top,
},
dimensions: { ...item.dimensions, left: clampedLeft, top: clampedTop },
update: true,
} : item
) : [],
};
}
// Cross page
const newContainerRect = hoveredContainer?.getBoundingClientRect() as DOMRect;
const newMouseScreenX = e.clientX - newContainerRect.left;
const newMouseScreenY = e.clientY - newContainerRect.top;
left = Math.max(0, Math.min(
(newMouseScreenX - clickOffsetX) / scale / scaleX,
pageWidth - currentItem.dimensions.width
));
top = Math.max(0, Math.min(
(newMouseScreenY - clickOffsetY) / scale / scaleY,
pageHeight - currentItem.dimensions.height
));
const targetKey = `${tagDocumentUpdate.documentId}_${tagDocumentUpdate.pageIndex}`;
const currentItems = Array.isArray(prevState[key]) ? [...prevState[key]] : [];
const movedItem = currentItems[index];
if (!movedItem) return prevState;
currentItems.splice(index, 1);
return {
...prevState,
// old container updated
[key]: currentItems,
// new container updated
[targetKey]: [
...(Array.isArray(prevState[targetKey])
? prevState[targetKey]
: []),
updatedItem,
...(Array.isArray(prevState[targetKey]) ? prevState[targetKey] : []),
{ ...movedItem, documentId: tagDocumentUpdate.documentId, pageIndex: tagDocumentUpdate.pageIndex, dimensions: { ...movedItem.dimensions, left, top }, update: true },
],
};
});
@ -493,15 +371,23 @@ const DroppedItemsTagsCon = forwardRef(({
document.removeEventListener("pointerup", handleMouseUp);
};
// Add event listeners
document.addEventListener("pointermove", handleMouseMove);
document.addEventListener("pointerup", handleMouseUp);
};
// HANDLE TAG MOUSE DOWN - END
// HANDLE EXPAND TAG - START
const handleExpandTag = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
event.preventDefault(); // Prevent text selection issues
event.preventDefault();
const scale = scaleFactor ?? 1;
const scaleX = renderedWidth && pageWidth ? renderedWidth / pageWidth : 1;
const scaleY = renderedHeight && pageHeight ? renderedHeight / pageHeight : 1;
// Combined scale: zoom * render ratio
const combinedScaleX = scaleX * scale;
const combinedScaleY = scaleY * scale;
const expandingElemIcon = event.currentTarget;
const targetElement = expandingElemIcon.nextElementSibling?.parentElement;
@ -514,49 +400,41 @@ const DroppedItemsTagsCon = forwardRef(({
const startX = event.clientX;
const startY = event.clientY;
const startWidth = elementRect.width;
const startHeight = elementRect.height;
const startWidth = elementRect.width / combinedScaleX;
const startHeight = elementRect.height / combinedScaleY;
const handleMouseMove = (event: MouseEvent) => {
const dropZone = targetContainer.getBoundingClientRect();
const deltaX = (event.clientX - startX) / combinedScaleX;
const deltaY = (event.clientY - startY) / combinedScaleY;
const dropZone: DOMRect = targetContainer?.getBoundingClientRect() as DOMRect;
const droppedElemRect: DOMRect = targetElement?.getBoundingClientRect() as DOMRect;
let newWidth = startWidth + deltaX;
let newHeight = startHeight + deltaY;
console.log(dropZone, "dropZone Line 309")
console.log(droppedElemRect, "rect Line 309")
if (newWidth > 400) newWidth = 400;
if (newHeight > 200) newHeight = 200;
if (newWidth < 20) newWidth = 20;
if (newHeight < 10) newHeight = 10;
let newWidth = startWidth + (event.clientX - startX);
let newHeight = startHeight + (event.clientY - startY);
const maxWidth = dropZone.width / combinedScaleX;
const maxHeight = dropZone.height / combinedScaleY;
const currentLeft = droppedItems[key][index].dimensions.left;
const currentTop = droppedItems[key][index].dimensions.top;
if (currentLeft + newWidth > maxWidth) newWidth = maxWidth - currentLeft;
if (currentTop + newHeight > maxHeight) newHeight = maxHeight - currentTop;
if (newHeight > 200 || newWidth > 400) {
if (newHeight > 200) {
newHeight = 200
}
if (newWidth > 400) {
newWidth = 400
}
}
if ((dropZone.right - (droppedElemRect.left + newWidth)) < 0) {
newWidth = startWidth
}
if ((dropZone.bottom - (droppedElemRect.bottom + newHeight)) < 0) {
newHeight = startHeight
}
// Apply the new width & height
setDroppedItems({
...droppedItems,
[key]: Array.isArray(droppedItems[key]) // Ensure it's an array
[key]: Array.isArray(droppedItems[key])
? droppedItems[key].map((item, i) =>
i == index ? { ...item, dimensions: { ...item.dimensions, width: newWidth, height: newHeight }, update: true } : item
i === index
? { ...item, dimensions: { ...item.dimensions, width: newWidth, height: newHeight }, update: true }
: item
)
: [], // Provide a fallback in case it's undefined or not an array
: [],
});
};
const handleMouseUp = () => {

View File

@ -308,7 +308,7 @@ const FieldList: React.FC<FieldListProps> = ({ color, recipientId, recipientIndx
// );
return (
<div className='flex flex-row sm:flex-wrap gap-2 justify-start items-start w-full overflow-x-auto'>
<div className='flex flex-row sm:flex-wrap gap-2 justify-start items-start overflow-x-auto w-[90vw] sm:w-full px-2'>
{tagsData.map((field, index) => {
const IconComponent = typeToIconMap[field.type] || <FaQuestionCircle />;

View File

@ -10,6 +10,7 @@ import { MdKeyboardArrowDown } from 'react-icons/md'
import { useParams } from 'react-router-dom';
import { fetchDocuments } from '../../../store/slices/documentSlice';
import { FaAngleUp } from 'react-icons/fa';
import { SlDocs } from "react-icons/sl";
interface LeftSideBarProps {
isFields: boolean;
@ -78,16 +79,16 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
}, [selectedRecipient])
if (selectedRecipient && selectedRecipient.type == recipientType.CC) {
return <div className='col-span-2 bg-white h-full p-4 overflow-y-auto'></div>;
return <div className='sm:col-span-2 bg-white h-full p-4 overflow-y-auto'></div>;
}
return (
<>
{/* Left Side Bar - START */}
<div className='col-span-2'>
<div className='sm:col-span-2'>
{
isFields && <div className={`sm:block p-4 absolute sm:top-[0] rounded-tr-md z-50 sm:relative sm:col-span-2 bg-white border-t-1 border-r-1 border-slate-300 sm:h-[calc(100vh-40px)] overflow-y-auto sm:w-auto ${isFields ? "h-[150px] bottom-0 overflow-x-hidden w-full" : "h-[calc(100vh-40px)]"} `}>
(isFields || window.innerWidth > 638) && <div className={`sm:block p-4 absolute sm:top-[0] rounded-tr-md z-50 sm:relative sm:col-span-2 bg-white border-t-1 border-r-1 border-slate-300 sm:h-[calc(100vh-40px)] overflow-y-auto sm:w-auto ${(isFields || window.innerWidth > 638) ? "h-[100px] bottom-0 overflow-x-hidden w-full" : "h-[calc(100vh-40px)]"} `}>
{/* <div
onClick={() => {
// handleSetIsPages(false)
@ -114,7 +115,7 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
</div> */}
{selectedRecipients.length > 0 && isFields && <>
{selectedRecipients.length > 0 && (isFields || window.innerWidth > 638) && <>
{/* Fields List and Recipeint - Start */}
{/* Side Bar showing Recipients */}
<div className='sm:relative sm:top-0 sm:left-0 fixed w-full'>
@ -177,7 +178,10 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
<div
id='tags-con-dash-left'
className='mt-2 w-full h-[400px] sm:h-auto'>
<FieldList color={selectedRecipient?.color || '#2d2d2d'} recipientId={selectedRecipient.id} recipientIndx={selectedRecipientIndx} />
<FieldList
color={selectedRecipient?.color || '#2d2d2d'}
recipientId={selectedRecipient.id}
recipientIndx={selectedRecipientIndx} />
</div>
</div>
@ -195,25 +199,26 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
{/* Left Side Pages */}
{<div
onClick={() => {
// handleSetIsPages(true)
handleSetIsFields(false)
handleSetIsPages(true)
// handleSetIsFields(false)
}}
className='sm:hidden fixed top-[8%] left-0 bg-[var(--primary)] z-50 flex items-center text-white rounded-tr-md rounded-br-md cursor-pointer opacity-50 hover:opacity-100 transition-all duration-300'>
<div className='py-2 pl-2 pr-1'>
<FaAngleUp className='rotate-90' />
<p className='flex flex-col justify-center items-center text-xs tracking-widest mt-1 pt-1 border-t-1 border-white'>
className='sm:hidden fixed top-[8%] left-0 bg-[var(--primary)] z-50 flex items-center text-white rounded-tr-sm rounded-br-sm cursor-pointer opacity-50 hover:opacity-100 transition-all duration-300'>
<div className='py-2 pl-2 pr-2'>
{/* <FaAngleUp className='rotate-90' /> */}
<SlDocs />
{/* <p className='flex flex-col justify-center items-center text-xs tracking-widest mt-1 pt-1 border-t-1 border-white'>
<span>P</span>
<span>A</span>
<span>G</span>
<span>E</span>
<span>S</span>
</p>
</p> */}
</div>
</div>}
{!isFields && <div
className='sm:hidden fixed bottom-0 left-0 rounded-tr-2xl rounded-tl-2xl bg-[var(--primary)] z-50 w-full flex justify-center text-white cursor-pointer opacity-50 hover:opacity-100 transition-all duration-300'
{(!isFields && window.innerWidth <= 638) && <div
className='sm:hidden fixed bottom-0 left-0 rounded-tr-sm rounded-tl-sm bg-[var(--primary)] z-50 w-full flex justify-center text-white cursor-pointer opacity-50 hover:opacity-100 transition-all duration-300'
onClick={() => {
// handleSetIsPages(false)
handleSetIsFields(true)

View File

@ -3,22 +3,18 @@ import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../../store/store'
import { FaAngleRight, FaCheck, FaChevronRight, FaPaste, FaRegCopy, FaSave, FaSignature } from 'react-icons/fa'
import { IoIosDocument, IoMdArrowRoundBack, IoMdHome } from 'react-icons/io'
import { LuRedo2, LuTableOfContents, LuUndo2 } from 'react-icons/lu'
import { MdKeyboardArrowDown, MdOutlineKeyboardBackspace } from 'react-icons/md'
import { RiResetRightLine } from 'react-icons/ri'
import { FaAngleRight, FaCheck, FaRegCopy, FaSignature } from 'react-icons/fa'
import { IoIosDocument, IoMdHome } from 'react-icons/io'
import { LuRedo2, LuUndo2 } from 'react-icons/lu'
import { MdKeyboardArrowDown } from 'react-icons/md'
import { fetchRecipients, recipientType, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'
import { fetchDocuments } from '../../../store/slices/documentSlice'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { Bounce, toast } from 'react-toastify'
import { FiChevronDown } from 'react-icons/fi'
import { useNavigate, useParams } from 'react-router-dom'
import RecipientsCon from '../processStepOne/RecipientsCon'
import MessageCon from '../processStepOne/MessageCon'
import { IoArrowBackSharp, IoCopy } from 'react-icons/io5'
import { IoArrowBackSharp } from 'react-icons/io5'
import { FaRegPaste } from 'react-icons/fa6'
import ConfirmSendModal from './TransactionSendConfirmationModal'
import { RxDividerVertical } from 'react-icons/rx'
import CloseConfirmationModal from './CloseCofirmationModal'
interface NavTwoProps {
@ -34,6 +30,7 @@ interface NavTwoProps {
aId?: string | null
tId?: string | null
onSendClick?: (isSave?: boolean, navigate?: boolean, navigateTo?: string) => void;
onZoomChange?: React.Dispatch<React.SetStateAction<number>>
}
@ -59,20 +56,37 @@ const fitObj = [
label: "200%",
value: "200"
},
{
label: "250%",
value: "250"
},
{
label: "300%",
value: "300"
},
{
label: "350%",
value: "350"
},
{
label: "400%",
value: "400"
},
]
const NavTwo: React.FC<NavTwoProps> = ({
transactionSaving = false,
isFields,
setIsFields,
isPages,
setIsPages,
isSend,
setIsSend,
// isFields,
// setIsFields,
// isPages,
// setIsPages,
// isSend,
// setIsSend,
isPreview = false,
aId = null,
tId = null,
onSendClick,
onZoomChange
}) => {
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
const dispatch = useDispatch();
@ -181,6 +195,17 @@ const NavTwo: React.FC<NavTwoProps> = ({
return transactionSendValidationObj;
}
// Remove the zoom() function and the useEffect that calls it
// Instead:
const handleFitSelect = (indx: number) => {
setSelFitIndx(indx);
let value = fitObj[indx].value;
if (value === "FIT") value = "100";
onZoomChange?.(Number(value));
}
function handleSend(isSignSend: boolean = false) {
// dispatch(showLoader("Loading ..."));
if (onSendClick === undefined) return;
@ -215,20 +240,20 @@ const NavTwo: React.FC<NavTwoProps> = ({
if (confirmSendModalOpen.confirm) handleSend()
}, [confirmSendModalOpen])
const zoom = (percentage: number, contentClass: string) => {
const content = document.querySelector(`.${contentClass}`);
if (content) {
(content as HTMLElement).style.zoom = `${percentage}%`;
}
}
// const zoom = (percentage: number, contentClass: string) => {
// const content = document.querySelector(`.${contentClass}`);
// if (content) {
// (content as HTMLElement).style.zoom = `${percentage}%`;
// }
// }
useEffect(() => {
let indx = fitObj[selFitIndx].value
if (indx == "FIT") {
indx = "100";
}
zoom(Number(indx), 'zoom-con')
}, [selFitIndx])
// useEffect(() => {
// let indx = fitObj[selFitIndx].value
// if (indx == "FIT") {
// indx = "100";
// }
// zoom(Number(indx), 'zoom-con')
// }, [selFitIndx])
useEffect(() => {
if (!transactionSaving) return;
@ -277,7 +302,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
className='hover:text-[var(--primary)] durartion-300 cursor-pointer'
onClick={() => setClosePage(true)}
>
<IoMdHome />
<IoMdHome className='text-[var(--primary)] sm:text-slate-600' />
{/* <img width={120} src="/sme-colored.png" alt="Sign Made Easy" /> */}
</button>
<button
@ -288,10 +313,10 @@ const NavTwo: React.FC<NavTwoProps> = ({
</button>
</div>
<div className='w-[1px] h-[28px] bg-slate-200'></div>
<div className='w-[0px] md:w-[1px] h-[28px] bg-slate-200'></div>
<div className='flex items-center gap-2 text-xs'>
<div className='hidden md:flex items-center gap-2 text-xs'>
<button
onClick={() => navigate(-1)}
className='underline font-thin cursor-pointer'>
@ -306,7 +331,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
<div
tabIndex={1}
onBlur={() => setIsFitDd(false)}
className='absolute gap-2 flex items-center left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] z-50'
className='hidden absolute gap-2 lg:flex items-center left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] z-50'
>
<div className='flex'>
@ -344,7 +369,8 @@ const NavTwo: React.FC<NavTwoProps> = ({
{fitObj.map((val, indx) => {
return (<div
key={indx}
onClick={() => setSelFitIndx(indx)}
// onClick={() => setSelFitIndx(indx)}
onClick={() => handleFitSelect(indx)}
className='flex z-50 items-center justify-between gap-2 w-[70px] p-2 text-xs cursor-default hover:bg-slate-100'>
<div

View File

@ -38,7 +38,7 @@ const ProcessStepTwo = () => {
const windowWidth = window.innerWidth;
const [isFields, setIsFields] = useState(windowWidth > 638 ? true : false);
const [isPages, setIsPages] = useState(false);
const [isPages, setIsPages] = useState(windowWidth > 638 ? true : false);
const [selectedRecipient, setSelectedRecipient] = useState(0);
@ -70,21 +70,13 @@ const ProcessStepTwo = () => {
const handleSetIsFields = (setTo: boolean) => {
if (setTo != undefined && setTo != isFields) {
setIsFields(setTo)
if (setTo == true) {
setIsFields(true)
setIsPages(false)
}
};
}
const handleSetIsPages = (setTo: boolean) => {
if (setTo != undefined && setTo != isPages) {
setIsPages(setTo)
if (setTo == true) {
setIsFields(false)
}
};
setIsPages(true)
}
const docRef = useRef<DocumentContainerRef>(null);
@ -93,13 +85,59 @@ const ProcessStepTwo = () => {
docRef.current?.handleTransactionProcess(isSave, isNavigate, navigateTo);
};
const [zoomScale, setZoomScale] = useState<number>(100); // percentage
// To center the container on every zoom scale change - START
useEffect(() => {
const container = document.getElementById("doc-con");
if (!container) return;
const prevScrollRatio = container.scrollTop / (container.scrollHeight || 1);
const newScrollTop = prevScrollRatio * container.scrollHeight;
const newScrollLeft = (container.scrollWidth - container.clientWidth) / 2;
container.scrollTo({
top: newScrollTop,
left: newScrollLeft,
behavior: "instant" as ScrollBehavior
});
}, [zoomScale]);
// To center the container on every zoom scale change - END
return (
<div className={`min-w-full relative h-screen bg-slate-100 overflow-hidden ${(globalLoader.isLoading) ? 'pointer-events-none' : ''}`}>
<NavTwo transactionSaving={transactionSaving} isFields={isFields} isPages={isPages} setIsPages={setIsPages} setIsFields={setIsFields} isSend={isSend} setIsSend={setIsSend} onSendClick={handleSendClick} />
<NavTwo
onZoomChange={setZoomScale}
transactionSaving={transactionSaving}
// isFields={isFields}
// isPages={isPages}
// setIsPages={setIsPages}
// setIsFields={setIsFields}
// isSend={isSend}
// setIsSend={setIsSend}
onSendClick={handleSendClick} />
<div className='sm:grid sm:grid-cols-12 sm:gap-1 mt-1 h-[calc(100vh-46px)] overflow-hidden'>
<LeftSideBar isFields={isFields} isPages={isPages} selectedRecipientIndx={selectedRecipient} setSelectedRecipient={setSelectedRecipient} handleSetIsFields={handleSetIsFields} handleSetIsPages={handleSetIsPages} />
<DocumentContainer ref={docRef} selectedRecipientIndx={selectedRecipient} transactionSaving={transactionSaving} setTransactionSaving={setTransactionSaving} isSend={isSend} setIsSend={setIsSend} />
<LeftSideBar
isFields={isFields}
isPages={isPages}
selectedRecipientIndx={selectedRecipient}
setSelectedRecipient={setSelectedRecipient}
handleSetIsFields={handleSetIsFields}
handleSetIsPages={handleSetIsPages}
/>
<DocumentContainer
isPages={isPages}
setIsPages={setIsPages}
ref={docRef}
zoomScale={zoomScale}
selectedRecipientIndx={selectedRecipient}
transactionSaving={transactionSaving}
setTransactionSaving={setTransactionSaving}
isSend={isSend}
setIsSend={setIsSend}
/>
</div>
{globalLoader.isLoading && <Loader />}

View File

@ -6,7 +6,11 @@ import { DroppedItemsObj } from '../DocumentContainer';
import { MdKeyboardArrowUp, MdMoreVert } from 'react-icons/md';
import { PiTagSimpleFill } from 'react-icons/pi';
const DocumentPages: React.FC<{ droppedItems: DroppedItemsObj }> = ({ droppedItems }) => {
const DocumentPages: React.FC<{
isPages: boolean;
droppedItems: DroppedItemsObj;
setIsPages: React.Dispatch<React.SetStateAction<boolean>>;
}> = ({ isPages, droppedItems, setIsPages }) => {
type RecipientPageTagInfo = {
color: string;
@ -132,8 +136,22 @@ const DocumentPages: React.FC<{ droppedItems: DroppedItemsObj }> = ({ droppedIte
}, [droppedItems]);
useEffect(() => {
setTimeout(() => {
if (!isPages) return
const docPagesCon = document.getElementById("document-pages");
if (docPagesCon) {
docPagesCon.focus();
}
}, 0)
}, [isPages]);
return (
<div className="col-span-2 flex flex-col w-full overflow-y-auto">
<div id='document-pages'
tabIndex={-1}
onBlur={() => window.innerWidth <= 638 && setIsPages(false)}
className={`h-[100vh] absolute ${isPages || window.innerWidth > 638 ? "" : "hidden"} top-0 left-0 sm:static max-w-max bg-slate-100 z-50 sm:col-span-2 flex flex-col w-full overflow-y-auto`}>
{selectedDocuments.map((document, indx) => (
<div key={indx} className="border-b border-slate-200 pb-3 mb-1">

View File

@ -49,6 +49,7 @@ interface Signature {
}
interface ClientSignConProps {
zoomScale: number,
setOpenSign?: (value: boolean) => void;
getAllSignatures: () => Promise<void>;
// editingSignature?: Signature | null;
@ -90,6 +91,7 @@ const fontsMap = [
const ClientSignCon: React.FC<ClientSignConProps> = ({
zoomScale,
recipient,
setOpenSign,
recipientId,
@ -98,7 +100,8 @@ const ClientSignCon: React.FC<ClientSignConProps> = ({
signTagId
}) => {
console.log("savedModal", recipient)
// const scaleFactor: number = (zoomScale ?? 100) / 100;
const [activeTab, setActiveTab] = useState<'SELECT' | 'DRAW' | 'UPLOAD' | 'SAVED'>('DRAW');
const [fullName, setFullName] = useState('');
const [initials, setInitials] = useState('');

View File

@ -9,6 +9,7 @@ export default defineConfig({
server: {
port: 5173,
allowedHosts: [
'smashing-albacore-engaging.ngrok-free.app',
'quality-friendly-goldfish.ngrok-free.app',
'officially-powerful-goat.ngrok-free.app',
'89f0-49-36-189-120.ngrok-free.app',