forked from github/plane
fix: gantt chart block left drag flicker (#1854)
* fix: left drag flicker * fix: opposite side manual scroll
This commit is contained in:
parent
f73239be92
commit
f7a596c113
@ -65,9 +65,7 @@ export const CyclesListGanttChartView: FC<Props> = ({ cycles, mutateCycles }) =>
|
|||||||
if (newPayload.sort_order && payload.sort_order)
|
if (newPayload.sort_order && payload.sort_order)
|
||||||
newPayload.sort_order = payload.sort_order.newSortOrder;
|
newPayload.sort_order = payload.sort_order.newSortOrder;
|
||||||
|
|
||||||
cyclesService
|
cyclesService.patchCycle(workspaceSlug.toString(), cycle.project, cycle.id, newPayload, user);
|
||||||
.patchCycle(workspaceSlug.toString(), cycle.project, cycle.id, newPayload, user)
|
|
||||||
.finally(() => mutateCycles());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const blockFormat = (blocks: ICycle[]) =>
|
const blockFormat = (blocks: ICycle[]) =>
|
||||||
|
@ -31,7 +31,36 @@ export const ChartDraggable: React.FC<Props> = ({
|
|||||||
|
|
||||||
const { currentViewData } = useChart();
|
const { currentViewData } = useChart();
|
||||||
|
|
||||||
const handleDrag = (dragDirection: "left" | "right") => {
|
const checkScrollEnd = (e: MouseEvent): number => {
|
||||||
|
let delWidth = 0;
|
||||||
|
|
||||||
|
const scrollContainer = document.querySelector("#scroll-container") as HTMLElement;
|
||||||
|
const appSidebar = document.querySelector("#app-sidebar") as HTMLElement;
|
||||||
|
|
||||||
|
const posFromLeft = e.clientX;
|
||||||
|
// manually scroll to left if reached the left end while dragging
|
||||||
|
if (posFromLeft - appSidebar.clientWidth <= 70) {
|
||||||
|
if (e.movementX > 0) return 0;
|
||||||
|
|
||||||
|
delWidth = -5;
|
||||||
|
|
||||||
|
scrollContainer.scrollBy(delWidth, 0);
|
||||||
|
} else delWidth = e.movementX;
|
||||||
|
|
||||||
|
// manually scroll to right if reached the right end while dragging
|
||||||
|
const posFromRight = window.innerWidth - e.clientX;
|
||||||
|
if (posFromRight <= 70) {
|
||||||
|
if (e.movementX < 0) return 0;
|
||||||
|
|
||||||
|
delWidth = 5;
|
||||||
|
|
||||||
|
scrollContainer.scrollBy(delWidth, 0);
|
||||||
|
} else delWidth = e.movementX;
|
||||||
|
|
||||||
|
return delWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLeftDrag = () => {
|
||||||
if (!currentViewData || !resizableRef.current || !parentDivRef.current || !block.position)
|
if (!currentViewData || !resizableRef.current || !parentDivRef.current || !block.position)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -44,54 +73,30 @@ export const ChartDraggable: React.FC<Props> = ({
|
|||||||
resizableDiv.clientWidth ?? parseInt(block.position.width.toString(), 10);
|
resizableDiv.clientWidth ?? parseInt(block.position.width.toString(), 10);
|
||||||
|
|
||||||
let initialWidth = resizableDiv.clientWidth ?? parseInt(block.position.width.toString(), 10);
|
let initialWidth = resizableDiv.clientWidth ?? parseInt(block.position.width.toString(), 10);
|
||||||
let initialMarginLeft = block?.position?.marginLeft;
|
let initialMarginLeft = parseInt(parentDiv.style.marginLeft);
|
||||||
|
|
||||||
const handleMouseMove = (e: MouseEvent) => {
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
if (!window) return;
|
if (!window) return;
|
||||||
|
|
||||||
let delWidth = 0;
|
let delWidth = 0;
|
||||||
|
|
||||||
const posFromLeft = e.clientX;
|
delWidth = checkScrollEnd(e);
|
||||||
const posFromRight = window.innerWidth - e.clientX;
|
|
||||||
|
|
||||||
const scrollContainer = document.querySelector("#scroll-container") as HTMLElement;
|
// calculate new width and update the initialMarginLeft using -=
|
||||||
const appSidebar = document.querySelector("#app-sidebar") as HTMLElement;
|
const newWidth = Math.round((initialWidth -= delWidth) / columnWidth) * columnWidth;
|
||||||
|
// calculate new marginLeft and update the initial marginLeft to the newly calculated one
|
||||||
// manually scroll to left if reached the left end while dragging
|
const newMarginLeft = initialMarginLeft - (newWidth - (block.position?.width ?? 0));
|
||||||
if (posFromLeft - appSidebar.clientWidth <= 70) {
|
initialMarginLeft = newMarginLeft;
|
||||||
if (e.movementX > 0) return;
|
|
||||||
|
|
||||||
delWidth = dragDirection === "left" ? -5 : 5;
|
|
||||||
|
|
||||||
scrollContainer.scrollBy(-1 * Math.abs(delWidth), 0);
|
|
||||||
} else delWidth = dragDirection === "left" ? -1 * e.movementX : e.movementX;
|
|
||||||
|
|
||||||
// manually scroll to right if reached the right end while dragging
|
|
||||||
if (posFromRight <= 70) {
|
|
||||||
if (e.movementX < 0) return;
|
|
||||||
|
|
||||||
delWidth = dragDirection === "left" ? -5 : 5;
|
|
||||||
|
|
||||||
scrollContainer.scrollBy(Math.abs(delWidth), 0);
|
|
||||||
} else delWidth = dragDirection === "left" ? -1 * e.movementX : e.movementX;
|
|
||||||
|
|
||||||
// calculate new width and update the initialMarginLeft using +=
|
|
||||||
const newWidth = Math.round((initialWidth += delWidth) / columnWidth) * columnWidth;
|
|
||||||
|
|
||||||
// block needs to be at least 1 column wide
|
// block needs to be at least 1 column wide
|
||||||
if (newWidth < columnWidth) return;
|
if (newWidth < columnWidth) return;
|
||||||
|
|
||||||
resizableDiv.style.width = `${newWidth}px`;
|
resizableDiv.style.width = `${newWidth}px`;
|
||||||
if (block.position) block.position.width = newWidth;
|
parentDiv.style.marginLeft = `${newMarginLeft}px`;
|
||||||
|
|
||||||
// update the margin left of the block if dragging from the left end
|
if (block.position) {
|
||||||
if (dragDirection === "left") {
|
block.position.width = newWidth;
|
||||||
// calculate new marginLeft and update the initial marginLeft using -=
|
block.position.marginLeft = newMarginLeft;
|
||||||
const newMarginLeft =
|
|
||||||
Math.round((initialMarginLeft -= delWidth) / columnWidth) * columnWidth;
|
|
||||||
|
|
||||||
parentDiv.style.marginLeft = `${newMarginLeft}px`;
|
|
||||||
if (block.position) block.position.marginLeft = newMarginLeft;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -103,7 +108,52 @@ export const ChartDraggable: React.FC<Props> = ({
|
|||||||
(resizableDiv.clientWidth - blockInitialWidth) / columnWidth
|
(resizableDiv.clientWidth - blockInitialWidth) / columnWidth
|
||||||
);
|
);
|
||||||
|
|
||||||
handleBlock(totalBlockShifts, dragDirection);
|
handleBlock(totalBlockShifts, "left");
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", handleMouseMove);
|
||||||
|
document.addEventListener("mouseup", handleMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRightDrag = () => {
|
||||||
|
if (!currentViewData || !resizableRef.current || !parentDivRef.current || !block.position)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const resizableDiv = resizableRef.current;
|
||||||
|
|
||||||
|
const columnWidth = currentViewData.data.width;
|
||||||
|
|
||||||
|
const blockInitialWidth =
|
||||||
|
resizableDiv.clientWidth ?? parseInt(block.position.width.toString(), 10);
|
||||||
|
|
||||||
|
let initialWidth = resizableDiv.clientWidth ?? parseInt(block.position.width.toString(), 10);
|
||||||
|
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
|
if (!window) return;
|
||||||
|
|
||||||
|
let delWidth = 0;
|
||||||
|
|
||||||
|
delWidth = checkScrollEnd(e);
|
||||||
|
|
||||||
|
// calculate new width and update the initialMarginLeft using +=
|
||||||
|
const newWidth = Math.round((initialWidth += delWidth) / columnWidth) * columnWidth;
|
||||||
|
|
||||||
|
// block needs to be at least 1 column wide
|
||||||
|
if (newWidth < columnWidth) return;
|
||||||
|
|
||||||
|
resizableDiv.style.width = `${Math.max(newWidth, 80)}px`;
|
||||||
|
if (block.position) block.position.width = Math.max(newWidth, 80);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
document.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
document.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
|
||||||
|
const totalBlockShifts = Math.ceil(
|
||||||
|
(resizableDiv.clientWidth - blockInitialWidth) / columnWidth
|
||||||
|
);
|
||||||
|
|
||||||
|
handleBlock(totalBlockShifts, "right");
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("mousemove", handleMouseMove);
|
document.addEventListener("mousemove", handleMouseMove);
|
||||||
@ -122,7 +172,7 @@ export const ChartDraggable: React.FC<Props> = ({
|
|||||||
{enableLeftDrag && (
|
{enableLeftDrag && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
onMouseDown={() => handleDrag("left")}
|
onMouseDown={handleLeftDrag}
|
||||||
onMouseEnter={() => setIsLeftResizing(true)}
|
onMouseEnter={() => setIsLeftResizing(true)}
|
||||||
onMouseLeave={() => setIsLeftResizing(false)}
|
onMouseLeave={() => setIsLeftResizing(false)}
|
||||||
className="absolute top-1/2 -left-2.5 -translate-y-1/2 z-[1] w-6 h-10 bg-brand-backdrop rounded-md cursor-col-resize"
|
className="absolute top-1/2 -left-2.5 -translate-y-1/2 z-[1] w-6 h-10 bg-brand-backdrop rounded-md cursor-col-resize"
|
||||||
@ -138,7 +188,7 @@ export const ChartDraggable: React.FC<Props> = ({
|
|||||||
{enableRightDrag && (
|
{enableRightDrag && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
onMouseDown={() => handleDrag("right")}
|
onMouseDown={handleRightDrag}
|
||||||
onMouseEnter={() => setIsRightResizing(true)}
|
onMouseEnter={() => setIsRightResizing(true)}
|
||||||
onMouseLeave={() => setIsRightResizing(false)}
|
onMouseLeave={() => setIsRightResizing(false)}
|
||||||
className="absolute top-1/2 -right-2.5 -translate-y-1/2 z-[1] w-6 h-6 bg-brand-backdrop rounded-md cursor-col-resize"
|
className="absolute top-1/2 -right-2.5 -translate-y-1/2 z-[1] w-6 h-6 bg-brand-backdrop rounded-md cursor-col-resize"
|
||||||
|
@ -37,7 +37,5 @@ export const updateGanttIssue = (
|
|||||||
if (newPayload.sort_order && payload.sort_order)
|
if (newPayload.sort_order && payload.sort_order)
|
||||||
newPayload.sort_order = payload.sort_order.newSortOrder;
|
newPayload.sort_order = payload.sort_order.newSortOrder;
|
||||||
|
|
||||||
issuesService
|
issuesService.patchIssue(workspaceSlug, issue.project, issue.id, newPayload, user);
|
||||||
.patchIssue(workspaceSlug, issue.project, issue.id, newPayload, user)
|
|
||||||
.finally(() => mutate());
|
|
||||||
};
|
};
|
||||||
|
@ -69,9 +69,13 @@ export const ModulesListGanttChartView: FC<Props> = ({ modules, mutateModules })
|
|||||||
if (newPayload.sort_order && payload.sort_order)
|
if (newPayload.sort_order && payload.sort_order)
|
||||||
newPayload.sort_order = payload.sort_order.newSortOrder;
|
newPayload.sort_order = payload.sort_order.newSortOrder;
|
||||||
|
|
||||||
modulesService
|
modulesService.patchModule(
|
||||||
.patchModule(workspaceSlug.toString(), module.project, module.id, newPayload, user)
|
workspaceSlug.toString(),
|
||||||
.finally(() => mutateModules());
|
module.project,
|
||||||
|
module.id,
|
||||||
|
newPayload,
|
||||||
|
user
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const blockFormat = (blocks: IModule[]) =>
|
const blockFormat = (blocks: IModule[]) =>
|
||||||
|
Loading…
Reference in New Issue
Block a user