refactor: replace keyboard events with command palette store (#2688)

This commit is contained in:
Aaryan Khandelwal 2023-11-08 13:51:55 +05:30 committed by GitHub
parent 53e7da08e4
commit 5a84ed279d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 219 additions and 344 deletions

View File

@ -3,6 +3,7 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
import { Command } from "cmdk";
import { Dialog, Transition } from "@headlessui/react";
import { observer } from "mobx-react-lite";
import {
FileText,
FolderPlus,
@ -16,12 +17,13 @@ import {
UserMinus2,
UserPlus2,
} from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// services
import { WorkspaceService } from "services/workspace.service";
import { IssueService } from "services/issue";
// hooks
import useDebounce from "hooks/use-debounce";
import useUser from "hooks/use-user";
import useToast from "hooks/use-toast";
// components
import {
@ -61,11 +63,8 @@ type Props = {
const workspaceService = new WorkspaceService();
const issueService = new IssueService();
export const CommandModal: React.FC<Props> = (props) => {
export const CommandModal: React.FC<Props> = observer((props) => {
const { deleteIssue, isPaletteOpen, closePalette } = props;
// router
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
// states
const [placeholder, setPlaceholder] = useState("Type a command or search...");
const [resultsCount, setResultsCount] = useState(0);
@ -86,14 +85,19 @@ export const CommandModal: React.FC<Props> = (props) => {
const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(false);
const [pages, setPages] = useState<string[]>([]);
const { user: userStore, commandPalette: commandPaletteStore } = useMobxStore();
const user = userStore.currentUser ?? undefined;
// router
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
const page = pages[pages.length - 1];
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const { setToastAlert } = useToast();
const { user } = useUser();
const { data: issueDetails } = useSWR(
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
workspaceSlug && projectId && issueId
@ -468,10 +472,7 @@ export const CommandModal: React.FC<Props> = (props) => {
<Command.Item
onSelect={() => {
closePalette();
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
commandPaletteStore.toggleCreateIssueModal(true);
}}
className="focus:bg-custom-background-80"
>
@ -488,10 +489,7 @@ export const CommandModal: React.FC<Props> = (props) => {
<Command.Item
onSelect={() => {
closePalette();
const e = new KeyboardEvent("keydown", {
key: "p",
});
document.dispatchEvent(e);
commandPaletteStore.toggleCreateProjectModal(true);
}}
className="focus:outline-none"
>
@ -510,10 +508,7 @@ export const CommandModal: React.FC<Props> = (props) => {
<Command.Item
onSelect={() => {
closePalette();
const e = new KeyboardEvent("keydown", {
key: "q",
});
document.dispatchEvent(e);
commandPaletteStore.toggleCreateCycleModal(true);
}}
className="focus:outline-none"
>
@ -528,10 +523,7 @@ export const CommandModal: React.FC<Props> = (props) => {
<Command.Item
onSelect={() => {
closePalette();
const e = new KeyboardEvent("keydown", {
key: "m",
});
document.dispatchEvent(e);
commandPaletteStore.toggleCreateModuleModal(true);
}}
className="focus:outline-none"
>
@ -546,10 +538,7 @@ export const CommandModal: React.FC<Props> = (props) => {
<Command.Item
onSelect={() => {
closePalette();
const e = new KeyboardEvent("keydown", {
key: "v",
});
document.dispatchEvent(e);
commandPaletteStore.toggleCreateViewModal(true);
}}
className="focus:outline-none"
>
@ -564,10 +553,7 @@ export const CommandModal: React.FC<Props> = (props) => {
<Command.Item
onSelect={() => {
closePalette();
const e = new KeyboardEvent("keydown", {
key: "d",
});
document.dispatchEvent(e);
commandPaletteStore.toggleCreatePageModal(true);
}}
className="focus:outline-none"
>
@ -621,10 +607,7 @@ export const CommandModal: React.FC<Props> = (props) => {
<Command.Item
onSelect={() => {
closePalette();
const e = new KeyboardEvent("keydown", {
key: "h",
});
document.dispatchEvent(e);
commandPaletteStore.toggleShortcutModal(true);
}}
className="focus:outline-none"
>
@ -762,4 +745,4 @@ export const CommandModal: React.FC<Props> = (props) => {
</Dialog>
</Transition.Root>
);
};
});

View File

@ -1,10 +1,12 @@
import { MouseEvent } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import useToast from "hooks/use-toast";
import { useMobxStore } from "lib/mobx/store-provider";
// ui
import { SingleProgressStats } from "components/core";
import {
@ -25,7 +27,6 @@ import { ActiveCycleProgressStats } from "components/cycles";
import { ViewIssueLabel } from "components/issues";
// icons
import { AlarmClock, AlertTriangle, ArrowRight, CalendarDays, Star, Target } from "lucide-react";
// helpers
import { getDateRangeStatus, renderShortDateWithYearFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
import { truncateText } from "helpers/string.helper";
@ -65,12 +66,12 @@ interface IActiveCycleDetails {
projectId: string;
}
export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = (props) => {
export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props) => {
const router = useRouter();
const { workspaceSlug, projectId } = props;
const { cycle: cycleStore } = useMobxStore();
const { cycle: cycleStore, commandPalette: commandPaletteStore } = useMobxStore();
const { setToastAlert } = useToast();
@ -117,12 +118,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = (props) => {
<button
type="button"
className="text-custom-primary-100 text-sm outline-none"
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "q",
});
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreateCycleModal(true)}
>
Create a new cycle
</button>
@ -485,4 +481,4 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = (props) => {
</div>
</div>
);
};
});

View File

@ -1,8 +1,11 @@
import { FC } from "react";
// types
import { ICycle } from "types";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { CyclePeekOverview, CyclesBoardCard } from "components/cycles";
// types
import { ICycle } from "types";
export interface ICyclesBoard {
cycles: ICycle[];
@ -12,9 +15,11 @@ export interface ICyclesBoard {
peekCycle: string;
}
export const CyclesBoard: FC<ICyclesBoard> = (props) => {
export const CyclesBoard: FC<ICyclesBoard> = observer((props) => {
const { cycles, filter, workspaceSlug, projectId, peekCycle } = props;
const { commandPalette: commandPaletteStore } = useMobxStore();
return (
<>
{cycles.length > 0 ? (
@ -53,12 +58,7 @@ export const CyclesBoard: FC<ICyclesBoard> = (props) => {
<button
type="button"
className="text-custom-primary-100 text-sm outline-none"
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "q",
});
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreateCycleModal(true)}
>
Create a new cycle
</button>
@ -67,4 +67,4 @@ export const CyclesBoard: FC<ICyclesBoard> = (props) => {
)}
</>
);
};
});

View File

@ -1,7 +1,9 @@
import { FC } from "react";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { CyclePeekOverview, CyclesListItem } from "components/cycles";
// ui
import { Loader } from "@plane/ui";
// types
@ -14,9 +16,11 @@ export interface ICyclesList {
projectId: string;
}
export const CyclesList: FC<ICyclesList> = (props) => {
export const CyclesList: FC<ICyclesList> = observer((props) => {
const { cycles, filter, workspaceSlug, projectId } = props;
const { commandPalette: commandPaletteStore } = useMobxStore();
return (
<>
{cycles ? (
@ -53,12 +57,7 @@ export const CyclesList: FC<ICyclesList> = (props) => {
<button
type="button"
className="text-custom-primary-100 text-sm outline-none"
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "q",
});
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreateCycleModal(true)}
>
Create a new cycle
</button>
@ -75,4 +74,4 @@ export const CyclesList: FC<ICyclesList> = (props) => {
)}
</>
);
};
});

View File

@ -317,11 +317,11 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
<LinkIcon className="h-3 w-3 text-custom-text-300" />
</button>
{!isCompleted && (
<CustomMenu width="lg" ellipsis>
<CustomMenu width="lg" placement="bottom-end" ellipsis>
<CustomMenu.MenuItem onClick={() => setCycleDeleteModal(true)}>
<span className="flex items-center justify-start gap-2">
<Trash2 className="h-4 w-4" />
<span>Delete</span>
<Trash2 className="h-3 w-3" />
<span>Delete cycle</span>
</span>
</CustomMenu.MenuItem>
</CustomMenu>

View File

@ -31,6 +31,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
cycle: cycleStore,
cycleIssueFilter: cycleIssueFilterStore,
project: projectStore,
commandPalette: commandPaletteStore,
} = useMobxStore();
const { currentProjectDetails } = projectStore;
@ -139,7 +140,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
type="component"
component={
<CustomMenu
placement="bottom-start"
label={
<>
<ContrastIcon className="h-3 w-3" />
@ -148,6 +148,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
}
className="ml-1.5 flex-shrink-0"
width="auto"
placement="bottom-start"
>
{cyclesList?.map((cycle) => (
<CustomMenu.MenuItem
@ -194,16 +195,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
Analytics
</Button>
<Button
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
}}
size="sm"
prependIcon={<Plus />}
>
<Button onClick={() => commandPaletteStore.toggleCreateIssueModal(true)} size="sm" prependIcon={<Plus />}>
Add Issue
</Button>
<button

View File

@ -1,22 +1,20 @@
import { FC } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Plus } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// ui
import { Breadcrumbs, Button, ContrastIcon } from "@plane/ui";
// helpers
import { renderEmoji } from "helpers/emoji.helper";
// hooks
import { useMobxStore } from "lib/mobx/store-provider";
export interface ICyclesHeader {}
export const CyclesHeader: FC<ICyclesHeader> = (props) => {
const {} = props;
export const CyclesHeader: FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// store
const { project: projectStore } = useMobxStore();
const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
return (
@ -54,14 +52,11 @@ export const CyclesHeader: FC<ICyclesHeader> = (props) => {
<Button
variant="primary"
prependIcon={<Plus />}
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "q" });
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreateCycleModal(true)}
>
Add Cycle
</Button>
</div>
</div>
);
};
});

View File

@ -31,6 +31,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
module: moduleStore,
moduleFilter: moduleFilterStore,
project: projectStore,
commandPalette: commandPaletteStore,
} = useMobxStore();
const activeLayout = issueFilterStore.userDisplayFilters.layout;
const { currentProjectDetails } = projectStore;
@ -146,6 +147,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
}
className="ml-1.5 flex-shrink-0"
width="auto"
placement="bottom-start"
>
{modulesList?.map((module) => (
<CustomMenu.MenuItem
@ -192,16 +194,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
Analytics
</Button>
<Button
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
}}
size="sm"
prependIcon={<Plus />}
>
<Button onClick={() => commandPaletteStore.toggleCreateIssueModal(true)} size="sm" prependIcon={<Plus />}>
Add Issue
</Button>
<button

View File

@ -17,7 +17,7 @@ export const ModulesListHeader: React.FC = observer(() => {
const router = useRouter();
const { workspaceSlug } = router.query;
// store
const { project: projectStore } = useMobxStore();
const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
const { storedValue: modulesView, setValue: setModulesView } = useLocalStorage("modules_view", "grid");
@ -77,10 +77,7 @@ export const ModulesListHeader: React.FC = observer(() => {
<Button
variant="primary"
prependIcon={<Plus />}
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "m" });
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreateModuleModal(true)}
>
Add Module
</Button>

View File

@ -24,10 +24,11 @@ const pageService = new PageService();
export const PageDetailsHeader: FC<IPagesHeaderProps> = observer((props) => {
const { showButton = false } = props;
const router = useRouter();
const { workspaceSlug, pageId } = router.query;
const { project: projectStore } = useMobxStore();
const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
const { data: pageDetails } = useSWR(
@ -78,10 +79,7 @@ export const PageDetailsHeader: FC<IPagesHeaderProps> = observer((props) => {
variant="primary"
prependIcon={<Plus />}
size="sm"
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "d" });
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreatePageModal(true)}
>
Create Page
</Button>

View File

@ -18,7 +18,7 @@ export const PagesHeader: FC<IPagesHeaderProps> = observer((props) => {
const router = useRouter();
const { workspaceSlug } = router.query;
const { project: projectStore } = useMobxStore();
const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
return (
@ -56,10 +56,7 @@ export const PagesHeader: FC<IPagesHeaderProps> = observer((props) => {
variant="primary"
prependIcon={<Plus />}
size="sm"
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "d" });
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreatePageModal(true)}
>
Create Page
</Button>

View File

@ -23,7 +23,12 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { issueFilter: issueFilterStore, project: projectStore, inbox: inboxStore } = useMobxStore();
const {
issueFilter: issueFilterStore,
project: projectStore,
inbox: inboxStore,
commandPalette: commandPaletteStore,
} = useMobxStore();
const activeLayout = issueFilterStore.userDisplayFilters.layout;
@ -198,16 +203,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
Analytics
</Button>
<Button
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
}}
size="sm"
prependIcon={<Plus />}
>
<Button onClick={() => commandPaletteStore.toggleCreateIssueModal(true)} size="sm" prependIcon={<Plus />}>
Add Issue
</Button>
</div>

View File

@ -11,7 +11,7 @@ export const ProjectsHeader = observer(() => {
const { workspaceSlug } = router.query;
// store
const { project: projectStore } = useMobxStore();
const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
const projectsList = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : [];
@ -43,14 +43,7 @@ export const ProjectsHeader = observer(() => {
</div>
)}
<Button
prependIcon={<Plus />}
size="md"
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "p" });
document.dispatchEvent(e);
}}
>
<Button prependIcon={<Plus />} size="md" onClick={() => commandPaletteStore.toggleCreateProjectModal(true)}>
Add Project
</Button>
</div>

View File

@ -16,15 +16,14 @@ export const JiraGetImportDetail: React.FC = observer(() => {
const router = useRouter();
const { workspaceSlug } = router.query;
const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined;
const {
control,
formState: { errors },
} = useFormContext<IJiraImporterForm>();
const { project: projectStore } = useMobxStore();
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined;
return (
<div className="h-full w-full space-y-8 overflow-y-auto">
<div className="grid grid-cols-1 gap-10 md:grid-cols-2">
@ -190,10 +189,7 @@ export const JiraGetImportDetail: React.FC = observer(() => {
<div>
<button
type="button"
onClick={() => {
const event = new KeyboardEvent("keydown", { key: "p" });
document.dispatchEvent(event);
}}
onClick={() => commandPaletteStore.toggleCreateProjectModal(true)}
className="flex cursor-pointer select-none items-center space-x-2 truncate rounded px-1 py-1.5 text-custom-text-200"
>
<Plus className="h-4 w-4 text-custom-text-200" />

View File

@ -26,7 +26,7 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
// states
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
const { cycleIssue: cycleIssueStore } = useMobxStore();
const { cycleIssue: cycleIssueStore, commandPalette: commandPaletteStore } = useMobxStore();
const { setToastAlert } = useToast();
@ -62,12 +62,7 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
primaryButton={{
text: "New issue",
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateIssueModal(true),
}}
secondaryButton={
<Button

View File

@ -1,10 +1,16 @@
import { observer } from "mobx-react-lite";
import { PlusIcon } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { EmptyState } from "components/common";
// assets
import emptyIssue from "public/empty-state/issue.svg";
export const GlobalViewEmptyState: React.FC = () => (
export const GlobalViewEmptyState: React.FC = observer(() => {
const { commandPalette: commandPaletteStore } = useMobxStore();
return (
<div className="h-full w-full grid place-items-center">
<EmptyState
title="View issues will appear here"
@ -13,13 +19,9 @@ export const GlobalViewEmptyState: React.FC = () => (
primaryButton={{
text: "New issue",
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateIssueModal(true),
}}
/>
</div>
);
);
});

View File

@ -22,7 +22,7 @@ export const ModuleEmptyState: React.FC<Props> = observer((props) => {
// states
const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false);
const { moduleIssue: moduleIssueStore } = useMobxStore();
const { moduleIssue: moduleIssueStore, commandPalette: commandPaletteStore } = useMobxStore();
const { setToastAlert } = useToast();
@ -58,12 +58,7 @@ export const ModuleEmptyState: React.FC<Props> = observer((props) => {
primaryButton={{
text: "New issue",
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateIssueModal(true),
}}
secondaryButton={
<Button

View File

@ -1,10 +1,16 @@
import { observer } from "mobx-react-lite";
import { PlusIcon } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { EmptyState } from "components/common";
// assets
import emptyIssue from "public/empty-state/issue.svg";
export const ProjectViewEmptyState: React.FC = () => (
export const ProjectViewEmptyState: React.FC = observer(() => {
const { commandPalette: commandPaletteStore } = useMobxStore();
return (
<div className="h-full w-full grid place-items-center">
<EmptyState
title="View issues will appear here"
@ -13,13 +19,9 @@ export const ProjectViewEmptyState: React.FC = () => (
primaryButton={{
text: "New issue",
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateIssueModal(true),
}}
/>
</div>
);
);
});

View File

@ -1,10 +1,16 @@
import { observer } from "mobx-react-lite";
import { PlusIcon } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { EmptyState } from "components/common";
// assets
import emptyIssue from "public/empty-state/issue.svg";
export const ProjectEmptyState: React.FC = () => (
export const ProjectEmptyState: React.FC = observer(() => {
const { commandPalette: commandPaletteStore } = useMobxStore();
return (
<div className="h-full w-full grid place-items-center">
<EmptyState
title="Project issues will appear here"
@ -13,13 +19,9 @@ export const ProjectEmptyState: React.FC = () => (
primaryButton={{
text: "New issue",
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateIssueModal(true),
}}
/>
</div>
);
);
});

View File

@ -17,7 +17,7 @@ export const ModulesListView: React.FC = observer(() => {
const router = useRouter();
const { workspaceSlug, projectId, peekModule } = router.query;
const { module: moduleStore } = useMobxStore();
const { module: moduleStore, commandPalette: commandPaletteStore } = useMobxStore();
const { storedValue: modulesView } = useLocalStorage("modules_view", "grid");
@ -85,12 +85,7 @@ export const ModulesListView: React.FC = observer(() => {
primaryButton={{
icon: <Plus className="h-4 w-4" />,
text: "New Module",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "m",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateModuleModal(true),
}}
/>
)}

View File

@ -246,11 +246,11 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<button onClick={handleCopyText}>
<LinkIcon className="h-3 w-3 text-custom-text-300" />
</button>
<CustomMenu width="lg" ellipsis>
<CustomMenu width="lg" placement="bottom-end" ellipsis>
<CustomMenu.MenuItem onClick={() => setModuleDeleteModal(true)}>
<span className="flex items-center justify-start gap-2">
<Trash2 className="h-4 w-4" />
<span>Delete</span>
<Trash2 className="h-3 w-3" />
<span>Delete module</span>
</span>
</CustomMenu.MenuItem>
</CustomMenu>

View File

@ -18,7 +18,8 @@ export const WorkspaceDashboardView = observer(() => {
const router = useRouter();
const { workspaceSlug } = router.query;
// store
const { user: userStore, project: projectStore } = useMobxStore();
const { user: userStore, project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
const user = userStore.currentUser;
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null;
const workspaceDashboardInfo = userStore.dashboardInfo;
@ -67,16 +68,7 @@ export const WorkspaceDashboardView = observer(() => {
<div className="p-5 md:p-8 pr-0">
<h5 className="text-xl font-semibold">Create a project</h5>
<p className="mt-2 mb-5">Manage your projects by creating issues, cycles, modules, views and pages.</p>
<Button
variant="primary"
size="sm"
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "p",
});
document.dispatchEvent(e);
}}
>
<Button variant="primary" size="sm" onClick={() => commandPaletteStore.toggleCreateProjectModal(true)}>
Create Project
</Button>
</div>

View File

@ -1,19 +1,18 @@
import React from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
import { Plus } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// services
import { PageService } from "services/page.service";
// components
import { PagesView } from "components/pages";
// ui
import { EmptyState } from "components/common";
// ui
import { Loader } from "@plane/ui";
// icons
import { Plus } from "lucide-react";
// images
// assets
import emptyPage from "public/empty-state/page.svg";
// helpers
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
@ -26,7 +25,11 @@ import { RECENT_PAGES_LIST } from "constants/fetch-keys";
// services
const pageService = new PageService();
export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
export const RecentPagesList: React.FC<TPagesListProps> = observer((props) => {
const { viewType } = props;
const { commandPalette: commandPaletteStore } = useMobxStore();
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -46,9 +49,7 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
return (
<div key={key} className="h-full overflow-hidden pb-9">
<h2 className="text-xl font-semibold capitalize mb-2">
{replaceUnderscoreIfSnakeCase(key)}
</h2>
<h2 className="text-xl font-semibold capitalize mb-2">{replaceUnderscoreIfSnakeCase(key)}</h2>
<PagesView pages={pages[key as keyof RecentPagesResponse]} viewType={viewType} />
</div>
);
@ -61,12 +62,7 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
primaryButton={{
icon: <Plus className="h-4 w-4" />,
text: "New Page",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "d",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreatePageModal(true),
}}
/>
)
@ -79,4 +75,4 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
)}
</>
);
};
});

View File

@ -1,21 +1,20 @@
import { useState } from "react";
import useSWR, { mutate } from "swr";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR, { mutate } from "swr";
import { Plus } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// services
import { PageService } from "services/page.service";
import { ProjectService } from "services/project";
// hooks
import useToast from "hooks/use-toast";
import useUserAuth from "hooks/use-user-auth";
// components
import { CreateUpdatePageModal, DeletePageModal, SinglePageDetailedItem, SinglePageListItem } from "components/pages";
// ui
import { EmptyState } from "components/common";
// ui
import { Loader } from "@plane/ui";
// icons
import { Plus } from "lucide-react";
// images
import emptyPage from "public/empty-state/page.svg";
// types
@ -37,17 +36,19 @@ type Props = {
const pageService = new PageService();
const projectService = new ProjectService();
export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
export const PagesView: React.FC<Props> = observer(({ pages, viewType }) => {
// states
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
const [selectedPageToUpdate, setSelectedPageToUpdate] = useState<IPage | null>(null);
const [deletePageModal, setDeletePageModal] = useState(false);
const [selectedPageToDelete, setSelectedPageToDelete] = useState<IPage | null>(null);
const { user } = useUserAuth();
const { user: userStore, commandPalette: commandPaletteStore } = useMobxStore();
const user = userStore.currentUser ?? undefined;
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { setToastAlert } = useToast();
@ -163,7 +164,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
};
const partialUpdatePage = (page: IPage, formData: Partial<IPage>) => {
if (!workspaceSlug || !projectId) return;
if (!workspaceSlug || !projectId || !user) return;
mutate<IPage[]>(
ALL_PAGES_LIST(projectId.toString()),
@ -264,12 +265,7 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
primaryButton={{
icon: <Plus className="h-4 w-4" />,
text: "New Page",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "d",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreatePageModal(true),
}}
/>
)}
@ -294,4 +290,4 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
)}
</>
);
};
});

View File

@ -18,7 +18,7 @@ export interface IProjectCardList {
export const ProjectCardList: FC<IProjectCardList> = observer((props) => {
const { workspaceSlug } = props;
// store
const { project: projectStore } = useMobxStore();
const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null;
@ -53,12 +53,7 @@ export const ProjectCardList: FC<IProjectCardList> = observer((props) => {
primaryButton={{
icon: <Plus className="h-4 w-4" />,
text: "New Project",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "p",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateProjectModal(true),
}}
/>
)}

View File

@ -19,11 +19,6 @@ import { IProject } from "types";
import { useMobxStore } from "lib/mobx/store-provider";
export const ProjectSidebarList: FC = observer(() => {
const { theme: themeStore, project: projectStore } = useMobxStore();
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// states
const [isFavoriteProjectCreate, setIsFavoriteProjectCreate] = useState(false);
const [isProjectModalOpen, setIsProjectModalOpen] = useState(false);
@ -31,6 +26,11 @@ export const ProjectSidebarList: FC = observer(() => {
// refs
const containerRef = useRef<HTMLDivElement | null>(null);
const { theme: themeStore, project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// toast
const { setToastAlert } = useToast();
@ -254,12 +254,7 @@ export const ProjectSidebarList: FC = observer(() => {
<button
type="button"
className="flex w-full items-center gap-2 px-3 text-sm text-custom-sidebar-text-200"
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "p",
});
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreateProjectModal(true)}
>
<Plus className="h-5 w-5" />
{!isCollapsed && "Add Project"}

View File

@ -20,7 +20,7 @@ export const ProjectViewsList = observer(() => {
const router = useRouter();
const { projectId } = router.query;
const { projectViews: projectViewsStore } = useMobxStore();
const { projectViews: projectViewsStore, commandPalette: commandPaletteStore } = useMobxStore();
const viewsList = projectId ? projectViewsStore.viewsList[projectId.toString()] : undefined;
@ -66,12 +66,7 @@ export const ProjectViewsList = observer(() => {
primaryButton={{
icon: <Plus size={14} strokeWidth={2} />,
text: "New View",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "v",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateViewModal(true),
}}
/>
)}

View File

@ -43,7 +43,7 @@ export interface WorkspaceHelpSectionProps {
export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = observer(() => {
// store
const { theme: themeStore } = useMobxStore();
const { theme: themeStore, commandPalette: commandPaletteStore } = useMobxStore();
// states
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
// refs
@ -71,12 +71,7 @@ export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = observe
className={`grid place-items-center rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
isCollapsed ? "w-full" : ""
}`}
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "h",
});
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleShortcutModal(true)}
>
<Zap className="h-3.5 w-3.5" />
</button>

View File

@ -8,14 +8,18 @@ import useLocalStorage from "hooks/use-local-storage";
import { CreateUpdateDraftIssueModal } from "components/issues";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
import { observer } from "mobx-react-lite";
export const WorkspaceSidebarQuickAction = () => {
const store: any = useMobxStore();
export const WorkspaceSidebarQuickAction = observer(() => {
// states
const [isDraftIssueModalOpen, setIsDraftIssueModalOpen] = useState(false);
const { theme: themeStore, commandPalette: commandPaletteStore } = useMobxStore();
const { storedValue, clearValue } = useLocalStorage<any>("draftedIssue", JSON.stringify({}));
const isSidebarCollapsed = themeStore.sidebarCollapsed;
return (
<>
<CreateUpdateDraftIssueModal
@ -31,12 +35,12 @@ export const WorkspaceSidebarQuickAction = () => {
<div
className={`flex items-center justify-between w-full cursor-pointer px-4 mt-4 ${
store?.theme?.sidebarCollapsed ? "flex-col gap-1" : "gap-2"
isSidebarCollapsed ? "flex-col gap-1" : "gap-2"
}`}
>
<div
className={`relative flex items-center justify-between w-full rounded cursor-pointer px-2 gap-1 group ${
store?.theme?.sidebarCollapsed
isSidebarCollapsed
? "px-2 hover:bg-custom-sidebar-background-80"
: "px-3 shadow border-[0.5px] border-custom-border-300"
}`}
@ -44,29 +48,22 @@ export const WorkspaceSidebarQuickAction = () => {
<button
type="button"
className={`relative flex items-center gap-2 flex-grow rounded flex-shrink-0 py-1.5 ${
store?.theme?.sidebarCollapsed ? "justify-center" : ""
isSidebarCollapsed ? "justify-center" : ""
}`}
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "c" });
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCreateIssueModal(true)}
>
<PenSquare className="h-4 w-4 text-custom-sidebar-text-300" />
{!store?.theme?.sidebarCollapsed && <span className="text-sm font-medium">New Issue</span>}
{!isSidebarCollapsed && <span className="text-sm font-medium">New Issue</span>}
</button>
{storedValue && Object.keys(JSON.parse(storedValue)).length > 0 && (
<>
<div
className={`h-8 w-0.5 bg-custom-sidebar-background-80 ${
store?.theme?.sidebarCollapsed ? "hidden" : "block"
}`}
/>
<div className={`h-8 w-0.5 bg-custom-sidebar-background-80 ${isSidebarCollapsed ? "hidden" : "block"}`} />
<button
type="button"
className={`flex items-center justify-center rounded flex-shrink-0 py-1.5 ml-1.5 ${
store?.theme?.sidebarCollapsed ? "hidden" : "block"
isSidebarCollapsed ? "hidden" : "block"
}`}
>
<ChevronDown
@ -77,7 +74,7 @@ export const WorkspaceSidebarQuickAction = () => {
<div
className={`fixed h-10 pt-2 w-[203px] left-4 opacity-0 group-hover:opacity-100 mt-0 pointer-events-none group-hover:pointer-events-auto ${
store?.theme?.sidebarCollapsed ? "top-[5.5rem]" : "top-24"
isSidebarCollapsed ? "top-[5.5rem]" : "top-24"
}`}
>
<div className="w-full h-full">
@ -96,18 +93,15 @@ export const WorkspaceSidebarQuickAction = () => {
<button
className={`flex items-center justify-center rounded flex-shrink-0 p-2 ${
store?.theme?.sidebarCollapsed
isSidebarCollapsed
? "hover:bg-custom-sidebar-background-80"
: "shadow border-[0.5px] border-custom-border-300"
}`}
onClick={() => {
const e = new KeyboardEvent("keydown", { key: "k", ctrlKey: true, metaKey: true });
document.dispatchEvent(e);
}}
onClick={() => commandPaletteStore.toggleCommandPaletteModal(true)}
>
<Search className="h-4 w-4 text-custom-sidebar-text-300" />
</button>
</div>
</>
);
};
});

View File

@ -25,6 +25,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
module: moduleStore,
projectViews: projectViewsStore,
inbox: inboxStore,
commandPalette: commandPaletteStore,
} = useMobxStore();
// router
const router = useRouter();
@ -131,12 +132,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
image={emptyProject}
primaryButton={{
text: "Create Project",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "p",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateProjectModal(true),
}}
/>
</div>

View File

@ -28,7 +28,7 @@ const AnalyticsPage: NextPageWithLayout = observer(() => {
const router = useRouter();
const { workspaceSlug } = router.query;
// store
const { project: projectStore, user: userStore } = useMobxStore();
const { project: projectStore, user: userStore, commandPalette: commandPaletteStore } = useMobxStore();
const user = userStore.currentUser;
const projects = workspaceSlug ? projectStore.projects[workspaceSlug?.toString()] : null;
@ -96,12 +96,7 @@ const AnalyticsPage: NextPageWithLayout = observer(() => {
primaryButton={{
icon: <Plus className="h-4 w-4" />,
text: "New Project",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "p",
});
document.dispatchEvent(e);
},
onClick: () => commandPaletteStore.toggleCreateProjectModal(true),
}}
/>
</>