[WEB-3978] chore: cmd k search result redirection improvements (#7012)
* fix: work item tab highlight * chore: projectListOpen state and toggle method added to command palette store * chore: openProjectAndScrollToSidebar helper function and highlight keyframes added * chore: SidebarProjectsListItem updated * chore: openProjectAndScrollToSidebar implementation * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor
This commit is contained in:
parent
5f8d5ea388
commit
079c3a3a99
5 changed files with 72 additions and 5 deletions
25
web/core/components/command-palette/actions/helper.tsx
Normal file
25
web/core/components/command-palette/actions/helper.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { store } from "@/lib/store-context";
|
||||
|
||||
export const openProjectAndScrollToSidebar = (itemProjectId: string | undefined) => {
|
||||
if (!itemProjectId) {
|
||||
console.warn("No project id provided. Cannot open project and scroll to sidebar.");
|
||||
return;
|
||||
}
|
||||
// open the project list
|
||||
store.commandPalette.toggleProjectListOpen(itemProjectId, true);
|
||||
// scroll to the element
|
||||
const scrollElementId = `sidebar-${itemProjectId}-JOINED`;
|
||||
const scrollElement = document.getElementById(scrollElementId);
|
||||
// if the element exists, scroll to it
|
||||
if (scrollElement) {
|
||||
setTimeout(() => {
|
||||
scrollElement.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
// Restart the highlight animation every time
|
||||
scrollElement.style.animation = "none";
|
||||
// Trigger a reflow to ensure the animation is restarted
|
||||
void scrollElement.offsetWidth;
|
||||
// Restart the highlight animation
|
||||
scrollElement.style.animation = "highlight 2s ease-in-out";
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { Command } from "cmdk";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import { IWorkspaceSearchResults } from "@plane/types";
|
||||
|
|
@ -8,13 +9,15 @@ import { IWorkspaceSearchResults } from "@plane/types";
|
|||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// plane web imports
|
||||
import { commandGroups } from "@/plane-web/components/command-palette";
|
||||
// helpers
|
||||
import { openProjectAndScrollToSidebar } from "./helper";
|
||||
|
||||
type Props = {
|
||||
closePalette: () => void;
|
||||
results: IWorkspaceSearchResults;
|
||||
};
|
||||
|
||||
export const CommandPaletteSearchResults: React.FC<Props> = (props) => {
|
||||
export const CommandPaletteSearchResults: React.FC<Props> = observer((props) => {
|
||||
const { closePalette, results } = props;
|
||||
// router
|
||||
const router = useAppRouter();
|
||||
|
|
@ -38,6 +41,12 @@ export const CommandPaletteSearchResults: React.FC<Props> = (props) => {
|
|||
onSelect={() => {
|
||||
closePalette();
|
||||
router.push(currentSection.path(item, projectId));
|
||||
const itemProjectId =
|
||||
item?.project_id ||
|
||||
(Array.isArray(item?.project_ids) && item?.project_ids?.length > 0
|
||||
? item?.project_ids[0]
|
||||
: undefined);
|
||||
if (itemProjectId) openProjectAndScrollToSidebar(itemProjectId);
|
||||
}}
|
||||
value={`${key}-${item?.id}-${item.name}-${item.project__identifier ?? ""}-${item.sequence_id ?? ""}`}
|
||||
className="focus:outline-none"
|
||||
|
|
@ -54,4 +63,4 @@ export const CommandPaletteSearchResults: React.FC<Props> = (props) => {
|
|||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { LeaveProjectModal, PublishProjectModal } from "@/components/project";
|
|||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useAppTheme, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppTheme, useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane-web components
|
||||
import { ProjectNavigationRoot } from "@/plane-web/components/sidebar";
|
||||
|
|
@ -64,12 +64,13 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
const { getPartialProjectById } = useProject();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { getIsProjectListOpen, toggleProjectListOpen } = useCommandPalette();
|
||||
// states
|
||||
const [leaveProjectModalOpen, setLeaveProjectModal] = useState(false);
|
||||
const [publishModalOpen, setPublishModal] = useState(false);
|
||||
const [isMenuActive, setIsMenuActive] = useState(false);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isProjectListOpen, setIsProjectListOpen] = useState(false);
|
||||
const isProjectListOpen = getIsProjectListOpen(projectId);
|
||||
const [instruction, setInstruction] = useState<"DRAG_OVER" | "DRAG_BELOW" | undefined>(undefined);
|
||||
// refs
|
||||
const actionSectionRef = useRef<HTMLDivElement | null>(null);
|
||||
|
|
@ -79,6 +80,8 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
const { workspaceSlug, projectId: URLProjectId } = useParams();
|
||||
// derived values
|
||||
const project = getPartialProjectById(projectId);
|
||||
// toggle project list open
|
||||
const setIsProjectListOpen = (value: boolean) => toggleProjectListOpen(projectId, value);
|
||||
// auth
|
||||
const isAdmin = allowPermissions(
|
||||
[EUserPermissions.ADMIN],
|
||||
|
|
@ -198,7 +201,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
if (URLProjectId === project.id) setIsProjectListOpen(true);
|
||||
}, [URLProjectId]);
|
||||
|
||||
const handleItemClick = () => setIsProjectListOpen((prev) => !prev);
|
||||
const handleItemClick = () => setIsProjectListOpen(!isProjectListOpen);
|
||||
return (
|
||||
<>
|
||||
<PublishProjectModal isOpen={publishModalOpen} project={project} onClose={() => setPublishModal(false)} />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { observable, action, makeObservable } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
import {
|
||||
EIssuesStoreType,
|
||||
TCreateModalStoreTypes,
|
||||
|
|
@ -26,6 +27,8 @@ export interface IBaseCommandPaletteStore {
|
|||
isBulkDeleteIssueModalOpen: boolean;
|
||||
createIssueStoreType: TCreateModalStoreTypes;
|
||||
allStickiesModal: boolean;
|
||||
projectListOpenMap: Record<string, boolean>;
|
||||
getIsProjectListOpen: (projectId: string) => boolean;
|
||||
// toggle actions
|
||||
toggleCommandPaletteModal: (value?: boolean) => void;
|
||||
toggleShortcutModal: (value?: boolean) => void;
|
||||
|
|
@ -38,6 +41,7 @@ export interface IBaseCommandPaletteStore {
|
|||
toggleDeleteIssueModal: (value?: boolean) => void;
|
||||
toggleBulkDeleteIssueModal: (value?: boolean) => void;
|
||||
toggleAllStickiesModal: (value?: boolean) => void;
|
||||
toggleProjectListOpen: (projectId: string, value?: boolean) => void;
|
||||
}
|
||||
|
||||
export abstract class BaseCommandPaletteStore implements IBaseCommandPaletteStore {
|
||||
|
|
@ -54,6 +58,7 @@ export abstract class BaseCommandPaletteStore implements IBaseCommandPaletteStor
|
|||
createPageModal: TCreatePageModal = DEFAULT_CREATE_PAGE_MODAL_DATA;
|
||||
createIssueStoreType: TCreateModalStoreTypes = EIssuesStoreType.PROJECT;
|
||||
allStickiesModal: boolean = false;
|
||||
projectListOpenMap: Record<string, boolean> = {};
|
||||
|
||||
constructor() {
|
||||
makeObservable(this, {
|
||||
|
|
@ -70,6 +75,7 @@ export abstract class BaseCommandPaletteStore implements IBaseCommandPaletteStor
|
|||
createPageModal: observable,
|
||||
createIssueStoreType: observable,
|
||||
allStickiesModal: observable,
|
||||
projectListOpenMap: observable,
|
||||
// projectPages: computed,
|
||||
// toggle actions
|
||||
toggleCommandPaletteModal: action,
|
||||
|
|
@ -83,6 +89,7 @@ export abstract class BaseCommandPaletteStore implements IBaseCommandPaletteStor
|
|||
toggleDeleteIssueModal: action,
|
||||
toggleBulkDeleteIssueModal: action,
|
||||
toggleAllStickiesModal: action,
|
||||
toggleProjectListOpen: action,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +111,18 @@ export abstract class BaseCommandPaletteStore implements IBaseCommandPaletteStor
|
|||
this.allStickiesModal
|
||||
);
|
||||
}
|
||||
// computedFn
|
||||
getIsProjectListOpen = computedFn((projectId: string) => this.projectListOpenMap[projectId]);
|
||||
|
||||
/**
|
||||
* Toggles the project list open state
|
||||
* @param projectId
|
||||
* @param value
|
||||
*/
|
||||
toggleProjectListOpen = (projectId: string, value?: boolean) => {
|
||||
if (value !== undefined) this.projectListOpenMap[projectId] = value;
|
||||
else this.projectListOpenMap[projectId] = !this.projectListOpenMap[projectId];
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the command palette modal
|
||||
|
|
|
|||
|
|
@ -942,3 +942,14 @@ div.web-view-spinner div.bar12 {
|
|||
.animate-fade-out {
|
||||
animation: fadeOut 500ms ease-in 100ms forwards;
|
||||
}
|
||||
|
||||
@keyframes highlight {
|
||||
0% {
|
||||
background-color: rgba(var(--color-background-90), 1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
100% {
|
||||
background-color: transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue