refactor: enhance command palette modularity (#6139)
* refactor: enhance command palette modularity * chore: minor updates to command palette store
This commit is contained in:
parent
ca0d50b229
commit
a9bd2e243a
16 changed files with 342 additions and 212 deletions
15
packages/types/src/command-palette.d.ts
vendored
Normal file
15
packages/types/src/command-palette.d.ts
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
export type TCommandPaletteActionList = Record<
|
||||||
|
string,
|
||||||
|
{ title: string; description: string; action: () => void }
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type TCommandPaletteShortcutList = {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
shortcuts: TCommandPaletteShortcut[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TCommandPaletteShortcut = {
|
||||||
|
keys: string; // comma separated keys
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
1
packages/types/src/index.d.ts
vendored
1
packages/types/src/index.d.ts
vendored
|
|
@ -32,3 +32,4 @@ export * from "./workspace-notifications";
|
||||||
export * from "./favorite";
|
export * from "./favorite";
|
||||||
export * from "./file";
|
export * from "./file";
|
||||||
export * from "./workspace-draft-issues/base";
|
export * from "./workspace-draft-issues/base";
|
||||||
|
export * from "./command-palette";
|
||||||
|
|
|
||||||
3
web/ce/components/command-palette/modals/index.ts
Normal file
3
web/ce/components/command-palette/modals/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "./workspace-level";
|
||||||
|
export * from "./project-level";
|
||||||
|
export * from "./issue-level";
|
||||||
73
web/ce/components/command-palette/modals/issue-level.tsx
Normal file
73
web/ce/components/command-palette/modals/issue-level.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { useParams, usePathname } from "next/navigation";
|
||||||
|
import useSWR from "swr";
|
||||||
|
// components
|
||||||
|
import { BulkDeleteIssuesModal } from "@/components/core";
|
||||||
|
import { CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||||
|
// constants
|
||||||
|
import { ISSUE_DETAILS } from "@/constants/fetch-keys";
|
||||||
|
// hooks
|
||||||
|
import { useCommandPalette, useUser } from "@/hooks/store";
|
||||||
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
|
||||||
|
// services
|
||||||
|
import { IssueService } from "@/services/issue";
|
||||||
|
|
||||||
|
// services
|
||||||
|
const issueService = new IssueService();
|
||||||
|
|
||||||
|
export const IssueLevelModals = observer(() => {
|
||||||
|
// router
|
||||||
|
const pathname = usePathname();
|
||||||
|
const { workspaceSlug, projectId, issueId, cycleId, moduleId } = useParams();
|
||||||
|
const router = useAppRouter();
|
||||||
|
// store hooks
|
||||||
|
const { data: currentUser } = useUser();
|
||||||
|
const {
|
||||||
|
issues: { removeIssue },
|
||||||
|
} = useIssuesStore();
|
||||||
|
const {
|
||||||
|
isCreateIssueModalOpen,
|
||||||
|
toggleCreateIssueModal,
|
||||||
|
isDeleteIssueModalOpen,
|
||||||
|
toggleDeleteIssueModal,
|
||||||
|
isBulkDeleteIssueModalOpen,
|
||||||
|
toggleBulkDeleteIssueModal,
|
||||||
|
} = useCommandPalette();
|
||||||
|
// derived values
|
||||||
|
const isDraftIssue = pathname?.includes("draft-issues") || false;
|
||||||
|
|
||||||
|
const { data: issueDetails } = useSWR(
|
||||||
|
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
||||||
|
workspaceSlug && projectId && issueId
|
||||||
|
? () => issueService.retrieve(workspaceSlug as string, projectId as string, issueId as string)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CreateUpdateIssueModal
|
||||||
|
isOpen={isCreateIssueModalOpen}
|
||||||
|
onClose={() => toggleCreateIssueModal(false)}
|
||||||
|
data={cycleId ? { cycle_id: cycleId.toString() } : moduleId ? { module_ids: [moduleId.toString()] } : undefined}
|
||||||
|
isDraft={isDraftIssue}
|
||||||
|
/>
|
||||||
|
{workspaceSlug && projectId && issueId && issueDetails && (
|
||||||
|
<DeleteIssueModal
|
||||||
|
handleClose={() => toggleDeleteIssueModal(false)}
|
||||||
|
isOpen={isDeleteIssueModalOpen}
|
||||||
|
data={issueDetails}
|
||||||
|
onSubmit={async () => {
|
||||||
|
await removeIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString());
|
||||||
|
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<BulkDeleteIssuesModal
|
||||||
|
isOpen={isBulkDeleteIssueModalOpen}
|
||||||
|
onClose={() => toggleBulkDeleteIssueModal(false)}
|
||||||
|
user={currentUser}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
59
web/ce/components/command-palette/modals/project-level.tsx
Normal file
59
web/ce/components/command-palette/modals/project-level.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
// components
|
||||||
|
import { CycleCreateUpdateModal } from "@/components/cycles";
|
||||||
|
import { CreateUpdateModuleModal } from "@/components/modules";
|
||||||
|
import { CreatePageModal } from "@/components/pages";
|
||||||
|
import { CreateUpdateProjectViewModal } from "@/components/views";
|
||||||
|
// hooks
|
||||||
|
import { useCommandPalette } from "@/hooks/store";
|
||||||
|
|
||||||
|
export type TProjectLevelModalsProps = {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProjectLevelModals = observer((props: TProjectLevelModalsProps) => {
|
||||||
|
const { workspaceSlug, projectId } = props;
|
||||||
|
// store hooks
|
||||||
|
const {
|
||||||
|
isCreateCycleModalOpen,
|
||||||
|
toggleCreateCycleModal,
|
||||||
|
isCreateModuleModalOpen,
|
||||||
|
toggleCreateModuleModal,
|
||||||
|
isCreateViewModalOpen,
|
||||||
|
toggleCreateViewModal,
|
||||||
|
createPageModal,
|
||||||
|
toggleCreatePageModal,
|
||||||
|
} = useCommandPalette();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CycleCreateUpdateModal
|
||||||
|
isOpen={isCreateCycleModalOpen}
|
||||||
|
handleClose={() => toggleCreateCycleModal(false)}
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
projectId={projectId.toString()}
|
||||||
|
/>
|
||||||
|
<CreateUpdateModuleModal
|
||||||
|
isOpen={isCreateModuleModalOpen}
|
||||||
|
onClose={() => toggleCreateModuleModal(false)}
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
projectId={projectId.toString()}
|
||||||
|
/>
|
||||||
|
<CreateUpdateProjectViewModal
|
||||||
|
isOpen={isCreateViewModalOpen}
|
||||||
|
onClose={() => toggleCreateViewModal(false)}
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
projectId={projectId.toString()}
|
||||||
|
/>
|
||||||
|
<CreatePageModal
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
projectId={projectId.toString()}
|
||||||
|
isModalOpen={createPageModal.isOpen}
|
||||||
|
pageAccess={createPageModal.pageAccess}
|
||||||
|
handleModalClose={() => toggleCreatePageModal({ isOpen: false })}
|
||||||
|
redirectionEnabled
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
25
web/ce/components/command-palette/modals/workspace-level.tsx
Normal file
25
web/ce/components/command-palette/modals/workspace-level.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
// components
|
||||||
|
import { CreateProjectModal } from "@/components/project";
|
||||||
|
// hooks
|
||||||
|
import { useCommandPalette } from "@/hooks/store";
|
||||||
|
|
||||||
|
export type TWorkspaceLevelModalsProps = {
|
||||||
|
workspaceSlug: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WorkspaceLevelModals = observer((props: TWorkspaceLevelModalsProps) => {
|
||||||
|
const { workspaceSlug } = props;
|
||||||
|
// store hooks
|
||||||
|
const { isCreateProjectModalOpen, toggleCreateProjectModal } = useCommandPalette();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CreateProjectModal
|
||||||
|
isOpen={isCreateProjectModalOpen}
|
||||||
|
onClose={() => toggleCreateProjectModal(false)}
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
95
web/ce/helpers/command-palette.ts
Normal file
95
web/ce/helpers/command-palette.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
// types
|
||||||
|
import { TCommandPaletteActionList, TCommandPaletteShortcut, TCommandPaletteShortcutList } from "@plane/types";
|
||||||
|
// store
|
||||||
|
import { store } from "@/lib/store-context";
|
||||||
|
|
||||||
|
export const getGlobalShortcutsList: () => TCommandPaletteActionList = () => {
|
||||||
|
const { toggleCreateIssueModal } = store.commandPalette;
|
||||||
|
|
||||||
|
return {
|
||||||
|
c: {
|
||||||
|
title: "Create a new issue",
|
||||||
|
description: "Create a new issue in the current project",
|
||||||
|
action: () => toggleCreateIssueModal(true),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWorkspaceShortcutsList: () => TCommandPaletteActionList = () => {
|
||||||
|
const { toggleCreateProjectModal } = store.commandPalette;
|
||||||
|
|
||||||
|
return {
|
||||||
|
p: {
|
||||||
|
title: "Create a new project",
|
||||||
|
description: "Create a new project in the current workspace",
|
||||||
|
action: () => toggleCreateProjectModal(true),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProjectShortcutsList: () => TCommandPaletteActionList = () => {
|
||||||
|
const {
|
||||||
|
toggleCreatePageModal,
|
||||||
|
toggleCreateModuleModal,
|
||||||
|
toggleCreateCycleModal,
|
||||||
|
toggleCreateViewModal,
|
||||||
|
toggleBulkDeleteIssueModal,
|
||||||
|
} = store.commandPalette;
|
||||||
|
|
||||||
|
return {
|
||||||
|
d: {
|
||||||
|
title: "Create a new page",
|
||||||
|
description: "Create a new page in the current project",
|
||||||
|
action: () => toggleCreatePageModal({ isOpen: true }),
|
||||||
|
},
|
||||||
|
m: {
|
||||||
|
title: "Create a new module",
|
||||||
|
description: "Create a new module in the current project",
|
||||||
|
action: () => toggleCreateModuleModal(true),
|
||||||
|
},
|
||||||
|
q: {
|
||||||
|
title: "Create a new cycle",
|
||||||
|
description: "Create a new cycle in the current project",
|
||||||
|
action: () => toggleCreateCycleModal(true),
|
||||||
|
},
|
||||||
|
v: {
|
||||||
|
title: "Create a new view",
|
||||||
|
description: "Create a new view in the current project",
|
||||||
|
action: () => toggleCreateViewModal(true),
|
||||||
|
},
|
||||||
|
backspace: {
|
||||||
|
title: "Bulk delete issues",
|
||||||
|
description: "Bulk delete issues in the current project",
|
||||||
|
action: () => toggleBulkDeleteIssueModal(true),
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
title: "Bulk delete issues",
|
||||||
|
description: "Bulk delete issues in the current project",
|
||||||
|
action: () => toggleBulkDeleteIssueModal(true),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export const handleAdditionalKeyDownEvents = (e: KeyboardEvent) => null;
|
||||||
|
|
||||||
|
export const getNavigationShortcutsList = (): TCommandPaletteShortcut[] => [
|
||||||
|
{ keys: "Ctrl,K", description: "Open command menu" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getCommonShortcutsList = (platform: string): TCommandPaletteShortcut[] => [
|
||||||
|
{ keys: "P", description: "Create project" },
|
||||||
|
{ keys: "C", description: "Create issue" },
|
||||||
|
{ keys: "Q", description: "Create cycle" },
|
||||||
|
{ keys: "M", description: "Create module" },
|
||||||
|
{ keys: "V", description: "Create view" },
|
||||||
|
{ keys: "D", description: "Create page" },
|
||||||
|
{ keys: "Delete", description: "Bulk delete issues" },
|
||||||
|
{ keys: "Shift,/", description: "Open shortcuts guide" },
|
||||||
|
{
|
||||||
|
keys: platform === "MacOS" ? "Ctrl,control,C" : "Ctrl,Alt,C",
|
||||||
|
description: "Copy issue URL from the issue details page",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getAdditionalShortcutsList = (): TCommandPaletteShortcutList[] => [];
|
||||||
12
web/ce/store/command-palette.store.ts
Normal file
12
web/ce/store/command-palette.store.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { makeObservable } from "mobx";
|
||||||
|
// types / constants
|
||||||
|
import { BaseCommandPaletteStore, IBaseCommandPaletteStore } from "@/store/base-command-palette.store";
|
||||||
|
|
||||||
|
export type ICommandPaletteStore = IBaseCommandPaletteStore;
|
||||||
|
|
||||||
|
export class CommandPaletteStore extends BaseCommandPaletteStore implements ICommandPaletteStore {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
makeObservable(this, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,87 +2,43 @@
|
||||||
|
|
||||||
import React, { useCallback, useEffect, FC, useMemo } from "react";
|
import React, { useCallback, useEffect, FC, useMemo } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import useSWR from "swr";
|
|
||||||
// ui
|
// ui
|
||||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { CommandModal, ShortcutsModal } from "@/components/command-palette";
|
import { CommandModal, ShortcutsModal } from "@/components/command-palette";
|
||||||
import { BulkDeleteIssuesModal } from "@/components/core";
|
|
||||||
import { CycleCreateUpdateModal } from "@/components/cycles";
|
|
||||||
import { CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
|
||||||
import { CreateUpdateModuleModal } from "@/components/modules";
|
|
||||||
import { CreatePageModal } from "@/components/pages";
|
|
||||||
import { CreateProjectModal } from "@/components/project";
|
|
||||||
import { CreateUpdateProjectViewModal } from "@/components/views";
|
|
||||||
// constants
|
|
||||||
import { ISSUE_DETAILS } from "@/constants/fetch-keys";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { copyTextToClipboard } from "@/helpers/string.helper";
|
import { copyTextToClipboard } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useUser, useAppTheme, useCommandPalette, useUserPermissions } from "@/hooks/store";
|
import { useEventTracker, useUser, useAppTheme, useCommandPalette, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
|
||||||
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
|
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
// plane web components
|
||||||
|
import {
|
||||||
|
IssueLevelModals,
|
||||||
|
ProjectLevelModals,
|
||||||
|
WorkspaceLevelModals,
|
||||||
|
} from "@/plane-web/components/command-palette/modals";
|
||||||
|
// plane web constants
|
||||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// services
|
// plane web helpers
|
||||||
import { IssueService } from "@/services/issue";
|
import {
|
||||||
|
getGlobalShortcutsList,
|
||||||
// services
|
getProjectShortcutsList,
|
||||||
const issueService = new IssueService();
|
getWorkspaceShortcutsList,
|
||||||
|
handleAdditionalKeyDownEvents,
|
||||||
|
} from "@/plane-web/helpers/command-palette";
|
||||||
|
|
||||||
export const CommandPalette: FC = observer(() => {
|
export const CommandPalette: FC = observer(() => {
|
||||||
// router
|
|
||||||
const router = useAppRouter();
|
|
||||||
// router params
|
// router params
|
||||||
const { workspaceSlug, projectId, issueId, cycleId, moduleId } = useParams();
|
const { workspaceSlug, projectId, issueId } = useParams();
|
||||||
// pathname
|
|
||||||
const pathname = usePathname();
|
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleSidebar } = useAppTheme();
|
const { toggleSidebar } = useAppTheme();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const { platform } = usePlatformOS();
|
const { platform } = usePlatformOS();
|
||||||
const {
|
const { data: currentUser, canPerformAnyCreateAction } = useUser();
|
||||||
data: currentUser,
|
const { toggleCommandPaletteModal, isShortcutModalOpen, toggleShortcutModal, isAnyModalOpen } = useCommandPalette();
|
||||||
// canPerformProjectMemberActions,
|
|
||||||
// canPerformWorkspaceMemberActions,
|
|
||||||
canPerformAnyCreateAction,
|
|
||||||
// canPerformProjectAdminActions,
|
|
||||||
} = useUser();
|
|
||||||
const {
|
|
||||||
issues: { removeIssue },
|
|
||||||
} = useIssuesStore();
|
|
||||||
const {
|
|
||||||
toggleCommandPaletteModal,
|
|
||||||
isCreateIssueModalOpen,
|
|
||||||
toggleCreateIssueModal,
|
|
||||||
isCreateCycleModalOpen,
|
|
||||||
toggleCreateCycleModal,
|
|
||||||
createPageModal,
|
|
||||||
toggleCreatePageModal,
|
|
||||||
isCreateProjectModalOpen,
|
|
||||||
toggleCreateProjectModal,
|
|
||||||
isCreateModuleModalOpen,
|
|
||||||
toggleCreateModuleModal,
|
|
||||||
isCreateViewModalOpen,
|
|
||||||
toggleCreateViewModal,
|
|
||||||
isShortcutModalOpen,
|
|
||||||
toggleShortcutModal,
|
|
||||||
isBulkDeleteIssueModalOpen,
|
|
||||||
toggleBulkDeleteIssueModal,
|
|
||||||
isDeleteIssueModalOpen,
|
|
||||||
toggleDeleteIssueModal,
|
|
||||||
isAnyModalOpen,
|
|
||||||
} = useCommandPalette();
|
|
||||||
const { allowPermissions } = useUserPermissions();
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
const { data: issueDetails } = useSWR(
|
|
||||||
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
|
||||||
workspaceSlug && projectId && issueId
|
|
||||||
? () => issueService.retrieve(workspaceSlug as string, projectId as string, issueId as string)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const canPerformWorkspaceMemberActions = allowPermissions(
|
const canPerformWorkspaceMemberActions = allowPermissions(
|
||||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
|
@ -170,62 +126,11 @@ export const CommandPalette: FC = observer(() => {
|
||||||
project: Record<string, { title: string; description: string; action: () => void }>;
|
project: Record<string, { title: string; description: string; action: () => void }>;
|
||||||
} = useMemo(
|
} = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
global: {
|
global: getGlobalShortcutsList(),
|
||||||
c: {
|
workspace: getWorkspaceShortcutsList(),
|
||||||
title: "Create a new issue",
|
project: getProjectShortcutsList(),
|
||||||
description: "Create a new issue in the current project",
|
|
||||||
action: () => toggleCreateIssueModal(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
workspace: {
|
|
||||||
p: {
|
|
||||||
title: "Create a new project",
|
|
||||||
description: "Create a new project in the current workspace",
|
|
||||||
action: () => toggleCreateProjectModal(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
project: {
|
|
||||||
d: {
|
|
||||||
title: "Create a new page",
|
|
||||||
description: "Create a new page in the current project",
|
|
||||||
action: () => toggleCreatePageModal({ isOpen: true }),
|
|
||||||
},
|
|
||||||
m: {
|
|
||||||
title: "Create a new module",
|
|
||||||
description: "Create a new module in the current project",
|
|
||||||
action: () => toggleCreateModuleModal(true),
|
|
||||||
},
|
|
||||||
q: {
|
|
||||||
title: "Create a new cycle",
|
|
||||||
description: "Create a new cycle in the current project",
|
|
||||||
action: () => toggleCreateCycleModal(true),
|
|
||||||
},
|
|
||||||
v: {
|
|
||||||
title: "Create a new view",
|
|
||||||
description: "Create a new view in the current project",
|
|
||||||
action: () => toggleCreateViewModal(true),
|
|
||||||
},
|
|
||||||
backspace: {
|
|
||||||
title: "Bulk delete issues",
|
|
||||||
description: "Bulk delete issues in the current project",
|
|
||||||
action: () => toggleBulkDeleteIssueModal(true),
|
|
||||||
},
|
|
||||||
delete: {
|
|
||||||
title: "Bulk delete issues",
|
|
||||||
description: "Bulk delete issues in the current project",
|
|
||||||
action: () => toggleBulkDeleteIssueModal(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
[
|
[]
|
||||||
toggleBulkDeleteIssueModal,
|
|
||||||
toggleCreateCycleModal,
|
|
||||||
toggleCreateIssueModal,
|
|
||||||
toggleCreateModuleModal,
|
|
||||||
toggleCreatePageModal,
|
|
||||||
toggleCreateProjectModal,
|
|
||||||
toggleCreateViewModal,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
|
|
@ -296,6 +201,8 @@ export const CommandPalette: FC = observer(() => {
|
||||||
shortcutsList.project[keyPressed].action();
|
shortcutsList.project[keyPressed].action();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Additional keydown events
|
||||||
|
handleAdditionalKeyDownEvents(e);
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
copyIssueUrlToClipboard,
|
copyIssueUrlToClipboard,
|
||||||
|
|
@ -320,75 +227,16 @@ export const CommandPalette: FC = observer(() => {
|
||||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||||
}, [handleKeyDown]);
|
}, [handleKeyDown]);
|
||||||
|
|
||||||
const isDraftIssue = pathname?.includes("draft-issues") || false;
|
|
||||||
|
|
||||||
if (!currentUser) return null;
|
if (!currentUser) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ShortcutsModal isOpen={isShortcutModalOpen} onClose={() => toggleShortcutModal(false)} />
|
<ShortcutsModal isOpen={isShortcutModalOpen} onClose={() => toggleShortcutModal(false)} />
|
||||||
{workspaceSlug && (
|
{workspaceSlug && <WorkspaceLevelModals workspaceSlug={workspaceSlug.toString()} />}
|
||||||
<CreateProjectModal
|
|
||||||
isOpen={isCreateProjectModalOpen}
|
|
||||||
onClose={() => toggleCreateProjectModal(false)}
|
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{workspaceSlug && projectId && (
|
{workspaceSlug && projectId && (
|
||||||
<>
|
<ProjectLevelModals workspaceSlug={workspaceSlug.toString()} projectId={projectId.toString()} />
|
||||||
<CycleCreateUpdateModal
|
|
||||||
isOpen={isCreateCycleModalOpen}
|
|
||||||
handleClose={() => toggleCreateCycleModal(false)}
|
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
|
||||||
projectId={projectId.toString()}
|
|
||||||
/>
|
|
||||||
<CreateUpdateModuleModal
|
|
||||||
isOpen={isCreateModuleModalOpen}
|
|
||||||
onClose={() => toggleCreateModuleModal(false)}
|
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
|
||||||
projectId={projectId.toString()}
|
|
||||||
/>
|
|
||||||
<CreateUpdateProjectViewModal
|
|
||||||
isOpen={isCreateViewModalOpen}
|
|
||||||
onClose={() => toggleCreateViewModal(false)}
|
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
|
||||||
projectId={projectId.toString()}
|
|
||||||
/>
|
|
||||||
<CreatePageModal
|
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
|
||||||
projectId={projectId.toString()}
|
|
||||||
isModalOpen={createPageModal.isOpen}
|
|
||||||
pageAccess={createPageModal.pageAccess}
|
|
||||||
handleModalClose={() => toggleCreatePageModal({ isOpen: false })}
|
|
||||||
redirectionEnabled
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
<IssueLevelModals />
|
||||||
<CreateUpdateIssueModal
|
|
||||||
isOpen={isCreateIssueModalOpen}
|
|
||||||
onClose={() => toggleCreateIssueModal(false)}
|
|
||||||
data={cycleId ? { cycle_id: cycleId.toString() } : moduleId ? { module_ids: [moduleId.toString()] } : undefined}
|
|
||||||
isDraft={isDraftIssue}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{workspaceSlug && projectId && issueId && issueDetails && (
|
|
||||||
<DeleteIssueModal
|
|
||||||
handleClose={() => toggleDeleteIssueModal(false)}
|
|
||||||
isOpen={isDeleteIssueModalOpen}
|
|
||||||
data={issueDetails}
|
|
||||||
onSubmit={async () => {
|
|
||||||
await removeIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString());
|
|
||||||
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<BulkDeleteIssuesModal
|
|
||||||
isOpen={isBulkDeleteIssueModalOpen}
|
|
||||||
onClose={() => toggleBulkDeleteIssueModal(false)}
|
|
||||||
user={currentUser}
|
|
||||||
/>
|
|
||||||
<CommandModal />
|
<CommandModal />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,12 @@ import { Command } from "lucide-react";
|
||||||
import { substringMatch } from "@/helpers/string.helper";
|
import { substringMatch } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
// plane web helpers
|
||||||
|
import {
|
||||||
|
getAdditionalShortcutsList,
|
||||||
|
getCommonShortcutsList,
|
||||||
|
getNavigationShortcutsList,
|
||||||
|
} from "@/plane-web/helpers/command-palette";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
|
|
@ -16,26 +22,14 @@ export const ShortcutCommandsList: React.FC<Props> = (props) => {
|
||||||
{
|
{
|
||||||
key: "navigation",
|
key: "navigation",
|
||||||
title: "Navigation",
|
title: "Navigation",
|
||||||
shortcuts: [{ keys: "Ctrl,K", description: "Open command menu" }],
|
shortcuts: getNavigationShortcutsList(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "common",
|
key: "common",
|
||||||
title: "Common",
|
title: "Common",
|
||||||
shortcuts: [
|
shortcuts: getCommonShortcutsList(platform),
|
||||||
{ keys: "P", description: "Create project" },
|
|
||||||
{ keys: "C", description: "Create issue" },
|
|
||||||
{ keys: "Q", description: "Create cycle" },
|
|
||||||
{ keys: "M", description: "Create module" },
|
|
||||||
{ keys: "V", description: "Create view" },
|
|
||||||
{ keys: "D", description: "Create page" },
|
|
||||||
{ keys: "Delete", description: "Bulk delete issues" },
|
|
||||||
{ keys: "Shift,/", description: "Open shortcuts guide" },
|
|
||||||
{
|
|
||||||
keys: platform === "MacOS" ? "Ctrl,control,C" : "Ctrl,Alt,C",
|
|
||||||
description: "Copy issue URL from the issue details page",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
...getAdditionalShortcutsList(),
|
||||||
];
|
];
|
||||||
|
|
||||||
const filteredShortcuts = KEYBOARD_SHORTCUTS.map((category) => {
|
const filteredShortcuts = KEYBOARD_SHORTCUTS.map((category) => {
|
||||||
|
|
@ -69,7 +63,11 @@ export const ShortcutCommandsList: React.FC<Props> = (props) => {
|
||||||
<div key={key} className="flex items-center gap-1">
|
<div key={key} className="flex items-center gap-1">
|
||||||
{key === "Ctrl" ? (
|
{key === "Ctrl" ? (
|
||||||
<div className="grid h-6 min-w-[1.5rem] place-items-center rounded-sm border-[0.5px] border-custom-border-200 bg-custom-background-90 px-1.5 text-[10px] text-custom-text-200">
|
<div className="grid h-6 min-w-[1.5rem] place-items-center rounded-sm border-[0.5px] border-custom-border-200 bg-custom-background-90 px-1.5 text-[10px] text-custom-text-200">
|
||||||
{ platform === "MacOS" ? <Command className="h-2.5 w-2.5 text-custom-text-200" /> : 'Ctrl'}
|
{platform === "MacOS" ? (
|
||||||
|
<Command className="h-2.5 w-2.5 text-custom-text-200" />
|
||||||
|
) : (
|
||||||
|
"Ctrl"
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<kbd className="grid h-6 min-w-[1.5rem] place-items-center rounded-sm border-[0.5px] border-custom-border-200 bg-custom-background-90 px-1.5 text-[10px] text-custom-text-200">
|
<kbd className="grid h-6 min-w-[1.5rem] place-items-center rounded-sm border-[0.5px] border-custom-border-200 bg-custom-background-90 px-1.5 text-[10px] text-custom-text-200">
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useContext } from "react";
|
||||||
// mobx store
|
// mobx store
|
||||||
import { StoreContext } from "@/lib/store-context";
|
import { StoreContext } from "@/lib/store-context";
|
||||||
// types
|
// types
|
||||||
import { ICommandPaletteStore } from "@/store/command-palette.store";
|
import { ICommandPaletteStore } from "@/plane-web/store/command-palette.store";
|
||||||
|
|
||||||
export const useCommandPalette = (): ICommandPaletteStore => {
|
export const useCommandPalette = (): ICommandPaletteStore => {
|
||||||
const context = useContext(StoreContext);
|
const context = useContext(StoreContext);
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,8 @@ export interface ModalData {
|
||||||
viewId: string;
|
viewId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICommandPaletteStore {
|
export interface IBaseCommandPaletteStore {
|
||||||
// observables
|
// observables
|
||||||
|
|
||||||
isCommandPaletteOpen: boolean;
|
isCommandPaletteOpen: boolean;
|
||||||
isShortcutModalOpen: boolean;
|
isShortcutModalOpen: boolean;
|
||||||
isCreateProjectModalOpen: boolean;
|
isCreateProjectModalOpen: boolean;
|
||||||
|
|
@ -22,6 +21,7 @@ export interface ICommandPaletteStore {
|
||||||
isCreateIssueModalOpen: boolean;
|
isCreateIssueModalOpen: boolean;
|
||||||
isDeleteIssueModalOpen: boolean;
|
isDeleteIssueModalOpen: boolean;
|
||||||
isBulkDeleteIssueModalOpen: boolean;
|
isBulkDeleteIssueModalOpen: boolean;
|
||||||
|
createIssueStoreType: TCreateModalStoreTypes;
|
||||||
// computed
|
// computed
|
||||||
isAnyModalOpen: boolean;
|
isAnyModalOpen: boolean;
|
||||||
// toggle actions
|
// toggle actions
|
||||||
|
|
@ -35,11 +35,9 @@ export interface ICommandPaletteStore {
|
||||||
toggleCreateModuleModal: (value?: boolean) => void;
|
toggleCreateModuleModal: (value?: boolean) => void;
|
||||||
toggleDeleteIssueModal: (value?: boolean) => void;
|
toggleDeleteIssueModal: (value?: boolean) => void;
|
||||||
toggleBulkDeleteIssueModal: (value?: boolean) => void;
|
toggleBulkDeleteIssueModal: (value?: boolean) => void;
|
||||||
|
|
||||||
createIssueStoreType: TCreateModalStoreTypes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommandPaletteStore implements ICommandPaletteStore {
|
export abstract class BaseCommandPaletteStore implements IBaseCommandPaletteStore {
|
||||||
// observables
|
// observables
|
||||||
isCommandPaletteOpen: boolean = false;
|
isCommandPaletteOpen: boolean = false;
|
||||||
isShortcutModalOpen: boolean = false;
|
isShortcutModalOpen: boolean = false;
|
||||||
|
|
@ -51,7 +49,6 @@ export class CommandPaletteStore implements ICommandPaletteStore {
|
||||||
isDeleteIssueModalOpen: boolean = false;
|
isDeleteIssueModalOpen: boolean = false;
|
||||||
isBulkDeleteIssueModalOpen: boolean = false;
|
isBulkDeleteIssueModalOpen: boolean = false;
|
||||||
createPageModal: TCreatePageModal = DEFAULT_CREATE_PAGE_MODAL_DATA;
|
createPageModal: TCreatePageModal = DEFAULT_CREATE_PAGE_MODAL_DATA;
|
||||||
|
|
||||||
createIssueStoreType: TCreateModalStoreTypes = EIssuesStoreType.PROJECT;
|
createIssueStoreType: TCreateModalStoreTypes = EIssuesStoreType.PROJECT;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -67,6 +64,7 @@ export class CommandPaletteStore implements ICommandPaletteStore {
|
||||||
isDeleteIssueModalOpen: observable.ref,
|
isDeleteIssueModalOpen: observable.ref,
|
||||||
isBulkDeleteIssueModalOpen: observable.ref,
|
isBulkDeleteIssueModalOpen: observable.ref,
|
||||||
createPageModal: observable,
|
createPageModal: observable,
|
||||||
|
createIssueStoreType: observable,
|
||||||
// computed
|
// computed
|
||||||
isAnyModalOpen: computed,
|
isAnyModalOpen: computed,
|
||||||
// projectPages: computed,
|
// projectPages: computed,
|
||||||
|
|
@ -85,20 +83,20 @@ export class CommandPaletteStore implements ICommandPaletteStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether any modal is open or not.
|
* Checks whether any modal is open or not in the base command palette.
|
||||||
* @returns boolean
|
* @returns boolean
|
||||||
*/
|
*/
|
||||||
get isAnyModalOpen() {
|
get isAnyModalOpen() {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
this.isCreateIssueModalOpen ||
|
this.isCreateIssueModalOpen ||
|
||||||
this.isCreateCycleModalOpen ||
|
this.isCreateCycleModalOpen ||
|
||||||
this.isCreateProjectModalOpen ||
|
this.isCreateProjectModalOpen ||
|
||||||
this.isCreateModuleModalOpen ||
|
this.isCreateModuleModalOpen ||
|
||||||
this.isCreateViewModalOpen ||
|
this.isCreateViewModalOpen ||
|
||||||
this.isShortcutModalOpen ||
|
this.isShortcutModalOpen ||
|
||||||
this.isBulkDeleteIssueModalOpen ||
|
this.isBulkDeleteIssueModalOpen ||
|
||||||
this.isDeleteIssueModalOpen ||
|
this.isDeleteIssueModalOpen ||
|
||||||
this.createPageModal.isOpen
|
this.createPageModal.isOpen
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { enableStaticRendering } from "mobx-react";
|
import { enableStaticRendering } from "mobx-react";
|
||||||
// plane web store
|
// plane web store
|
||||||
|
import { CommandPaletteStore, ICommandPaletteStore } from "@/plane-web/store/command-palette.store";
|
||||||
import { RootStore } from "@/plane-web/store/root.store";
|
import { RootStore } from "@/plane-web/store/root.store";
|
||||||
import { IStateStore, StateStore } from "@/plane-web/store/state.store";
|
import { IStateStore, StateStore } from "@/plane-web/store/state.store";
|
||||||
// stores
|
// stores
|
||||||
import { CommandPaletteStore, ICommandPaletteStore } from "./command-palette.store";
|
|
||||||
import { CycleStore, ICycleStore } from "./cycle.store";
|
import { CycleStore, ICycleStore } from "./cycle.store";
|
||||||
import { CycleFilterStore, ICycleFilterStore } from "./cycle_filter.store";
|
import { CycleFilterStore, ICycleFilterStore } from "./cycle_filter.store";
|
||||||
import { DashboardStore, IDashboardStore } from "./dashboard.store";
|
import { DashboardStore, IDashboardStore } from "./dashboard.store";
|
||||||
|
|
|
||||||
1
web/ee/components/command-palette/modals/index.ts
Normal file
1
web/ee/components/command-palette/modals/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "ce/components/command-palette/modals";
|
||||||
1
web/ee/store/command-palette.store.ts
Normal file
1
web/ee/store/command-palette.store.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "ce/store/command-palette.store";
|
||||||
1
web/helpers/command-palette.ts
Normal file
1
web/helpers/command-palette.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "ce/helpers/command-palette";
|
||||||
Loading…
Add table
Add a link
Reference in a new issue