mirror of
https://github.com/makeplane/plane.git
synced 2025-12-30 18:30:36 -06:00
[WEB-3872]chore: header switcher enhancements (#6935)
* * chore: alignment and size for header * fix: switcher close on click * chore: moved acces icon component to components
This commit is contained in:
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
// icons
|
||||
import { ArrowRight, PanelRight } from "lucide-react";
|
||||
import { PanelRight } from "lucide-react";
|
||||
// plane constants
|
||||
import {
|
||||
EIssueLayoutTypes,
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
IIssueFilterOptions,
|
||||
} from "@plane/types";
|
||||
// ui
|
||||
import { Breadcrumbs, Button, ContrastIcon, CustomMenu, Tooltip, Header, CustomSearchSelect } from "@plane/ui";
|
||||
import { Breadcrumbs, Button, ContrastIcon, Tooltip, Header, CustomSearchSelect } from "@plane/ui";
|
||||
// components
|
||||
import { ProjectAnalyticsModal } from "@/components/analytics";
|
||||
import { BreadcrumbLink, SwitcherLabel } from "@/components/common";
|
||||
@@ -34,7 +34,6 @@ import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelect
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { isIssueFilterActive } from "@/helpers/filter.helper";
|
||||
import { truncateText } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import {
|
||||
useEventTracker,
|
||||
@@ -53,27 +52,6 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web
|
||||
import { ProjectBreadcrumb } from "@/plane-web/components/breadcrumbs";
|
||||
|
||||
const CycleDropdownOption: React.FC<{ cycleId: string }> = ({ cycleId }) => {
|
||||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const { getCycleById } = useCycle();
|
||||
// derived values
|
||||
const cycle = getCycleById(cycleId);
|
||||
//
|
||||
|
||||
if (!cycle) return null;
|
||||
|
||||
return (
|
||||
<CustomMenu.MenuItem key={cycle.id}>
|
||||
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`} className="flex items-center gap-1.5">
|
||||
<ContrastIcon className="h-3 w-3" />
|
||||
{truncateText(cycle.name, 40)}
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
);
|
||||
};
|
||||
|
||||
export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
// refs
|
||||
const parentRef = useRef<HTMLDivElement>(null);
|
||||
@@ -171,23 +149,16 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
?.map((id) => {
|
||||
const _cycle = id === cycleId ? cycleDetails : getCycleById(id);
|
||||
if (!_cycle) return;
|
||||
const cycleLink = `/${workspaceSlug}/projects/${projectId}/cycles/${_cycle.id}`;
|
||||
return {
|
||||
value: _cycle.id,
|
||||
query: _cycle.name,
|
||||
content: (
|
||||
<Link href={cycleLink}>
|
||||
<SwitcherLabel name={_cycle.name} LabelIcon={ContrastIcon} />
|
||||
</Link>
|
||||
),
|
||||
content: <SwitcherLabel name={_cycle.name} LabelIcon={ContrastIcon} />,
|
||||
};
|
||||
})
|
||||
.filter((option) => option !== undefined) as ICustomSearchSelectOption[];
|
||||
|
||||
const workItemsCount = getGroupIssueCount(undefined, undefined, false);
|
||||
|
||||
const issuesCount = getGroupIssueCount(undefined, undefined, false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProjectAnalyticsModal
|
||||
@@ -231,7 +202,9 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
<CustomSearchSelect
|
||||
options={switcherOptions}
|
||||
value={cycleId}
|
||||
onChange={() => {}}
|
||||
onChange={(value: string) => {
|
||||
router.push(`/${workspaceSlug}/projects/${projectId}/cycles/${value}`);
|
||||
}}
|
||||
label={
|
||||
<div className="flex items-center gap-1">
|
||||
<SwitcherLabel name={cycleDetails?.name} LabelIcon={ContrastIcon} />
|
||||
@@ -256,7 +229,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
</Header.LeftItem>
|
||||
<Header.RightItem>
|
||||
<Header.RightItem className="items-center">
|
||||
<div className="hidden items-center gap-2 md:flex ">
|
||||
<LayoutSelection
|
||||
layouts={[
|
||||
@@ -325,7 +298,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="p-1 rounded outline-none hover:bg-custom-sidebar-background-80 bg-custom-background-80/70"
|
||||
className="p-1.5 rounded outline-none hover:bg-custom-sidebar-background-80 bg-custom-background-80/70"
|
||||
onClick={toggleSidebar}
|
||||
>
|
||||
<PanelRight className={cn("h-4 w-4", !isSidebarCollapsed ? "text-[#3E63DD]" : "text-custom-text-200")} />
|
||||
@@ -335,7 +308,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
cycleId={cycleId}
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
customClassName="flex-shrink-0 flex items-center justify-center size-6 bg-custom-background-80/70 rounded"
|
||||
customClassName="flex-shrink-0 flex items-center justify-center size-[26px] bg-custom-background-80/70 rounded"
|
||||
/>
|
||||
</div>
|
||||
</Header.RightItem>
|
||||
|
||||
@@ -145,15 +145,10 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
?.map((id) => {
|
||||
const _module = id === moduleId ? moduleDetails : getModuleById(id);
|
||||
if (!_module) return;
|
||||
const moduleLink = `/${workspaceSlug}/projects/${projectId}/modules/${_module.id}`;
|
||||
return {
|
||||
value: _module.id,
|
||||
query: _module.name,
|
||||
content: (
|
||||
<Link href={moduleLink}>
|
||||
<SwitcherLabel name={_module.name} LabelIcon={DiceIcon} />
|
||||
</Link>
|
||||
),
|
||||
content: <SwitcherLabel name={_module.name} LabelIcon={DiceIcon} />,
|
||||
};
|
||||
})
|
||||
.filter((option) => option !== undefined) as ICustomSearchSelectOption[];
|
||||
@@ -218,13 +213,15 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
</div>
|
||||
}
|
||||
value={moduleId}
|
||||
onChange={() => {}}
|
||||
onChange={(value: string) => {
|
||||
router.push(`/${workspaceSlug}/projects/${projectId}/modules/${value}`);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Breadcrumbs>
|
||||
</Header.LeftItem>
|
||||
<Header.RightItem>
|
||||
<Header.RightItem className="items-center">
|
||||
<div className="hidden gap-2 md:flex">
|
||||
<LayoutSelection
|
||||
layouts={[
|
||||
@@ -299,7 +296,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="p-1 rounded outline-none hover:bg-custom-sidebar-background-80 bg-custom-background-80/70"
|
||||
className="p-1.5 rounded outline-none hover:bg-custom-sidebar-background-80 bg-custom-background-80/70"
|
||||
onClick={toggleSidebar}
|
||||
>
|
||||
<PanelRight className={cn("h-4 w-4", !isSidebarCollapsed ? "text-[#3E63DD]" : "text-custom-text-200")} />
|
||||
@@ -309,7 +306,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
moduleId={moduleId?.toString()}
|
||||
projectId={projectId.toString()}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
customClassName="flex-shrink-0 flex items-center justify-center size-6 bg-custom-background-80/70 rounded"
|
||||
customClassName="flex-shrink-0 flex items-center justify-center bg-custom-background-80/70 rounded size-[26px]"
|
||||
/>
|
||||
</Header.RightItem>
|
||||
</Header>
|
||||
|
||||
@@ -1,44 +1,32 @@
|
||||
"use client";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { ArchiveIcon, Earth, FileText, Lock } from "lucide-react";
|
||||
import { FileText } from "lucide-react";
|
||||
// types
|
||||
import { EPageAccess } from "@plane/constants";
|
||||
import { ICustomSearchSelectOption, TPage } from "@plane/types";
|
||||
import { ICustomSearchSelectOption } from "@plane/types";
|
||||
// ui
|
||||
import { Breadcrumbs, Header, CustomSearchSelect } from "@plane/ui";
|
||||
// components
|
||||
import { BreadcrumbLink, SwitcherLabel } from "@/components/common";
|
||||
import { BreadcrumbLink, PageAccessIcon, SwitcherLabel } from "@/components/common";
|
||||
import { PageEditInformationPopover } from "@/components/pages";
|
||||
// helpers
|
||||
// hooks
|
||||
import { getPageName } from "@/helpers/page.helper";
|
||||
import { useProject } from "@/hooks/store";
|
||||
// plane web components
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { ProjectBreadcrumb } from "@/plane-web/components/breadcrumbs";
|
||||
import { PageDetailsHeaderExtraActions } from "@/plane-web/components/pages";
|
||||
// plane web hooks
|
||||
import { EPageStoreType, usePage, usePageStore } from "@/plane-web/hooks/store";
|
||||
|
||||
const PageAccessIcon = (page: TPage) => (
|
||||
<div>
|
||||
{page.archived_at ? (
|
||||
<ArchiveIcon className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
) : page.access === EPageAccess.PUBLIC ? (
|
||||
<Earth className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
) : (
|
||||
<Lock className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export interface IPagesHeaderProps {
|
||||
showButton?: boolean;
|
||||
}
|
||||
|
||||
export const PageDetailsHeader = observer(() => {
|
||||
// router
|
||||
const router = useAppRouter();
|
||||
const { workspaceSlug, pageId, projectId } = useParams();
|
||||
// store hooks
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
@@ -55,15 +43,12 @@ export const PageDetailsHeader = observer(() => {
|
||||
.map((id) => {
|
||||
const _page = id === pageId ? page : getPageById(id);
|
||||
if (!_page) return;
|
||||
const pageLink = `/${workspaceSlug}/projects/${projectId}/pages/${_page.id}`;
|
||||
return {
|
||||
value: _page.id,
|
||||
query: _page.name,
|
||||
content: (
|
||||
<div className="flex gap-2 items-center justify-between">
|
||||
<Link href={pageLink} className="flex gap-2 items-center justify-between w-full">
|
||||
<SwitcherLabel logo_props={_page.logo_props} name={getPageName(_page.name)} LabelIcon={FileText} />
|
||||
</Link>
|
||||
<SwitcherLabel logo_props={_page.logo_props} name={getPageName(_page.name)} LabelIcon={FileText} />
|
||||
<PageAccessIcon {..._page} />
|
||||
</div>
|
||||
),
|
||||
@@ -114,7 +99,9 @@ export const PageDetailsHeader = observer(() => {
|
||||
label={
|
||||
<SwitcherLabel logo_props={page.logo_props} name={getPageName(page.name)} LabelIcon={FileText} />
|
||||
}
|
||||
onChange={() => {}}
|
||||
onChange={(value: string) => {
|
||||
router.push(`/${workspaceSlug}/projects/${projectId}/pages/${value}`);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { useCallback, useRef } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Layers, Lock } from "lucide-react";
|
||||
// plane constants
|
||||
@@ -44,6 +43,7 @@ import {
|
||||
useUserPermissions,
|
||||
} from "@/hooks/store";
|
||||
// plane web
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { ProjectBreadcrumb } from "@/plane-web/components/breadcrumbs";
|
||||
|
||||
export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
@@ -51,6 +51,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
const parentRef = useRef(null);
|
||||
// router
|
||||
const { workspaceSlug, projectId, viewId } = useParams();
|
||||
const router = useAppRouter();
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
@@ -151,15 +152,10 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
?.map((id) => {
|
||||
const _view = id === viewId ? viewDetails : getViewById(id);
|
||||
if (!_view) return;
|
||||
const viewLink = `/${workspaceSlug}/projects/${projectId}/views/${_view.id}`;
|
||||
return {
|
||||
value: _view.id,
|
||||
query: _view.name,
|
||||
content: (
|
||||
<Link href={viewLink}>
|
||||
<SwitcherLabel logo_props={_view.logo_props} name={_view.name} LabelIcon={Layers} />
|
||||
</Link>
|
||||
),
|
||||
content: <SwitcherLabel logo_props={_view.logo_props} name={_view.name} LabelIcon={Layers} />,
|
||||
};
|
||||
})
|
||||
.filter((option) => option !== undefined) as ICustomSearchSelectOption[];
|
||||
@@ -186,7 +182,9 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
options={switcherOptions}
|
||||
value={viewId}
|
||||
label={<SwitcherLabel logo_props={viewDetails.logo_props} name={viewDetails.name} LabelIcon={Layers} />}
|
||||
onChange={() => {}}
|
||||
onChange={(value: string) => {
|
||||
router.push(`/${workspaceSlug}/projects/${projectId}/views/${value}`);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -272,7 +270,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
<div className="hidden md:block">
|
||||
<ViewQuickActions
|
||||
parentRef={parentRef}
|
||||
customClassName="flex-shrink-0 flex items-center justify-center size-6 bg-custom-background-80/70 rounded"
|
||||
customClassName="flex-shrink-0 flex items-center justify-center size-[26px] bg-custom-background-80/70 rounded"
|
||||
projectId={projectId.toString()}
|
||||
view={viewDetails}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
|
||||
@@ -7,3 +7,4 @@ export * from "./pro-icon";
|
||||
export * from "./count-chip";
|
||||
export * from "./activity";
|
||||
export * from "./switcher-label";
|
||||
export * from "./page-access-icon";
|
||||
|
||||
15
web/core/components/common/page-access-icon.tsx
Normal file
15
web/core/components/common/page-access-icon.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ArchiveIcon, Earth, Lock } from "lucide-react";
|
||||
import { EPageAccess } from "@plane/constants";
|
||||
import { TPage } from "@plane/types";
|
||||
|
||||
export const PageAccessIcon = (page: TPage) => (
|
||||
<div>
|
||||
{page.archived_at ? (
|
||||
<ArchiveIcon className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
) : page.access === EPageAccess.PUBLIC ? (
|
||||
<Earth className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
) : (
|
||||
<Lock className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
Reference in New Issue
Block a user