forked from github/plane
Merge pull request #335 from makeplane/style/ui_consistency
style: kanban dropdowns
This commit is contained in:
commit
3c6752807d
@ -15,7 +15,7 @@ const Breadcrumbs = ({ children }: BreadcrumbsProps) => {
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className="grid h-8 w-8 cursor-pointer place-items-center rounded border border-gray-300 text-center text-sm hover:bg-gray-100"
|
||||
className="grid h-8 w-8 cursor-pointer place-items-center flex-shrink-0 rounded border border-gray-300 text-center text-sm hover:bg-gray-100"
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
<ArrowLeftIcon className="h-3 w-3" />
|
||||
|
@ -226,12 +226,13 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
</h5>
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex flex-wrap items-center gap-x-1 gap-y-2 text-xs">
|
||||
<div className="relative flex flex-wrap items-center gap-x-1 gap-y-2 text-xs">
|
||||
{properties.priority && selectedGroup !== "priority" && (
|
||||
<ViewPrioritySelect
|
||||
issue={issue}
|
||||
partialUpdateIssue={partialUpdateIssue}
|
||||
isNotAllowed={isNotAllowed}
|
||||
selfPositioned
|
||||
/>
|
||||
)}
|
||||
{properties.state && selectedGroup !== "state_detail.name" && (
|
||||
@ -239,6 +240,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
issue={issue}
|
||||
partialUpdateIssue={partialUpdateIssue}
|
||||
isNotAllowed={isNotAllowed}
|
||||
selfPositioned
|
||||
/>
|
||||
)}
|
||||
{properties.due_date && (
|
||||
@ -277,6 +279,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
partialUpdateIssue={partialUpdateIssue}
|
||||
isNotAllowed={isNotAllowed}
|
||||
tooltipPosition="left"
|
||||
selfPositioned
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -29,7 +29,7 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
|
||||
{links.map((link) => (
|
||||
<div key={link.id} className="relative">
|
||||
{!isNotAllowed && (
|
||||
<div className="absolute top-1.5 right-1.5 z-10 flex items-center gap-1">
|
||||
<div className="absolute top-1.5 right-1.5 z-[1] flex items-center gap-1">
|
||||
<Link href={link.url}>
|
||||
<a
|
||||
className="grid h-7 w-7 place-items-center rounded bg-gray-100 p-1 outline-none"
|
||||
@ -56,8 +56,8 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
|
||||
<h5 className="w-4/5">{link.title}</h5>
|
||||
<p className="mt-0.5 text-gray-500">
|
||||
Added {timeAgo(link.created_at)}
|
||||
<br />
|
||||
by {link.created_by_detail.email}
|
||||
{/* <br />
|
||||
by {link.created_by_detail.email} */}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -18,16 +18,16 @@ import { PROJECT_MEMBERS } from "constants/fetch-keys";
|
||||
type Props = {
|
||||
issue: IIssue;
|
||||
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
||||
selfPositioned?: boolean;
|
||||
tooltipPosition?: "left" | "right";
|
||||
position?: "left" | "right";
|
||||
isNotAllowed: boolean;
|
||||
};
|
||||
|
||||
export const ViewAssigneeSelect: React.FC<Props> = ({
|
||||
issue,
|
||||
partialUpdateIssue,
|
||||
selfPositioned = false,
|
||||
tooltipPosition = "right",
|
||||
position = "right",
|
||||
isNotAllowed,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
@ -52,7 +52,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
|
||||
|
||||
partialUpdateIssue({ assignees_list: newData });
|
||||
}}
|
||||
className="group relative flex-shrink-0"
|
||||
className={`group ${!selfPositioned ? "relative" : ""} flex-shrink-0`}
|
||||
disabled={isNotAllowed}
|
||||
>
|
||||
{({ open }) => (
|
||||
@ -88,11 +88,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Listbox.Options
|
||||
className={`absolute z-10 mt-1 max-h-48 overflow-auto rounded-md bg-white py-1 text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none ${
|
||||
position === "left" ? "left-0" : "right-0"
|
||||
}`}
|
||||
>
|
||||
<Listbox.Options className="absolute right-0 z-10 mt-1 max-h-48 overflow-auto rounded-md bg-white py-1 text-xs shadow-lg min-w-full ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
{members?.map((member) => (
|
||||
<Listbox.Option
|
||||
key={member.member.id}
|
||||
|
@ -12,14 +12,14 @@ import { PRIORITIES } from "constants/project";
|
||||
type Props = {
|
||||
issue: IIssue;
|
||||
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
||||
position?: "left" | "right";
|
||||
selfPositioned?: boolean;
|
||||
isNotAllowed: boolean;
|
||||
};
|
||||
|
||||
export const ViewPrioritySelect: React.FC<Props> = ({
|
||||
issue,
|
||||
partialUpdateIssue,
|
||||
position = "right",
|
||||
selfPositioned = false,
|
||||
isNotAllowed,
|
||||
}) => (
|
||||
<CustomSelect
|
||||
@ -53,6 +53,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
|
||||
} border-none`}
|
||||
noChevron
|
||||
disabled={isNotAllowed}
|
||||
selfPositioned={selfPositioned}
|
||||
>
|
||||
{PRIORITIES?.map((priority) => (
|
||||
<CustomSelect.Option key={priority} value={priority} className="capitalize">
|
||||
|
@ -17,14 +17,14 @@ import { STATE_LIST } from "constants/fetch-keys";
|
||||
type Props = {
|
||||
issue: IIssue;
|
||||
partialUpdateIssue: (formData: Partial<IIssue>) => void;
|
||||
position?: "left" | "right";
|
||||
selfPositioned?: boolean;
|
||||
isNotAllowed: boolean;
|
||||
};
|
||||
|
||||
export const ViewStateSelect: React.FC<Props> = ({
|
||||
issue,
|
||||
partialUpdateIssue,
|
||||
position = "right",
|
||||
selfPositioned = false,
|
||||
isNotAllowed,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
@ -67,6 +67,7 @@ export const ViewStateSelect: React.FC<Props> = ({
|
||||
maxHeight="md"
|
||||
noChevron
|
||||
disabled={isNotAllowed}
|
||||
selfPositioned={selfPositioned}
|
||||
>
|
||||
{states?.map((state) => (
|
||||
<CustomSelect.Option key={state.id} value={state.id}>
|
||||
|
@ -8,7 +8,7 @@ import workspaceService from "services/workspace.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
import { Button } from "components/ui";
|
||||
import { Button, Loader } from "components/ui";
|
||||
// icons
|
||||
import GithubLogo from "public/logos/github-black.png";
|
||||
import useSWR, { mutate } from "swr";
|
||||
@ -105,7 +105,8 @@ const OAuthPopUp = ({ integration }: any) => {
|
||||
<div>
|
||||
<h3 className="flex items-center gap-4 font-semibold text-xl">
|
||||
{integration.title}
|
||||
{isInstalled ? (
|
||||
{workspaceIntegrations ? (
|
||||
isInstalled ? (
|
||||
<span className="flex items-center text-green-500 font-normal text-sm gap-1">
|
||||
<span className="h-1.5 w-1.5 bg-green-500 flex-shrink-0 rounded-full" /> Installed
|
||||
</span>
|
||||
@ -114,16 +115,21 @@ const OAuthPopUp = ({ integration }: any) => {
|
||||
<span className="h-1.5 w-1.5 bg-gray-400 flex-shrink-0 rounded-full" /> Not
|
||||
Installed
|
||||
</span>
|
||||
)}
|
||||
)
|
||||
) : null}
|
||||
</h3>
|
||||
<p className="text-gray-400 text-sm">
|
||||
{isInstalled
|
||||
{workspaceIntegrations
|
||||
? isInstalled
|
||||
? "Activate GitHub integrations on individual projects to sync with specific repositories."
|
||||
: "Connect with GitHub with your Plane workspace to sync project issues."}
|
||||
: "Connect with GitHub with your Plane workspace to sync project issues."
|
||||
: "Loading..."}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{isInstalled ? (
|
||||
|
||||
{workspaceIntegrations ? (
|
||||
isInstalled ? (
|
||||
<Button
|
||||
theme="danger"
|
||||
size="rg"
|
||||
@ -137,6 +143,11 @@ const OAuthPopUp = ({ integration }: any) => {
|
||||
<Button theme="secondary" size="rg" className="text-xs" onClick={startAuth}>
|
||||
Add installation
|
||||
</Button>
|
||||
)
|
||||
) : (
|
||||
<Loader>
|
||||
<Loader.Item height="35px" width="150px" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -15,7 +15,9 @@ type CustomSelectProps = {
|
||||
input?: boolean;
|
||||
noChevron?: boolean;
|
||||
buttonClassName?: string;
|
||||
optionsClassName?: string;
|
||||
disabled?: boolean;
|
||||
selfPositioned?: boolean;
|
||||
};
|
||||
|
||||
const CustomSelect = ({
|
||||
@ -29,13 +31,15 @@ const CustomSelect = ({
|
||||
input = false,
|
||||
noChevron = false,
|
||||
buttonClassName = "",
|
||||
optionsClassName = "",
|
||||
disabled = false,
|
||||
selfPositioned = false,
|
||||
}: CustomSelectProps) => (
|
||||
<Listbox
|
||||
as="div"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
className="relative flex-shrink-0 text-left"
|
||||
className={`${!selfPositioned ? "relative" : ""} flex-shrink-0 text-left`}
|
||||
disabled={disabled}
|
||||
>
|
||||
<div>
|
||||
@ -67,8 +71,8 @@ const CustomSelect = ({
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Listbox.Options
|
||||
className={`absolute right-0 z-10 mt-1 origin-top-right overflow-y-auto rounded-md bg-white text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none ${
|
||||
width === "auto" ? "min-w-full whitespace-nowrap" : "w-56"
|
||||
className={`${optionsClassName} absolute right-0 z-10 mt-1 origin-top-right overflow-y-auto rounded-md bg-white text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none ${
|
||||
width === "auto" ? "min-w-full whitespace-nowrap" : width
|
||||
} ${input ? "max-h-48" : ""} ${
|
||||
maxHeight === "lg"
|
||||
? "max-h-60"
|
||||
@ -97,9 +101,9 @@ const Option: React.FC<OptionProps> = ({ children, value, className }) => (
|
||||
<Listbox.Option
|
||||
value={value}
|
||||
className={({ active, selected }) =>
|
||||
`${active || selected ? "bg-indigo-50" : ""} ${
|
||||
`${className} ${active || selected ? "bg-indigo-50" : ""} ${
|
||||
selected ? "font-medium" : ""
|
||||
} relative flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900 ${className}`
|
||||
} relative flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900`
|
||||
}
|
||||
>
|
||||
{children}
|
||||
|
@ -4,6 +4,9 @@ export const replaceUnderscoreIfSnakeCase = (str: string) => str.replace(/_/g, "
|
||||
|
||||
export const capitalizeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
||||
|
||||
export const truncateText = (str: string, length: number) =>
|
||||
str.length > length ? `${str.substring(0, length)}...` : str;
|
||||
|
||||
export const createSimilarString = (str: string) => {
|
||||
const shuffled = str
|
||||
.split("")
|
||||
|
@ -23,6 +23,8 @@ import projectService from "services/project.service";
|
||||
// ui
|
||||
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
// helpers
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
// types
|
||||
import { CycleIssueResponse, UserAuth } from "types";
|
||||
// fetch-keys
|
||||
@ -135,7 +137,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
|
||||
label={
|
||||
<>
|
||||
<CyclesIcon className="h-3 w-3" />
|
||||
{cycleDetails?.name}
|
||||
{cycleDetails?.name && truncateText(cycleDetails.name, 40)}
|
||||
</>
|
||||
}
|
||||
className="ml-1.5"
|
||||
@ -147,7 +149,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
|
||||
renderAs="a"
|
||||
href={`/${workspaceSlug}/projects/${activeProject?.id}/cycles/${cycle.id}`}
|
||||
>
|
||||
{cycle.name}
|
||||
{truncateText(cycle.name, 40)}
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
|
@ -27,6 +27,8 @@ import { ModuleDetailsSidebar } from "components/modules";
|
||||
// ui
|
||||
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
// helpers
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
// types
|
||||
import { IModule, ModuleIssueResponse, UserAuth } from "types";
|
||||
|
||||
@ -130,7 +132,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
|
||||
label={
|
||||
<>
|
||||
<RectangleGroupIcon className="h-3 w-3" />
|
||||
{moduleDetails?.name}
|
||||
{moduleDetails?.name && truncateText(moduleDetails.name, 40)}
|
||||
</>
|
||||
}
|
||||
className="ml-1.5"
|
||||
@ -142,7 +144,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
|
||||
renderAs="a"
|
||||
href={`/${workspaceSlug}/projects/${projectId}/modules/${module.id}`}
|
||||
>
|
||||
{module.name}
|
||||
{truncateText(module.name, 40)}
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
|
@ -13,6 +13,7 @@ import AppLayout from "layouts/app-layout";
|
||||
// componentss
|
||||
import OAuthPopUp from "components/popup";
|
||||
// ui
|
||||
import { Loader } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
// types
|
||||
import type { NextPage, GetServerSideProps } from "next";
|
||||
@ -54,13 +55,20 @@ const WorkspaceIntegrations: NextPage<UserAuth> = (props) => {
|
||||
<p className="mt-4 text-sm text-gray-500">Manage the workspace integrations.</p>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{integrations?.map((integration) => (
|
||||
{integrations ? (
|
||||
integrations.map((integration) => (
|
||||
<OAuthPopUp
|
||||
key={integration.id}
|
||||
workspaceSlug={workspaceSlug}
|
||||
integration={integration}
|
||||
/>
|
||||
))}
|
||||
))
|
||||
) : (
|
||||
<Loader className="space-y-5">
|
||||
<Loader.Item height="60px" />
|
||||
<Loader.Item height="60px" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</AppLayout>
|
||||
|
Loading…
Reference in New Issue
Block a user