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%",
|
||||
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}
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
||||
|
|
|
|||
|
|
@ -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 />;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 />}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
||||
|
|
|
|||
|
|
@ -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('');
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Reference in New Issue