mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge pull request #356 from makeplane/style/kanban_board
style: kanban board
This commit is contained in:
commit
99cf2d4e8d
@ -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}
|
||||||
|
@ -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" />
|
||||||
|
@ -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
|
||||||
>
|
>
|
||||||
|
@ -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}
|
||||||
|
@ -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>
|
||||||
)}
|
)}
|
||||||
|
21
apps/app/components/icons/backlog-state-icon.tsx
Normal file
21
apps/app/components/icons/backlog-state-icon.tsx
Normal 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>
|
||||||
|
);
|
78
apps/app/components/icons/cancelled-state-icon.tsx
Normal file
78
apps/app/components/icons/cancelled-state-icon.tsx
Normal 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>
|
||||||
|
);
|
69
apps/app/components/icons/completed-state-icon.tsx
Normal file
69
apps/app/components/icons/completed-state-icon.tsx
Normal 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>
|
||||||
|
);
|
@ -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";
|
||||||
|
77
apps/app/components/icons/started-state-icon.tsx
Normal file
77
apps/app/components/icons/started-state-icon.tsx
Normal 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>
|
||||||
|
);
|
29
apps/app/components/icons/state-group-icon.tsx
Normal file
29
apps/app/components/icons/state-group-icon.tsx
Normal 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 <></>;
|
||||||
|
}
|
||||||
|
};
|
59
apps/app/components/icons/unstarted-state-icon.tsx
Normal file
59
apps/app/components/icons/unstarted-state-icon.tsx
Normal 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>
|
||||||
|
);
|
@ -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}
|
||||||
|
@ -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}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user