chore: added optional tooltip to dropdowns (#3462)

This commit is contained in:
Aaryan Khandelwal 2024-01-25 13:29:56 +05:30 committed by GitHub
parent 7fd625e0e3
commit eae32593cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 673 additions and 420 deletions

View file

@ -8,7 +8,7 @@ import { useApplication, useCycle } from "hooks/store";
import { useDropdownKeyDown } from "hooks/use-dropdown-key-down";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// icons
import { ContrastIcon } from "@plane/ui";
import { ContrastIcon, Tooltip } from "@plane/ui";
// helpers
import { cn } from "helpers/common.helper";
// types
@ -32,6 +32,7 @@ type ButtonProps = {
dropdownArrow: boolean;
dropdownArrowClassName: string;
placeholder: string;
tooltip: boolean;
};
type DropdownOptions =
@ -51,21 +52,24 @@ const BorderButton = (props: ButtonProps) => {
hideIcon = false,
hideText = false,
placeholder,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && <ContrastIcon className="h-3 w-3 flex-shrink-0" />}{" "}
{!hideText && <span className="flex-grow truncate">{cycle?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Cycle" tooltipContent={cycle?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && <ContrastIcon className="h-3 w-3 flex-shrink-0" />}{" "}
{!hideText && <span className="flex-grow truncate">{cycle?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -78,18 +82,24 @@ const BackgroundButton = (props: ButtonProps) => {
hideIcon = false,
hideText = false,
placeholder,
tooltip,
} = props;
return (
<div
className={cn("h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80", className)}
>
{!hideIcon && <ContrastIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{cycle?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Cycle" tooltipContent={cycle?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80",
className
)}
>
{!hideIcon && <ContrastIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{cycle?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -102,21 +112,24 @@ const TransparentButton = (props: ButtonProps) => {
hideIcon = false,
hideText = false,
placeholder,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && <ContrastIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{cycle?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Cycle" tooltipContent={cycle?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && <ContrastIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{cycle?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -135,8 +148,9 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
placeholder = "Cycle",
placement,
projectId,
value,
tabIndex,
tooltip = false,
value,
} = props;
// states
const [query, setQuery] = useState("");
@ -254,6 +268,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -264,6 +279,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-with-text" ? (
<BackgroundButton
@ -273,6 +289,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -283,6 +300,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-with-text" ? (
<TransparentButton
@ -292,6 +310,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -302,6 +321,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : null}
</button>

View file

@ -6,6 +6,8 @@ import { CalendarDays, X } from "lucide-react";
// hooks
import { useDropdownKeyDown } from "hooks/use-dropdown-key-down";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// ui
import { Tooltip } from "@plane/ui";
// helpers
import { renderFormattedDate } from "helpers/date-time.helper";
import { cn } from "helpers/common.helper";
@ -33,6 +35,7 @@ type ButtonProps = {
hideText?: boolean;
onClear: () => void;
placeholder: string;
tooltip: boolean;
};
const BorderButton = (props: ButtonProps) => {
@ -46,27 +49,34 @@ const BorderButton = (props: ButtonProps) => {
hideText = false,
onClear,
placeholder,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
<Tooltip
tooltipHeading={placeholder}
tooltipContent={date ? renderFormattedDate(date) : "None"}
disabled={!tooltip}
>
{!hideIcon && icon}
{!hideText && <span className="flex-grow truncate">{date ? renderFormattedDate(date) : placeholder}</span>}
{isClearable && (
<X
className={cn("h-2 w-2 flex-shrink-0", clearIconClassName)}
onClick={(e) => {
e.stopPropagation();
onClear();
}}
/>
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && icon}
{!hideText && <span className="flex-grow truncate">{date ? renderFormattedDate(date) : placeholder}</span>}
{isClearable && (
<X
className={cn("h-2 w-2 flex-shrink-0", clearIconClassName)}
onClick={(e) => {
e.stopPropagation();
onClear();
}}
/>
)}
</div>
</Tooltip>
);
};
@ -81,24 +91,34 @@ const BackgroundButton = (props: ButtonProps) => {
hideText = false,
onClear,
placeholder,
tooltip,
} = props;
return (
<div
className={cn("h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80", className)}
<Tooltip
tooltipHeading={placeholder}
tooltipContent={date ? renderFormattedDate(date) : "None"}
disabled={!tooltip}
>
{!hideIcon && icon}
{!hideText && <span className="flex-grow truncate">{date ? renderFormattedDate(date) : placeholder}</span>}
{isClearable && (
<X
className={cn("h-2 w-2 flex-shrink-0", clearIconClassName)}
onClick={(e) => {
e.stopPropagation();
onClear();
}}
/>
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80",
className
)}
>
{!hideIcon && icon}
{!hideText && <span className="flex-grow truncate">{date ? renderFormattedDate(date) : placeholder}</span>}
{isClearable && (
<X
className={cn("h-2 w-2 flex-shrink-0", clearIconClassName)}
onClick={(e) => {
e.stopPropagation();
onClear();
}}
/>
)}
</div>
</Tooltip>
);
};
@ -113,27 +133,34 @@ const TransparentButton = (props: ButtonProps) => {
hideText = false,
onClear,
placeholder,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
<Tooltip
tooltipHeading={placeholder}
tooltipContent={date ? renderFormattedDate(date) : "None"}
disabled={!tooltip}
>
{!hideIcon && icon}
{!hideText && <span className="flex-grow truncate">{date ? renderFormattedDate(date) : placeholder}</span>}
{isClearable && (
<X
className={cn("h-2 w-2 flex-shrink-0", clearIconClassName)}
onClick={(e) => {
e.stopPropagation();
onClear();
}}
/>
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && icon}
{!hideText && <span className="flex-grow truncate">{date ? renderFormattedDate(date) : placeholder}</span>}
{isClearable && (
<X
className={cn("h-2 w-2 flex-shrink-0", clearIconClassName)}
onClick={(e) => {
e.stopPropagation();
onClear();
}}
/>
)}
</div>
</Tooltip>
);
};
@ -144,6 +171,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
buttonVariant,
className = "",
clearIconClassName = "",
closeOnSelect = true,
disabled = false,
hideIcon = false,
icon = <CalendarDays className="h-3 w-3 flex-shrink-0" />,
@ -153,9 +181,9 @@ export const DateDropdown: React.FC<Props> = (props) => {
onChange,
placeholder = "Date",
placement,
value,
closeOnSelect = true,
tabIndex,
tooltip = false,
value,
} = props;
const [isOpen, setIsOpen] = useState(false);
// refs
@ -218,6 +246,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
placeholder={placeholder}
isClearable={isClearable && isDateSelected}
onClear={() => onChange(null)}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -229,6 +258,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
placeholder={placeholder}
isClearable={isClearable && isDateSelected}
onClear={() => onChange(null)}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "background-with-text" ? (
@ -241,6 +271,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
placeholder={placeholder}
isClearable={isClearable && isDateSelected}
onClear={() => onChange(null)}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -252,6 +283,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
placeholder={placeholder}
isClearable={isClearable && isDateSelected}
onClear={() => onChange(null)}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "transparent-with-text" ? (
@ -264,6 +296,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
placeholder={placeholder}
isClearable={isClearable && isDateSelected}
onClear={() => onChange(null)}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -275,6 +308,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
placeholder={placeholder}
isClearable={isClearable && isDateSelected}
onClear={() => onChange(null)}
tooltip={tooltip}
hideText
/>
) : null}

View file

@ -8,6 +8,8 @@ import sortBy from "lodash/sortBy";
import { useApplication, useEstimate } from "hooks/store";
import { useDropdownKeyDown } from "hooks/use-dropdown-key-down";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// ui
import { Tooltip } from "@plane/ui";
// helpers
import { cn } from "helpers/common.helper";
// types
@ -30,6 +32,7 @@ type ButtonProps = {
hideIcon?: boolean;
hideText?: boolean;
placeholder: string;
tooltip: boolean;
};
type DropdownOptions =
@ -49,21 +52,30 @@ const BorderButton = (props: ButtonProps) => {
hideIcon = false,
hideText = false,
placeholder,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
<Tooltip
tooltipHeading="Estimate"
tooltipContent={estimatePoint !== null ? estimatePoint : placeholder}
disabled={!tooltip}
>
{!hideIcon && <Triangle className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{estimatePoint !== null ? estimatePoint : placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && <Triangle className="h-3 w-3 flex-shrink-0" />}
{!hideText && (
<span className="flex-grow truncate">{estimatePoint !== null ? estimatePoint : placeholder}</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -76,18 +88,30 @@ const BackgroundButton = (props: ButtonProps) => {
hideIcon = false,
hideText = false,
placeholder,
tooltip,
} = props;
return (
<div
className={cn("h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80", className)}
<Tooltip
tooltipHeading="Estimate"
tooltipContent={estimatePoint !== null ? estimatePoint : placeholder}
disabled={!tooltip}
>
{!hideIcon && <Triangle className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{estimatePoint !== null ? estimatePoint : placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80",
className
)}
>
{!hideIcon && <Triangle className="h-3 w-3 flex-shrink-0" />}
{!hideText && (
<span className="flex-grow truncate">{estimatePoint !== null ? estimatePoint : placeholder}</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -100,21 +124,30 @@ const TransparentButton = (props: ButtonProps) => {
hideIcon = false,
hideText = false,
placeholder,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
<Tooltip
tooltipHeading="Estimate"
tooltipContent={estimatePoint !== null ? estimatePoint : placeholder}
disabled={!tooltip}
>
{!hideIcon && <Triangle className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{estimatePoint !== null ? estimatePoint : placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && <Triangle className="h-3 w-3 flex-shrink-0" />}
{!hideText && (
<span className="flex-grow truncate">{estimatePoint !== null ? estimatePoint : placeholder}</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -133,8 +166,9 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
placeholder = "Estimate",
placement,
projectId,
value,
tabIndex,
tooltip = false,
value,
} = props;
// states
const [query, setQuery] = useState("");
@ -242,6 +276,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -252,6 +287,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-with-text" ? (
<BackgroundButton
@ -261,6 +297,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -271,6 +308,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-with-text" ? (
<TransparentButton
@ -280,6 +318,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -290,6 +329,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : null}
</button>

View file

@ -3,7 +3,7 @@ import { ChevronDown } from "lucide-react";
// hooks
import { useMember } from "hooks/store";
// ui
import { Avatar, AvatarGroup, UserGroupIcon } from "@plane/ui";
import { Avatar, AvatarGroup, Tooltip, UserGroupIcon } from "@plane/ui";
// helpers
import { cn } from "helpers/common.helper";
@ -14,16 +14,17 @@ type ButtonProps = {
placeholder: string;
hideIcon?: boolean;
hideText?: boolean;
tooltip: boolean;
userIds: string | string[] | null;
};
const ButtonAvatars = observer(({ userIds }: { userIds: string | string[] | null }) => {
const ButtonAvatars = observer(({ tooltip, userIds }: { tooltip: boolean; userIds: string | string[] | null }) => {
const { getUserDetails } = useMember();
if (Array.isArray(userIds)) {
if (userIds.length > 0)
return (
<AvatarGroup size="md">
<AvatarGroup size="md" showTooltip={!tooltip}>
{userIds.map((userId) => {
const userDetails = getUserDetails(userId);
@ -35,7 +36,7 @@ const ButtonAvatars = observer(({ userIds }: { userIds: string | string[] | null
} else {
if (userIds) {
const userDetails = getUserDetails(userIds);
return <Avatar src={userDetails?.avatar} name={userDetails?.display_name} size="md" />;
return <Avatar src={userDetails?.avatar} name={userDetails?.display_name} size="md" showTooltip={!tooltip} />;
}
}
@ -51,6 +52,7 @@ export const BorderButton = observer((props: ButtonProps) => {
hideText = false,
placeholder,
userIds,
tooltip,
} = props;
// store hooks
const { getUserDetails } = useMember();
@ -58,22 +60,28 @@ export const BorderButton = observer((props: ButtonProps) => {
const isMultiple = Array.isArray(userIds);
return (
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
<Tooltip
tooltipHeading={placeholder}
tooltipContent={`${userIds?.length ?? 0} assignee${userIds?.length !== 1 ? "s" : ""}`}
disabled={!tooltip}
>
{!hideIcon && <ButtonAvatars userIds={userIds} />}
{!hideText && (
<span className="flex-grow truncate">
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && <ButtonAvatars tooltip={tooltip} userIds={userIds} />}
{!hideText && (
<span className="flex-grow truncate">
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
});
@ -86,6 +94,7 @@ export const BackgroundButton = observer((props: ButtonProps) => {
hideText = false,
placeholder,
userIds,
tooltip,
} = props;
// store hooks
const { getUserDetails } = useMember();
@ -93,19 +102,28 @@ export const BackgroundButton = observer((props: ButtonProps) => {
const isMultiple = Array.isArray(userIds);
return (
<div
className={cn("h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80", className)}
<Tooltip
tooltipHeading={placeholder}
tooltipContent={`${userIds?.length ?? 0} assignee${userIds?.length !== 1 ? "s" : ""}`}
disabled={!tooltip}
>
{!hideIcon && <ButtonAvatars userIds={userIds} />}
{!hideText && (
<span className="flex-grow truncate">
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80",
className
)}
>
{!hideIcon && <ButtonAvatars tooltip={tooltip} userIds={userIds} />}
{!hideText && (
<span className="flex-grow truncate">
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
});
@ -118,6 +136,7 @@ export const TransparentButton = observer((props: ButtonProps) => {
hideText = false,
placeholder,
userIds,
tooltip,
} = props;
// store hooks
const { getUserDetails } = useMember();
@ -125,21 +144,27 @@ export const TransparentButton = observer((props: ButtonProps) => {
const isMultiple = Array.isArray(userIds);
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
<Tooltip
tooltipHeading={placeholder}
tooltipContent={`${userIds?.length ?? 0} assignee${userIds?.length !== 1 ? "s" : ""}`}
disabled={!tooltip}
>
{!hideIcon && <ButtonAvatars userIds={userIds} />}
{!hideText && (
<span className="flex-grow truncate">
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && <ButtonAvatars tooltip={tooltip} userIds={userIds} />}
{!hideText && (
<span className="flex-grow truncate">
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
</span>
)}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
});

View file

@ -36,8 +36,9 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
placeholder = "Members",
placement,
projectId,
value,
tabIndex,
tooltip = false,
value,
} = props;
// states
const [query, setQuery] = useState("");
@ -146,6 +147,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -155,6 +157,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "background-with-text" ? (
@ -165,6 +168,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -174,6 +178,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "transparent-with-text" ? (
@ -184,6 +189,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -193,6 +199,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
hideText
/>
) : null}

View file

@ -31,8 +31,9 @@ export const WorkspaceMemberDropdown: React.FC<MemberDropdownProps> = observer((
onChange,
placeholder = "Members",
placement,
value,
tabIndex,
tooltip = false,
value,
} = props;
// states
const [query, setQuery] = useState("");
@ -133,6 +134,7 @@ export const WorkspaceMemberDropdown: React.FC<MemberDropdownProps> = observer((
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -142,6 +144,7 @@ export const WorkspaceMemberDropdown: React.FC<MemberDropdownProps> = observer((
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "background-with-text" ? (
@ -152,6 +155,7 @@ export const WorkspaceMemberDropdown: React.FC<MemberDropdownProps> = observer((
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -161,6 +165,7 @@ export const WorkspaceMemberDropdown: React.FC<MemberDropdownProps> = observer((
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "transparent-with-text" ? (
@ -171,6 +176,7 @@ export const WorkspaceMemberDropdown: React.FC<MemberDropdownProps> = observer((
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -180,6 +186,7 @@ export const WorkspaceMemberDropdown: React.FC<MemberDropdownProps> = observer((
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
hideText
/>
) : null}

View file

@ -8,7 +8,7 @@ import { useApplication, useModule } from "hooks/store";
import { useDropdownKeyDown } from "hooks/use-dropdown-key-down";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// icons
import { DiceIcon } from "@plane/ui";
import { DiceIcon, Tooltip } from "@plane/ui";
// helpers
import { cn } from "helpers/common.helper";
// types
@ -40,6 +40,7 @@ type ButtonProps = {
hideText?: boolean;
module: IModule | null;
placeholder: string;
tooltip: boolean;
};
const BorderButton = (props: ButtonProps) => {
@ -51,21 +52,24 @@ const BorderButton = (props: ButtonProps) => {
hideText = false,
module,
placeholder,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && <DiceIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{module?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Module" tooltipContent={module?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && <DiceIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{module?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -78,18 +82,24 @@ const BackgroundButton = (props: ButtonProps) => {
hideText = false,
module,
placeholder,
tooltip,
} = props;
return (
<div
className={cn("h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80", className)}
>
{!hideIcon && <DiceIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{module?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Module" tooltipContent={module?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80",
className
)}
>
{!hideIcon && <DiceIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{module?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -102,21 +112,24 @@ const TransparentButton = (props: ButtonProps) => {
hideText = false,
module,
placeholder,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && <DiceIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{module?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Module" tooltipContent={module?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && <DiceIcon className="h-3 w-3 flex-shrink-0" />}
{!hideText && <span className="flex-grow truncate">{module?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -135,8 +148,9 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
placeholder = "Module",
placement,
projectId,
value,
tabIndex,
tooltip = false,
value,
} = props;
// states
const [query, setQuery] = useState("");
@ -253,6 +267,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -263,6 +278,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-with-text" ? (
<BackgroundButton
@ -272,6 +288,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -282,6 +299,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-with-text" ? (
<TransparentButton
@ -291,6 +309,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -301,6 +320,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : null}
</button>

View file

@ -2,11 +2,12 @@ import { Fragment, ReactNode, useRef, useState } from "react";
import { Combobox } from "@headlessui/react";
import { usePopper } from "react-popper";
import { Check, ChevronDown, Search } from "lucide-react";
import { useTheme } from "next-themes";
// hooks
import { useDropdownKeyDown } from "hooks/use-dropdown-key-down";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// icons
import { PriorityIcon } from "@plane/ui";
import { PriorityIcon, Tooltip } from "@plane/ui";
// helpers
import { cn } from "helpers/common.helper";
// types
@ -14,7 +15,6 @@ import { TIssuePriorities } from "@plane/types";
import { TDropdownProps } from "./types";
// constants
import { ISSUE_PRIORITIES } from "constants/issue";
import { useTheme } from "next-themes";
type Props = TDropdownProps & {
button?: ReactNode;
@ -33,6 +33,7 @@ type ButtonProps = {
hideText?: boolean;
highlightUrgent: boolean;
priority: TIssuePriorities;
tooltip: boolean;
};
const BorderButton = (props: ButtonProps) => {
@ -44,6 +45,7 @@ const BorderButton = (props: ButtonProps) => {
hideText = false,
highlightUrgent,
priority,
tooltip,
} = props;
const priorityDetails = ISSUE_PRIORITIES.find((p) => p.key === priority);
@ -57,47 +59,49 @@ const BorderButton = (props: ButtonProps) => {
};
return (
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] rounded text-xs px-2 py-0.5",
priorityClasses[priority],
{
// compact the icons if text is hidden
"px-0.5": hideText,
// highlight the whole button if text is hidden and priority is urgent
"bg-red-500 border-red-500": priority === "urgent" && hideText && highlightUrgent,
},
className
)}
>
{!hideIcon && (
<div
className={cn({
// highlight just the icon if text is visible and priority is urgent
"bg-red-500 p-1 rounded": priority === "urgent" && !hideText && highlightUrgent,
})}
>
<PriorityIcon
priority={priority}
size={12}
className={cn("flex-shrink-0", {
// increase the icon size if text is hidden
"h-3.5 w-3.5": hideText,
// centre align the icons if text is hidden
"translate-x-[0.0625rem]": hideText && priority === "high",
"translate-x-0.5": hideText && priority === "medium",
"translate-x-1": hideText && priority === "low",
// highlight the icon if priority is urgent
"text-white": priority === "urgent" && highlightUrgent,
<Tooltip tooltipHeading="Priority" tooltipContent={priorityDetails?.title ?? "None"} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] rounded text-xs px-2 py-0.5",
priorityClasses[priority],
{
// compact the icons if text is hidden
"px-0.5": hideText,
// highlight the whole button if text is hidden and priority is urgent
"bg-red-500 border-red-500": priority === "urgent" && hideText && highlightUrgent,
},
className
)}
>
{!hideIcon && (
<div
className={cn({
// highlight just the icon if text is visible and priority is urgent
"bg-red-500 p-1 rounded": priority === "urgent" && !hideText && highlightUrgent,
})}
/>
</div>
)}
{!hideText && <span className="flex-grow truncate">{priorityDetails?.title}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
>
<PriorityIcon
priority={priority}
size={12}
className={cn("flex-shrink-0", {
// increase the icon size if text is hidden
"h-3.5 w-3.5": hideText,
// centre align the icons if text is hidden
"translate-x-[0.0625rem]": hideText && priority === "high",
"translate-x-0.5": hideText && priority === "medium",
"translate-x-1": hideText && priority === "low",
// highlight the icon if priority is urgent
"text-white": priority === "urgent" && highlightUrgent,
})}
/>
</div>
)}
{!hideText && <span className="flex-grow truncate">{priorityDetails?.title}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -110,6 +114,7 @@ const BackgroundButton = (props: ButtonProps) => {
hideText = false,
highlightUrgent,
priority,
tooltip,
} = props;
const priorityDetails = ISSUE_PRIORITIES.find((p) => p.key === priority);
@ -123,47 +128,49 @@ const BackgroundButton = (props: ButtonProps) => {
};
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5",
priorityClasses[priority],
{
// compact the icons if text is hidden
"px-0.5": hideText,
// highlight the whole button if text is hidden and priority is urgent
"bg-red-500 border-red-500": priority === "urgent" && hideText && highlightUrgent,
},
className
)}
>
{!hideIcon && (
<div
className={cn({
// highlight just the icon if text is visible and priority is urgent
"bg-red-500 p-1 rounded": priority === "urgent" && !hideText && highlightUrgent,
})}
>
<PriorityIcon
priority={priority}
size={12}
className={cn("flex-shrink-0", {
// increase the icon size if text is hidden
"h-3.5 w-3.5": hideText,
// centre align the icons if text is hidden
"translate-x-[0.0625rem]": hideText && priority === "high",
"translate-x-0.5": hideText && priority === "medium",
"translate-x-1": hideText && priority === "low",
// highlight the icon if priority is urgent
"text-white": priority === "urgent" && highlightUrgent,
<Tooltip tooltipHeading="Priority" tooltipContent={priorityDetails?.title ?? "None"} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5",
priorityClasses[priority],
{
// compact the icons if text is hidden
"px-0.5": hideText,
// highlight the whole button if text is hidden and priority is urgent
"bg-red-500 border-red-500": priority === "urgent" && hideText && highlightUrgent,
},
className
)}
>
{!hideIcon && (
<div
className={cn({
// highlight just the icon if text is visible and priority is urgent
"bg-red-500 p-1 rounded": priority === "urgent" && !hideText && highlightUrgent,
})}
/>
</div>
)}
{!hideText && <span className="flex-grow truncate">{priorityDetails?.title}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
>
<PriorityIcon
priority={priority}
size={12}
className={cn("flex-shrink-0", {
// increase the icon size if text is hidden
"h-3.5 w-3.5": hideText,
// centre align the icons if text is hidden
"translate-x-[0.0625rem]": hideText && priority === "high",
"translate-x-0.5": hideText && priority === "medium",
"translate-x-1": hideText && priority === "low",
// highlight the icon if priority is urgent
"text-white": priority === "urgent" && highlightUrgent,
})}
/>
</div>
)}
{!hideText && <span className="flex-grow truncate">{priorityDetails?.title}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -176,6 +183,7 @@ const TransparentButton = (props: ButtonProps) => {
hideText = false,
highlightUrgent,
priority,
tooltip,
} = props;
const priorityDetails = ISSUE_PRIORITIES.find((p) => p.key === priority);
@ -189,47 +197,49 @@ const TransparentButton = (props: ButtonProps) => {
};
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
priorityClasses[priority],
{
// compact the icons if text is hidden
"px-0.5": hideText,
// highlight the whole button if text is hidden and priority is urgent
"bg-red-500 border-red-500": priority === "urgent" && hideText && highlightUrgent,
},
className
)}
>
{!hideIcon && (
<div
className={cn({
// highlight just the icon if text is visible and priority is urgent
"bg-red-500 p-1 rounded": priority === "urgent" && !hideText && highlightUrgent,
})}
>
<PriorityIcon
priority={priority}
size={12}
className={cn("flex-shrink-0", {
// increase the icon size if text is hidden
"h-3.5 w-3.5": hideText,
// centre align the icons if text is hidden
"translate-x-[0.0625rem]": hideText && priority === "high",
"translate-x-0.5": hideText && priority === "medium",
"translate-x-1": hideText && priority === "low",
// highlight the icon if priority is urgent
"text-white": priority === "urgent" && highlightUrgent,
<Tooltip tooltipHeading="Priority" tooltipContent={priorityDetails?.title ?? "None"} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
priorityClasses[priority],
{
// compact the icons if text is hidden
"px-0.5": hideText,
// highlight the whole button if text is hidden and priority is urgent
"bg-red-500 border-red-500": priority === "urgent" && hideText && highlightUrgent,
},
className
)}
>
{!hideIcon && (
<div
className={cn({
// highlight just the icon if text is visible and priority is urgent
"bg-red-500 p-1 rounded": priority === "urgent" && !hideText && highlightUrgent,
})}
/>
</div>
)}
{!hideText && <span className="flex-grow truncate">{priorityDetails?.title}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
>
<PriorityIcon
priority={priority}
size={12}
className={cn("flex-shrink-0", {
// increase the icon size if text is hidden
"h-3.5 w-3.5": hideText,
// centre align the icons if text is hidden
"translate-x-[0.0625rem]": hideText && priority === "high",
"translate-x-0.5": hideText && priority === "medium",
"translate-x-1": hideText && priority === "low",
// highlight the icon if priority is urgent
"text-white": priority === "urgent" && highlightUrgent,
})}
/>
</div>
)}
{!hideText && <span className="flex-grow truncate">{priorityDetails?.title}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -247,8 +257,9 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
highlightUrgent = true,
onChange,
placement,
value,
tabIndex,
tooltip = false,
value,
} = props;
// states
const [query, setQuery] = useState("");
@ -341,6 +352,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -352,6 +364,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "background-with-text" ? (
@ -364,6 +377,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -375,6 +389,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "transparent-with-text" ? (
@ -387,6 +402,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -398,6 +414,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
hideText
/>
) : null}

View file

@ -7,6 +7,8 @@ import { Check, ChevronDown, Search } from "lucide-react";
import { useProject } from "hooks/store";
import { useDropdownKeyDown } from "hooks/use-dropdown-key-down";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// ui
import { Tooltip } from "@plane/ui";
// helpers
import { cn } from "helpers/common.helper";
import { renderEmoji } from "helpers/emoji.helper";
@ -30,6 +32,7 @@ type ButtonProps = {
hideText?: boolean;
placeholder: string;
project: IProject | null;
tooltip: boolean;
};
const BorderButton = (props: ButtonProps) => {
@ -41,25 +44,28 @@ const BorderButton = (props: ButtonProps) => {
hideText = false,
placeholder,
project,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && (
<span className="grid place-items-center flex-shrink-0">
{project?.emoji ? renderEmoji(project?.emoji) : project?.icon_prop ? renderEmoji(project?.icon_prop) : null}
</span>
)}
{!hideText && <span className="flex-grow truncate">{project?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Project" tooltipContent={project?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && (
<span className="grid place-items-center flex-shrink-0">
{project?.emoji ? renderEmoji(project?.emoji) : project?.icon_prop ? renderEmoji(project?.icon_prop) : null}
</span>
)}
{!hideText && <span className="flex-grow truncate">{project?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -72,22 +78,28 @@ const BackgroundButton = (props: ButtonProps) => {
hideText = false,
placeholder,
project,
tooltip,
} = props;
return (
<div
className={cn("h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80", className)}
>
{!hideIcon && (
<span className="grid place-items-center flex-shrink-0">
{project?.emoji ? renderEmoji(project?.emoji) : project?.icon_prop ? renderEmoji(project?.icon_prop) : null}
</span>
)}
{!hideText && <span className="flex-grow truncate">{project?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Project" tooltipContent={project?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80",
className
)}
>
{!hideIcon && (
<span className="grid place-items-center flex-shrink-0">
{project?.emoji ? renderEmoji(project?.emoji) : project?.icon_prop ? renderEmoji(project?.icon_prop) : null}
</span>
)}
{!hideText && <span className="flex-grow truncate">{project?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -100,25 +112,28 @@ const TransparentButton = (props: ButtonProps) => {
hideText = false,
placeholder,
project,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && (
<span className="grid place-items-center flex-shrink-0">
{project?.emoji ? renderEmoji(project?.emoji) : project?.icon_prop ? renderEmoji(project?.icon_prop) : null}
</span>
)}
{!hideText && <span className="flex-grow truncate">{project?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="Project" tooltipContent={project?.name ?? placeholder} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && (
<span className="grid place-items-center flex-shrink-0">
{project?.emoji ? renderEmoji(project?.emoji) : project?.icon_prop ? renderEmoji(project?.icon_prop) : null}
</span>
)}
{!hideText && <span className="flex-grow truncate">{project?.name ?? placeholder}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -136,8 +151,9 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
onChange,
placeholder = "Project",
placement,
value,
tabIndex,
tooltip = false,
value,
} = props;
// states
const [query, setQuery] = useState("");
@ -239,6 +255,7 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -249,6 +266,7 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-with-text" ? (
<BackgroundButton
@ -258,6 +276,7 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -268,6 +287,7 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-with-text" ? (
<TransparentButton
@ -277,6 +297,7 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
placeholder={placeholder}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -287,6 +308,7 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
hideIcon={hideIcon}
hideText
placeholder={placeholder}
tooltip={tooltip}
/>
) : null}
</button>

View file

@ -8,7 +8,7 @@ import { useApplication, useProjectState } from "hooks/store";
import { useDropdownKeyDown } from "hooks/use-dropdown-key-down";
import useOutsideClickDetector from "hooks/use-outside-click-detector";
// icons
import { StateGroupIcon } from "@plane/ui";
import { StateGroupIcon, Tooltip } from "@plane/ui";
// helpers
import { cn } from "helpers/common.helper";
// types
@ -31,65 +31,111 @@ type ButtonProps = {
hideIcon?: boolean;
hideText?: boolean;
state: IState | undefined;
tooltip: boolean;
};
const BorderButton = (props: ButtonProps) => {
const { className, dropdownArrow, dropdownArrowClassName, hideIcon = false, hideText = false, state } = props;
const {
className,
dropdownArrow,
dropdownArrowClassName,
hideIcon = false,
hideText = false,
state,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && (
<StateGroupIcon stateGroup={state?.group ?? "backlog"} color={state?.color} className="h-3 w-3 flex-shrink-0" />
)}
{!hideText && <span className="flex-grow truncate">{state?.name ?? "State"}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="State" tooltipContent={state?.name ?? "State"} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
className
)}
>
{!hideIcon && (
<StateGroupIcon
stateGroup={state?.group ?? "backlog"}
color={state?.color}
className="h-3 w-3 flex-shrink-0"
/>
)}
{!hideText && <span className="flex-grow truncate">{state?.name ?? "State"}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
const BackgroundButton = (props: ButtonProps) => {
const { className, dropdownArrow, dropdownArrowClassName, hideIcon = false, hideText = false, state } = props;
const {
className,
dropdownArrow,
dropdownArrowClassName,
hideIcon = false,
hideText = false,
state,
tooltip,
} = props;
return (
<div
className={cn("h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80", className)}
>
{!hideIcon && (
<StateGroupIcon stateGroup={state?.group ?? "backlog"} color={state?.color} className="h-3 w-3 flex-shrink-0" />
)}
{!hideText && <span className="flex-grow truncate">{state?.name ?? "State"}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="State" tooltipContent={state?.name ?? "State"} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 bg-custom-background-80",
className
)}
>
{!hideIcon && (
<StateGroupIcon
stateGroup={state?.group ?? "backlog"}
color={state?.color}
className="h-3 w-3 flex-shrink-0"
/>
)}
{!hideText && <span className="flex-grow truncate">{state?.name ?? "State"}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
const TransparentButton = (props: ButtonProps) => {
const { className, dropdownArrow, dropdownArrowClassName, hideIcon = false, hideText = false, state } = props;
const {
className,
dropdownArrow,
dropdownArrowClassName,
hideIcon = false,
hideText = false,
state,
tooltip,
} = props;
return (
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && (
<StateGroupIcon stateGroup={state?.group ?? "backlog"} color={state?.color} className="h-3 w-3 flex-shrink-0" />
)}
{!hideText && <span className="flex-grow truncate">{state?.name ?? "State"}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
<Tooltip tooltipHeading="State" tooltipContent={state?.name ?? "State"} disabled={!tooltip}>
<div
className={cn(
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
className
)}
>
{!hideIcon && (
<StateGroupIcon
stateGroup={state?.group ?? "backlog"}
color={state?.color}
className="h-3 w-3 flex-shrink-0"
/>
)}
{!hideText && <span className="flex-grow truncate">{state?.name ?? "State"}</span>}
{dropdownArrow && (
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
)}
</div>
</Tooltip>
);
};
@ -107,8 +153,9 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
onChange,
placement,
projectId,
value,
tabIndex,
tooltip = false,
value,
} = props;
// states
const [query, setQuery] = useState("");
@ -204,6 +251,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
/>
) : buttonVariant === "border-without-text" ? (
<BorderButton
@ -212,6 +260,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "background-with-text" ? (
@ -221,6 +270,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
/>
) : buttonVariant === "background-without-text" ? (
<BackgroundButton
@ -229,6 +279,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
hideText
/>
) : buttonVariant === "transparent-with-text" ? (
@ -238,6 +289,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
/>
) : buttonVariant === "transparent-without-text" ? (
<TransparentButton
@ -245,6 +297,8 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
className={buttonClassName}
dropdownArrow={dropdownArrow && !disabled}
dropdownArrowClassName={dropdownArrowClassName}
hideIcon={hideIcon}
tooltip={tooltip}
hideText
/>
) : null}

View file

@ -18,4 +18,5 @@ export type TDropdownProps = {
placeholder?: string;
placement?: Placement;
tabIndex?: number;
tooltip?: boolean;
};