style: made new issue filter dropdown (#462)

This commit is contained in:
Dakshesh Jain 2023-03-16 16:27:18 +05:30 committed by GitHub
parent a84abc60b2
commit 0f06589b83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 232 additions and 55 deletions

View File

@ -14,7 +14,7 @@ import useIssuesView from "hooks/use-issues-view";
// headless ui // headless ui
import { Popover, Transition } from "@headlessui/react"; import { Popover, Transition } from "@headlessui/react";
// ui // ui
import { CustomMenu } from "components/ui"; import { CustomMenu, MultiLevelDropdown } from "components/ui";
// icons // icons
import { ChevronDownIcon, ListBulletIcon } from "@heroicons/react/24/outline"; import { ChevronDownIcon, ListBulletIcon } from "@heroicons/react/24/outline";
import { Squares2X2Icon } from "@heroicons/react/20/solid"; import { Squares2X2Icon } from "@heroicons/react/20/solid";
@ -96,59 +96,69 @@ export const IssuesFilterView: React.FC = () => {
<Squares2X2Icon className="h-4 w-4" /> <Squares2X2Icon className="h-4 w-4" />
</button> </button>
</div> </div>
<CustomMenu <MultiLevelDropdown
label={ label="Filter"
<span className="flex items-center gap-2 rounded-md py-1 text-xs font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-900 focus:outline-none"> onSelect={(option) => {
Filters setFilters({
</span> ...filters,
} [option.key]: [
> ...((filters?.[option.key as keyof typeof filters] as any[]) ?? []),
<h4 className="px-1 py-2 font-medium">Status</h4> option.value,
{statesList?.map((state) => ( ],
<CustomMenu.MenuItem });
onClick={() => { }}
const filterStates = filters?.state ?? []; direction="left"
const newFilterState = filterStates.includes(state.id) options={[
? filterStates.filter((id) => id !== state.id) {
: [...filterStates, state.id]; id: "priority",
setFilters({ ...filters, state: newFilterState }); label: "Priority",
}} value: PRIORITIES,
> children: [
<>{state.name}</> ...PRIORITIES.map((priority) => ({
</CustomMenu.MenuItem> id: priority ?? "none",
))} label: priority ?? "None",
<h4 className="px-1 py-2 font-medium">Members</h4> value: {
{members?.map((member) => ( key: "priority",
<CustomMenu.MenuItem onClick={() => {}}> value: priority,
<> },
{member.member.first_name && member.member.first_name !== "" selected: filters?.priority?.includes(priority ?? "none"),
? member.member.first_name + " " + member.member.last_name })),
: member.member.email} ],
</> },
</CustomMenu.MenuItem> {
))} id: "state",
<h4 className="px-1 py-2 font-medium">Labels</h4> label: "State",
{issueLabels?.map((label) => ( value: statesList,
<CustomMenu.MenuItem onClick={() => {}}> children: [
<>{label.name}</> ...statesList.map((state) => ({
</CustomMenu.MenuItem> id: state.id,
))} label: state.name,
<h4 className="px-1 py-2 font-medium">Priority</h4> value: {
{PRIORITIES?.map((priority) => ( key: "state",
<CustomMenu.MenuItem value: state.id,
onClick={() => { },
if (priority === null) return; selected: filters?.state?.includes(state.id),
const filterPriorities = filters?.priority ?? []; })),
const newFilterPriority = filterPriorities.includes(priority) ],
? filterPriorities.filter((id) => id !== priority) },
: [...filterPriorities, priority]; {
setFilters({ ...filters, priority: newFilterPriority }); id: "assignee",
}} label: "Assignee",
> value: members,
<span className="capitalize">{priority ?? "None"}</span> children: [
</CustomMenu.MenuItem> ...(members?.map((member) => ({
))} id: member.member.id,
</CustomMenu> label: member.member.first_name,
value: {
key: "assignee",
value: member.member.id,
},
selected: filters?.assignees?.includes(member.member.id),
})) ?? []),
],
},
]}
/>
<Popover className="relative"> <Popover className="relative">
{({ open }) => ( {({ open }) => (
<> <>

View File

@ -458,6 +458,35 @@ export const IssuesView: React.FC<Props> = ({ type = "issue", openIssuesListModa
</p> </p>
) )
) )
: key === "assignee"
? (filters[key as keyof IIssueFilterOptions] as any)?.map(
(member: any) => (
<p
key={member}
className="inline-flex items-center gap-x-1 rounded-full bg-gray-500 px-2 py-0.5 text-xs font-medium capitalize text-white"
>
<span>
{
members?.find((m) => m.member.id === member)?.member
.first_name
}
</span>
<span
className="cursor-pointer"
onClick={() => {
setFilters({
...filters,
[key as keyof IIssueFilterOptions]: (
filters[key as keyof IIssueFilterOptions] as any
)?.filter((p: any) => p !== member),
});
}}
>
<XMarkIcon className="h-3 w-3" />
</span>
</p>
)
)
: (filters[key as keyof IIssueFilterOptions] as any)?.join(", ")} : (filters[key as keyof IIssueFilterOptions] as any)?.join(", ")}
</p> </p>
) )

View File

@ -19,3 +19,4 @@ export * from "./tooltip";
export * from "./labels-list"; export * from "./labels-list";
export * from "./linear-progress-indicator"; export * from "./linear-progress-indicator";
export * from "./empty-state"; export * from "./empty-state";
export * from "./multi-level-dropdown";

View File

@ -0,0 +1,137 @@
import { Menu, Transition } from "@headlessui/react";
import { Fragment, useState } from "react";
import { ChevronDownIcon, ChevronRightIcon, ChevronLeftIcon } from "@heroicons/react/20/solid";
type MultiLevelDropdownProps = {
label: string;
options: {
id: string;
label: string;
value: any;
selected?: boolean;
children?: {
id: string;
label: string;
value: any;
selected?: boolean;
}[];
}[];
onSelect: (value: any) => void;
direction?: "left" | "right";
};
export const MultiLevelDropdown: React.FC<MultiLevelDropdownProps> = (props) => {
const { label, options, onSelect, direction = "right" } = props;
const [openChildFor, setOpenChildFor] = useState<string | null>(null);
return (
<Menu as="div" className="relative inline-block text-left">
{({ open }) => (
<>
<div>
<Menu.Button
onClick={() => {
setOpenChildFor(null);
}}
className={`group flex items-center gap-2 rounded-md border bg-transparent p-2 text-xs font-medium hover:bg-gray-100 hover:text-gray-900 focus:outline-none ${
open ? "bg-gray-100 text-gray-900" : "text-gray-500"
}`}
>
{label}
<ChevronDownIcon className="h-4 w-4" aria-hidden="true" />
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
static
className="absolute right-0 mt-2 w-36 origin-top-right select-none divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
>
{options.map((option) => (
<div className="relative px-1 py-1" key={option.id}>
<Menu.Item
as="button"
onClick={(e: any) => {
if (option.children) {
if (openChildFor === option.id) {
e.stopPropagation();
e.preventDefault();
setOpenChildFor(null);
} else {
e.stopPropagation();
e.preventDefault();
setOpenChildFor(option.id);
}
} else {
onSelect(option.value);
}
}}
className="w-full"
>
{({ active }) => (
<>
<div
className={`${
active || option.selected ? "bg-gray-100" : "text-gray-900"
} group flex w-full items-center justify-between rounded-md px-2 py-2 text-sm`}
>
{direction === "left" && option.children && (
<ChevronLeftIcon className="h-4 w-4" aria-hidden="true" />
)}
<span>{option.label}</span>
{direction === "right" && option.children && (
<ChevronRightIcon className="h-4 w-4" aria-hidden="true" />
)}
</div>
</>
)}
</Menu.Item>
{option.children && option.id === openChildFor && (
<Menu.Items
static
className={`absolute top-0 mt-2 w-36 origin-top-right select-none divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none ${
direction === "left"
? "right-full -translate-x-2"
: "left-full translate-x-2"
}`}
>
{option.children.map((child) => (
<div className="relative px-1 py-1" key={child.id}>
<Menu.Item as="div" className="flex items-center justify-between">
{({ active }) => (
<>
<button
type="button"
onClick={() => {
onSelect(child.value);
}}
className={`${
active || option.selected ? "bg-gray-100" : "text-gray-900"
} group flex w-full items-center rounded-md px-2 py-2 text-sm capitalize`}
>
{child.label}
</button>
</>
)}
</Menu.Item>
</div>
))}
</Menu.Items>
)}
</div>
))}
</Menu.Items>
</Transition>
</>
)}
</Menu>
);
};