UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: контроль цветов и реворк интерфейса проектов
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { CSSProperties, ReactNode } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
// plane imports
|
||||
import { Row } from "@plane/ui";
|
||||
// components
|
||||
@@ -25,8 +26,13 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) {
|
||||
const { header, mobileHeader, className, rowClassName } = props;
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [dockStyle, setDockStyle] = useState<CSSProperties | undefined>(undefined);
|
||||
const pathname = usePathname();
|
||||
const { workspaceSlug } = useParams();
|
||||
const { data: userProfile } = useUserProfile();
|
||||
const isCompactToolbar = userProfile?.theme?.nodedcCompactToolbar === true;
|
||||
const workspaceSlugValue = workspaceSlug?.toString();
|
||||
const isWorkspaceHome =
|
||||
!!workspaceSlugValue && (pathname === `/${workspaceSlugValue}` || pathname === `/${workspaceSlugValue}/`);
|
||||
const effectiveDockStyle = isCompactToolbar
|
||||
? dockStyle
|
||||
: {
|
||||
@@ -49,10 +55,7 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) {
|
||||
width,
|
||||
});
|
||||
|
||||
document.documentElement.style.setProperty(
|
||||
"--nodedc-bottom-dock-offset",
|
||||
`${Math.max(height, 0)}px`
|
||||
);
|
||||
document.documentElement.style.setProperty("--nodedc-bottom-dock-offset", `${Math.max(height, 0)}px`);
|
||||
};
|
||||
|
||||
updateDockBounds();
|
||||
@@ -73,9 +76,10 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) {
|
||||
ref={containerRef}
|
||||
className={cn(
|
||||
"fixed bottom-0 z-[18]",
|
||||
isCompactToolbar ? "right-0 nodedc-app-header-compact" : "nodedc-app-header-expanded",
|
||||
isCompactToolbar ? "nodedc-app-header-compact right-0" : "nodedc-app-header-expanded",
|
||||
className
|
||||
)}
|
||||
data-nodedc-footer-scrim={isWorkspaceHome ? "false" : "true"}
|
||||
style={effectiveDockStyle}
|
||||
>
|
||||
<Row
|
||||
@@ -87,7 +91,7 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) {
|
||||
<ExtendedAppHeader header={header} />
|
||||
<div className="nodedc-bottom-dock-voice-slot" data-nodedc-voice-task-dock-slot />
|
||||
</Row>
|
||||
{mobileHeader && mobileHeader}
|
||||
{mobileHeader ?? null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -35,6 +35,12 @@ import { IssueStats } from "@/plane-web/components/issues/issue-layouts/issue-st
|
||||
// types
|
||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||
import { calculateIdentifierWidth } from "../utils";
|
||||
import {
|
||||
applyNodedcListPropertiesWidth,
|
||||
NODEDC_LIST_PROPERTIES_WIDTH_CSS_VAR,
|
||||
NODEDC_LIST_PROPERTIES_WIDTH_DEFAULT,
|
||||
persistNodedcListPropertiesWidth,
|
||||
} from "./list-properties-width";
|
||||
import type { TRenderQuickActions } from "./list-view-types";
|
||||
|
||||
interface IssueBlockProps {
|
||||
@@ -157,6 +163,44 @@ export const IssueBlock = observer(function IssueBlock(props: IssueBlockProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const handlePropertiesResizePointerDown = (event: React.PointerEvent<HTMLSpanElement>) => {
|
||||
if (event.button !== 0 || typeof window === "undefined") return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const root = document.documentElement;
|
||||
const computedWidth = Number.parseFloat(
|
||||
getComputedStyle(root).getPropertyValue(NODEDC_LIST_PROPERTIES_WIDTH_CSS_VAR)
|
||||
);
|
||||
const initialWidth = Number.isFinite(computedWidth) ? computedWidth : NODEDC_LIST_PROPERTIES_WIDTH_DEFAULT;
|
||||
const initialClientX = event.clientX;
|
||||
let latestWidth = initialWidth;
|
||||
const previousCursor = document.body.style.cursor;
|
||||
const previousUserSelect = document.body.style.userSelect;
|
||||
|
||||
document.body.style.cursor = "col-resize";
|
||||
document.body.style.userSelect = "none";
|
||||
|
||||
const handlePointerMove = (moveEvent: PointerEvent) => {
|
||||
moveEvent.preventDefault();
|
||||
latestWidth = applyNodedcListPropertiesWidth(initialWidth - (moveEvent.clientX - initialClientX));
|
||||
};
|
||||
|
||||
const handlePointerUp = () => {
|
||||
persistNodedcListPropertiesWidth(latestWidth);
|
||||
document.body.style.cursor = previousCursor;
|
||||
document.body.style.userSelect = previousUserSelect;
|
||||
window.removeEventListener("pointermove", handlePointerMove);
|
||||
window.removeEventListener("pointerup", handlePointerUp);
|
||||
window.removeEventListener("pointercancel", handlePointerUp);
|
||||
};
|
||||
|
||||
window.addEventListener("pointermove", handlePointerMove);
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
window.addEventListener("pointercancel", handlePointerUp);
|
||||
};
|
||||
|
||||
// Calculate width for: projectIdentifier + "-" + dynamic sequence number digits
|
||||
// Use next_work_item_sequence from backend (static value from project endpoint)
|
||||
const maxSequenceId = currentProjectNextSequenceId ?? 1;
|
||||
@@ -184,7 +228,7 @@ export const IssueBlock = observer(function IssueBlock(props: IssueBlockProps) {
|
||||
<Row
|
||||
ref={issueRef}
|
||||
className={cn(
|
||||
"group/list-block relative flex min-h-11 flex-col gap-3 bg-layer-transparent py-3 text-13 transition-colors hover:bg-layer-transparent-hover",
|
||||
"nodedc-list-work-item-row group/list-block relative flex min-h-11 flex-col gap-3 bg-layer-transparent py-3 text-13 transition-colors hover:bg-layer-transparent-hover",
|
||||
{
|
||||
"border-accent-strong": getIsIssuePeeked(issue.id) && peekIssue?.nestingLevel === nestingLevel,
|
||||
"border-strong-1": isIssueActive,
|
||||
@@ -207,8 +251,19 @@ export const IssueBlock = observer(function IssueBlock(props: IssueBlockProps) {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full gap-2 truncate">
|
||||
<div className="flex flex-grow items-center gap-0.5 truncate">
|
||||
<span
|
||||
className="nodedc-list-properties-resize-handle"
|
||||
role="separator"
|
||||
aria-orientation="vertical"
|
||||
aria-label="Resize list properties panel"
|
||||
onPointerDown={handlePropertiesResizePointerDown}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
<div className="nodedc-list-work-item-main flex w-full gap-2 truncate">
|
||||
<div className="flex min-w-0 flex-grow items-center gap-0.5 truncate">
|
||||
<div className="flex items-center gap-1" style={isSubIssue ? { marginLeft } : {}}>
|
||||
{/* select checkbox */}
|
||||
{projectId && canSelectIssues && !isEpic && (
|
||||
@@ -282,7 +337,9 @@ export const IssueBlock = observer(function IssueBlock(props: IssueBlockProps) {
|
||||
disabled={isCurrentBlockDragging}
|
||||
renderByDefault={false}
|
||||
>
|
||||
<p className="cursor-pointer truncate text-body-xs-medium text-primary">{issue.name}</p>
|
||||
<p className="nodedc-list-work-item-title cursor-pointer truncate text-body-xs-medium text-primary">
|
||||
{issue.name}
|
||||
</p>
|
||||
</Tooltip>
|
||||
{isEpic && displayProperties && (
|
||||
<WithDisplayPropertiesHOC
|
||||
@@ -308,11 +365,11 @@ export const IssueBlock = observer(function IssueBlock(props: IssueBlockProps) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 items-center gap-2">
|
||||
<div className="nodedc-list-work-item-side flex flex-shrink-0 items-center gap-2">
|
||||
{!issue?.tempId ? (
|
||||
<>
|
||||
<IssueProperties
|
||||
className={`relative flex flex-wrap ${isSidebarCollapsed ? "md:flex-shrink-0 md:flex-grow" : "lg:flex-shrink-0 lg:flex-grow"} items-center gap-2 whitespace-nowrap`}
|
||||
className={`nodedc-list-work-item-properties relative flex flex-wrap ${isSidebarCollapsed ? "md:flex-shrink-0 md:flex-grow" : "lg:flex-shrink-0 lg:flex-grow"} items-center gap-2 whitespace-nowrap`}
|
||||
issue={issue}
|
||||
isReadOnly={!canEditIssueProperties}
|
||||
updateIssue={updateIssue}
|
||||
|
||||
@@ -34,6 +34,7 @@ import { useBulkOperationStatus } from "@/plane-web/hooks/use-bulk-operation-sta
|
||||
import type { GroupDropLocation } from "../utils";
|
||||
import { getGroupByColumns, isWorkspaceLevel, isSubGrouped } from "../utils";
|
||||
import { ListGroup } from "./list-group";
|
||||
import { applyStoredNodedcListPropertiesWidth } from "./list-properties-width";
|
||||
import type { TRenderQuickActions } from "./list-view-types";
|
||||
|
||||
export interface IList {
|
||||
@@ -107,6 +108,17 @@ export const List = observer(function List(props: IList) {
|
||||
);
|
||||
}, [containerRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
const syncPropertiesWidth = () => applyStoredNodedcListPropertiesWidth();
|
||||
|
||||
syncPropertiesWidth();
|
||||
window.addEventListener("resize", syncPropertiesWidth);
|
||||
|
||||
return () => window.removeEventListener("resize", syncPropertiesWidth);
|
||||
}, []);
|
||||
|
||||
if (!groups) return null;
|
||||
|
||||
const getGroupIndex = (groupId: string | undefined) => groups.findIndex(({ id }) => id === groupId);
|
||||
|
||||
@@ -252,6 +252,12 @@ export const ListGroup = observer(function ListGroup(props: Props) {
|
||||
|
||||
const isGroupByCreatedBy = group_by === "created_by";
|
||||
const shouldExpand = (!!groupIssueCount && isExpanded) || !group_by;
|
||||
const shouldShowQuickAdd =
|
||||
enableIssueQuickAdd &&
|
||||
!disableIssueCreation &&
|
||||
!isGroupByCreatedBy &&
|
||||
!isCompletedCycle &&
|
||||
!isWorkflowIssueCreationDisabled;
|
||||
|
||||
return validateEmptyIssueGroups(groupIssueCount) ? (
|
||||
<div
|
||||
@@ -295,51 +301,49 @@ export const ListGroup = observer(function ListGroup(props: Props) {
|
||||
isDraggingOverColumn={isDraggingOverColumn}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
{groupIssueIds && (
|
||||
<IssueBlocksList
|
||||
issueIds={groupIssueIds}
|
||||
groupId={group.id}
|
||||
issuesMap={issuesMap}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
canEditProperties={canEditProperties}
|
||||
containerRef={containerRef}
|
||||
isDragAllowed={isDragAllowed}
|
||||
canDropOverIssue={!canOverlayBeVisible}
|
||||
selectionHelpers={selectionHelpers}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
)}
|
||||
|
||||
{shouldLoadMore &&
|
||||
(group_by ? (
|
||||
<>{loadMore}</>
|
||||
) : (
|
||||
<>
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<ListLoaderItemRow key={index} />
|
||||
))}
|
||||
<ListLoaderItemRow ref={setIntersectionElement} />
|
||||
</>
|
||||
))}
|
||||
|
||||
{enableIssueQuickAdd &&
|
||||
!disableIssueCreation &&
|
||||
!isGroupByCreatedBy &&
|
||||
!isCompletedCycle &&
|
||||
!isWorkflowIssueCreationDisabled && (
|
||||
<div className="nodedc-bottom-dock-sticky-offset sticky z-[1] w-full flex-shrink-0">
|
||||
<QuickAddIssueRoot
|
||||
layout={EIssueLayoutTypes.LIST}
|
||||
QuickAddButton={ListQuickAddIssueButton}
|
||||
prePopulatedData={prePopulateQuickAddData(group_by, group.id)}
|
||||
containerClassName="border-b border-t border-subtle bg-surface-1 "
|
||||
quickAddCallback={quickAddCallback}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
<div className={cn({ "nodedc-bottom-dock-aware-padding": shouldShowQuickAdd })}>
|
||||
{groupIssueIds && (
|
||||
<IssueBlocksList
|
||||
issueIds={groupIssueIds}
|
||||
groupId={group.id}
|
||||
issuesMap={issuesMap}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
canEditProperties={canEditProperties}
|
||||
containerRef={containerRef}
|
||||
isDragAllowed={isDragAllowed}
|
||||
canDropOverIssue={!canOverlayBeVisible}
|
||||
selectionHelpers={selectionHelpers}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
)}
|
||||
|
||||
{shouldLoadMore &&
|
||||
(group_by ? (
|
||||
<>{loadMore}</>
|
||||
) : (
|
||||
<>
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<ListLoaderItemRow key={index} />
|
||||
))}
|
||||
<ListLoaderItemRow ref={setIntersectionElement} />
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{shouldShowQuickAdd && (
|
||||
<div className="nodedc-list-quick-add-sticky nodedc-bottom-dock-sticky-offset sticky z-[2] w-full flex-shrink-0 bg-transparent py-0.5">
|
||||
<QuickAddIssueRoot
|
||||
layout={EIssueLayoutTypes.LIST}
|
||||
QuickAddButton={ListQuickAddIssueButton}
|
||||
prePopulatedData={prePopulateQuickAddData(group_by, group.id)}
|
||||
containerClassName="border-b border-t border-subtle bg-surface-1 "
|
||||
quickAddCallback={quickAddCallback}
|
||||
isEpic={isEpic}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
export const NODEDC_LIST_PROPERTIES_WIDTH_STORAGE_KEY = "nodedc_project_list_properties_width";
|
||||
export const NODEDC_LIST_PROPERTIES_WIDTH_CSS_VAR = "--nodedc-list-properties-panel-width";
|
||||
|
||||
export const NODEDC_LIST_PROPERTIES_WIDTH_DEFAULT = 1024;
|
||||
export const NODEDC_LIST_PROPERTIES_WIDTH_MIN = 760;
|
||||
export const NODEDC_LIST_PROPERTIES_WIDTH_MAX = 1280;
|
||||
|
||||
const getRuntimeMaxWidth = () => {
|
||||
if (typeof window === "undefined") return NODEDC_LIST_PROPERTIES_WIDTH_MAX;
|
||||
|
||||
return Math.max(
|
||||
NODEDC_LIST_PROPERTIES_WIDTH_MIN,
|
||||
Math.min(NODEDC_LIST_PROPERTIES_WIDTH_MAX, window.innerWidth - 420)
|
||||
);
|
||||
};
|
||||
|
||||
export const clampNodedcListPropertiesWidth = (width: number) => {
|
||||
const maxWidth = getRuntimeMaxWidth();
|
||||
const normalizedWidth = Number.isFinite(width) ? width : NODEDC_LIST_PROPERTIES_WIDTH_DEFAULT;
|
||||
|
||||
return Math.min(Math.max(normalizedWidth, NODEDC_LIST_PROPERTIES_WIDTH_MIN), maxWidth);
|
||||
};
|
||||
|
||||
export const applyNodedcListPropertiesWidth = (width: number) => {
|
||||
if (typeof document === "undefined") return width;
|
||||
|
||||
const clampedWidth = clampNodedcListPropertiesWidth(width);
|
||||
document.documentElement.style.setProperty(NODEDC_LIST_PROPERTIES_WIDTH_CSS_VAR, `${clampedWidth}px`);
|
||||
|
||||
return clampedWidth;
|
||||
};
|
||||
|
||||
export const applyStoredNodedcListPropertiesWidth = () => {
|
||||
if (typeof window === "undefined") return NODEDC_LIST_PROPERTIES_WIDTH_DEFAULT;
|
||||
|
||||
const storedWidth = Number(window.localStorage.getItem(NODEDC_LIST_PROPERTIES_WIDTH_STORAGE_KEY));
|
||||
return applyNodedcListPropertiesWidth(
|
||||
Number.isFinite(storedWidth) && storedWidth > 0 ? storedWidth : NODEDC_LIST_PROPERTIES_WIDTH_DEFAULT
|
||||
);
|
||||
};
|
||||
|
||||
export const persistNodedcListPropertiesWidth = (width: number) => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
window.localStorage.setItem(NODEDC_LIST_PROPERTIES_WIDTH_STORAGE_KEY, String(clampNodedcListPropertiesWidth(width)));
|
||||
};
|
||||
+90
-22
@@ -59,7 +59,7 @@ export interface IIssueProperties {
|
||||
}
|
||||
|
||||
export const IssueProperties = observer(function IssueProperties(props: IIssueProperties) {
|
||||
const { issue, updateIssue, displayProperties, isReadOnly, className, isEpic = false } = props;
|
||||
const { issue, updateIssue, displayProperties, isReadOnly, className, activeLayout, isEpic = false } = props;
|
||||
// i18n
|
||||
const { t } = useTranslation();
|
||||
// store hooks
|
||||
@@ -175,6 +175,15 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
const redirectToIssueDetail = () => router.push(`${workItemLink}#sub-issues`);
|
||||
|
||||
if (!displayProperties || !issue.project_id) return null;
|
||||
const isListLayout = activeLayout === "List";
|
||||
const propertySlotClassName = (slot: string, ...classNames: (string | false | null | undefined)[]) =>
|
||||
cn(
|
||||
"h-5",
|
||||
isListLayout && "nodedc-list-property-slot",
|
||||
isListLayout && `nodedc-list-property-${slot}`,
|
||||
...classNames
|
||||
);
|
||||
const listIconControlClassName = isListLayout ? "nodedc-list-icon-control" : undefined;
|
||||
|
||||
// date range is enabled only when both dates are available and both dates are enabled
|
||||
const isDateRangeEnabled: boolean = Boolean(
|
||||
@@ -196,7 +205,11 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
{/* basic properties */}
|
||||
{/* state */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="state">
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName("state")}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<StateDropdown
|
||||
buttonContainerClassName="truncate max-w-40"
|
||||
value={issue.state_id}
|
||||
@@ -212,12 +225,17 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
|
||||
{/* priority */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="priority">
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName("priority", isListLayout && "nodedc-list-property-icon-only")}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<PriorityDropdown
|
||||
value={issue?.priority}
|
||||
onChange={handlePriority}
|
||||
disabled={isReadOnly}
|
||||
buttonVariant="border-without-text"
|
||||
buttonContainerClassName={listIconControlClassName}
|
||||
renderByDefault={isMobile}
|
||||
showTooltip
|
||||
/>
|
||||
@@ -230,7 +248,11 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
displayPropertyKey={["start_date", "due_date"]}
|
||||
shouldRenderProperty={() => isDateRangeEnabled}
|
||||
>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName("date-range")}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<DateRangeDropdown
|
||||
value={{
|
||||
from: getDate(issue.start_date) || undefined,
|
||||
@@ -255,6 +277,7 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
showTooltip
|
||||
renderPlaceholder={false}
|
||||
customTooltipHeading="Date Range"
|
||||
buttonContainerClassName={isListLayout ? "nodedc-list-valued-date-control" : undefined}
|
||||
/>
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
@@ -265,7 +288,14 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
displayPropertyKey="start_date"
|
||||
shouldRenderProperty={() => !isDateRangeEnabled}
|
||||
>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName(
|
||||
"start-date",
|
||||
isListLayout && (issue.start_date ? "nodedc-list-property-valued-date" : "nodedc-list-property-icon-only")
|
||||
)}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<DateDropdown
|
||||
value={issue.start_date ?? null}
|
||||
rangePreview={{
|
||||
@@ -277,6 +307,7 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
placeholder={t("common.order_by.start_date")}
|
||||
icon={<StartDatePropertyIcon className="h-3 w-3 flex-shrink-0" />}
|
||||
buttonVariant={issue.start_date ? "border-with-text" : "border-without-text"}
|
||||
buttonContainerClassName={issue.start_date ? undefined : listIconControlClassName}
|
||||
optionsClassName="z-10"
|
||||
disabled={isReadOnly}
|
||||
renderByDefault={isMobile}
|
||||
@@ -292,7 +323,14 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
displayPropertyKey="due_date"
|
||||
shouldRenderProperty={() => !isDateRangeEnabled}
|
||||
>
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName(
|
||||
"due-date",
|
||||
isListLayout && (issue.target_date ? "nodedc-list-property-valued-date" : "nodedc-list-property-icon-only")
|
||||
)}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<DateDropdown
|
||||
value={issue?.target_date ?? null}
|
||||
rangePreview={{
|
||||
@@ -304,6 +342,7 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
placeholder={t("common.order_by.due_date")}
|
||||
icon={<DueDatePropertyIcon className="h-3 w-3 shrink-0" />}
|
||||
buttonVariant={issue.target_date ? "border-with-text" : "border-without-text"}
|
||||
buttonContainerClassName={issue.target_date ? undefined : listIconControlClassName}
|
||||
buttonClassName={
|
||||
shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group) ? "text-danger-primary" : ""
|
||||
}
|
||||
@@ -319,7 +358,11 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
|
||||
{/* assignee */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="assignee">
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName("assignee")}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<MemberDropdown
|
||||
projectId={issue?.project_id}
|
||||
value={issue?.assignee_ids}
|
||||
@@ -343,7 +386,11 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
{/* modules */}
|
||||
{projectDetails?.module_view && (
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="modules">
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName("module")}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<ModuleDropdown
|
||||
buttonContainerClassName="truncate max-w-40"
|
||||
projectId={issue?.project_id}
|
||||
@@ -363,7 +410,11 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
{/* cycles */}
|
||||
{projectDetails?.cycle_view && (
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="cycle">
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName("cycle")}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<CycleDropdown
|
||||
buttonContainerClassName="truncate max-w-40"
|
||||
projectId={issue?.project_id}
|
||||
@@ -384,7 +435,11 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
{/* estimates */}
|
||||
{projectId && areEstimateEnabledByProjectId(projectId?.toString()) && (
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="estimate">
|
||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<div
|
||||
className={propertySlotClassName("estimate")}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<EstimateDropdown
|
||||
value={issue.estimate_point ?? undefined}
|
||||
onChange={handleEstimate}
|
||||
@@ -421,6 +476,7 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
}}
|
||||
className={cn(
|
||||
"flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1",
|
||||
isListLayout && "nodedc-list-property-slot nodedc-list-property-sub-issues",
|
||||
{
|
||||
"cursor-pointer hover:bg-layer-1": subIssueCount,
|
||||
}
|
||||
@@ -446,7 +502,10 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
renderByDefault={false}
|
||||
>
|
||||
<div
|
||||
className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1"
|
||||
className={cn(
|
||||
"flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1",
|
||||
isListLayout && "nodedc-list-property-slot nodedc-list-property-attachments"
|
||||
)}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
@@ -469,7 +528,10 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
renderByDefault={false}
|
||||
>
|
||||
<div
|
||||
className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1"
|
||||
className={cn(
|
||||
"flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1",
|
||||
isListLayout && "nodedc-list-property-slot nodedc-list-property-links"
|
||||
)}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
@@ -484,16 +546,22 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
|
||||
{/* label */}
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="labels">
|
||||
<IssuePropertyLabels
|
||||
projectId={issue?.project_id || null}
|
||||
value={issue?.label_ids || []}
|
||||
defaultOptions={defaultLabelOptions}
|
||||
onChange={handleLabel}
|
||||
disabled={isReadOnly}
|
||||
renderByDefault={isMobile}
|
||||
hideDropdownArrow
|
||||
maxRender={3}
|
||||
/>
|
||||
<div
|
||||
className={propertySlotClassName("labels")}
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={handleEventPropagation}
|
||||
>
|
||||
<IssuePropertyLabels
|
||||
projectId={issue?.project_id || null}
|
||||
value={issue?.label_ids || []}
|
||||
defaultOptions={defaultLabelOptions}
|
||||
onChange={handleLabel}
|
||||
disabled={isReadOnly}
|
||||
renderByDefault={isMobile}
|
||||
hideDropdownArrow
|
||||
maxRender={3}
|
||||
/>
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user