[WEB-1004] feat: Pragmatic dnd implementation for Kanban (#4189)
* Pragmatic drag and drop implmentation of Kanban * refactor pragmatic dnd implementation and fix bugs * fix dnd for modules, cycles, draft and project views
This commit is contained in:
parent
384624a21b
commit
7a21855ab6
22 changed files with 756 additions and 377 deletions
|
|
@ -23,6 +23,7 @@ export interface ICycleIssues {
|
|||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
|
|
@ -142,6 +143,30 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
|||
return issues;
|
||||
}
|
||||
|
||||
getIssueIds = (groupId?: string, subGroupId?: string) => {
|
||||
const groupedIssueIds = this.groupedIssueIds;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.cycleIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters || !groupedIssueIds) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
|
||||
if (!groupBy && !subGroupBy) {
|
||||
return groupedIssueIds as string[];
|
||||
}
|
||||
|
||||
if (groupBy && subGroupBy && groupId && subGroupId) {
|
||||
return (groupedIssueIds as TSubGroupedIssues)?.[subGroupId]?.[groupId] as string[];
|
||||
}
|
||||
|
||||
if (groupBy && groupId) {
|
||||
return (groupedIssueIds as TGroupedIssues)?.[groupId] as string[];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export interface IDraftIssues {
|
|||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
|
||||
fetchIssues: (workspaceSlug: string, projectId: string, loadType: TLoader) => Promise<TIssue[]>;
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
|
|
@ -97,6 +98,30 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues {
|
|||
return issues;
|
||||
}
|
||||
|
||||
getIssueIds = (groupId?: string, subGroupId?: string) => {
|
||||
const groupedIssueIds = this.groupedIssueIds;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.draftIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters || !groupedIssueIds) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
|
||||
if (!groupBy && !subGroupBy) {
|
||||
return groupedIssueIds as string[];
|
||||
}
|
||||
|
||||
if (groupBy && subGroupBy && groupId && subGroupId) {
|
||||
return (groupedIssueIds as TSubGroupedIssues)?.[subGroupId]?.[groupId] as string[];
|
||||
}
|
||||
|
||||
if (groupBy && groupId) {
|
||||
return (groupedIssueIds as TGroupedIssues)?.[groupId] as string[];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, projectId: string, loadType: TLoader = "init-loader") => {
|
||||
try {
|
||||
this.loader = loadType;
|
||||
|
|
@ -141,8 +166,6 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues {
|
|||
|
||||
updateIssue = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
try {
|
||||
await this.issueDraftService.updateDraftIssue(workspaceSlug, projectId, issueId, data);
|
||||
|
||||
this.rootStore.issues.updateIssue(issueId, data);
|
||||
|
||||
if (data.hasOwnProperty("is_draft") && data?.is_draft === false) {
|
||||
|
|
@ -153,6 +176,8 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
await this.issueDraftService.updateDraftIssue(workspaceSlug, projectId, issueId, data);
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
throw error;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export type TIssueHelperStore = {
|
|||
getGroupArray(value: boolean | number | string | string[] | null, isDate?: boolean): string[];
|
||||
};
|
||||
|
||||
const ISSUE_FILTER_DEFAULT_DATA: Record<TIssueDisplayFilterOptions, keyof TIssue> = {
|
||||
export const ISSUE_FILTER_DEFAULT_DATA: Record<TIssueDisplayFilterOptions, keyof TIssue> = {
|
||||
project: "project_id",
|
||||
cycle: "cycle_id",
|
||||
module: "module_ids",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { action, computed, makeObservable, observable } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
import { IssueRootStore } from "./root.store";
|
||||
import { TIssueGroupByOptions } from "@plane/types";
|
||||
// types
|
||||
|
||||
export interface IIssueKanBanViewStore {
|
||||
|
|
@ -8,12 +9,17 @@ export interface IIssueKanBanViewStore {
|
|||
groupByHeaderMinMax: string[];
|
||||
subgroupByIssuesVisibility: string[];
|
||||
};
|
||||
isDragging: boolean;
|
||||
// computed
|
||||
getCanUserDragDrop: (group_by: string | null, sub_group_by: string | null) => boolean;
|
||||
getCanUserDragDrop: (
|
||||
group_by: TIssueGroupByOptions | undefined,
|
||||
sub_group_by: TIssueGroupByOptions | undefined
|
||||
) => boolean;
|
||||
canUserDragDropVertically: boolean;
|
||||
canUserDragDropHorizontally: boolean;
|
||||
// actions
|
||||
handleKanBanToggle: (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => void;
|
||||
setIsDragging: (isDragging: boolean) => void;
|
||||
}
|
||||
|
||||
export class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
||||
|
|
@ -21,30 +27,39 @@ export class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
|||
groupByHeaderMinMax: string[];
|
||||
subgroupByIssuesVisibility: string[];
|
||||
} = { groupByHeaderMinMax: [], subgroupByIssuesVisibility: [] };
|
||||
isDragging = false;
|
||||
// root store
|
||||
rootStore;
|
||||
|
||||
constructor(_rootStore: IssueRootStore) {
|
||||
makeObservable(this, {
|
||||
kanBanToggle: observable,
|
||||
isDragging: observable.ref,
|
||||
// computed
|
||||
canUserDragDropVertically: computed,
|
||||
canUserDragDropHorizontally: computed,
|
||||
|
||||
// actions
|
||||
handleKanBanToggle: action,
|
||||
setIsDragging: action.bound,
|
||||
});
|
||||
|
||||
this.rootStore = _rootStore;
|
||||
}
|
||||
|
||||
getCanUserDragDrop = computedFn((group_by: string | null, sub_group_by: string | null) => {
|
||||
if (group_by && ["state", "priority"].includes(group_by)) {
|
||||
if (!sub_group_by) return true;
|
||||
if (sub_group_by && ["state", "priority"].includes(sub_group_by)) return true;
|
||||
setIsDragging = (isDragging: boolean) => {
|
||||
this.isDragging = isDragging;
|
||||
};
|
||||
|
||||
getCanUserDragDrop = computedFn(
|
||||
(group_by: TIssueGroupByOptions | undefined, sub_group_by: TIssueGroupByOptions | undefined) => {
|
||||
if (group_by && ["state", "priority"].includes(group_by)) {
|
||||
if (!sub_group_by) return true;
|
||||
if (sub_group_by && ["state", "priority"].includes(sub_group_by)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
);
|
||||
|
||||
get canUserDragDropVertically() {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export interface IModuleIssues {
|
|||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
|
|
@ -146,6 +147,30 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
|||
return issues;
|
||||
}
|
||||
|
||||
getIssueIds = (groupId?: string, subGroupId?: string) => {
|
||||
const groupedIssueIds = this.groupedIssueIds;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.moduleIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters || !groupedIssueIds) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
|
||||
if (!groupBy && !subGroupBy) {
|
||||
return groupedIssueIds as string[];
|
||||
}
|
||||
|
||||
if (groupBy && subGroupBy && groupId && subGroupId) {
|
||||
return (groupedIssueIds as TSubGroupedIssues)?.[subGroupId]?.[groupId] as string[];
|
||||
}
|
||||
|
||||
if (groupBy && groupId) {
|
||||
return (groupedIssueIds as TGroupedIssues)?.[groupId] as string[];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export interface IProfileIssues {
|
|||
viewFlags: ViewFlags;
|
||||
// actions
|
||||
setViewId: (viewId: "assigned" | "created" | "subscribed") => void;
|
||||
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string | undefined,
|
||||
|
|
@ -118,6 +119,30 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
|||
return issues;
|
||||
}
|
||||
|
||||
getIssueIds = (groupId?: string, subGroupId?: string) => {
|
||||
const groupedIssueIds = this.groupedIssueIds;
|
||||
|
||||
const displayFilters = this.rootStore?.projectIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters || !groupedIssueIds) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
|
||||
if (!groupBy && !subGroupBy) {
|
||||
return groupedIssueIds as string[];
|
||||
}
|
||||
|
||||
if (groupBy && subGroupBy && groupId && subGroupId) {
|
||||
return (groupedIssueIds as TSubGroupedIssues)?.[subGroupId]?.[groupId] as string[];
|
||||
}
|
||||
|
||||
if (groupBy && groupId) {
|
||||
return (groupedIssueIds as TGroupedIssues)?.[groupId] as string[];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
get viewFlags() {
|
||||
if (this.currentView === "subscribed")
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export interface IProjectViewIssues {
|
|||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
|
|
@ -114,6 +115,30 @@ export class ProjectViewIssues extends IssueHelperStore implements IProjectViewI
|
|||
return issues;
|
||||
}
|
||||
|
||||
getIssueIds = (groupId?: string, subGroupId?: string) => {
|
||||
const groupedIssueIds = this.groupedIssueIds;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.projectViewIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters || !groupedIssueIds) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
|
||||
if (!groupBy && !subGroupBy) {
|
||||
return groupedIssueIds as string[];
|
||||
}
|
||||
|
||||
if (groupBy && subGroupBy && groupId && subGroupId) {
|
||||
return (groupedIssueIds as TSubGroupedIssues)?.[subGroupId]?.[groupId] as string[];
|
||||
}
|
||||
|
||||
if (groupBy && groupId) {
|
||||
return (groupedIssueIds as TGroupedIssues)?.[groupId] as string[];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, projectId: string, loadType: TLoader = "init-loader", viewId: string) => {
|
||||
try {
|
||||
this.loader = loadType;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ export interface IProjectIssues {
|
|||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
|
||||
// action
|
||||
fetchIssues: (workspaceSlug: string, projectId: string, loadType: TLoader) => Promise<TIssue[]>;
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
|
|
@ -100,6 +101,30 @@ export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
|
|||
return issues;
|
||||
}
|
||||
|
||||
getIssueIds = (groupId?: string, subGroupId?: string) => {
|
||||
const groupedIssueIds = this.groupedIssueIds;
|
||||
|
||||
const displayFilters = this.rootStore?.projectIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters || !groupedIssueIds) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
|
||||
if (!groupBy && !subGroupBy) {
|
||||
return groupedIssueIds as string[];
|
||||
}
|
||||
|
||||
if (groupBy && subGroupBy && groupId && subGroupId) {
|
||||
return (groupedIssueIds as TSubGroupedIssues)?.[subGroupId]?.[groupId] as string[];
|
||||
}
|
||||
|
||||
if (groupBy && groupId) {
|
||||
return (groupedIssueIds as TGroupedIssues)?.[groupId] as string[];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, projectId: string, loadType: TLoader = "init-loader") => {
|
||||
try {
|
||||
this.loader = loadType;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue