UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: quick-add, composer внешнего контура и range-календарь
This commit is contained in:
parent
85bd24c45b
commit
312fc1eca4
|
|
@ -360,6 +360,10 @@ export const ExternalContoursBoardItem = observer(function ExternalContoursBoard
|
||||||
|
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.target_date}
|
value={issue.target_date}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={(targetDate) =>
|
onChange={(targetDate) =>
|
||||||
void handleCardUpdate({
|
void handleCardUpdate({
|
||||||
target_date: targetDate ? renderFormattedPayloadDate(targetDate) : null,
|
target_date: targetDate ? renderFormattedPayloadDate(targetDate) : null,
|
||||||
|
|
|
||||||
|
|
@ -344,7 +344,7 @@ export const ExternalContoursIssueMainContent = observer(function ExternalContou
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="nodedc-external-section overflow-visible px-4 py-4">
|
<div className="nodedc-external-section overflow-visible px-4 py-4">
|
||||||
<IssueActivity workspaceSlug={workspaceSlug} projectId={targetProjectId} issueId={issue.id} />
|
<IssueActivity workspaceSlug={workspaceSlug} projectId={targetProjectId} issueId={issue.id} compactComposer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,17 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) {
|
||||||
|
|
||||||
const updateDockBounds = () => {
|
const updateDockBounds = () => {
|
||||||
const { left, width } = main.getBoundingClientRect();
|
const { left, width } = main.getBoundingClientRect();
|
||||||
|
const height = container?.getBoundingClientRect().height ?? 0;
|
||||||
|
|
||||||
setDockStyle({
|
setDockStyle({
|
||||||
left,
|
left,
|
||||||
width,
|
width,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--nodedc-bottom-dock-offset",
|
||||||
|
`${Math.max(height, 0)}px`
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateDockBounds();
|
updateDockBounds();
|
||||||
|
|
@ -50,12 +56,18 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) {
|
||||||
return () => {
|
return () => {
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
window.removeEventListener("resize", updateDockBounds);
|
window.removeEventListener("resize", updateDockBounds);
|
||||||
|
document.documentElement.style.removeProperty("--nodedc-bottom-dock-offset");
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className={cn("fixed right-0 bottom-0 z-[18]", className)} style={dockStyle}>
|
<div ref={containerRef} className={cn("fixed right-0 bottom-0 z-[18]", className)} style={dockStyle}>
|
||||||
<Row className={cn("nodedc-bottom-dock flex h-11 w-full items-center gap-2", rowClassName)}>
|
<Row
|
||||||
|
className={cn(
|
||||||
|
"nodedc-bottom-dock flex h-[var(--nodedc-bottom-dock-height)] w-full items-center gap-2",
|
||||||
|
rowClassName
|
||||||
|
)}
|
||||||
|
>
|
||||||
<ExtendedAppHeader header={header} />
|
<ExtendedAppHeader header={header} />
|
||||||
</Row>
|
</Row>
|
||||||
{mobileHeader && mobileHeader}
|
{mobileHeader && mobileHeader}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ export function DateFilterModal({ title, handleClose, isOpen, onSelect }: Props)
|
||||||
const date2Value = getDate(watch("date2"));
|
const date2Value = getDate(watch("date2"));
|
||||||
return (
|
return (
|
||||||
<Calendar
|
<Calendar
|
||||||
className="rounded-md border border-subtle p-3"
|
className="nodedc-calendar-shell"
|
||||||
captionLayout="dropdown"
|
captionLayout="dropdown"
|
||||||
selected={dateValue}
|
selected={dateValue}
|
||||||
defaultMonth={dateValue}
|
defaultMonth={dateValue}
|
||||||
|
|
@ -94,8 +94,8 @@ export function DateFilterModal({ title, handleClose, isOpen, onSelect }: Props)
|
||||||
const dateValue = getDate(value);
|
const dateValue = getDate(value);
|
||||||
const date1Value = getDate(watch("date1"));
|
const date1Value = getDate(watch("date1"));
|
||||||
return (
|
return (
|
||||||
<Calendar
|
<Calendar
|
||||||
className="rounded-md border border-subtle p-3"
|
className="nodedc-calendar-shell"
|
||||||
captionLayout="dropdown"
|
captionLayout="dropdown"
|
||||||
selected={dateValue}
|
selected={dateValue}
|
||||||
defaultMonth={dateValue}
|
defaultMonth={dateValue}
|
||||||
|
|
|
||||||
|
|
@ -263,15 +263,16 @@ export const DateRangeDropdown = observer(function DateRangeDropdown(props: Prop
|
||||||
const comboOptions = (
|
const comboOptions = (
|
||||||
<Combobox.Options data-prevent-outside-click static>
|
<Combobox.Options data-prevent-outside-click static>
|
||||||
<div
|
<div
|
||||||
className="z-30 my-1 overflow-hidden rounded-md border-[0.5px] border-subtle-1 bg-surface-1"
|
className="nodedc-dropdown-surface z-30 my-1 overflow-hidden"
|
||||||
ref={setPopperElement}
|
ref={setPopperElement}
|
||||||
style={styles.popper}
|
style={styles.popper}
|
||||||
{...attributes.popper}
|
{...attributes.popper}
|
||||||
>
|
>
|
||||||
<Calendar
|
<Calendar
|
||||||
className="rounded-md border border-subtle p-3 text-12"
|
className="nodedc-calendar-shell"
|
||||||
captionLayout="dropdown"
|
captionLayout="dropdown"
|
||||||
selected={dateRange}
|
selected={dateRange}
|
||||||
|
defaultMonth={dateRange.from ?? dateRange.to}
|
||||||
onSelect={(val: DateRange | undefined) => {
|
onSelect={(val: DateRange | undefined) => {
|
||||||
onSelect?.(val);
|
onSelect?.(val);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
* See the LICENSE file for details.
|
* See the LICENSE file for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useRef, useState } from "react";
|
import React, { useMemo, useRef, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
|
|
@ -38,6 +38,10 @@ type Props = TDropdownProps & {
|
||||||
maxDate?: Date;
|
maxDate?: Date;
|
||||||
onChange: (val: Date | null) => void;
|
onChange: (val: Date | null) => void;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
|
rangePreview?: {
|
||||||
|
from?: Date | string | null;
|
||||||
|
to?: Date | string | null;
|
||||||
|
};
|
||||||
value: Date | string | null;
|
value: Date | string | null;
|
||||||
closeOnSelect?: boolean;
|
closeOnSelect?: boolean;
|
||||||
formatToken?: string;
|
formatToken?: string;
|
||||||
|
|
@ -64,6 +68,7 @@ export const DateDropdown = observer(function DateDropdown(props: Props) {
|
||||||
maxDate,
|
maxDate,
|
||||||
onChange,
|
onChange,
|
||||||
onClose,
|
onClose,
|
||||||
|
rangePreview,
|
||||||
placeholder = "Date",
|
placeholder = "Date",
|
||||||
placement,
|
placement,
|
||||||
showTooltip = false,
|
showTooltip = false,
|
||||||
|
|
@ -122,6 +127,15 @@ export const DateDropdown = observer(function DateDropdown(props: Props) {
|
||||||
if (minDate) disabledDays.push({ before: minDate });
|
if (minDate) disabledDays.push({ before: minDate });
|
||||||
if (maxDate) disabledDays.push({ after: maxDate });
|
if (maxDate) disabledDays.push({ after: maxDate });
|
||||||
|
|
||||||
|
const rangePreviewValue = useMemo(() => {
|
||||||
|
const from = getDate(rangePreview?.from ?? null);
|
||||||
|
const to = getDate(rangePreview?.to ?? null);
|
||||||
|
|
||||||
|
if (!from || !to) return undefined;
|
||||||
|
if (from <= to) return { from, to };
|
||||||
|
return { from: to, to: from };
|
||||||
|
}, [rangePreview?.from, rangePreview?.to]);
|
||||||
|
|
||||||
const comboButton = (
|
const comboButton = (
|
||||||
<>
|
<>
|
||||||
{button ? (
|
{button ? (
|
||||||
|
|
@ -207,21 +221,40 @@ export const DateDropdown = observer(function DateDropdown(props: Props) {
|
||||||
style={styles.popper}
|
style={styles.popper}
|
||||||
{...attributes.popper}
|
{...attributes.popper}
|
||||||
>
|
>
|
||||||
<Calendar
|
{rangePreviewValue ? (
|
||||||
className="nodedc-calendar-shell"
|
<Calendar
|
||||||
captionLayout="dropdown"
|
className="nodedc-calendar-shell"
|
||||||
selected={getDate(value)}
|
captionLayout="dropdown"
|
||||||
defaultMonth={getDate(value)}
|
selected={rangePreviewValue}
|
||||||
onSelect={(date: Date | undefined) => {
|
defaultMonth={getDate(value) ?? rangePreviewValue.to ?? rangePreviewValue.from}
|
||||||
dropdownOnChange(date ?? null);
|
onDayClick={(date: Date, modifiers) => {
|
||||||
}}
|
if (modifiers.disabled) return;
|
||||||
showOutsideDays
|
dropdownOnChange(date ?? null);
|
||||||
initialFocus
|
}}
|
||||||
disabled={disabledDays}
|
showOutsideDays
|
||||||
mode="single"
|
initialFocus
|
||||||
fixedWeeks
|
disabled={disabledDays}
|
||||||
weekStartsOn={startOfWeek}
|
mode="range"
|
||||||
/>
|
fixedWeeks
|
||||||
|
weekStartsOn={startOfWeek}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Calendar
|
||||||
|
className="nodedc-calendar-shell"
|
||||||
|
captionLayout="dropdown"
|
||||||
|
selected={getDate(value)}
|
||||||
|
defaultMonth={getDate(value)}
|
||||||
|
onSelect={(date: Date | undefined) => {
|
||||||
|
dropdownOnChange(date ?? null);
|
||||||
|
}}
|
||||||
|
showOutsideDays
|
||||||
|
initialFocus
|
||||||
|
disabled={disabledDays}
|
||||||
|
mode="single"
|
||||||
|
fixedWeeks
|
||||||
|
weekStartsOn={startOfWeek}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Combobox.Options>,
|
</Combobox.Options>,
|
||||||
document.body
|
document.body
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,10 @@ export const SubIssuesListItemProperties = observer(function SubIssuesListItemPr
|
||||||
<div className="h-5">
|
<div className="h-5">
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.start_date ?? null}
|
value={issue.start_date ?? null}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={handleStartDate}
|
onChange={handleStartDate}
|
||||||
maxDate={maxDate}
|
maxDate={maxDate}
|
||||||
placeholder={t("common.order_by.start_date")}
|
placeholder={t("common.order_by.start_date")}
|
||||||
|
|
@ -190,6 +194,10 @@ export const SubIssuesListItemProperties = observer(function SubIssuesListItemPr
|
||||||
<div className="h-5">
|
<div className="h-5">
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue?.target_date ?? null}
|
value={issue?.target_date ?? null}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={handleTargetDate}
|
onChange={handleTargetDate}
|
||||||
minDate={minDate}
|
minDate={minDate}
|
||||||
placeholder={t("common.order_by.due_date")}
|
placeholder={t("common.order_by.due_date")}
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,10 @@ export const IssueDetailsSidebar = observer(function IssueDetailsSidebar(props:
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
placeholder={t("issue.add.start_date")}
|
placeholder={t("issue.add.start_date")}
|
||||||
value={issue.start_date}
|
value={issue.start_date}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={(val) =>
|
onChange={(val) =>
|
||||||
issueOperations.update(workspaceSlug, projectId, issueId, {
|
issueOperations.update(workspaceSlug, projectId, issueId, {
|
||||||
start_date: val ? renderFormattedPayloadDate(val) : null,
|
start_date: val ? renderFormattedPayloadDate(val) : null,
|
||||||
|
|
@ -167,6 +171,10 @@ export const IssueDetailsSidebar = observer(function IssueDetailsSidebar(props:
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
placeholder={t("issue.add.due_date")}
|
placeholder={t("issue.add.due_date")}
|
||||||
value={issue.target_date}
|
value={issue.target_date}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={(val) =>
|
onChange={(val) =>
|
||||||
issueOperations.update(workspaceSlug, projectId, issueId, {
|
issueOperations.update(workspaceSlug, projectId, issueId, {
|
||||||
target_date: val ? renderFormattedPayloadDate(val) : null,
|
target_date: val ? renderFormattedPayloadDate(val) : null,
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,10 @@ export const InternalContourKanbanCard = observer(function InternalContourKanban
|
||||||
<div className="flex items-center justify-end">
|
<div className="flex items-center justify-end">
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.target_date}
|
value={issue.target_date}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={(targetDate) =>
|
onChange={(targetDate) =>
|
||||||
updateIssue?.(issue.project_id ?? null, issue.id, {
|
updateIssue?.(issue.project_id ?? null, issue.id, {
|
||||||
target_date: targetDate ? renderFormattedPayloadDate(targetDate) : null,
|
target_date: targetDate ? renderFormattedPayloadDate(targetDate) : null,
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ export const KanbanGroup = observer(function KanbanGroup(props: IKanbanGroup) {
|
||||||
<KanbanIssueBlockLoader />
|
<KanbanIssueBlockLoader />
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="sticky bottom-0 w-full cursor-pointer p-3 text-13 font-medium text-accent-primary hover:text-accent-secondary hover:underline"
|
className="nodedc-bottom-dock-sticky-offset sticky z-[1] w-full cursor-pointer p-3 text-13 font-medium text-accent-primary hover:text-accent-secondary hover:underline"
|
||||||
onClick={loadMoreIssuesInThisGroup}
|
onClick={loadMoreIssuesInThisGroup}
|
||||||
>
|
>
|
||||||
{t("common.load_more")} ↓
|
{t("common.load_more")} ↓
|
||||||
|
|
@ -278,6 +278,10 @@ export const KanbanGroup = observer(function KanbanGroup(props: IKanbanGroup) {
|
||||||
!!group_by &&
|
!!group_by &&
|
||||||
DRAG_ALLOWED_GROUPS.includes(group_by) &&
|
DRAG_ALLOWED_GROUPS.includes(group_by) &&
|
||||||
(sub_group_by ? DRAG_ALLOWED_GROUPS.includes(sub_group_by) : true);
|
(sub_group_by ? DRAG_ALLOWED_GROUPS.includes(sub_group_by) : true);
|
||||||
|
const shouldShowQuickAdd =
|
||||||
|
!!enableQuickIssueCreate &&
|
||||||
|
!disableIssueCreation &&
|
||||||
|
!getIsWorkflowWorkItemCreationDisabled(groupId, sub_group_id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -299,49 +303,49 @@ export const KanbanGroup = observer(function KanbanGroup(props: IKanbanGroup) {
|
||||||
isDraggingOverColumn={isDraggingOverColumn}
|
isDraggingOverColumn={isDraggingOverColumn}
|
||||||
isEpic={isEpic}
|
isEpic={isEpic}
|
||||||
/>
|
/>
|
||||||
<KanbanIssueBlocksList
|
<div className={cn({ "nodedc-bottom-dock-aware-padding": shouldShowQuickAdd })}>
|
||||||
sub_group_id={sub_group_id}
|
<KanbanIssueBlocksList
|
||||||
groupId={groupId}
|
sub_group_id={sub_group_id}
|
||||||
issuesMap={issuesMap}
|
groupId={groupId}
|
||||||
issueIds={issueIds || []}
|
issuesMap={issuesMap}
|
||||||
displayProperties={displayProperties}
|
issueIds={issueIds || []}
|
||||||
updateIssue={updateIssue}
|
displayProperties={displayProperties}
|
||||||
quickActions={quickActions}
|
updateIssue={updateIssue}
|
||||||
canEditProperties={canEditProperties}
|
quickActions={quickActions}
|
||||||
cardVariant={cardVariant}
|
canEditProperties={canEditProperties}
|
||||||
scrollableContainerRef={scrollableContainerRef}
|
cardVariant={cardVariant}
|
||||||
canDropOverIssue={!canOverlayBeVisible}
|
scrollableContainerRef={scrollableContainerRef}
|
||||||
canDragIssuesInCurrentGrouping={canDragIssuesInCurrentGrouping}
|
canDropOverIssue={!canOverlayBeVisible}
|
||||||
isEpic={isEpic}
|
canDragIssuesInCurrentGrouping={canDragIssuesInCurrentGrouping}
|
||||||
/>
|
isEpic={isEpic}
|
||||||
|
/>
|
||||||
|
|
||||||
{shouldLoadMore &&
|
{shouldLoadMore &&
|
||||||
(isSubGroup ? (
|
(isSubGroup ? (
|
||||||
<>{loadMore}</>
|
<>{loadMore}</>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{Array.from({ length: 2 }).map((_, index) => (
|
{Array.from({ length: 2 }).map((_, index) => (
|
||||||
<KanbanIssueBlockLoader key={index} />
|
<KanbanIssueBlockLoader key={index} />
|
||||||
))}
|
))}
|
||||||
<KanbanIssueBlockLoader ref={setIntersectionElement} />
|
<KanbanIssueBlockLoader ref={setIntersectionElement} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
{enableQuickIssueCreate &&
|
{shouldShowQuickAdd && (
|
||||||
!disableIssueCreation &&
|
<div className="nodedc-bottom-dock-sticky-offset sticky z-[2] w-full bg-surface-2 py-0.5">
|
||||||
!getIsWorkflowWorkItemCreationDisabled(groupId, sub_group_id) && (
|
<QuickAddIssueRoot
|
||||||
<div className="sticky bottom-0 w-full bg-surface-2 py-0.5">
|
layout={EIssueLayoutTypes.KANBAN}
|
||||||
<QuickAddIssueRoot
|
QuickAddButton={KanbanQuickAddIssueButton}
|
||||||
layout={EIssueLayoutTypes.KANBAN}
|
prePopulatedData={{
|
||||||
QuickAddButton={KanbanQuickAddIssueButton}
|
...(group_by && prePopulateQuickAddData(group_by, sub_group_by, groupId, sub_group_id)),
|
||||||
prePopulatedData={{
|
}}
|
||||||
...(group_by && prePopulateQuickAddData(group_by, sub_group_by, groupId, sub_group_id)),
|
quickAddCallback={quickAddCallback}
|
||||||
}}
|
isEpic={isEpic}
|
||||||
quickAddCallback={quickAddCallback}
|
/>
|
||||||
isEpic={isEpic}
|
</div>
|
||||||
/>
|
)}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,7 @@ export const ListGroup = observer(function ListGroup(props: Props) {
|
||||||
!isGroupByCreatedBy &&
|
!isGroupByCreatedBy &&
|
||||||
!isCompletedCycle &&
|
!isCompletedCycle &&
|
||||||
!isWorkflowIssueCreationDisabled && (
|
!isWorkflowIssueCreationDisabled && (
|
||||||
<div className="sticky bottom-0 z-[1] w-full flex-shrink-0">
|
<div className="nodedc-bottom-dock-sticky-offset sticky z-[1] w-full flex-shrink-0">
|
||||||
<QuickAddIssueRoot
|
<QuickAddIssueRoot
|
||||||
layout={EIssueLayoutTypes.LIST}
|
layout={EIssueLayoutTypes.LIST}
|
||||||
QuickAddButton={ListQuickAddIssueButton}
|
QuickAddButton={ListQuickAddIssueButton}
|
||||||
|
|
|
||||||
|
|
@ -268,6 +268,10 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.start_date ?? null}
|
value={issue.start_date ?? null}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={handleStartDate}
|
onChange={handleStartDate}
|
||||||
maxDate={maxDate}
|
maxDate={maxDate}
|
||||||
placeholder={t("common.order_by.start_date")}
|
placeholder={t("common.order_by.start_date")}
|
||||||
|
|
@ -291,6 +295,10 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue?.target_date ?? null}
|
value={issue?.target_date ?? null}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={handleTargetDate}
|
onChange={handleTargetDate}
|
||||||
minDate={minDate}
|
minDate={minDate}
|
||||||
placeholder={t("common.order_by.due_date")}
|
placeholder={t("common.order_by.due_date")}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ export const SpreadsheetDueDateColumn = observer(function SpreadsheetDueDateColu
|
||||||
<div className="h-11 border-b-[0.5px] border-subtle">
|
<div className="h-11 border-b-[0.5px] border-subtle">
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.target_date}
|
value={issue.target_date}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
minDate={getDate(issue.start_date)}
|
minDate={getDate(issue.start_date)}
|
||||||
onChange={(data) => {
|
onChange={(data) => {
|
||||||
const targetDate = data ? renderFormattedPayloadDate(data) : null;
|
const targetDate = data ? renderFormattedPayloadDate(data) : null;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ export const SpreadsheetStartDateColumn = observer(function SpreadsheetStartDate
|
||||||
<div className="h-11 border-b-[0.5px] border-subtle">
|
<div className="h-11 border-b-[0.5px] border-subtle">
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.start_date}
|
value={issue.start_date}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
maxDate={getDate(issue.target_date)}
|
maxDate={getDate(issue.target_date)}
|
||||||
onChange={(data) => {
|
onChange={(data) => {
|
||||||
const startDate = data ? renderFormattedPayloadDate(data) : null;
|
const startDate = data ? renderFormattedPayloadDate(data) : null;
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ export const SpreadsheetView = observer(function SpreadsheetView(props: Props) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-subtle">
|
<div className="border-t border-subtle">
|
||||||
<div className="sticky bottom-0 left-0 z-5">
|
<div className="nodedc-bottom-dock-sticky-offset sticky bottom-0 left-0 z-5">
|
||||||
{enableQuickCreateIssue && !disableIssueCreation && (
|
{enableQuickCreateIssue && !disableIssueCreation && (
|
||||||
<QuickAddIssueRoot
|
<QuickAddIssueRoot
|
||||||
layout={EIssueLayoutTypes.SPREADSHEET}
|
layout={EIssueLayoutTypes.SPREADSHEET}
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,10 @@ export const IssueDefaultProperties = observer(function IssueDefaultProperties(p
|
||||||
<div className="h-7">
|
<div className="h-7">
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={value}
|
value={value}
|
||||||
|
rangePreview={{
|
||||||
|
from: startDate,
|
||||||
|
to: targetDate,
|
||||||
|
}}
|
||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
onChange(date ? renderFormattedPayloadDate(date) : null);
|
onChange(date ? renderFormattedPayloadDate(date) : null);
|
||||||
handleFormChange();
|
handleFormChange();
|
||||||
|
|
@ -211,6 +215,10 @@ export const IssueDefaultProperties = observer(function IssueDefaultProperties(p
|
||||||
<div className="h-7">
|
<div className="h-7">
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={value}
|
value={value}
|
||||||
|
rangePreview={{
|
||||||
|
from: startDate,
|
||||||
|
to: targetDate,
|
||||||
|
}}
|
||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
onChange(date ? renderFormattedPayloadDate(date) : null);
|
onChange(date ? renderFormattedPayloadDate(date) : null);
|
||||||
handleFormChange();
|
handleFormChange();
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,10 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
||||||
<SidebarPropertyListItem icon={StartDatePropertyIcon} label={t("common.order_by.start_date")}>
|
<SidebarPropertyListItem icon={StartDatePropertyIcon} label={t("common.order_by.start_date")}>
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.start_date}
|
value={issue.start_date}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={(val) =>
|
onChange={(val) =>
|
||||||
issueOperations.update(workspaceSlug, projectId, issueId, {
|
issueOperations.update(workspaceSlug, projectId, issueId, {
|
||||||
start_date: val ? renderFormattedPayloadDate(val) : null,
|
start_date: val ? renderFormattedPayloadDate(val) : null,
|
||||||
|
|
@ -167,6 +171,10 @@ export const PeekOverviewProperties = observer(function PeekOverviewProperties(p
|
||||||
<div className="flex w-full items-center gap-2">
|
<div className="flex w-full items-center gap-2">
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.target_date}
|
value={issue.target_date}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={(val) =>
|
onChange={(val) =>
|
||||||
issueOperations.update(workspaceSlug, projectId, issueId, {
|
issueOperations.update(workspaceSlug, projectId, issueId, {
|
||||||
target_date: val ? renderFormattedPayloadDate(val) : null,
|
target_date: val ? renderFormattedPayloadDate(val) : null,
|
||||||
|
|
|
||||||
|
|
@ -38,25 +38,25 @@ export const WorkItemPreviewCard = observer(function WorkItemPreviewCard(props:
|
||||||
const stateName = stateDetails?.name ?? fallbackStateDetails?.name;
|
const stateName = stateDetails?.name ?? fallbackStateDetails?.name;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-72 space-y-2 rounded-lg border-[0.5px] border-strong bg-surface-1 p-3 shadow-raised-200">
|
<div className="nodedc-dropdown-surface w-80 space-y-3 p-4 shadow-[0_24px_48px_rgba(0,0,0,0.32)]">
|
||||||
<div className="flex items-center justify-between gap-3 text-secondary">
|
<div className="flex items-center justify-between gap-3 text-tertiary">
|
||||||
<IssueIdentifier
|
<IssueIdentifier
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="secondary"
|
variant="tertiary"
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
projectIdentifier={projectIdentifier}
|
projectIdentifier={projectIdentifier}
|
||||||
issueSequenceId={workItem.sequence_id}
|
issueSequenceId={workItem.sequence_id}
|
||||||
issueTypeId={workItem.type_id}
|
issueTypeId={workItem.type_id}
|
||||||
/>
|
/>
|
||||||
<div className="flex shrink-0 items-center gap-1">
|
<div className="flex shrink-0 items-center gap-1 rounded-full bg-black/20 px-2 py-1">
|
||||||
<StateGroupIcon stateGroup={stateGroup} className="size-3 shrink-0" />
|
<StateGroupIcon stateGroup={stateGroup} className="size-3 shrink-0" />
|
||||||
<p className="text-11 font-medium">{stateName}</p>
|
<p className="text-11 font-medium">{stateName}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h6 className="text-13 wrap-break-word">{workItem.name}</h6>
|
<h6 className="wrap-break-word text-[15px] font-semibold leading-5 text-primary">{workItem.name}</h6>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-5 items-center gap-1">
|
<div className="flex min-h-8 items-center gap-1.5 rounded-full bg-black/15 px-2.5 py-1.5 text-secondary">
|
||||||
<PriorityIcon priority={workItem.priority} withContainer />
|
<PriorityIcon priority={workItem.priority} withContainer />
|
||||||
<WorkItemPreviewCardDate
|
<WorkItemPreviewCardDate
|
||||||
startDate={workItem.start_date}
|
startDate={workItem.start_date}
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,10 @@ export const DraftIssueProperties = observer(function DraftIssueProperties(props
|
||||||
<div className="h-5" onClick={handleEventPropagation}>
|
<div className="h-5" onClick={handleEventPropagation}>
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue.start_date ?? null}
|
value={issue.start_date ?? null}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={handleStartDate}
|
onChange={handleStartDate}
|
||||||
maxDate={maxDate}
|
maxDate={maxDate}
|
||||||
placeholder={t("start_date")}
|
placeholder={t("start_date")}
|
||||||
|
|
@ -195,6 +199,10 @@ export const DraftIssueProperties = observer(function DraftIssueProperties(props
|
||||||
<div className="h-5" onClick={handleEventPropagation}>
|
<div className="h-5" onClick={handleEventPropagation}>
|
||||||
<DateDropdown
|
<DateDropdown
|
||||||
value={issue?.target_date ?? null}
|
value={issue?.target_date ?? null}
|
||||||
|
rangePreview={{
|
||||||
|
from: issue.start_date,
|
||||||
|
to: issue.target_date,
|
||||||
|
}}
|
||||||
onChange={handleTargetDate}
|
onChange={handleTargetDate}
|
||||||
minDate={minDate}
|
minDate={minDate}
|
||||||
placeholder={t("due_date")}
|
placeholder={t("due_date")}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@
|
||||||
--nodedc-on-card-passive-rgb: 245 247 251;
|
--nodedc-on-card-passive-rgb: 245 247 251;
|
||||||
--nodedc-card-active-rgb: 195 255 102;
|
--nodedc-card-active-rgb: 195 255 102;
|
||||||
--nodedc-on-card-active-rgb: 11 17 23;
|
--nodedc-on-card-active-rgb: 11 17 23;
|
||||||
|
--nodedc-bottom-dock-height: 2.75rem;
|
||||||
|
--nodedc-bottom-dock-offset: 2.75rem;
|
||||||
|
--nodedc-bottom-dock-visual-overlap: 0.625rem;
|
||||||
|
--nodedc-quick-add-reserve: 2.5rem;
|
||||||
--brand-default: rgb(var(--nodedc-accent-rgb));
|
--brand-default: rgb(var(--nodedc-accent-rgb));
|
||||||
--brand-300: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 65%, white);
|
--brand-300: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 65%, white);
|
||||||
--brand-700: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 75%, black);
|
--brand-700: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 75%, black);
|
||||||
|
|
@ -276,6 +280,20 @@
|
||||||
backdrop-filter: blur(34px);
|
backdrop-filter: blur(34px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodedc-bottom-dock-aware-padding {
|
||||||
|
padding-bottom: calc(var(--nodedc-quick-add-reserve, 2.5rem) + 0.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-bottom-dock-sticky-offset {
|
||||||
|
bottom: max(
|
||||||
|
calc(
|
||||||
|
var(--nodedc-bottom-dock-offset, var(--nodedc-bottom-dock-height, 2.75rem)) -
|
||||||
|
var(--nodedc-bottom-dock-visual-overlap, 0.625rem)
|
||||||
|
),
|
||||||
|
0px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
.nodedc-bottom-dock [class~="bg-surface-1"] {
|
.nodedc-bottom-dock [class~="bg-surface-1"] {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
@ -1042,7 +1060,6 @@
|
||||||
@apply bg-transparent;
|
@apply bg-transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nodedc-calendar-shell .rdp-day_button,
|
|
||||||
.nodedc-calendar-shell .rdp-button_previous,
|
.nodedc-calendar-shell .rdp-button_previous,
|
||||||
.nodedc-calendar-shell .rdp-button_next,
|
.nodedc-calendar-shell .rdp-button_next,
|
||||||
.nodedc-calendar-shell .rdp-dropdown_root {
|
.nodedc-calendar-shell .rdp-dropdown_root {
|
||||||
|
|
@ -1052,6 +1069,13 @@
|
||||||
border-radius: 0.95rem !important;
|
border-radius: 0.95rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodedc-calendar-shell .rdp-day_button {
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 999px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.nodedc-auth-shell {
|
.nodedc-auth-shell {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 32rem;
|
max-width: 32rem;
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,17 @@
|
||||||
/* Font size for the caption labels. */
|
/* Font size for the caption labels. */
|
||||||
--rdp-caption-navigation-size: 1.25rem;
|
--rdp-caption-navigation-size: 1.25rem;
|
||||||
/* Font size for the caption labels. */
|
/* Font size for the caption labels. */
|
||||||
--rdp-accent-color: var(--background-color-accent-primary);
|
--rdp-accent-color: rgb(var(--nodedc-accent-rgb, 51 163 255));
|
||||||
/* Accent color for the background of selected days. */
|
/* Accent color for the background of selected days. */
|
||||||
--rdp-background-color: --alpha(var(--background-color-accent-primary) / 50%);
|
--rdp-background-color: color-mix(in srgb, var(--rdp-accent-color) 18%, transparent);
|
||||||
/* Background color for the hovered/focused elements. */
|
/* Background color for the hovered/focused elements. */
|
||||||
--rdp-dark-background-color: var(--background-color-accent-primary-hover);
|
--rdp-dark-background-color: color-mix(in srgb, var(--rdp-accent-color) 82%, white);
|
||||||
|
--rdp-range-background-color: var(--rdp-accent-color);
|
||||||
/* Background color for the hovered/focused, already selected elements. */
|
/* Background color for the hovered/focused, already selected elements. */
|
||||||
--rdp-outline: 2px solid var(--rdp-accent-color);
|
--rdp-outline: 2px solid var(--rdp-accent-color);
|
||||||
/* Outline border for focused elements */
|
/* Outline border for focused elements */
|
||||||
--rdp-selected-color: var(--text-color-on-color);
|
--rdp-selected-color: rgb(var(--nodedc-on-card-active-rgb, 11 17 23));
|
||||||
|
--rdp-today-outline-color: rgb(var(--nodedc-on-card-active-rgb, 11 17 23));
|
||||||
/* Color of selected day text */
|
/* Color of selected day text */
|
||||||
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
@ -34,6 +36,7 @@
|
||||||
.rdp-day_button {
|
.rdp-day_button {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
@ -43,6 +46,20 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
transition:
|
||||||
|
background-color 160ms ease,
|
||||||
|
color 160ms ease,
|
||||||
|
box-shadow 160ms ease,
|
||||||
|
border-color 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdp-month_grid {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdp-day {
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-day.rdp-outside:not(.rdp-selected) .rdp-day_button {
|
.rdp-day.rdp-outside:not(.rdp-selected) .rdp-day_button {
|
||||||
|
|
@ -87,22 +104,25 @@
|
||||||
.rdp-week {
|
.rdp-week {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
height: var(--rdp-cell-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-today:not(.rdp-outside) {
|
.rdp-today:not(.rdp-outside) .rdp-day_button {
|
||||||
position: relative;
|
border-radius: 999px;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-today:not(.rdp-outside)::after {
|
.rdp-today.rdp-selected .rdp-day_button {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdp-today:not(.rdp-outside) .rdp-day_button::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
inset: 4px;
|
||||||
bottom: 2px;
|
border: 1.5px solid var(--rdp-today-outline-color);
|
||||||
width: 0.5em;
|
border-radius: 999px;
|
||||||
height: 0.5em;
|
pointer-events: none;
|
||||||
background-color: var(--rdp-background-color);
|
|
||||||
border-radius: 100%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-selected .rdp-day_button:focus-visible,
|
.rdp-selected .rdp-day_button:focus-visible,
|
||||||
|
|
@ -274,40 +294,46 @@
|
||||||
.rdp-range_end::before {
|
.rdp-range_end::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: var(--rdp-background-color);
|
background-color: var(--rdp-range-background-color);
|
||||||
top: 50%;
|
top: -1px;
|
||||||
height: 100%;
|
bottom: -1px;
|
||||||
width: 50%;
|
left: 0;
|
||||||
transform: translate(0, -50%);
|
width: 100%;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-range_start::before {
|
.rdp-range_start::before {
|
||||||
left: 50%;
|
border-radius: 999px 0 0 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-range_middle::before {
|
.rdp-week .rdp-range_middle:first-child::before {
|
||||||
left: 50%;
|
border-radius: 999px 0 0 999px;
|
||||||
width: 100%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-range_end::before {
|
.rdp-range_end::before {
|
||||||
right: 50%;
|
border-radius: 0 999px 999px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rdp-week .rdp-range_middle:last-child::before {
|
||||||
|
border-radius: 0 999px 999px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-range_start.rdp-range_end::before {
|
.rdp-range_start.rdp-range_end::before {
|
||||||
display: none;
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-range_middle .rdp-day_button {
|
.rdp-range_start .rdp-day_button,
|
||||||
|
.rdp-range_middle .rdp-day_button,
|
||||||
|
.rdp-range_end .rdp-day_button {
|
||||||
|
color: var(--rdp-selected-color);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: inherit;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rdp-day.rdp-range_middle .rdp-day_button:hover,
|
.rdp-day.rdp-range_middle .rdp-day_button:hover,
|
||||||
.rdp-day.rdp-range_middle .rdp-day_button:focus-visible {
|
.rdp-day.rdp-range_middle .rdp-day_button:focus-visible {
|
||||||
background-color: var(--rdp-background-color);
|
background-color: transparent;
|
||||||
color: inherit;
|
color: var(--rdp-selected-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue