projects list responsiveness (#4279)

This commit is contained in:
Ramesh Kumar Chandra 2024-04-29 00:54:02 +05:30 committed by GitHub
parent 4bccbc9804
commit ea436c925a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 130 additions and 27 deletions

View File

@ -108,7 +108,7 @@ export const ProjectsHeader = observer(() => {
className={cn(
"ml-auto flex items-center justify-start gap-1 rounded-md border border-transparent bg-custom-background-100 text-custom-text-400 w-0 transition-[width] ease-linear overflow-hidden opacity-0",
{
"w-64 px-2.5 py-1.5 border-custom-border-200 opacity-100": isSearchOpen,
"w-30 md:w-64 px-2.5 py-1.5 border-custom-border-200 opacity-100": isSearchOpen,
}
)}
>
@ -135,27 +135,29 @@ export const ProjectsHeader = observer(() => {
)}
</div>
</div>
<ProjectOrderByDropdown
value={displayFilters?.order_by}
onChange={(val) => {
if (!workspaceSlug || val === displayFilters?.order_by) return;
updateDisplayFilters(workspaceSlug, {
order_by: val,
});
}}
/>
<FiltersDropdown icon={<ListFilter className="h-3 w-3" />} title="Filters" placement="bottom-end">
<ProjectFiltersSelection
displayFilters={displayFilters ?? {}}
filters={filters ?? {}}
handleFiltersUpdate={handleFilters}
handleDisplayFiltersUpdate={(val) => {
if (!workspaceSlug) return;
updateDisplayFilters(workspaceSlug, val);
<div className="hidden md:flex gap-3">
<ProjectOrderByDropdown
value={displayFilters?.order_by}
onChange={(val) => {
if (!workspaceSlug || val === displayFilters?.order_by) return;
updateDisplayFilters(workspaceSlug, {
order_by: val,
});
}}
memberIds={workspaceMemberIds ?? undefined}
/>
</FiltersDropdown>
<FiltersDropdown icon={<ListFilter className="h-3 w-3" />} title="Filters" placement="bottom-end">
<ProjectFiltersSelection
displayFilters={displayFilters ?? {}}
filters={filters ?? {}}
handleFiltersUpdate={handleFilters}
handleDisplayFiltersUpdate={(val) => {
if (!workspaceSlug) return;
updateDisplayFilters(workspaceSlug, val);
}}
memberIds={workspaceMemberIds ?? undefined}
/>
</FiltersDropdown>
</div>
{isAuthorizedUser && (
<Button
prependIcon={<Plus />}

View File

@ -11,12 +11,13 @@ import { cn } from "@/helpers/common.helper";
type Props = {
onChange: (value: TProjectOrderByOptions) => void;
value: TProjectOrderByOptions | undefined;
isMobile?: boolean;
};
const DISABLED_ORDERING_OPTIONS = ["sort_order"];
export const ProjectOrderByDropdown: React.FC<Props> = (props) => {
const { onChange, value } = props;
const { onChange, value, isMobile = false } = props;
const orderByDetails = PROJECT_ORDER_BY_OPTIONS.find((option) => value?.includes(option.key));
@ -25,12 +26,23 @@ export const ProjectOrderByDropdown: React.FC<Props> = (props) => {
return (
<CustomMenu
className={`${isMobile ? "flex w-full justify-center" : ""}`}
customButton={
<div className={cn(getButtonStyling("neutral-primary", "sm"), "px-2 text-custom-text-300")}>
<ArrowDownWideNarrow className="h-3 w-3" />
{orderByDetails?.label}
<ChevronDown className="h-3 w-3" strokeWidth={2} />
</div>
<>
{isMobile ? (
<div className="flex text-sm items-center gap-2 neutral-primary text-custom-text-200">
<ArrowDownWideNarrow className="h-3 w-3" />
{orderByDetails?.label}
<ChevronDown className="h-3 w-3" strokeWidth={2} />
</div>
) : (
<div className={cn(getButtonStyling("neutral-primary", "sm"), "px-2 text-custom-text-200")}>
<ArrowDownWideNarrow className="h-3 w-3" />
{orderByDetails?.label}
<ChevronDown className="h-3 w-3" strokeWidth={2} />
</div>
)}
</>
}
placement="bottom-end"
closeOnSelect

View File

@ -0,0 +1,88 @@
import { useCallback } from "react";
import { observer } from "mobx-react";
// icons
import { ChevronDown, ListFilter } from "lucide-react";
// types
import { TProjectFilters } from "@plane/types";
// hooks
import { useApplication, useMember, useProjectFilter } from "@/hooks/store";
// components
import { FiltersDropdown } from "../issues";
import { ProjectFiltersSelection, ProjectOrderByDropdown } from "./dropdowns";
const ProjectsMobileHeader = observer(() => {
const {
currentWorkspaceDisplayFilters: displayFilters,
currentWorkspaceFilters: filters,
updateDisplayFilters,
updateFilters,
} = useProjectFilter();
const {
router: { workspaceSlug },
} = useApplication();
const {
workspace: { workspaceMemberIds },
} = useMember();
const handleFilters = useCallback(
(key: keyof TProjectFilters, value: string | string[]) => {
if (!workspaceSlug) return;
const newValues = filters?.[key] ?? [];
if (Array.isArray(value))
value.forEach((val) => {
if (!newValues.includes(val)) newValues.push(val);
else newValues.splice(newValues.indexOf(val), 1);
});
else {
if (filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
else newValues.push(value);
}
updateFilters(workspaceSlug, { [key]: newValues });
},
[filters, updateFilters, workspaceSlug]
);
return (
<div className="flex py-2 border-b border-custom-border-200 md:hidden bg-custom-background-100 w-full">
<ProjectOrderByDropdown
value={displayFilters?.order_by}
onChange={(val) => {
if (!workspaceSlug || val === displayFilters?.order_by) return;
updateDisplayFilters(workspaceSlug, {
order_by: val,
});
}}
isMobile
/>
<div className="border-l border-custom-border-200 flex justify-around w-full">
<FiltersDropdown
icon={<ListFilter className="h-3 w-3" />}
title="Filters"
placement="bottom-end"
menuButton={
<div className="flex text-sm items-center gap-2 neutral-primary text-custom-text-200">
<ListFilter className="h-3 w-3" />
Filters
<ChevronDown className="h-3 w-3" strokeWidth={2} />
</div>
}
>
<ProjectFiltersSelection
displayFilters={displayFilters ?? {}}
filters={filters ?? {}}
handleFiltersUpdate={handleFilters}
handleDisplayFiltersUpdate={(val) => {
if (!workspaceSlug) return;
updateDisplayFilters(workspaceSlug, val);
}}
memberIds={workspaceMemberIds ?? undefined}
/>
</FiltersDropdown>
</div>
</div>
);
});
export default ProjectsMobileHeader;

View File

@ -6,6 +6,7 @@ import { PageHead } from "@/components/core";
import { ProjectsHeader } from "@/components/headers";
import { ProjectAppliedFiltersList, ProjectCardList } from "@/components/project";
// layouts
import ProjectsMobileHeader from "@/components/project/projects-mobile-header";
import { calculateTotalFilters } from "@/helpers/filter.helper";
import { useApplication, useProject, useProjectFilter, useWorkspace } from "@/hooks/store";
import { AppLayout } from "@/layouts/app-layout";
@ -84,7 +85,7 @@ const ProjectsPage: NextPageWithLayout = observer(() => {
});
ProjectsPage.getLayout = function getLayout(page: ReactElement) {
return <AppLayout header={<ProjectsHeader />}>{page}</AppLayout>;
return <AppLayout header={<ProjectsHeader />} mobileHeader={<ProjectsMobileHeader/>}>{page}</AppLayout>;
};
export default ProjectsPage;