mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
edf8109735
* new Kanban column design changes * fix minor build error
263 lines
9.1 KiB
TypeScript
263 lines
9.1 KiB
TypeScript
import { MutableRefObject, useEffect, useRef, useState } from "react";
|
|
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
|
import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-scroll/element";
|
|
import { AlertCircle } from "lucide-react";
|
|
//types
|
|
import {
|
|
TGroupedIssues,
|
|
TIssue,
|
|
IIssueDisplayProperties,
|
|
IIssueMap,
|
|
TSubGroupedIssues,
|
|
TUnGroupedIssues,
|
|
TIssueGroupByOptions,
|
|
TIssueOrderByOptions,
|
|
} from "@plane/types";
|
|
import { highlightIssueOnDrop } from "@/components/issues/issue-layouts/utils";
|
|
import { ISSUE_ORDER_BY_OPTIONS } from "@/constants/issue";
|
|
// helpers
|
|
import { cn } from "@/helpers/common.helper";
|
|
// hooks
|
|
import { useProjectState } from "@/hooks/store";
|
|
//components
|
|
import { TRenderQuickActions } from "../list/list-view-types";
|
|
import { GroupDropLocation, getSourceFromDropPayload, getDestinationFromDropPayload, getIssueBlockId } from "../utils";
|
|
import { KanbanIssueBlocksList, KanBanQuickAddIssueForm } from ".";
|
|
|
|
interface IKanbanGroup {
|
|
groupId: string;
|
|
issuesMap: IIssueMap;
|
|
issueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues;
|
|
displayProperties: IIssueDisplayProperties | undefined;
|
|
sub_group_by: TIssueGroupByOptions | undefined;
|
|
group_by: TIssueGroupByOptions | undefined;
|
|
sub_group_id: string;
|
|
isDragDisabled: boolean;
|
|
isDropDisabled: boolean;
|
|
dropErrorMessage: string | undefined;
|
|
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
|
quickActions: TRenderQuickActions;
|
|
enableQuickIssueCreate?: boolean;
|
|
quickAddCallback?: (
|
|
workspaceSlug: string,
|
|
projectId: string,
|
|
data: TIssue,
|
|
viewId?: string
|
|
) => Promise<TIssue | undefined>;
|
|
viewId?: string;
|
|
disableIssueCreation?: boolean;
|
|
canEditProperties: (projectId: string | undefined) => boolean;
|
|
groupByVisibilityToggle?: boolean;
|
|
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
|
handleOnDrop: (source: GroupDropLocation, destination: GroupDropLocation) => Promise<void>;
|
|
orderBy: TIssueOrderByOptions | undefined;
|
|
}
|
|
|
|
export const KanbanGroup = (props: IKanbanGroup) => {
|
|
const {
|
|
groupId,
|
|
sub_group_id,
|
|
group_by,
|
|
orderBy,
|
|
sub_group_by,
|
|
issuesMap,
|
|
displayProperties,
|
|
issueIds,
|
|
isDragDisabled,
|
|
isDropDisabled,
|
|
dropErrorMessage,
|
|
updateIssue,
|
|
quickActions,
|
|
canEditProperties,
|
|
enableQuickIssueCreate,
|
|
disableIssueCreation,
|
|
quickAddCallback,
|
|
viewId,
|
|
scrollableContainerRef,
|
|
handleOnDrop,
|
|
} = props;
|
|
// hooks
|
|
const projectState = useProjectState();
|
|
|
|
const [isDraggingOverColumn, setIsDraggingOverColumn] = useState(false);
|
|
|
|
const columnRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
// Enable Kanban Columns as Drop Targets
|
|
useEffect(() => {
|
|
const element = columnRef.current;
|
|
|
|
if (!element) return;
|
|
|
|
return combine(
|
|
dropTargetForElements({
|
|
element,
|
|
getData: () => ({ groupId, subGroupId: sub_group_id, columnId: `${groupId}__${sub_group_id}`, type: "COLUMN" }),
|
|
onDragEnter: () => {
|
|
setIsDraggingOverColumn(true);
|
|
},
|
|
onDragLeave: () => {
|
|
setIsDraggingOverColumn(false);
|
|
},
|
|
onDragStart: () => {
|
|
setIsDraggingOverColumn(true);
|
|
},
|
|
onDrop: (payload) => {
|
|
setIsDraggingOverColumn(false);
|
|
const source = getSourceFromDropPayload(payload);
|
|
const destination = getDestinationFromDropPayload(payload);
|
|
|
|
if (!source || !destination || isDropDisabled) return;
|
|
|
|
handleOnDrop(source, destination);
|
|
|
|
highlightIssueOnDrop(
|
|
getIssueBlockId(source.id, destination?.groupId, destination?.subGroupId),
|
|
orderBy !== "sort_order"
|
|
);
|
|
},
|
|
}),
|
|
autoScrollForElements({
|
|
element,
|
|
})
|
|
);
|
|
}, [columnRef?.current, groupId, sub_group_id, setIsDraggingOverColumn, orderBy, isDropDisabled, handleOnDrop]);
|
|
|
|
const prePopulateQuickAddData = (
|
|
groupByKey: string | undefined,
|
|
subGroupByKey: string | undefined | null,
|
|
groupValue: string,
|
|
subGroupValue: string
|
|
) => {
|
|
const defaultState = projectState.projectStates?.find((state) => state.default);
|
|
let preloadedData: object = { state_id: defaultState?.id };
|
|
|
|
if (groupByKey) {
|
|
if (groupByKey === "state") {
|
|
preloadedData = { ...preloadedData, state_id: groupValue };
|
|
} else if (groupByKey === "priority") {
|
|
preloadedData = { ...preloadedData, priority: groupValue };
|
|
} else if (groupByKey === "cycle") {
|
|
preloadedData = { ...preloadedData, cycle_id: groupValue };
|
|
} else if (groupByKey === "module") {
|
|
preloadedData = { ...preloadedData, module_ids: [groupValue] };
|
|
} else if (groupByKey === "labels" && groupValue != "None") {
|
|
preloadedData = { ...preloadedData, label_ids: [groupValue] };
|
|
} else if (groupByKey === "assignees" && groupValue != "None") {
|
|
preloadedData = { ...preloadedData, assignee_ids: [groupValue] };
|
|
} else if (groupByKey === "created_by") {
|
|
preloadedData = { ...preloadedData };
|
|
} else {
|
|
preloadedData = { ...preloadedData, [groupByKey]: groupValue };
|
|
}
|
|
}
|
|
|
|
if (subGroupByKey) {
|
|
if (subGroupByKey === "state") {
|
|
preloadedData = { ...preloadedData, state_id: subGroupValue };
|
|
} else if (subGroupByKey === "priority") {
|
|
preloadedData = { ...preloadedData, priority: subGroupValue };
|
|
} else if (groupByKey === "cycle") {
|
|
preloadedData = { ...preloadedData, cycle_id: subGroupValue };
|
|
} else if (groupByKey === "module") {
|
|
preloadedData = { ...preloadedData, module_ids: [subGroupValue] };
|
|
} else if (subGroupByKey === "labels" && subGroupValue != "None") {
|
|
preloadedData = { ...preloadedData, label_ids: [subGroupValue] };
|
|
} else if (subGroupByKey === "assignees" && subGroupValue != "None") {
|
|
preloadedData = { ...preloadedData, assignee_ids: [subGroupValue] };
|
|
} else if (subGroupByKey === "created_by") {
|
|
preloadedData = { ...preloadedData };
|
|
} else {
|
|
preloadedData = { ...preloadedData, [subGroupByKey]: subGroupValue };
|
|
}
|
|
}
|
|
|
|
return preloadedData;
|
|
};
|
|
|
|
const canDropOverIssue = orderBy === "sort_order";
|
|
const shouldOverlay = isDraggingOverColumn && (!canDropOverIssue || isDropDisabled);
|
|
const readableOrderBy = ISSUE_ORDER_BY_OPTIONS.find((orderByObj) => orderByObj.key === orderBy)?.title;
|
|
|
|
return (
|
|
<div
|
|
id={`${groupId}__${sub_group_id}`}
|
|
className={cn(
|
|
"relative h-full transition-all min-h-[120px]",
|
|
{ "bg-custom-background-80 rounded": isDraggingOverColumn },
|
|
{ "vertical-scrollbar scrollbar-md": !sub_group_by && !shouldOverlay }
|
|
)}
|
|
ref={columnRef}
|
|
>
|
|
<div
|
|
//column overlay when issues are not sorted by manual
|
|
className={cn(
|
|
"absolute top-0 left-0 h-full w-full items-center text-sm font-medium text-custom-text-300 rounded bg-custom-background-overlay",
|
|
{
|
|
"flex flex-col border-[1px] border-custom-border-300 z-[2]": shouldOverlay,
|
|
},
|
|
{ hidden: !shouldOverlay },
|
|
{ "justify-center": !sub_group_by }
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"p-3 mt-8 flex flex-col rounded items-center",
|
|
{
|
|
"text-custom-text-200": shouldOverlay,
|
|
},
|
|
{
|
|
"text-custom-text-error": isDropDisabled,
|
|
}
|
|
)}
|
|
>
|
|
{dropErrorMessage ? (
|
|
<div className="flex items-center">
|
|
<AlertCircle width={13} height={13} />
|
|
<span>{dropErrorMessage}</span>
|
|
</div>
|
|
) : (
|
|
<>
|
|
{readableOrderBy && (
|
|
<span>
|
|
The layout is ordered by <span className="font-semibold">{readableOrderBy}</span>.
|
|
</span>
|
|
)}
|
|
<span>Drop here to move the issue.</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<KanbanIssueBlocksList
|
|
sub_group_id={sub_group_id}
|
|
groupId={groupId}
|
|
issuesMap={issuesMap}
|
|
issueIds={(issueIds as TGroupedIssues)?.[groupId] || []}
|
|
displayProperties={displayProperties}
|
|
isDragDisabled={isDragDisabled}
|
|
updateIssue={updateIssue}
|
|
quickActions={quickActions}
|
|
canEditProperties={canEditProperties}
|
|
scrollableContainerRef={sub_group_by ? scrollableContainerRef : columnRef}
|
|
canDropOverIssue={canDropOverIssue}
|
|
/>
|
|
|
|
{enableQuickIssueCreate && !disableIssueCreation && (
|
|
<div className="w-full bg-custom-background-90 py-0.5 sticky bottom-0">
|
|
<KanBanQuickAddIssueForm
|
|
formKey="name"
|
|
groupId={groupId}
|
|
subGroupId={sub_group_id}
|
|
prePopulatedData={{
|
|
...(group_by && prePopulateQuickAddData(group_by, sub_group_by, groupId, sub_group_id)),
|
|
}}
|
|
quickAddCallback={quickAddCallback}
|
|
viewId={viewId}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|