[WEB-5762] fix: workitem detail sidebar properties design consistency (#8400)
This commit is contained in:
parent
3876bf054c
commit
67c39dfc3d
14 changed files with 101 additions and 62 deletions
|
|
@ -19,7 +19,7 @@ export function SidebarPropertyListItem(props: TSidebarPropertyListItemProps) {
|
|||
<span>{label}</span>
|
||||
{appendElement}
|
||||
</div>
|
||||
<div className={cn("grow flex items-center flex-wrap gap-2", childrenClassName)}>{children}</div>
|
||||
<div className={cn("grow flex items-center flex-wrap gap-1", childrenClassName)}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from "react";
|
||||
// helpers
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import { cn } from "@plane/utils";
|
||||
// types
|
||||
|
|
@ -71,9 +72,11 @@ function BorderButton(props: ButtonProps) {
|
|||
isMobile={isMobile}
|
||||
renderByDefault={renderToolTipByDefault}
|
||||
>
|
||||
<div
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={cn(
|
||||
"h-full w-full flex items-center gap-1.5 border-[0.5px] border-strong hover:bg-layer-transparent-hover text-11 px-2 py-0.5 rounded-sm",
|
||||
"h-full w-full flex items-center justify-start gap-1.5 border-[0.5px] border-strong",
|
||||
{
|
||||
"bg-layer-transparent-active": isActive,
|
||||
},
|
||||
|
|
@ -81,7 +84,7 @@ function BorderButton(props: ButtonProps) {
|
|||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
@ -97,14 +100,16 @@ function BackgroundButton(props: ButtonProps) {
|
|||
isMobile={isMobile}
|
||||
renderByDefault={renderToolTipByDefault}
|
||||
>
|
||||
<div
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={cn(
|
||||
"h-full w-full flex items-center gap-1.5 rounded-sm text-11 px-2 py-0.5 bg-layer-3 hover:bg-layer-1-hover",
|
||||
"h-full w-full flex items-center justify-start gap-1.5 bg-layer-3 hover:bg-layer-1-hover",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
@ -120,9 +125,11 @@ function TransparentButton(props: ButtonProps) {
|
|||
isMobile={isMobile}
|
||||
renderByDefault={renderToolTipByDefault}
|
||||
>
|
||||
<div
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={cn(
|
||||
"h-full w-full flex items-center gap-1.5 rounded-sm text-11 px-2 py-0.5 hover:bg-layer-transparent-hover",
|
||||
"h-full w-full flex items-center justify-start gap-1.5",
|
||||
{
|
||||
"bg-layer-transparent-active": isActive,
|
||||
},
|
||||
|
|
@ -130,7 +137,7 @@ function TransparentButton(props: ButtonProps) {
|
|||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,9 @@ export const DateDropdown = observer(function DateDropdown(props: Props) {
|
|||
>
|
||||
{!hideIcon && icon}
|
||||
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
||||
<span className="flex-grow truncate">{value ? renderFormattedDate(value, formatToken) : placeholder}</span>
|
||||
<span className="flex-grow truncate text-left text-body-xs-medium">
|
||||
{value ? renderFormattedDate(value, formatToken) : placeholder}
|
||||
</span>
|
||||
)}
|
||||
{isClearable && !disabled && isDateSelected && (
|
||||
<CloseIcon
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ export const MemberDropdownBase = observer(function MemberDropdownBase(props: TM
|
|||
>
|
||||
{!hideIcon && <ButtonAvatars showTooltip={showTooltip} userIds={value} icon={icon} />}
|
||||
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
||||
<span className="flex-grow truncate leading-5">
|
||||
<span className="flex-grow truncate leading-5 text-left text-body-xs-medium">
|
||||
{getDisplayName(value, showUserDetails, placeholder)}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,14 @@ function BorderButton(props: ButtonProps) {
|
|||
<SignalHigh className="size-3" />
|
||||
))}
|
||||
{!hideText && (
|
||||
<span className="flex-grow truncate text-caption-sm-regular">{priorityDetails?.title ?? placeholder}</span>
|
||||
<span
|
||||
className={cn("flex-grow truncate text-body-xs-medium", {
|
||||
"text-secondary": priority && priority !== "none",
|
||||
"text-placeholder": !priority || priority === "none",
|
||||
})}
|
||||
>
|
||||
{priorityDetails?.title ?? placeholder}
|
||||
</span>
|
||||
)}
|
||||
{dropdownArrow && (
|
||||
<ChevronDownIcon className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
||||
|
|
@ -204,7 +211,12 @@ function BackgroundButton(props: ButtonProps) {
|
|||
<SignalHigh className="size-3" />
|
||||
))}
|
||||
{!hideText && (
|
||||
<span className="flex-grow truncate text-caption-sm-regular">
|
||||
<span
|
||||
className={cn("flex-grow truncate text-body-xs-medium", {
|
||||
"text-secondary": priority && priority !== "none",
|
||||
"text-placeholder": !priority || priority === "none",
|
||||
})}
|
||||
>
|
||||
{priorityDetails?.title ?? t("common.priority") ?? placeholder}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -246,7 +258,7 @@ function TransparentButton(props: ButtonProps) {
|
|||
>
|
||||
<div
|
||||
className={cn(
|
||||
"h-full w-full flex items-center gap-1.5 rounded-sm px-2 py-0.5 hover:bg-layer-transparent-hover",
|
||||
"h-full w-full flex items-center gap-1.5 rounded-sm hover:bg-layer-transparent-hover px-2",
|
||||
{
|
||||
// compact the icons if text is hidden
|
||||
"px-0.5": hideText,
|
||||
|
|
@ -283,7 +295,12 @@ function TransparentButton(props: ButtonProps) {
|
|||
<SignalHigh className="size-3" />
|
||||
))}
|
||||
{!hideText && (
|
||||
<span className="flex-grow truncate text-caption-sm-regular">
|
||||
<span
|
||||
className={cn("flex-grow truncate text-body-xs-medium", {
|
||||
"text-secondary": priority && priority !== "none",
|
||||
"text-placeholder": !priority || priority === "none",
|
||||
})}
|
||||
>
|
||||
{priorityDetails?.title ?? t("common.priority") ?? placeholder}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ export const IssueCycleSelect = observer(function IssueCycleSelect(props: TIssue
|
|||
disabled={disableSelect}
|
||||
buttonVariant="transparent-with-text"
|
||||
className="group w-full"
|
||||
buttonContainerClassName="w-full text-left rounded-sm"
|
||||
buttonClassName={`text-13 justify-between ${issue?.cycle_id ? "" : "text-placeholder"}`}
|
||||
buttonContainerClassName="w-full text-left h-7.5 rounded-sm"
|
||||
buttonClassName={`text-body-xs-medium justify-between ${issue?.cycle_id ? "" : "text-placeholder"}`}
|
||||
placeholder={t("cycle.no_cycle")}
|
||||
hideIcon
|
||||
dropdownArrow
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { observer } from "mobx-react";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { CloseIcon, LabelFilledIcon } from "@plane/propel/icons";
|
||||
import { cn } from "@plane/utils";
|
||||
// types
|
||||
import { useLabel } from "@/hooks/store/use-label";
|
||||
import type { TLabelOperations } from "./root";
|
||||
|
|
@ -31,25 +31,14 @@ export const LabelListItem = observer(function LabelListItem(props: TLabelListIt
|
|||
|
||||
if (!label) return <></>;
|
||||
return (
|
||||
<button
|
||||
key={labelId}
|
||||
type="button"
|
||||
className={cn(
|
||||
"h-full w-min flex items-center gap-1.5 rounded-sm px-2 py-0.5 bg-layer-transparent-active group text-body-xs-regular text-tertiary",
|
||||
{
|
||||
"cursor-pointer": !disabled,
|
||||
}
|
||||
)}
|
||||
onClick={handleLabel}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Button key={labelId} type="button" variant="tertiary" onClick={() => void handleLabel()} disabled={disabled}>
|
||||
<LabelFilledIcon className="size-3" color={label.color ?? "#000000"} />
|
||||
<div className="flex-shrink-0 text-body-xs-regular">{label.name}</div>
|
||||
<div className="flex-shrink-0">{label.name}</div>
|
||||
{!disabled && (
|
||||
<div className="flex-shrink-0">
|
||||
<CloseIcon className="transition-all h-2.5 w-2.5 group-hover:text-danger" />
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
</Button>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ export const IssueLabel = observer(function IssueLabel(props: TIssueLabel) {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-wrap items-center gap-1 min-h-7.5 w-full">
|
||||
<div className="relative flex flex-wrap items-center gap-1 min-h-7.5 w-full px-2">
|
||||
<LabelList
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { Fragment, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { usePopper } from "react-popper";
|
||||
import { Check, Loader, Search } from "lucide-react";
|
||||
import { Check, Loader, Plus, Search } from "lucide-react";
|
||||
import { Combobox } from "@headlessui/react";
|
||||
// plane imports
|
||||
import { EUserPermissionsLevel, getRandomLabelColor } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import type { IIssueLabel } from "@plane/types";
|
||||
import { EUserProjectRoles } from "@plane/types";
|
||||
// helpers
|
||||
|
|
@ -84,11 +85,7 @@ export const IssueLabelSelect = observer(function IssueLabelSelect(props: IIssue
|
|||
|
||||
const issueLabels = values ?? [];
|
||||
|
||||
const label = (
|
||||
<span className="size-full flex items-center rounded-sm px-2 py-0.5 bg-layer-transparent hover:bg-layer-transparent-hover text-body-xs-regular text-tertiary">
|
||||
{t("label.select")}
|
||||
</span>
|
||||
);
|
||||
const label = <span className="text-body-xs-medium text-placeholder">{t("label.select")}</span>;
|
||||
|
||||
const searchInputKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (query !== "" && e.key === "Escape") {
|
||||
|
|
@ -123,14 +120,16 @@ export const IssueLabelSelect = observer(function IssueLabelSelect(props: IIssue
|
|||
multiple
|
||||
>
|
||||
<Combobox.Button as={Fragment}>
|
||||
<button
|
||||
<Button
|
||||
ref={setReferenceElement}
|
||||
type="button"
|
||||
className="cursor-pointer size-full"
|
||||
variant="tertiary"
|
||||
size="sm"
|
||||
prependIcon={<Plus />}
|
||||
onClick={() => !projectLabels && fetchLabels()}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
</Button>
|
||||
</Combobox.Button>
|
||||
|
||||
<Combobox.Options className="fixed z-10">
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ export const IssueModuleSelect = observer(function IssueModuleSelect(props: TIss
|
|||
placeholder={t("module.no_module")}
|
||||
disabled={disableSelect}
|
||||
className="group h-full w-full"
|
||||
buttonContainerClassName="w-full rounded-sm"
|
||||
buttonClassName={`min-h-8 text-13 justify-between ${issue?.module_ids?.length ? "" : "text-placeholder"}`}
|
||||
buttonContainerClassName="w-full text-left h-7.5 rounded-sm"
|
||||
buttonClassName={`text-body-xs-medium justify-between ${issue?.module_ids?.length ? "" : "text-placeholder"}`}
|
||||
buttonVariant="transparent-with-text"
|
||||
hideIcon
|
||||
dropdownArrow
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ export const IssueParentSelect = observer(function IssueParentSelect(props: TIss
|
|||
disabled={disabled}
|
||||
>
|
||||
{issue.parent_id && parentIssue ? (
|
||||
<div className="flex items-center gap-1 bg-success-subtle rounded-sm px-1.5 py-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={parentIssue.name} isMobile={isMobile}>
|
||||
<Link href={workItemLink} target="_blank" rel="noopener noreferrer" onClick={(e) => e.stopPropagation()}>
|
||||
{parentIssue?.project_id && parentIssueProjectDetails && (
|
||||
|
|
@ -94,7 +94,7 @@ export const IssueParentSelect = observer(function IssueParentSelect(props: TIss
|
|||
projectIdentifier={parentIssueProjectDetails?.identifier}
|
||||
issueSequenceId={parentIssue.sequence_id}
|
||||
size="xs"
|
||||
variant="success"
|
||||
variant="secondary"
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
|
|
@ -115,7 +115,7 @@ export const IssueParentSelect = observer(function IssueParentSelect(props: TIss
|
|||
)}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-body-xs-regular text-placeholder">{t("issue.add.parent")}</span>
|
||||
<span className="text-body-xs-medium text-placeholder">{t("issue.add.parent")}</span>
|
||||
)}
|
||||
{!disabled && (
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
|||
return (
|
||||
<div>
|
||||
<h6 className="text-body-xs-medium">{t("common.properties")}</h6>
|
||||
<div className={`w-full space-y-2 mt-3 ${disabled ? "opacity-60" : ""}`}>
|
||||
<div className={`w-full space-y-3 mt-3 ${disabled ? "opacity-60" : ""}`}>
|
||||
<SidebarPropertyListItem icon={StatePropertyIcon} label={t("common.state")}>
|
||||
<StateDropdown
|
||||
value={issue?.state_id}
|
||||
|
|
@ -85,7 +85,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
|||
buttonVariant="transparent-with-text"
|
||||
className="w-full grow group"
|
||||
buttonContainerClassName="w-full text-left h-7.5"
|
||||
buttonClassName="text-body-xs-regular"
|
||||
buttonClassName={`text-body-xs-medium ${issue?.state_id ? "" : "text-placeholder"}`}
|
||||
dropdownArrow
|
||||
dropdownArrowClassName="h-3.5 w-3.5 hidden group-hover:inline"
|
||||
/>
|
||||
|
|
@ -102,7 +102,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
|||
buttonVariant={issue?.assignee_ids?.length > 1 ? "transparent-without-text" : "transparent-with-text"}
|
||||
className="w-full grow group"
|
||||
buttonContainerClassName="w-full text-left h-7.5"
|
||||
buttonClassName={`text-body-xs-regular justify-between ${issue?.assignee_ids?.length > 0 ? "" : "text-placeholder"}`}
|
||||
buttonClassName={`text-body-xs-medium justify-between ${issue?.assignee_ids?.length > 0 ? "" : "text-placeholder"}`}
|
||||
hideIcon={issue.assignee_ids?.length === 0}
|
||||
dropdownArrow
|
||||
dropdownArrowClassName="h-3.5 w-3.5 hidden group-hover:inline"
|
||||
|
|
@ -116,18 +116,22 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
|||
disabled={disabled}
|
||||
buttonVariant="transparent-with-text"
|
||||
className="w-full h-7.5 grow rounded-sm"
|
||||
buttonContainerClassName="size-full text-left"
|
||||
buttonClassName="size-full px-2 py-0.5 whitespace-nowrap [&_svg]:size-3.5"
|
||||
buttonContainerClassName="w-full text-left h-7.5"
|
||||
buttonClassName={`text-body-xs-medium whitespace-nowrap [&_svg]:size-3.5 ${!issue?.priority || issue?.priority === "none" ? "text-placeholder" : ""}`}
|
||||
/>
|
||||
</SidebarPropertyListItem>
|
||||
|
||||
{createdByDetails && (
|
||||
<SidebarPropertyListItem icon={UserCirclePropertyIcon} label={t("common.created_by")}>
|
||||
<SidebarPropertyListItem
|
||||
icon={UserCirclePropertyIcon}
|
||||
label={t("common.created_by")}
|
||||
childrenClassName="px-2"
|
||||
>
|
||||
<ButtonAvatars
|
||||
showTooltip
|
||||
userIds={createdByDetails?.display_name.includes("-intake") ? null : createdByDetails?.id}
|
||||
/>
|
||||
<span className="grow truncate leading-5">
|
||||
<span className="grow truncate text-body-xs-medium text-secondary leading-5">
|
||||
{createdByDetails?.display_name.includes("-intake") ? "Plane" : createdByDetails?.display_name}
|
||||
</span>
|
||||
</SidebarPropertyListItem>
|
||||
|
|
@ -147,7 +151,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
|||
disabled={disabled}
|
||||
className="w-full grow group"
|
||||
buttonContainerClassName="w-full text-left h-7.5"
|
||||
buttonClassName={`text-body-xs-regular ${issue?.start_date ? "" : "text-placeholder"}`}
|
||||
buttonClassName={`text-body-xs-medium ${issue?.start_date ? "" : "text-placeholder"}`}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline"
|
||||
/>
|
||||
|
|
@ -168,7 +172,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
|||
disabled={disabled}
|
||||
className="w-full grow group"
|
||||
buttonContainerClassName="w-full text-left h-7.5"
|
||||
buttonClassName={cn("text-body-xs-regular", {
|
||||
buttonClassName={cn("text-body-xs-medium", {
|
||||
"text-placeholder": !issue.target_date,
|
||||
"text-danger": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
||||
})}
|
||||
|
|
@ -189,7 +193,7 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
|||
buttonVariant="transparent-with-text"
|
||||
className="w-full grow group"
|
||||
buttonContainerClassName="w-full text-left h-7.5"
|
||||
buttonClassName={`text-body-xs-regular ${issue?.estimate_point !== undefined ? "" : "text-placeholder"}`}
|
||||
buttonClassName={`text-body-xs-medium ${issue?.estimate_point !== undefined ? "" : "text-placeholder"}`}
|
||||
placeholder="None"
|
||||
hideIcon
|
||||
dropdownArrow
|
||||
|
|
|
|||
|
|
@ -34,6 +34,28 @@ export const FiltersToggle = observer(function FiltersToggle<P extends TFilterPr
|
|||
filter.toggleVisibility();
|
||||
};
|
||||
|
||||
// Base classes when filter is active
|
||||
const activeFilterBaseClasses =
|
||||
"text-accent-primary border border-accent-subtle-1 hover:border-accent-subtle-1 active:border-accent-subtle-1 focus:border-accent-subtle-1";
|
||||
|
||||
// State classes that prevent hover/active/focus color changes
|
||||
const noHoverStateClasses = "hover:text-accent-primary active:text-accent-primary focus:text-accent-primary";
|
||||
|
||||
// Background classes based on toggle state (darker when open, lighter when closed)
|
||||
const backgroundClasses = isFilterRowVisible
|
||||
? "bg-accent-subtle-hover hover:bg-accent-subtle-hover active:bg-accent-subtle-hover focus:bg-accent-subtle-hover"
|
||||
: "bg-accent-subtle hover:bg-accent-subtle active:bg-accent-subtle focus:bg-accent-subtle";
|
||||
|
||||
const buttonClassName = cn({
|
||||
[activeFilterBaseClasses]: showFilterRowChangesPill,
|
||||
[backgroundClasses]: showFilterRowChangesPill,
|
||||
[noHoverStateClasses]: showFilterRowChangesPill,
|
||||
});
|
||||
|
||||
const iconClassName = cn({
|
||||
"text-accent-primary [&_path]:fill-current": showFilterRowChangesPill,
|
||||
});
|
||||
|
||||
// Show the add filter button when there are no active conditions, the filter row is hidden, and no unsaved changes exist
|
||||
if (filter && showAddFilterButton) {
|
||||
return (
|
||||
|
|
@ -55,9 +77,8 @@ export const FiltersToggle = observer(function FiltersToggle<P extends TFilterPr
|
|||
variant="secondary"
|
||||
icon={showFilterRowChangesPill ? FilterAppliedIcon : FilterIcon}
|
||||
onClick={handleToggleFilter}
|
||||
className={cn({
|
||||
"text-accent-primary bg-accent-subtle border border-accent-subtle-1": showFilterRowChangesPill,
|
||||
})}
|
||||
className={buttonClassName}
|
||||
iconClassName={iconClassName}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue