zoom fix + tag placement fix for process step two + expand and collapse tag
This commit is contained in:
parent
6efe737e50
commit
68c45678a3
|
|
@ -142,6 +142,22 @@ const fitObj = [
|
||||||
label: "200%",
|
label: "200%",
|
||||||
value: "200"
|
value: "200"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "250%",
|
||||||
|
value: "250"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "300%",
|
||||||
|
value: "300"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "350%",
|
||||||
|
value: "350"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "400%",
|
||||||
|
value: "400"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
enum RightSideNav {
|
enum RightSideNav {
|
||||||
|
|
@ -150,6 +166,7 @@ enum RightSideNav {
|
||||||
}
|
}
|
||||||
|
|
||||||
const SignDocument = () => {
|
const SignDocument = () => {
|
||||||
|
|
||||||
const windowWidth = window.innerWidth;
|
const windowWidth = window.innerWidth;
|
||||||
const { recipientId } = useParams();
|
const { recipientId } = useParams();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
@ -158,6 +175,9 @@ const SignDocument = () => {
|
||||||
|
|
||||||
// Signer data - START
|
// Signer data - START
|
||||||
const [selectedDocuments, setSelectedDocuments] = useState<DocumentType[] | null>(null);
|
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 [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null);
|
||||||
const [isPages, setIsPages] = useState<boolean>(true);
|
const [isPages, setIsPages] = useState<boolean>(true);
|
||||||
|
|
||||||
|
|
@ -208,6 +228,55 @@ const SignDocument = () => {
|
||||||
const [provideTags, setProvideTags] = useState(false)
|
const [provideTags, setProvideTags] = useState(false)
|
||||||
const [showCompletionScreen, setShowCompletionScreen] = 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>({
|
const [saved, setSaved] = useState<SaveType>({
|
||||||
signatureId: null,
|
signatureId: null,
|
||||||
saveType: null,
|
saveType: null,
|
||||||
|
|
@ -229,25 +298,31 @@ const SignDocument = () => {
|
||||||
|
|
||||||
const [signConfObj, setSignConfObj] = useState<SignConfTypeObj | {}>({});
|
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 zoom = (percentage: number, contentClass: string) => {
|
||||||
const content = document.querySelector(`.${contentClass}`);
|
// const content = document.querySelector(`.${contentClass}`);
|
||||||
if (content) {
|
// if (content) {
|
||||||
(content as HTMLElement).style.zoom = `${percentage}%`;
|
// (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) => {
|
const handleTagClick = (key: string, index: number) => {
|
||||||
setSelectedTag({ key, index })
|
setSelectedTag({ key, index })
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let indx = fitObj[selFitIndx].value
|
|
||||||
if (indx == "FIT") {
|
|
||||||
indx = "100";
|
|
||||||
}
|
|
||||||
zoom(Number(indx), 'zoom-con')
|
|
||||||
}, [selFitIndx])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(showLoader("Loading ..."));
|
dispatch(showLoader("Loading ..."));
|
||||||
|
|
@ -894,7 +969,8 @@ const SignDocument = () => {
|
||||||
{fitObj.map((val, indx) => {
|
{fitObj.map((val, indx) => {
|
||||||
return (<div
|
return (<div
|
||||||
key={indx}
|
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'>
|
className='flex z-50 items-center justify-between gap-2 w-[70px] p-2 text-xs cursor-default hover:bg-slate-100'>
|
||||||
<div
|
<div
|
||||||
|
|
||||||
|
|
@ -1087,6 +1163,22 @@ const SignDocument = () => {
|
||||||
id="doc-con"
|
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]`}
|
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) => {
|
selectedDocuments && selectedDocuments.length > 0 && selectedDocuments.map((doc) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -1119,6 +1211,16 @@ const SignDocument = () => {
|
||||||
// style={{ "width": page.width, "height": page.height, "maxWidth": "100%", "maxHeight": "100%" }}
|
// 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]`}
|
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)}
|
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"
|
draggable="false"
|
||||||
// onLoad={(imgElem) => {
|
// onLoad={(imgElem) => {
|
||||||
// setPageScales((prev) => ({
|
// setPageScales((prev) => ({
|
||||||
|
|
@ -1137,10 +1239,11 @@ const SignDocument = () => {
|
||||||
|
|
||||||
{!hideTags && <>
|
{!hideTags && <>
|
||||||
<TagsCon
|
<TagsCon
|
||||||
|
zoomScale={zoomScale}
|
||||||
pageWidth={page.width}
|
pageWidth={page.width}
|
||||||
pageHeight={page.height}
|
pageHeight={page.height}
|
||||||
renderedWidth={imageRefs.current[page.index]?.offsetWidth || 0}
|
renderedWidth={imageSizes[page.index]?.width || imageRefs.current[page.index]?.offsetWidth || 0}
|
||||||
renderedHeight={imageRefs.current[page.index]?.offsetHeight || 0}
|
renderedHeight={imageSizes[page.index]?.height || imageRefs.current[page.index]?.offsetHeight || 0}
|
||||||
currDocTags={currDocTags}
|
currDocTags={currDocTags}
|
||||||
pageIdkey={key}
|
pageIdkey={key}
|
||||||
provideTags={provideTags}
|
provideTags={provideTags}
|
||||||
|
|
@ -1221,6 +1324,8 @@ const SignDocument = () => {
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{provideTags && selectedTag && selectedTag != null && "key" in selectedTag && "index" in selectedTag &&
|
{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`}>
|
<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'}` }}>
|
<div style={{ display: `${openSign ? 'block' : 'none'}` }}>
|
||||||
{recipient &&
|
{recipient &&
|
||||||
<ClientSignCon
|
<ClientSignCon
|
||||||
|
zoomScale={zoomScale}
|
||||||
recipient={recipient}
|
recipient={recipient}
|
||||||
recipientId={recipientId as string}
|
recipientId={recipientId as string}
|
||||||
setOpenSign={setOpenSign} saved={saved}
|
setOpenSign={setOpenSign} saved={saved}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import axios from 'axios';
|
||||||
|
|
||||||
|
|
||||||
interface TagsConProps {
|
interface TagsConProps {
|
||||||
|
zoomScale: number,
|
||||||
pageWidth: number,
|
pageWidth: number,
|
||||||
pageHeight: number,
|
pageHeight: number,
|
||||||
renderedWidth: number,
|
renderedWidth: number,
|
||||||
|
|
@ -43,6 +44,7 @@ interface TagsConProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const TagsCon: React.FC<TagsConProps> = ({
|
const TagsCon: React.FC<TagsConProps> = ({
|
||||||
|
zoomScale,
|
||||||
pageWidth,
|
pageWidth,
|
||||||
pageHeight,
|
pageHeight,
|
||||||
renderedWidth,
|
renderedWidth,
|
||||||
|
|
@ -71,60 +73,76 @@ const TagsCon: React.FC<TagsConProps> = ({
|
||||||
handleTagNavClick
|
handleTagNavClick
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
|
const scaleFactor: number = (zoomScale ?? 100) / 100;
|
||||||
|
|
||||||
const [dateValues, setDateValues] = useState<{ [key: string]: string }>({});
|
const [dateValues, setDateValues] = useState<{ [key: string]: string }>({});
|
||||||
const [isDateOpen, setIsDateOpen] = useState<string | null>(null);
|
const [isDateOpen, setIsDateOpen] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleTagMouseDown = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
|
const handleTagMouseDown = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
|
||||||
setSelectedTag({ key, index })
|
setSelectedTag({ key, index });
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
const scale = scaleFactor ?? 1; // zoom factor
|
||||||
|
|
||||||
let targetContainer = event?.currentTarget?.parentElement?.parentElement;
|
let targetContainer = event?.currentTarget?.parentElement?.parentElement;
|
||||||
let targetElement = event?.currentTarget;
|
let targetElement = event?.currentTarget?.parentElement;
|
||||||
|
|
||||||
if (!targetContainer || !targetElement) return;
|
if (!targetContainer || !targetElement) return;
|
||||||
|
|
||||||
const containerRect = targetContainer.getBoundingClientRect();
|
const containerRect = targetContainer.getBoundingClientRect();
|
||||||
const elementRect = targetElement.getBoundingClientRect();
|
const elementRect = targetElement.getBoundingClientRect();
|
||||||
|
|
||||||
// Calculate the initial offset (where the mouse was clicked inside the element)
|
const scaleX = renderedWidth && pageWidth ? renderedWidth / pageWidth : 1;
|
||||||
const offsetX = event.clientX - elementRect.left;
|
const scaleY = renderedHeight && pageHeight ? renderedHeight / pageHeight : 1;
|
||||||
const offsetY = event.clientY - elementRect.top;
|
|
||||||
|
|
||||||
const conScrollWidth = targetContainer?.scrollWidth;
|
// Click offset in screen pixels
|
||||||
const conScrollHeight = targetContainer?.scrollHeight;
|
const clickOffsetX = event.clientX - elementRect.left;
|
||||||
|
const clickOffsetY = event.clientY - elementRect.top;
|
||||||
|
|
||||||
let isGroupAttr = event.currentTarget.hasAttribute("group");
|
let isGroupAttr = event.currentTarget.hasAttribute("group");
|
||||||
let groupId = null;
|
let groupId = null;
|
||||||
if (isGroupAttr) {
|
if (isGroupAttr) groupId = event.currentTarget.getAttribute("group");
|
||||||
groupId = event.currentTarget.getAttribute("group");
|
|
||||||
}
|
|
||||||
|
|
||||||
let groupElements: NodeListOf<HTMLElement> | null = null
|
let groupElements: NodeListOf<HTMLElement> | null = null;
|
||||||
if (isGroupAttr && groupId) {
|
if (isGroupAttr && groupId) {
|
||||||
groupElements = document.querySelectorAll(`div[group="${groupId}"]`);
|
groupElements = document.querySelectorAll(`div[group="${groupId}"]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseMove = (event: MouseEvent) => {
|
const handleMouseMove = (event: MouseEvent) => {
|
||||||
// Get new X and Y positions relative to the container
|
const currentContainerRect = targetContainer!.getBoundingClientRect();
|
||||||
let left = event.clientX - containerRect.left - offsetX;
|
|
||||||
let top = event.clientY - containerRect.top - offsetY;
|
|
||||||
|
|
||||||
|
// 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**
|
// Convert to rendered pixel space (undo zoom)
|
||||||
left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
|
const renderedLeft = (mouseX - clickOffsetX) / scale;
|
||||||
top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height));
|
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(
|
setDroppedItems(
|
||||||
droppedItems.map((item, i) =>
|
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 = () => {
|
const handleMouseUp = () => {
|
||||||
|
|
@ -132,7 +150,6 @@ const TagsCon: React.FC<TagsConProps> = ({
|
||||||
document.removeEventListener("pointerup", handleMouseUp);
|
document.removeEventListener("pointerup", handleMouseUp);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add event listeners
|
|
||||||
document.addEventListener("pointermove", handleMouseMove);
|
document.addEventListener("pointermove", handleMouseMove);
|
||||||
document.addEventListener("pointerup", handleMouseUp);
|
document.addEventListener("pointerup", handleMouseUp);
|
||||||
};
|
};
|
||||||
|
|
@ -308,9 +325,6 @@ const TagsCon: React.FC<TagsConProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
console.log(signConfObj, saved, "Line 305")
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{currDocTags.map((item, index) => {
|
{currDocTags.map((item, index) => {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ export const droppedTagsDefaultField: DroppedTagsDefaultFieldType = {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DocumentContainerProps {
|
interface DocumentContainerProps {
|
||||||
|
isPages: boolean;
|
||||||
|
setIsPages: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
zoomScale: number;
|
||||||
transactionSaving?: boolean;
|
transactionSaving?: boolean;
|
||||||
setTransactionSaving?: React.Dispatch<React.SetStateAction<boolean>>;
|
setTransactionSaving?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
isSend?: boolean | null;
|
isSend?: boolean | null;
|
||||||
|
|
@ -62,10 +65,11 @@ export interface DocumentContainerRef {
|
||||||
handleTransactionProcess: (isSave?: boolean, isNavigate?: boolean, navigateTo?: string) => void;
|
handleTransactionProcess: (isSave?: boolean, isNavigate?: boolean, navigateTo?: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProps>(
|
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;
|
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>();
|
const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>();
|
||||||
|
|
@ -93,12 +97,42 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
|
||||||
const tagUndoRef = useRef<DroppedItem[] | []>([]);
|
const tagUndoRef = useRef<DroppedItem[] | []>([]);
|
||||||
|
|
||||||
const imageRefs = useRef<{ [key: string]: HTMLImageElement | null }>({});
|
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 selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
|
||||||
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
|
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
|
||||||
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
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
|
// Redux state for dropped items
|
||||||
const transactionKey = `${accountId}_${transactionId}`;
|
const transactionKey = `${accountId}_${transactionId}`;
|
||||||
const droppedItems = useSelector((state: RootState) => state.documentTags.items[transactionKey] || {});
|
const droppedItems = useSelector((state: RootState) => state.documentTags.items[transactionKey] || {});
|
||||||
|
|
@ -358,8 +392,11 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
|
||||||
clientY = event.clientY;
|
clientY = event.clientY;
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = clientX - dropZone.left - (dragElemData.width / 2);
|
// let x = (clientX - dropZone.left) - (dragElemData.width / 2);
|
||||||
let y = clientY - dropZone.top - (dragElemData.height / 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
|
// Handling Drop Over Right
|
||||||
// if (dropZone.width < x + dragElemData.width) {
|
// if (dropZone.width < x + dragElemData.width) {
|
||||||
|
|
@ -378,8 +415,11 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
|
||||||
// y = 0;
|
// y = 0;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
x = Math.max(0, Math.min(x, dropZone.width - dragElemData.width));
|
// x = Math.max(0, Math.min(x, dropZone.width - dragElemData.width));
|
||||||
y = Math.max(0, Math.min(y, dropZone.height - dragElemData.height));
|
// 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}`;
|
const key = `${docId}_${pageIndex}`;
|
||||||
|
|
||||||
|
|
@ -561,8 +601,21 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Center Doc Container - START */}
|
{/* 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) => {
|
selectedDocuments.length > 0 ? selectedDocuments.map((doc, indxDoc) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -620,6 +673,7 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DroppedItemsTagsCon
|
<DroppedItemsTagsCon
|
||||||
|
scaleFactor={scaleFactor}
|
||||||
pageWidth={page.width}
|
pageWidth={page.width}
|
||||||
pageHeight={page.height}
|
pageHeight={page.height}
|
||||||
renderedWidth={imageSizes[key]?.width || imageRefs.current[key]?.offsetWidth || 0}
|
renderedWidth={imageSizes[key]?.width || imageRefs.current[key]?.offsetWidth || 0}
|
||||||
|
|
@ -667,8 +721,8 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
|
||||||
)
|
)
|
||||||
}) : <Loader />
|
}) : <Loader />
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -695,6 +749,8 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
|
||||||
</>
|
</>
|
||||||
|
|
||||||
{selectedDocuments && selectedDocuments.length > 0 && <DocumentPages
|
{selectedDocuments && selectedDocuments.length > 0 && <DocumentPages
|
||||||
|
isPages={isPages}
|
||||||
|
setIsPages={setIsPages}
|
||||||
droppedItems={droppedItems}
|
droppedItems={droppedItems}
|
||||||
/>}
|
/>}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { DroppedItem } from '../../../utlity/types';
|
||||||
import { setDroppedItems as setDroppedItemsAction } from '../../../store/slices/documentTagsSlice';
|
import { setDroppedItems as setDroppedItemsAction } from '../../../store/slices/documentTagsSlice';
|
||||||
|
|
||||||
interface DroppedItemsTagsConProps {
|
interface DroppedItemsTagsConProps {
|
||||||
|
scaleFactor: number,
|
||||||
pageWidth: number,
|
pageWidth: number,
|
||||||
pageHeight: number,
|
pageHeight: number,
|
||||||
renderedWidth: number,
|
renderedWidth: number,
|
||||||
|
|
@ -41,6 +42,7 @@ interface DroppedItemsTagsConProps {
|
||||||
|
|
||||||
// 👶 Child component with forwardRef
|
// 👶 Child component with forwardRef
|
||||||
const DroppedItemsTagsCon = forwardRef(({
|
const DroppedItemsTagsCon = forwardRef(({
|
||||||
|
scaleFactor,
|
||||||
pageWidth,
|
pageWidth,
|
||||||
pageHeight,
|
pageHeight,
|
||||||
renderedWidth,
|
renderedWidth,
|
||||||
|
|
@ -163,109 +165,85 @@ const DroppedItemsTagsCon = forwardRef(({
|
||||||
handleRemoveTag
|
handleRemoveTag
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// HANDLE TAG MOUSE DOWN - START
|
|
||||||
// Updated handleTagMouseDown function with proper group border positioning
|
|
||||||
const handleTagMouseDown = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
|
const handleTagMouseDown = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
|
||||||
event.preventDefault();
|
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 targetContainer = event?.currentTarget?.parentElement?.parentElement;
|
||||||
let targetElement = event?.currentTarget?.parentElement;
|
let targetElement = event?.currentTarget?.parentElement;
|
||||||
|
|
||||||
if (!targetContainer || !targetElement) return;
|
if (!targetContainer || !targetElement) return;
|
||||||
|
|
||||||
const containerRect = targetContainer.getBoundingClientRect();
|
// Get container rect in screen space
|
||||||
|
// const containerRect = targetContainer.getBoundingClientRect();
|
||||||
const elementRect = targetElement.getBoundingClientRect();
|
const elementRect = targetElement.getBoundingClientRect();
|
||||||
|
|
||||||
let clientX = event.clientX
|
const containerRect = targetContainer.getBoundingClientRect();
|
||||||
let clientY = event.clientY
|
|
||||||
|
|
||||||
if ((event as any).changedTouches?.[0]) {
|
console.log("containerRect:", containerRect.left, containerRect.top, containerRect.width, containerRect.height);
|
||||||
clientX = (event as any).changedTouches[0].pageX;
|
console.log("elementRect:", elementRect.left, elementRect.top, elementRect.width, elementRect.height);
|
||||||
clientY = (event as any).changedTouches[0].pageY;
|
console.log("mouse at:", event.clientX, event.clientY);
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the initial offset (where the mouse was clicked inside the element)
|
// Current tag position in original coord space (from state)
|
||||||
const offsetY = event.clientY - elementRect.top;
|
const currentItem = droppedItems[key][index];
|
||||||
const offsetX = event.clientX - elementRect.left;
|
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;
|
// Click offset within tag in screen pixels
|
||||||
const conScrollHeight = targetContainer?.scrollHeight;
|
const clickOffsetX = event.clientX - tagScreenLeft;
|
||||||
|
const clickOffsetY = event.clientY - tagScreenTop;
|
||||||
|
|
||||||
let isGroupAttr = event.currentTarget.hasAttribute("group");
|
let isGroupAttr = event.currentTarget.hasAttribute("group");
|
||||||
let groupId = null;
|
let groupId = null;
|
||||||
if (isGroupAttr) {
|
if (isGroupAttr) groupId = event.currentTarget.getAttribute("group");
|
||||||
groupId = event.currentTarget.getAttribute("group");
|
|
||||||
}
|
|
||||||
|
|
||||||
let groupElements: NodeListOf<HTMLElement> | null = null;
|
let groupElements: NodeListOf<HTMLElement> | null = null;
|
||||||
if (isGroupAttr && groupId) {
|
if (isGroupAttr && groupId) {
|
||||||
groupElements = document.querySelectorAll(`div[group="${groupId}"]`);
|
groupElements = document.querySelectorAll(`div[group="${groupId}"]`);
|
||||||
// Show initial border position during drag start
|
handleGroupBorderPosition(groupElements, containerRect, clickOffsetX, clickOffsetY);
|
||||||
handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// let timeOut = null
|
|
||||||
const handleMouseMove = (event: MouseEvent) => {
|
const handleMouseMove = (event: MouseEvent) => {
|
||||||
|
const currentContainerRect = targetContainer!.getBoundingClientRect();
|
||||||
|
|
||||||
// AUTO SCROLL ON DRAG - START
|
// Mouse relative to container in screen pixels
|
||||||
// const scrollContainer = document.getElementById("doc-con");
|
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
|
// Convert screen pixels to original coord space
|
||||||
// const scrollSpeed = 15;
|
// screen = original * scaleX * scale → original = screen / scaleX / scale
|
||||||
|
const originalLeft = tagScreenX / scale / scaleX;
|
||||||
|
const originalTop = tagScreenY / scale / scaleY;
|
||||||
|
|
||||||
// // mouse near bottom
|
positionRef.current = { left: originalLeft, top: originalTop };
|
||||||
// if (event.clientY > rect.bottom - threshold) {
|
|
||||||
// scrollContainer.scrollTop += scrollSpeed;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // mouse near top
|
// Set style in rendered pixel space (original * scaleX)
|
||||||
// else if (event.clientY < rect.top + threshold) {
|
// The parent transform:scale handles the rest
|
||||||
// scrollContainer.scrollTop -= scrollSpeed;
|
targetElement!.style.left = `${originalLeft * scaleX}px`;
|
||||||
// }
|
targetElement!.style.top = `${originalTop * scaleY}px`;
|
||||||
// AUTO SCROLL ON DRAG - END
|
|
||||||
|
|
||||||
// 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) {
|
if (groupElements != null && groupElements.length) {
|
||||||
// Use a small timeout to ensure DOM is updated before recalculating borders
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
handleGroupBorderPosition(groupElements!, containerRect, offsetX, offsetY);
|
handleGroupBorderPosition(groupElements!, currentContainerRect, clickOffsetX, clickOffsetY);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -284,33 +262,24 @@ const DroppedItemsTagsCon = forwardRef(({
|
||||||
documentId: ""
|
documentId: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// N: Adding pointer event to none get page not tag itselef from elementFromPoint
|
|
||||||
targetElement.style.pointerEvents = "none";
|
targetElement.style.pointerEvents = "none";
|
||||||
|
const el = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null;
|
||||||
const el = document.elementFromPoint(
|
const hoveredContainer = el?.closest(".drop-page-in");
|
||||||
e.clientX,
|
|
||||||
e.clientY
|
|
||||||
) as HTMLElement | null;
|
|
||||||
|
|
||||||
const hoveredContainer = el?.closest(".drop-page-in")
|
|
||||||
|
|
||||||
targetElement.style.pointerEvents = "auto";
|
targetElement.style.pointerEvents = "auto";
|
||||||
// N: Adding pointer event to auto changing back the changes
|
|
||||||
|
|
||||||
if (hoveredContainer) {
|
if (hoveredContainer) {
|
||||||
const imgElem: HTMLElement | null = hoveredContainer.querySelector("[data-doc-img]");
|
const imgElem: HTMLElement | null = hoveredContainer.querySelector("[data-doc-img]");
|
||||||
|
hoveredDocId = imgElem ? imgElem.getAttribute("id") || "" : "";
|
||||||
hoveredDocId = imgElem ? imgElem.getAttribute("id") || "" : ""
|
|
||||||
hoveredDocId = hoveredDocId.split("-");
|
hoveredDocId = hoveredDocId.split("-");
|
||||||
hoveredPageIndex = hoveredDocId[hoveredDocId.length - 1];
|
hoveredPageIndex = hoveredDocId[hoveredDocId.length - 1];
|
||||||
hoveredDocId.pop()
|
hoveredDocId.pop();
|
||||||
hoveredDocId = hoveredDocId.join("-");
|
hoveredDocId = hoveredDocId.join("-");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hoveredContainer === null || hoveredDocId != currentDocumentId) { // NOT WORKING STILL MOVING THE TAG OUTSIDE THE COTEND DOCUMENT PAGE OVERFLOWING
|
if (hoveredContainer === null || hoveredDocId != currentDocumentId) {
|
||||||
targetElement!.style.top = `${droppedItems[key][index].dimensions.top}px`;
|
// Revert
|
||||||
targetElement!.style.left = `${droppedItems[key][index].dimensions.left}px`;
|
targetElement!.style.left = `${tagOriginalLeft * scaleX}px`;
|
||||||
|
targetElement!.style.top = `${tagOriginalTop * scaleY}px`;
|
||||||
|
|
||||||
document.removeEventListener("pointermove", handleMouseMove);
|
document.removeEventListener("pointermove", handleMouseMove);
|
||||||
document.removeEventListener("pointerup", handleMouseUp);
|
document.removeEventListener("pointerup", handleMouseUp);
|
||||||
|
|
@ -318,18 +287,11 @@ const DroppedItemsTagsCon = forwardRef(({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key in droppedItems && index in droppedItems[key]) {
|
if (key in droppedItems && index in droppedItems[key]) {
|
||||||
// const tag = droppedItems[key][index];
|
|
||||||
const documentId = droppedItems[key][index]["documentId"];
|
const documentId = droppedItems[key][index]["documentId"];
|
||||||
const pageIndex = droppedItems[key][index]["pageIndex"];
|
const pageIndex = droppedItems[key][index]["pageIndex"];
|
||||||
// const targetElemTag = e.target.closest("[data-doc-id]")
|
|
||||||
|
|
||||||
tagDocumentUpdate.pageIndex = pageIndex
|
tagDocumentUpdate.pageIndex = pageIndex;
|
||||||
tagDocumentUpdate.documentId = documentId
|
tagDocumentUpdate.documentId = documentId;
|
||||||
|
|
||||||
|
|
||||||
// if (targetElemTag) {
|
|
||||||
// let hoveredDocId = targetElemTag.getAttribute("data-doc-id");
|
|
||||||
// if (hoveredDocId) {
|
|
||||||
|
|
||||||
if (hoveredDocId == documentId) {
|
if (hoveredDocId == documentId) {
|
||||||
if (hoveredPageIndex != pageIndex) {
|
if (hoveredPageIndex != pageIndex) {
|
||||||
|
|
@ -341,150 +303,66 @@ const DroppedItemsTagsCon = forwardRef(({
|
||||||
tagDocumentUpdate.updatingDocument = true;
|
tagDocumentUpdate.updatingDocument = true;
|
||||||
tagDocumentUpdate.updatingPage = true;
|
tagDocumentUpdate.updatingPage = true;
|
||||||
tagDocumentUpdate.pageIndex = hoveredPageIndex;
|
tagDocumentUpdate.pageIndex = hoveredPageIndex;
|
||||||
tagDocumentUpdate.documentId = hoveredDocId
|
tagDocumentUpdate.documentId = hoveredDocId;
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupElements != null && groupElements.length && groupId) {
|
if (groupElements != null && groupElements.length && groupId) {
|
||||||
// Multiple approaches to ensure the positioning works:
|
setTimeout(() => { handleIGroupBorderPosition(groupId); }, 10);
|
||||||
|
setTimeout(() => { handleIGroupBorderPosition(groupId); }, 100);
|
||||||
// 1. Immediate update
|
requestAnimationFrame(() => { setTimeout(() => { handleIGroupBorderPosition(groupId); }, 50); });
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 => {
|
setDroppedItems(prevState => {
|
||||||
// check documentId match
|
const isSameDocument = currentDocumentId == tagDocumentUpdate.documentId;
|
||||||
const isSameDocument =
|
if (!isSameDocument) return prevState;
|
||||||
currentDocumentId == tagDocumentUpdate.documentId;
|
|
||||||
|
|
||||||
if (!isSameDocument) return prevState
|
|
||||||
|
|
||||||
// check pageIndex match
|
|
||||||
const isSamePage = currentPageIndex == tagDocumentUpdate.pageIndex;
|
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) {
|
if (isSameDocument && isSamePage) {
|
||||||
left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
|
const clampedLeft = Math.max(0, Math.min(left, pageWidth - currentItem.dimensions.width));
|
||||||
top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height));
|
const clampedTop = Math.max(0, Math.min(top, pageHeight - currentItem.dimensions.height));
|
||||||
|
|
||||||
const normalizedLeft = left / scaleX;
|
|
||||||
const normalizedTop = top / scaleY;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...prevState,
|
...prevState,
|
||||||
[key]: Array.isArray(prevState[key])
|
[key]: Array.isArray(prevState[key])
|
||||||
? prevState[key].map((item, i) =>
|
? prevState[key].map((item, i) =>
|
||||||
i === index
|
i === index ? {
|
||||||
? {
|
|
||||||
...item,
|
...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,
|
documentId: tagDocumentUpdate.documentId,
|
||||||
pageIndex: tagDocumentUpdate.pageIndex,
|
pageIndex: tagDocumentUpdate.pageIndex,
|
||||||
dimensions: {
|
dimensions: { ...item.dimensions, left: clampedLeft, top: clampedTop },
|
||||||
...movedItem.dimensions,
|
|
||||||
left,
|
|
||||||
top,
|
|
||||||
},
|
|
||||||
update: true,
|
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 {
|
return {
|
||||||
...prevState,
|
...prevState,
|
||||||
|
|
||||||
// old container updated
|
|
||||||
[key]: currentItems,
|
[key]: currentItems,
|
||||||
|
|
||||||
// new container updated
|
|
||||||
[targetKey]: [
|
[targetKey]: [
|
||||||
...(Array.isArray(prevState[targetKey])
|
...(Array.isArray(prevState[targetKey]) ? prevState[targetKey] : []),
|
||||||
? prevState[targetKey]
|
{ ...movedItem, documentId: tagDocumentUpdate.documentId, pageIndex: tagDocumentUpdate.pageIndex, dimensions: { ...movedItem.dimensions, left, top }, update: true },
|
||||||
: []),
|
|
||||||
updatedItem,
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -493,15 +371,23 @@ const DroppedItemsTagsCon = forwardRef(({
|
||||||
document.removeEventListener("pointerup", handleMouseUp);
|
document.removeEventListener("pointerup", handleMouseUp);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add event listeners
|
|
||||||
document.addEventListener("pointermove", handleMouseMove);
|
document.addEventListener("pointermove", handleMouseMove);
|
||||||
document.addEventListener("pointerup", handleMouseUp);
|
document.addEventListener("pointerup", handleMouseUp);
|
||||||
};
|
};
|
||||||
|
|
||||||
// HANDLE TAG MOUSE DOWN - END
|
// HANDLE TAG MOUSE DOWN - END
|
||||||
|
|
||||||
// HANDLE EXPAND TAG - START
|
// HANDLE EXPAND TAG - START
|
||||||
const handleExpandTag = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
|
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 expandingElemIcon = event.currentTarget;
|
||||||
const targetElement = expandingElemIcon.nextElementSibling?.parentElement;
|
const targetElement = expandingElemIcon.nextElementSibling?.parentElement;
|
||||||
|
|
@ -514,49 +400,41 @@ const DroppedItemsTagsCon = forwardRef(({
|
||||||
const startX = event.clientX;
|
const startX = event.clientX;
|
||||||
const startY = event.clientY;
|
const startY = event.clientY;
|
||||||
|
|
||||||
const startWidth = elementRect.width;
|
const startWidth = elementRect.width / combinedScaleX;
|
||||||
const startHeight = elementRect.height;
|
const startHeight = elementRect.height / combinedScaleY;
|
||||||
|
|
||||||
const handleMouseMove = (event: MouseEvent) => {
|
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;
|
let newWidth = startWidth + deltaX;
|
||||||
const droppedElemRect: DOMRect = targetElement?.getBoundingClientRect() as DOMRect;
|
let newHeight = startHeight + deltaY;
|
||||||
|
|
||||||
console.log(dropZone, "dropZone Line 309")
|
if (newWidth > 400) newWidth = 400;
|
||||||
console.log(droppedElemRect, "rect Line 309")
|
if (newHeight > 200) newHeight = 200;
|
||||||
|
if (newWidth < 20) newWidth = 20;
|
||||||
|
if (newHeight < 10) newHeight = 10;
|
||||||
|
|
||||||
let newWidth = startWidth + (event.clientX - startX);
|
const maxWidth = dropZone.width / combinedScaleX;
|
||||||
let newHeight = startHeight + (event.clientY - startY);
|
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({
|
setDroppedItems({
|
||||||
...droppedItems,
|
...droppedItems,
|
||||||
[key]: Array.isArray(droppedItems[key]) // Ensure it's an array
|
[key]: Array.isArray(droppedItems[key])
|
||||||
? droppedItems[key].map((item, i) =>
|
? 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 = () => {
|
const handleMouseUp = () => {
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ const FieldList: React.FC<FieldListProps> = ({ color, recipientId, recipientIndx
|
||||||
// );
|
// );
|
||||||
|
|
||||||
return (
|
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) => {
|
{tagsData.map((field, index) => {
|
||||||
|
|
||||||
const IconComponent = typeToIconMap[field.type] || <FaQuestionCircle />;
|
const IconComponent = typeToIconMap[field.type] || <FaQuestionCircle />;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { MdKeyboardArrowDown } from 'react-icons/md'
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { fetchDocuments } from '../../../store/slices/documentSlice';
|
import { fetchDocuments } from '../../../store/slices/documentSlice';
|
||||||
import { FaAngleUp } from 'react-icons/fa';
|
import { FaAngleUp } from 'react-icons/fa';
|
||||||
|
import { SlDocs } from "react-icons/sl";
|
||||||
|
|
||||||
interface LeftSideBarProps {
|
interface LeftSideBarProps {
|
||||||
isFields: boolean;
|
isFields: boolean;
|
||||||
|
|
@ -78,16 +79,16 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
|
||||||
}, [selectedRecipient])
|
}, [selectedRecipient])
|
||||||
|
|
||||||
if (selectedRecipient && selectedRecipient.type == recipientType.CC) {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Left Side Bar - START */}
|
{/* 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
|
{/* <div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// handleSetIsPages(false)
|
// handleSetIsPages(false)
|
||||||
|
|
@ -114,7 +115,7 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
|
||||||
</div> */}
|
</div> */}
|
||||||
|
|
||||||
|
|
||||||
{selectedRecipients.length > 0 && isFields && <>
|
{selectedRecipients.length > 0 && (isFields || window.innerWidth > 638) && <>
|
||||||
{/* Fields List and Recipeint - Start */}
|
{/* Fields List and Recipeint - Start */}
|
||||||
{/* Side Bar showing Recipients */}
|
{/* Side Bar showing Recipients */}
|
||||||
<div className='sm:relative sm:top-0 sm:left-0 fixed w-full'>
|
<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
|
<div
|
||||||
id='tags-con-dash-left'
|
id='tags-con-dash-left'
|
||||||
className='mt-2 w-full h-[400px] sm:h-auto'>
|
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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -195,25 +199,26 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
|
||||||
{/* Left Side Pages */}
|
{/* Left Side Pages */}
|
||||||
{<div
|
{<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// handleSetIsPages(true)
|
handleSetIsPages(true)
|
||||||
handleSetIsFields(false)
|
// 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'>
|
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-1'>
|
<div className='py-2 pl-2 pr-2'>
|
||||||
<FaAngleUp className='rotate-90' />
|
{/* <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'>
|
<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>P</span>
|
||||||
<span>A</span>
|
<span>A</span>
|
||||||
<span>G</span>
|
<span>G</span>
|
||||||
<span>E</span>
|
<span>E</span>
|
||||||
<span>S</span>
|
<span>S</span>
|
||||||
</p>
|
</p> */}
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
|
|
||||||
{!isFields && <div
|
{(!isFields && window.innerWidth <= 638) && <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'
|
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={() => {
|
onClick={() => {
|
||||||
// handleSetIsPages(false)
|
// handleSetIsPages(false)
|
||||||
handleSetIsFields(true)
|
handleSetIsFields(true)
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,18 @@ import React, { useEffect, useState } from 'react'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { RootState } from '../../../store/store'
|
import { RootState } from '../../../store/store'
|
||||||
|
|
||||||
import { FaAngleRight, FaCheck, FaChevronRight, FaPaste, FaRegCopy, FaSave, FaSignature } from 'react-icons/fa'
|
import { FaAngleRight, FaCheck, FaRegCopy, FaSignature } from 'react-icons/fa'
|
||||||
import { IoIosDocument, IoMdArrowRoundBack, IoMdHome } from 'react-icons/io'
|
import { IoIosDocument, IoMdHome } from 'react-icons/io'
|
||||||
import { LuRedo2, LuTableOfContents, LuUndo2 } from 'react-icons/lu'
|
import { LuRedo2, LuUndo2 } from 'react-icons/lu'
|
||||||
import { MdKeyboardArrowDown, MdOutlineKeyboardBackspace } from 'react-icons/md'
|
import { MdKeyboardArrowDown } from 'react-icons/md'
|
||||||
import { RiResetRightLine } from 'react-icons/ri'
|
|
||||||
import { fetchRecipients, recipientType, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'
|
import { fetchRecipients, recipientType, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'
|
||||||
import { fetchDocuments } from '../../../store/slices/documentSlice'
|
import { fetchDocuments } from '../../../store/slices/documentSlice'
|
||||||
import { Link, useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import { Bounce, toast } from 'react-toastify'
|
|
||||||
import { FiChevronDown } from 'react-icons/fi'
|
|
||||||
import RecipientsCon from '../processStepOne/RecipientsCon'
|
import RecipientsCon from '../processStepOne/RecipientsCon'
|
||||||
import MessageCon from '../processStepOne/MessageCon'
|
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 { FaRegPaste } from 'react-icons/fa6'
|
||||||
import ConfirmSendModal from './TransactionSendConfirmationModal'
|
import ConfirmSendModal from './TransactionSendConfirmationModal'
|
||||||
import { RxDividerVertical } from 'react-icons/rx'
|
|
||||||
import CloseConfirmationModal from './CloseCofirmationModal'
|
import CloseConfirmationModal from './CloseCofirmationModal'
|
||||||
|
|
||||||
interface NavTwoProps {
|
interface NavTwoProps {
|
||||||
|
|
@ -34,6 +30,7 @@ interface NavTwoProps {
|
||||||
aId?: string | null
|
aId?: string | null
|
||||||
tId?: string | null
|
tId?: string | null
|
||||||
onSendClick?: (isSave?: boolean, navigate?: boolean, navigateTo?: string) => void;
|
onSendClick?: (isSave?: boolean, navigate?: boolean, navigateTo?: string) => void;
|
||||||
|
onZoomChange?: React.Dispatch<React.SetStateAction<number>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -59,20 +56,37 @@ const fitObj = [
|
||||||
label: "200%",
|
label: "200%",
|
||||||
value: "200"
|
value: "200"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "250%",
|
||||||
|
value: "250"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "300%",
|
||||||
|
value: "300"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "350%",
|
||||||
|
value: "350"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "400%",
|
||||||
|
value: "400"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const NavTwo: React.FC<NavTwoProps> = ({
|
const NavTwo: React.FC<NavTwoProps> = ({
|
||||||
transactionSaving = false,
|
transactionSaving = false,
|
||||||
isFields,
|
// isFields,
|
||||||
setIsFields,
|
// setIsFields,
|
||||||
isPages,
|
// isPages,
|
||||||
setIsPages,
|
// setIsPages,
|
||||||
isSend,
|
// isSend,
|
||||||
setIsSend,
|
// setIsSend,
|
||||||
isPreview = false,
|
isPreview = false,
|
||||||
aId = null,
|
aId = null,
|
||||||
tId = null,
|
tId = null,
|
||||||
onSendClick,
|
onSendClick,
|
||||||
|
onZoomChange
|
||||||
}) => {
|
}) => {
|
||||||
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
@ -181,6 +195,17 @@ const NavTwo: React.FC<NavTwoProps> = ({
|
||||||
return transactionSendValidationObj;
|
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) {
|
function handleSend(isSignSend: boolean = false) {
|
||||||
// dispatch(showLoader("Loading ..."));
|
// dispatch(showLoader("Loading ..."));
|
||||||
if (onSendClick === undefined) return;
|
if (onSendClick === undefined) return;
|
||||||
|
|
@ -215,20 +240,20 @@ const NavTwo: React.FC<NavTwoProps> = ({
|
||||||
if (confirmSendModalOpen.confirm) handleSend()
|
if (confirmSendModalOpen.confirm) handleSend()
|
||||||
}, [confirmSendModalOpen])
|
}, [confirmSendModalOpen])
|
||||||
|
|
||||||
const zoom = (percentage: number, contentClass: string) => {
|
// const zoom = (percentage: number, contentClass: string) => {
|
||||||
const content = document.querySelector(`.${contentClass}`);
|
// const content = document.querySelector(`.${contentClass}`);
|
||||||
if (content) {
|
// if (content) {
|
||||||
(content as HTMLElement).style.zoom = `${percentage}%`;
|
// (content as HTMLElement).style.zoom = `${percentage}%`;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
let indx = fitObj[selFitIndx].value
|
// let indx = fitObj[selFitIndx].value
|
||||||
if (indx == "FIT") {
|
// if (indx == "FIT") {
|
||||||
indx = "100";
|
// indx = "100";
|
||||||
}
|
// }
|
||||||
zoom(Number(indx), 'zoom-con')
|
// zoom(Number(indx), 'zoom-con')
|
||||||
}, [selFitIndx])
|
// }, [selFitIndx])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!transactionSaving) return;
|
if (!transactionSaving) return;
|
||||||
|
|
@ -277,7 +302,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
|
||||||
className='hover:text-[var(--primary)] durartion-300 cursor-pointer'
|
className='hover:text-[var(--primary)] durartion-300 cursor-pointer'
|
||||||
onClick={() => setClosePage(true)}
|
onClick={() => setClosePage(true)}
|
||||||
>
|
>
|
||||||
<IoMdHome />
|
<IoMdHome className='text-[var(--primary)] sm:text-slate-600' />
|
||||||
{/* <img width={120} src="/sme-colored.png" alt="Sign Made Easy" /> */}
|
{/* <img width={120} src="/sme-colored.png" alt="Sign Made Easy" /> */}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|
@ -288,10 +313,10 @@ const NavTwo: React.FC<NavTwoProps> = ({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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
|
<button
|
||||||
onClick={() => navigate(-1)}
|
onClick={() => navigate(-1)}
|
||||||
className='underline font-thin cursor-pointer'>
|
className='underline font-thin cursor-pointer'>
|
||||||
|
|
@ -306,7 +331,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
|
||||||
<div
|
<div
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
onBlur={() => setIsFitDd(false)}
|
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'>
|
<div className='flex'>
|
||||||
|
|
@ -344,7 +369,8 @@ const NavTwo: React.FC<NavTwoProps> = ({
|
||||||
{fitObj.map((val, indx) => {
|
{fitObj.map((val, indx) => {
|
||||||
return (<div
|
return (<div
|
||||||
key={indx}
|
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'>
|
className='flex z-50 items-center justify-between gap-2 w-[70px] p-2 text-xs cursor-default hover:bg-slate-100'>
|
||||||
<div
|
<div
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ const ProcessStepTwo = () => {
|
||||||
const windowWidth = window.innerWidth;
|
const windowWidth = window.innerWidth;
|
||||||
|
|
||||||
const [isFields, setIsFields] = useState(windowWidth > 638 ? true : false);
|
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);
|
const [selectedRecipient, setSelectedRecipient] = useState(0);
|
||||||
|
|
||||||
|
|
@ -70,21 +70,13 @@ const ProcessStepTwo = () => {
|
||||||
|
|
||||||
|
|
||||||
const handleSetIsFields = (setTo: boolean) => {
|
const handleSetIsFields = (setTo: boolean) => {
|
||||||
if (setTo != undefined && setTo != isFields) {
|
setIsFields(true)
|
||||||
setIsFields(setTo)
|
|
||||||
if (setTo == true) {
|
|
||||||
setIsPages(false)
|
setIsPages(false)
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSetIsPages = (setTo: boolean) => {
|
const handleSetIsPages = (setTo: boolean) => {
|
||||||
if (setTo != undefined && setTo != isPages) {
|
|
||||||
setIsPages(setTo)
|
|
||||||
if (setTo == true) {
|
|
||||||
setIsFields(false)
|
setIsFields(false)
|
||||||
}
|
setIsPages(true)
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const docRef = useRef<DocumentContainerRef>(null);
|
const docRef = useRef<DocumentContainerRef>(null);
|
||||||
|
|
@ -93,13 +85,59 @@ const ProcessStepTwo = () => {
|
||||||
docRef.current?.handleTransactionProcess(isSave, isNavigate, navigateTo);
|
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 (
|
return (
|
||||||
<div className={`min-w-full relative h-screen bg-slate-100 overflow-hidden ${(globalLoader.isLoading) ? 'pointer-events-none' : ''}`}>
|
<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'>
|
<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} />
|
<LeftSideBar
|
||||||
<DocumentContainer ref={docRef} selectedRecipientIndx={selectedRecipient} transactionSaving={transactionSaving} setTransactionSaving={setTransactionSaving} isSend={isSend} setIsSend={setIsSend} />
|
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>
|
</div>
|
||||||
|
|
||||||
{globalLoader.isLoading && <Loader />}
|
{globalLoader.isLoading && <Loader />}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,11 @@ import { DroppedItemsObj } from '../DocumentContainer';
|
||||||
import { MdKeyboardArrowUp, MdMoreVert } from 'react-icons/md';
|
import { MdKeyboardArrowUp, MdMoreVert } from 'react-icons/md';
|
||||||
import { PiTagSimpleFill } from 'react-icons/pi';
|
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 = {
|
type RecipientPageTagInfo = {
|
||||||
color: string;
|
color: string;
|
||||||
|
|
@ -132,8 +136,22 @@ const DocumentPages: React.FC<{ droppedItems: DroppedItemsObj }> = ({ droppedIte
|
||||||
|
|
||||||
}, [droppedItems]);
|
}, [droppedItems]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!isPages) return
|
||||||
|
const docPagesCon = document.getElementById("document-pages");
|
||||||
|
|
||||||
|
if (docPagesCon) {
|
||||||
|
docPagesCon.focus();
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
|
}, [isPages]);
|
||||||
|
|
||||||
return (
|
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) => (
|
{selectedDocuments.map((document, indx) => (
|
||||||
<div key={indx} className="border-b border-slate-200 pb-3 mb-1">
|
<div key={indx} className="border-b border-slate-200 pb-3 mb-1">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ interface Signature {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClientSignConProps {
|
interface ClientSignConProps {
|
||||||
|
zoomScale: number,
|
||||||
setOpenSign?: (value: boolean) => void;
|
setOpenSign?: (value: boolean) => void;
|
||||||
getAllSignatures: () => Promise<void>;
|
getAllSignatures: () => Promise<void>;
|
||||||
// editingSignature?: Signature | null;
|
// editingSignature?: Signature | null;
|
||||||
|
|
@ -90,6 +91,7 @@ const fontsMap = [
|
||||||
|
|
||||||
|
|
||||||
const ClientSignCon: React.FC<ClientSignConProps> = ({
|
const ClientSignCon: React.FC<ClientSignConProps> = ({
|
||||||
|
zoomScale,
|
||||||
recipient,
|
recipient,
|
||||||
setOpenSign,
|
setOpenSign,
|
||||||
recipientId,
|
recipientId,
|
||||||
|
|
@ -98,7 +100,8 @@ const ClientSignCon: React.FC<ClientSignConProps> = ({
|
||||||
signTagId
|
signTagId
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
console.log("savedModal", recipient)
|
// const scaleFactor: number = (zoomScale ?? 100) / 100;
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<'SELECT' | 'DRAW' | 'UPLOAD' | 'SAVED'>('DRAW');
|
const [activeTab, setActiveTab] = useState<'SELECT' | 'DRAW' | 'UPLOAD' | 'SAVED'>('DRAW');
|
||||||
const [fullName, setFullName] = useState('');
|
const [fullName, setFullName] = useState('');
|
||||||
const [initials, setInitials] = useState('');
|
const [initials, setInitials] = useState('');
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5173,
|
||||||
allowedHosts: [
|
allowedHosts: [
|
||||||
|
'smashing-albacore-engaging.ngrok-free.app',
|
||||||
'quality-friendly-goldfish.ngrok-free.app',
|
'quality-friendly-goldfish.ngrok-free.app',
|
||||||
'officially-powerful-goat.ngrok-free.app',
|
'officially-powerful-goat.ngrok-free.app',
|
||||||
'89f0-49-36-189-120.ngrok-free.app',
|
'89f0-49-36-189-120.ngrok-free.app',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue