Merge pull request #356 from makeplane/style/kanban_board

style: kanban board
This commit is contained in:
Aaryan Khandelwal 2023-03-01 15:07:21 +05:30 committed by GitHub
commit 99cf2d4e8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 479 additions and 117 deletions

View File

@ -40,8 +40,13 @@ export const AllBoards: React.FC<Props> = ({
<div className="h-[calc(100vh-157px)] lg:h-[calc(100vh-115px)] w-full"> <div className="h-[calc(100vh-157px)] lg:h-[calc(100vh-115px)] w-full">
<div className="h-full w-full overflow-hidden"> <div className="h-full w-full overflow-hidden">
<div className="h-full w-full"> <div className="h-full w-full">
<div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden"> <div className="flex h-full gap-x-9 overflow-x-auto overflow-y-hidden">
{Object.keys(groupedByIssues).map((singleGroup, index) => { {Object.keys(groupedByIssues).map((singleGroup, index) => {
const currentState =
selectedGroup === "state_detail.name"
? states?.find((s) => s.name === singleGroup)
: null;
const stateId = const stateId =
selectedGroup === "state_detail.name" selectedGroup === "state_detail.name"
? states?.find((s) => s.name === singleGroup)?.id ?? null ? states?.find((s) => s.name === singleGroup)?.id ?? null
@ -56,6 +61,7 @@ export const AllBoards: React.FC<Props> = ({
<SingleBoard <SingleBoard
key={index} key={index}
type={type} type={type}
currentState={currentState}
bgColor={bgColor} bgColor={bgColor}
groupTitle={singleGroup} groupTitle={singleGroup}
groupedByIssues={groupedByIssues} groupedByIssues={groupedByIssues}

View File

@ -1,22 +1,17 @@
import React from "react"; import React from "react";
// react-beautiful-dnd
import { DraggableProvided } from "react-beautiful-dnd";
// icons // icons
import { import { ArrowsPointingInIcon, ArrowsPointingOutIcon, PlusIcon } from "@heroicons/react/24/outline";
ArrowsPointingInIcon,
ArrowsPointingOutIcon,
EllipsisHorizontalIcon,
PlusIcon,
} from "@heroicons/react/24/outline";
// helpers // helpers
import { addSpaceIfCamelCase } from "helpers/string.helper"; import { addSpaceIfCamelCase } from "helpers/string.helper";
// types // types
import { IIssue, IProjectMember, NestedKeyOf } from "types"; import { IIssue, IProjectMember, IState, NestedKeyOf } from "types";
import { getStateGroupIcon } from "components/icons";
type Props = { type Props = {
groupedByIssues: { groupedByIssues: {
[key: string]: IIssue[]; [key: string]: IIssue[];
}; };
currentState?: IState | null;
selectedGroup: NestedKeyOf<IIssue> | null; selectedGroup: NestedKeyOf<IIssue> | null;
groupTitle: string; groupTitle: string;
bgColor?: string; bgColor?: string;
@ -28,6 +23,7 @@ type Props = {
export const BoardHeader: React.FC<Props> = ({ export const BoardHeader: React.FC<Props> = ({
groupedByIssues, groupedByIssues,
currentState,
selectedGroup, selectedGroup,
groupTitle, groupTitle,
bgColor, bgColor,
@ -54,22 +50,19 @@ export const BoardHeader: React.FC<Props> = ({
return ( return (
<div <div
className={`flex justify-between p-3 pb-0 ${ className={`flex justify-between px-1 ${
!isCollapsed ? "flex-col rounded-md border bg-gray-50" : "" !isCollapsed ? "flex-col rounded-md border bg-gray-50" : ""
}`} }`}
> >
<div className={`flex items-center ${!isCollapsed ? "flex-col gap-2" : "gap-1"}`}> <div className={`flex items-center ${!isCollapsed ? "flex-col gap-2" : "gap-1"}`}>
<div <div
className={`flex cursor-pointer items-center gap-x-1 rounded-md bg-slate-900 px-2 ${ className={`flex cursor-pointer items-center gap-x-3.5 ${
!isCollapsed ? "mb-2 flex-col gap-y-2 py-2" : "" !isCollapsed ? "mb-2 flex-col gap-y-2 py-2" : ""
}`} }`}
style={{
border: `2px solid ${bgColor}`,
backgroundColor: `${bgColor}20`,
}}
> >
{currentState && getStateGroupIcon(currentState.group, "20", "20", bgColor)}
<h2 <h2
className={`text-[0.9rem] font-medium capitalize`} className={`text-xl font-semibold capitalize`}
style={{ style={{
writingMode: !isCollapsed ? "vertical-rl" : "horizontal-tb", writingMode: !isCollapsed ? "vertical-rl" : "horizontal-tb",
}} }}
@ -80,14 +73,16 @@ export const BoardHeader: React.FC<Props> = ({
? assignees ? assignees
: addSpaceIfCamelCase(groupTitle)} : addSpaceIfCamelCase(groupTitle)}
</h2> </h2>
<span className="ml-0.5 text-sm text-gray-500">{groupedByIssues[groupTitle].length}</span> <span className="ml-0.5 text-sm bg-gray-100 py-1 px-3 rounded-full">
{groupedByIssues[groupTitle].length}
</span>
</div> </div>
</div> </div>
<div className={`flex items-center ${!isCollapsed ? "flex-col pb-2" : ""}`}> <div className={`flex items-center ${!isCollapsed ? "flex-col pb-2" : ""}`}>
<button <button
type="button" type="button"
className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200" className="grid h-7 w-7 place-items-center rounded p-1 text-gray-700 outline-none duration-300 hover:bg-gray-100"
onClick={() => { onClick={() => {
setIsCollapsed((prevData) => !prevData); setIsCollapsed((prevData) => !prevData);
}} }}
@ -100,7 +95,7 @@ export const BoardHeader: React.FC<Props> = ({
</button> </button>
<button <button
type="button" type="button"
className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-200" className="grid h-7 w-7 place-items-center rounded p-1 text-gray-700 outline-none duration-300 hover:bg-gray-100"
onClick={addIssueToState} onClick={addIssueToState}
> >
<PlusIcon className="h-4 w-4" /> <PlusIcon className="h-4 w-4" />

View File

@ -13,11 +13,14 @@ import { BoardHeader, SingleBoardIssue } from "components/core";
import { CustomMenu } from "components/ui"; import { CustomMenu } from "components/ui";
// icons // icons
import { PlusIcon } from "@heroicons/react/24/outline"; import { PlusIcon } from "@heroicons/react/24/outline";
// helpers
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
// types // types
import { IIssue, IProjectMember, NestedKeyOf, UserAuth } from "types"; import { IIssue, IProjectMember, IState, NestedKeyOf, UserAuth } from "types";
type Props = { type Props = {
type?: "issue" | "cycle" | "module"; type?: "issue" | "cycle" | "module";
currentState?: IState | null;
bgColor?: string; bgColor?: string;
groupTitle: string; groupTitle: string;
groupedByIssues: { groupedByIssues: {
@ -37,6 +40,7 @@ type Props = {
export const SingleBoard: React.FC<Props> = ({ export const SingleBoard: React.FC<Props> = ({
type, type,
currentState,
bgColor, bgColor,
groupTitle, groupTitle,
groupedByIssues, groupedByIssues,
@ -71,10 +75,11 @@ export const SingleBoard: React.FC<Props> = ({
const isNotAllowed = userAuth.isGuest || userAuth.isViewer; const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
return ( return (
<div className={`h-full flex-shrink-0 rounded ${!isCollapsed ? "" : "w-80 border bg-gray-50"}`}> <div className={`h-full flex-shrink-0 rounded ${!isCollapsed ? "" : "w-96 bg-gray-50"}`}>
<div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3"}`}> <div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3"}`}>
<BoardHeader <BoardHeader
addIssueToState={addIssueToState} addIssueToState={addIssueToState}
currentState={currentState}
bgColor={bgColor} bgColor={bgColor}
selectedGroup={selectedGroup} selectedGroup={selectedGroup}
groupTitle={groupTitle} groupTitle={groupTitle}
@ -86,7 +91,7 @@ export const SingleBoard: React.FC<Props> = ({
<StrictModeDroppable key={groupTitle} droppableId={groupTitle}> <StrictModeDroppable key={groupTitle} droppableId={groupTitle}>
{(provided, snapshot) => ( {(provided, snapshot) => (
<div <div
className={`relative mt-3 h-full px-3 pb-3 overflow-y-auto ${ className={`relative h-full p-1 overflow-y-auto ${
snapshot.isDraggingOver ? "bg-indigo-50 bg-opacity-50" : "" snapshot.isDraggingOver ? "bg-indigo-50 bg-opacity-50" : ""
} ${!isCollapsed ? "hidden" : "block"}`} } ${!isCollapsed ? "hidden" : "block"}`}
ref={provided.innerRef} ref={provided.innerRef}
@ -104,7 +109,7 @@ export const SingleBoard: React.FC<Props> = ({
snapshot.isDraggingOver ? "block" : "hidden" snapshot.isDraggingOver ? "block" : "hidden"
} top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2 text-xs whitespace-nowrap bg-white p-2 rounded pointer-events-none z-[99999999]`} } top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2 text-xs whitespace-nowrap bg-white p-2 rounded pointer-events-none z-[99999999]`}
> >
This board is ordered by {orderBy} This board is ordered by {replaceUnderscoreIfSnakeCase(orderBy ?? "")}
</div> </div>
</> </>
)} )}
@ -148,21 +153,23 @@ export const SingleBoard: React.FC<Props> = ({
{type === "issue" ? ( {type === "issue" ? (
<button <button
type="button" type="button"
className="flex items-center rounded p-2 text-xs font-medium outline-none duration-300 hover:bg-gray-100" className="flex items-center gap-2 text-theme font-medium outline-none"
onClick={addIssueToState} onClick={addIssueToState}
> >
<PlusIcon className="mr-1 h-3 w-3" /> <PlusIcon className="h-4 w-4" />
Create Add Issue
</button> </button>
) : ( ) : (
<CustomMenu <CustomMenu
label={ customButton={
<span className="flex items-center gap-1"> <button
<PlusIcon className="h-3 w-3" /> type="button"
Add issue className="flex items-center gap-2 text-theme font-medium outline-none"
</span> >
<PlusIcon className="h-4 w-4" />
Add Issue
</button>
} }
className="mt-1"
optionsPosition="left" optionsPosition="left"
noBorder noBorder
> >

View File

@ -184,15 +184,15 @@ export const SingleBoardIssue: React.FC<Props> = ({
return ( return (
<div <div
className={`rounded border bg-white shadow-sm mb-3 ${ className={`rounded bg-white shadow mb-3 ${
snapshot.isDragging ? "border-theme bg-indigo-50 shadow-lg" : "" snapshot.isDragging ? "border-2 border-theme shadow-lg" : ""
}`} }`}
ref={provided.innerRef} ref={provided.innerRef}
{...provided.draggableProps} {...provided.draggableProps}
{...provided.dragHandleProps} {...provided.dragHandleProps}
style={getStyle(provided.draggableProps.style, snapshot)} style={getStyle(provided.draggableProps.style, snapshot)}
> >
<div className="group/card relative select-none p-2"> <div className="group/card relative select-none p-4">
{!isNotAllowed && ( {!isNotAllowed && (
<div className="absolute top-1.5 right-1.5 z-10 opacity-0 group-hover/card:opacity-100"> <div className="absolute top-1.5 right-1.5 z-10 opacity-0 group-hover/card:opacity-100">
{type && !isNotAllowed && ( {type && !isNotAllowed && (
@ -214,19 +214,19 @@ export const SingleBoardIssue: React.FC<Props> = ({
<Link href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}> <Link href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}>
<a> <a>
{properties.key && ( {properties.key && (
<div className="mb-2 text-xs font-medium text-gray-500"> <div className="mb-2.5 text-xs font-medium text-gray-700">
{issue.project_detail.identifier}-{issue.sequence_id} {issue.project_detail.identifier}-{issue.sequence_id}
</div> </div>
)} )}
<h5 <h5
className="mb-3 text-sm group-hover:text-theme" className="text-sm group-hover:text-theme"
style={{ lineClamp: 3, WebkitLineClamp: 3 }} style={{ lineClamp: 3, WebkitLineClamp: 3 }}
> >
{issue.name} {issue.name}
</h5> </h5>
</a> </a>
</Link> </Link>
<div className="relative flex flex-wrap items-center gap-x-1 gap-y-2 text-xs"> <div className="relative flex flex-wrap items-center gap-2 mt-2.5 text-xs">
{properties.priority && selectedGroup !== "priority" && ( {properties.priority && selectedGroup !== "priority" && (
<ViewPrioritySelect <ViewPrioritySelect
issue={issue} issue={issue}

View File

@ -371,13 +371,13 @@ export const IssuesView: React.FC<Props> = ({
<div <div
className={`${ className={`${
trashBox ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none" trashBox ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"
} fixed z-20 top-12 left-1/2 -translate-x-1/2 flex items-center gap-2 bg-red-100 border-2 border-red-500 p-3 text-xs rounded ${ } fixed z-20 top-9 right-9 flex justify-center items-center gap-2 bg-red-100 border-2 border-red-500 p-3 w-96 h-28 text-xs italic text-red-500 font-medium rounded ${
snapshot.isDraggingOver ? "bg-red-500 text-white" : "" snapshot.isDraggingOver ? "bg-red-500 text-white" : ""
} duration-200`} } duration-200`}
ref={provided.innerRef} ref={provided.innerRef}
{...provided.droppableProps} {...provided.droppableProps}
> >
<TrashIcon className="h-3 w-3" /> <TrashIcon className="h-4 w-4" />
Drop issue here to delete Drop issue here to delete
</div> </div>
)} )}

View File

@ -0,0 +1,21 @@
import React from "react";
import type { Props } from "./types";
export const BacklogStateIcon: React.FC<Props> = ({
width = "20",
height = "20",
className,
color = "#858e96",
}) => (
<svg
width={width}
height={height}
className={className}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="10" cy="10" r="9" stroke={color} strokeLinecap="round" strokeDasharray="4 4" />
</svg>
);

View File

@ -0,0 +1,78 @@
import React from "react";
import type { Props } from "./types";
export const CancelledStateIcon: React.FC<Props> = ({
width = "20",
height = "20",
className,
color = "#f2655a",
}) => (
<svg
width={width}
height={height}
className={className}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 84.36 84.36"
>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
fill="none"
strokeWidth={3}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
d="M20.45,7.69a39.74,39.74,0,0,1,43.43.54"
/>
<path
className="cls-1"
fill="none"
strokeWidth={3}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
d="M76.67,20.45a39.76,39.76,0,0,1-.53,43.43"
/>
<path
className="cls-1"
fill="none"
strokeWidth={3}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
d="M63.92,76.67a39.78,39.78,0,0,1-43.44-.53"
/>
<path
className="cls-1"
fill="none"
strokeWidth={3}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
d="M7.69,63.92a39.75,39.75,0,0,1,.54-43.44"
/>
<circle className="cls-2" fill={color} cx="42.18" cy="42.18" r="31.04" />
<path
className="cls-3"
fill="none"
strokeWidth={3}
stroke="#ffffff"
strokeLinecap="square"
strokeMiterlimit={10}
d="M32.64,32.44q9.54,9.75,19.09,19.48"
/>
<path
className="cls-3"
fill="none"
strokeWidth={3}
stroke="#ffffff"
strokeLinecap="square"
strokeMiterlimit={10}
d="M32.64,51.92,51.73,32.44"
/>
</g>
</g>
</svg>
);

View File

@ -0,0 +1,69 @@
import React from "react";
import type { Props } from "./types";
export const CompletedStateIcon: React.FC<Props> = ({
width = "20",
height = "20",
className,
color = "#438af3",
}) => (
<svg
width={width}
height={height}
className={className}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 84.36 84.36"
>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
fill="none"
strokeWidth={3}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
d="M20.45,7.69a39.74,39.74,0,0,1,43.43.54"
/>
<path
className="cls-1"
fill="none"
strokeWidth={3}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
d="M76.67,20.45a39.76,39.76,0,0,1-.53,43.43"
/>
<path
className="cls-1"
fill="none"
strokeWidth={3}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
d="M63.92,76.67a39.78,39.78,0,0,1-43.44-.53"
/>
<path
className="cls-1"
fill="none"
strokeWidth={3}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
d="M7.69,63.92a39.75,39.75,0,0,1,.54-43.44"
/>
<circle className="cls-2" fill={color} cx="42.18" cy="42.18" r="31.04" />
<path
className="cls-3"
fill="none"
strokeWidth={3}
stroke="#ffffff"
strokeLinecap="square"
strokeMiterlimit={10}
d="M30.45,43.75l6.61,6.61L53.92,34"
/>
</g>
</g>
</svg>
);

View File

@ -1,12 +1,15 @@
export * from "./attachment-icon"; export * from "./attachment-icon";
export * from "./backlog-state-icon";
export * from "./blocked-icon"; export * from "./blocked-icon";
export * from "./blocker-icon"; export * from "./blocker-icon";
export * from "./bolt-icon"; export * from "./bolt-icon";
export * from "./calendar-month-icon"; export * from "./calendar-month-icon";
export * from "./cancel-icon"; export * from "./cancel-icon";
export * from "./cancelled-state-icon";
export * from "./clipboard-icon"; export * from "./clipboard-icon";
export * from "./comment-icon"; export * from "./comment-icon";
export * from "./completed-cycle-icon"; export * from "./completed-cycle-icon";
export * from "./completed-state-icon";
export * from "./current-cycle-icon"; export * from "./current-cycle-icon";
export * from "./cycle-icon"; export * from "./cycle-icon";
export * from "./discord-icon"; export * from "./discord-icon";
@ -16,6 +19,7 @@ export * from "./ellipsis-horizontal-icon";
export * from "./external-link-icon"; export * from "./external-link-icon";
export * from "./github-icon"; export * from "./github-icon";
export * from "./heartbeat-icon"; export * from "./heartbeat-icon";
export * from "./started-state-icon";
export * from "./layer-diagonal-icon"; export * from "./layer-diagonal-icon";
export * from "./lock-icon"; export * from "./lock-icon";
export * from "./menu-icon"; export * from "./menu-icon";
@ -23,8 +27,11 @@ export * from "./plus-icon";
export * from "./question-mark-circle-icon"; export * from "./question-mark-circle-icon";
export * from "./setting-icon"; export * from "./setting-icon";
export * from "./signal-cellular-icon"; export * from "./signal-cellular-icon";
export * from "./started-state-icon";
export * from "./state-group-icon";
export * from "./tag-icon"; export * from "./tag-icon";
export * from "./tune-icon"; export * from "./tune-icon";
export * from "./unstarted-state-icon";
export * from "./upcoming-cycle-icon"; export * from "./upcoming-cycle-icon";
export * from "./user-group-icon"; export * from "./user-group-icon";
export * from "./user-icon-circle"; export * from "./user-icon-circle";

View File

@ -0,0 +1,77 @@
import React from "react";
import type { Props } from "./types";
export const StartedStateIcon: React.FC<Props> = ({
width = "20",
height = "20",
className,
color = "#fbb040",
}) => (
<svg
width={width}
height={height}
className={className}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 83.36 83.36"
>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
fill="none"
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M20,7.19a39.74,39.74,0,0,1,43.43.54"
/>
<path
className="cls-1"
fill="none"
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M76.17,20a39.76,39.76,0,0,1-.53,43.43"
/>
<path
className="cls-1"
fill="none"
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M63.42,76.17A39.78,39.78,0,0,1,20,75.64"
/>
<path
className="cls-1"
fill="none"
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M7.19,63.42A39.75,39.75,0,0,1,7.73,20"
/>
<path
className="cls-2"
fill={color}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M42.32,41.21q9.57-14.45,19.13-28.9a35.8,35.8,0,0,0-39.09,0Z"
/>
<path
className="cls-2"
fill={color}
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M42.32,41.7,61.45,70.6a35.75,35.75,0,0,1-39.09,0Z"
/>
</g>
</g>
</svg>
);

View File

@ -0,0 +1,29 @@
import {
BacklogStateIcon,
CancelledStateIcon,
CompletedStateIcon,
StartedStateIcon,
UnstartedStateIcon,
} from "components/icons";
export const getStateGroupIcon = (
stateGroup: "backlog" | "unstarted" | "started" | "completed" | "cancelled",
width = "20",
height = "20",
color?: string
) => {
switch (stateGroup) {
case "backlog":
return <BacklogStateIcon width={width} height={height} color={color} />;
case "unstarted":
return <UnstartedStateIcon width={width} height={height} color={color} />;
case "started":
return <StartedStateIcon width={width} height={height} color={color} />;
case "completed":
return <CompletedStateIcon width={width} height={height} color={color} />;
case "cancelled":
return <CancelledStateIcon width={width} height={height} color={color} />;
default:
return <></>;
}
};

View File

@ -0,0 +1,59 @@
import React from "react";
import type { Props } from "./types";
export const UnstartedStateIcon: React.FC<Props> = ({
width = "20",
height = "20",
className,
color = "#858e96",
}) => (
<svg
width={width}
height={height}
className={className}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 84.36 84.36"
>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
fill="none"
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M20.45,7.69a39.74,39.74,0,0,1,43.43.54"
/>
<path
className="cls-1"
fill="none"
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M76.67,20.45a39.76,39.76,0,0,1-.53,43.43"
/>
<path
className="cls-1"
fill="none"
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M63.92,76.67a39.78,39.78,0,0,1-43.44-.53"
/>
<path
className="cls-1"
fill="none"
stroke={color}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M7.69,63.92a39.75,39.75,0,0,1,.54-43.44"
/>
</g>
</g>
</svg>
);

View File

@ -23,34 +23,38 @@ export const ViewPrioritySelect: React.FC<Props> = ({
isNotAllowed, isNotAllowed,
}) => ( }) => (
<CustomSelect <CustomSelect
label={
<Tooltip tooltipHeading="Priority" tooltipContent={issue.priority ?? "None"}>
<span>
{getPriorityIcon(
issue.priority && issue.priority !== "" ? issue.priority ?? "" : "None",
"text-sm"
)}
</span>
</Tooltip>
}
value={issue.state} value={issue.state}
onChange={(data: string) => { onChange={(data: string) => {
partialUpdateIssue({ priority: data }); partialUpdateIssue({ priority: data });
}} }}
maxHeight="md" maxHeight="md"
buttonClassName={`flex ${ customButton={
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer" <button
} items-center gap-x-2 rounded px-2 py-0.5 capitalize shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${ type="button"
issue.priority === "urgent" className={`grid place-items-center rounded w-6 h-6 ${
? "bg-red-100 text-red-600 hover:bg-red-100" isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
: issue.priority === "high" } items-center shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
? "bg-orange-100 text-orange-500 hover:bg-orange-100" issue.priority === "urgent"
: issue.priority === "medium" ? "bg-red-100 text-red-600 hover:bg-red-100"
? "bg-yellow-100 text-yellow-500 hover:bg-yellow-100" : issue.priority === "high"
: issue.priority === "low" ? "bg-orange-100 text-orange-500 hover:bg-orange-100"
? "bg-green-100 text-green-500 hover:bg-green-100" : issue.priority === "medium"
: "bg-gray-100" ? "bg-yellow-100 text-yellow-500 hover:bg-yellow-100"
} border-none`} : issue.priority === "low"
? "bg-green-100 text-green-500 hover:bg-green-100"
: "bg-gray-100"
} border-none`}
>
<Tooltip tooltipHeading="Priority" tooltipContent={issue.priority ?? "None"}>
<span>
{getPriorityIcon(
issue.priority && issue.priority !== "" ? issue.priority ?? "" : "None",
"text-sm"
)}
</span>
</Tooltip>
</button>
}
noChevron noChevron
disabled={isNotAllowed} disabled={isNotAllowed}
selfPositioned={selfPositioned} selfPositioned={selfPositioned}

View File

@ -15,6 +15,7 @@ type Props = {
textAlignment?: "left" | "center" | "right"; textAlignment?: "left" | "center" | "right";
noBorder?: boolean; noBorder?: boolean;
optionsPosition?: "left" | "right"; optionsPosition?: "left" | "right";
customButton?: JSX.Element;
}; };
type MenuItemProps = { type MenuItemProps = {
@ -34,42 +35,47 @@ const CustomMenu = ({
textAlignment, textAlignment,
noBorder = false, noBorder = false,
optionsPosition = "right", optionsPosition = "right",
customButton,
}: Props) => ( }: Props) => (
<Menu as="div" className={`relative w-min whitespace-nowrap text-left ${className}`}> <Menu as="div" className={`relative w-min whitespace-nowrap text-left ${className}`}>
<div> {customButton ? (
{ellipsis ? ( <Menu.Button as="div">{customButton}</Menu.Button>
<Menu.Button className="relative grid place-items-center rounded p-1 hover:bg-gray-100 focus:outline-none"> ) : (
<EllipsisHorizontalIcon className="h-4 w-4" /> <div>
</Menu.Button> {ellipsis ? (
) : ( <Menu.Button className="relative grid place-items-center rounded p-1 hover:bg-gray-100 focus:outline-none">
<Menu.Button <EllipsisHorizontalIcon className="h-4 w-4" />
className={`flex cursor-pointer items-center justify-between gap-1 px-2 py-1 text-xs duration-300 hover:bg-gray-100 ${ </Menu.Button>
textAlignment === "right" ) : (
? "text-right" <Menu.Button
: textAlignment === "center" className={`flex cursor-pointer items-center justify-between gap-1 px-2 py-1 text-xs duration-300 hover:bg-gray-100 ${
? "text-center" textAlignment === "right"
: "text-left" ? "text-right"
} ${ : textAlignment === "center"
noBorder ? "text-center"
? "rounded" : "text-left"
: "rounded-md border shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" } ${
} ${ noBorder
width === "sm" ? "rounded"
? "w-10" : "rounded-md border shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
: width === "md" } ${
? "w-20" width === "sm"
: width === "lg" ? "w-10"
? "w-32" : width === "md"
: width === "xl" ? "w-20"
? "w-48" : width === "lg"
: "w-full" ? "w-32"
}`} : width === "xl"
> ? "w-48"
{label} : "w-full"
{!noBorder && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />} }`}
</Menu.Button> >
)} {label}
</div> {!noBorder && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
</Menu.Button>
)}
</div>
)}
<Transition <Transition
as={React.Fragment} as={React.Fragment}

View File

@ -8,13 +8,13 @@ type CustomSelectProps = {
value: any; value: any;
onChange: any; onChange: any;
children: React.ReactNode; children: React.ReactNode;
label: string | JSX.Element; label?: string | JSX.Element;
textAlignment?: "left" | "center" | "right"; textAlignment?: "left" | "center" | "right";
maxHeight?: "sm" | "rg" | "md" | "lg" | "none"; maxHeight?: "sm" | "rg" | "md" | "lg" | "none";
width?: "auto" | string; width?: "auto" | string;
input?: boolean; input?: boolean;
noChevron?: boolean; noChevron?: boolean;
buttonClassName?: string; customButton?: JSX.Element;
optionsClassName?: string; optionsClassName?: string;
disabled?: boolean; disabled?: boolean;
selfPositioned?: boolean; selfPositioned?: boolean;
@ -30,7 +30,7 @@ const CustomSelect = ({
width = "auto", width = "auto",
input = false, input = false,
noChevron = false, noChevron = false,
buttonClassName = "", customButton,
optionsClassName = "", optionsClassName = "",
disabled = false, disabled = false,
selfPositioned = false, selfPositioned = false,
@ -43,22 +43,26 @@ const CustomSelect = ({
disabled={disabled} disabled={disabled}
> >
<div> <div>
<Listbox.Button {customButton ? (
className={`${buttonClassName} flex w-full ${ <Listbox.Button as="div">{customButton}</Listbox.Button>
disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100" ) : (
} items-center justify-between gap-1 rounded-md border shadow-sm duration-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${ <Listbox.Button
input ? "border-gray-300 px-3 py-2 text-sm" : "px-2 py-1 text-xs" className={`flex w-full ${
} ${ disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-gray-100"
textAlignment === "right" } items-center justify-between gap-1 rounded-md border shadow-sm duration-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
? "text-right" input ? "border-gray-300 px-3 py-2 text-sm" : "px-2 py-1 text-xs"
: textAlignment === "center" } ${
? "text-center" textAlignment === "right"
: "text-left" ? "text-right"
}`} : textAlignment === "center"
> ? "text-center"
{label} : "text-left"
{!noChevron && !disabled && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />} }`}
</Listbox.Button> >
{label}
{!noChevron && !disabled && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
</Listbox.Button>
)}
</div> </div>
<Transition <Transition