mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
style: peek overview
This commit is contained in:
parent
0aa02a603d
commit
899d49b1f0
@ -1,10 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { MoveRight } from "lucide-react";
|
import { Link2, MoveRight } from "lucide-react";
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { setToast, TOAST_TYPE } from "@plane/ui";
|
import { CenterPanelIcon, FullScreenPanelIcon, setToast, SidePanelIcon, TOAST_TYPE } from "@plane/ui";
|
||||||
import { Icon } from "@/components/ui";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { copyTextToClipboard } from "@/helpers/string.helper";
|
import { copyTextToClipboard } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@ -18,21 +17,21 @@ type Props = {
|
|||||||
issueDetails: IIssue | undefined;
|
issueDetails: IIssue | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const peekModes: {
|
const PEEK_MODES: {
|
||||||
key: IPeekMode;
|
key: IPeekMode;
|
||||||
icon: string;
|
icon: any;
|
||||||
label: string;
|
label: string;
|
||||||
}[] = [
|
}[] = [
|
||||||
{ key: "side", icon: "side_navigation", label: "Side Peek" },
|
{ key: "side", icon: SidePanelIcon, label: "Side Peek" },
|
||||||
{
|
{
|
||||||
key: "modal",
|
key: "modal",
|
||||||
icon: "dialogs",
|
icon: CenterPanelIcon,
|
||||||
label: "Modal Peek",
|
label: "Modal",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "full",
|
key: "full",
|
||||||
icon: "nearby",
|
icon: FullScreenPanelIcon,
|
||||||
label: "Full Screen Peek",
|
label: "Full Screen",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -47,20 +46,22 @@ export const PeekOverviewHeader: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
copyTextToClipboard(urlToCopy).then(() => {
|
copyTextToClipboard(urlToCopy).then(() => {
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.INFO,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Link copied!",
|
title: "Link copied!",
|
||||||
message: "Issue link copied to clipboard",
|
message: "Issue link copied to clipboard.",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Icon = PEEK_MODES.find((m) => m.key === peekMode)?.icon ?? SidePanelIcon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{peekMode === "side" && (
|
{peekMode === "side" && (
|
||||||
<button type="button" onClick={handleClose}>
|
<button type="button" onClick={handleClose} className="text-custom-text-300 hover:text-custom-text-200">
|
||||||
<MoveRight className="h-4 w-4" strokeWidth={2} />
|
<MoveRight className="size-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Listbox
|
<Listbox
|
||||||
@ -69,8 +70,10 @@ export const PeekOverviewHeader: React.FC<Props> = observer((props) => {
|
|||||||
onChange={(val) => setPeekMode(val)}
|
onChange={(val) => setPeekMode(val)}
|
||||||
className="relative flex-shrink-0 text-left"
|
className="relative flex-shrink-0 text-left"
|
||||||
>
|
>
|
||||||
<Listbox.Button className={`grid place-items-center ${peekMode === "full" ? "rotate-45" : ""}`}>
|
<Listbox.Button
|
||||||
<Icon iconName={peekModes.find((m) => m.key === peekMode)?.icon ?? ""} className="text-[1rem]" />
|
className={`grid place-items-center text-custom-text-300 hover:text-custom-text-200 ${peekMode === "full" ? "rotate-45" : ""}`}
|
||||||
|
>
|
||||||
|
<Icon className="h-4 w-4 text-custom-text-300 hover:text-custom-text-200" />
|
||||||
</Listbox.Button>
|
</Listbox.Button>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
@ -84,7 +87,7 @@ export const PeekOverviewHeader: React.FC<Props> = observer((props) => {
|
|||||||
>
|
>
|
||||||
<Listbox.Options className="absolute left-0 z-10 mt-1 min-w-[8rem] origin-top-left overflow-y-auto whitespace-nowrap rounded-md border border-custom-border-300 bg-custom-background-90 text-xs shadow-lg focus:outline-none">
|
<Listbox.Options className="absolute left-0 z-10 mt-1 min-w-[8rem] origin-top-left overflow-y-auto whitespace-nowrap rounded-md border border-custom-border-300 bg-custom-background-90 text-xs shadow-lg focus:outline-none">
|
||||||
<div className="space-y-1 p-2">
|
<div className="space-y-1 p-2">
|
||||||
{peekModes.map((mode) => (
|
{PEEK_MODES.map((mode) => (
|
||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
key={mode.key}
|
key={mode.key}
|
||||||
value={mode.key}
|
value={mode.key}
|
||||||
@ -117,8 +120,13 @@ export const PeekOverviewHeader: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
{isClipboardWriteAllowed && (peekMode === "side" || peekMode === "modal") && (
|
{isClipboardWriteAllowed && (peekMode === "side" || peekMode === "modal") && (
|
||||||
<div className="flex flex-shrink-0 items-center gap-2">
|
<div className="flex flex-shrink-0 items-center gap-2">
|
||||||
<button type="button" onClick={handleCopyLink} className="-rotate-45 focus:outline-none" tabIndex={1}>
|
<button
|
||||||
<Icon iconName="link" className="text-[1rem]" />
|
type="button"
|
||||||
|
onClick={handleCopyLink}
|
||||||
|
className="focus:outline-none text-custom-text-300 hover:text-custom-text-200"
|
||||||
|
tabIndex={1}
|
||||||
|
>
|
||||||
|
<Link2 className="h-4 w-4 -rotate-45" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -16,10 +16,10 @@ export const PeekOverviewIssueDetails: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h6 className="font-medium text-custom-text-200">
|
<h6 className="text-base font-medium text-custom-text-400">
|
||||||
{issueDetails.project_detail.identifier}-{issueDetails.sequence_id}
|
{issueDetails.project_detail?.identifier}-{issueDetails?.sequence_id}
|
||||||
</h6>
|
</h6>
|
||||||
<h4 className="break-words text-2xl font-semibold">{issueDetails.name}</h4>
|
<h4 className="break-words text-2xl font-medium">{issueDetails.name}</h4>
|
||||||
{description !== "" && description !== "<p></p>" && (
|
{description !== "" && description !== "<p></p>" && (
|
||||||
<RichTextReadOnlyEditor
|
<RichTextReadOnlyEditor
|
||||||
initialValue={
|
initialValue={
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { CalendarCheck2 } from "lucide-react";
|
import { CalendarCheck2, Signal } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { StateGroupIcon, TOAST_TYPE, setToast } from "@plane/ui";
|
import { DoubleCircleIcon, StateGroupIcon, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { Icon } from "@/components/ui";
|
import { Icon } from "@/components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { issueGroupFilter, issuePriorityFilter } from "@/constants/issue";
|
import { issuePriorityFilter } from "@/constants/issue";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||||
@ -20,7 +20,6 @@ type Props = {
|
|||||||
|
|
||||||
export const PeekOverviewIssueProperties: React.FC<Props> = ({ issueDetails, mode }) => {
|
export const PeekOverviewIssueProperties: React.FC<Props> = ({ issueDetails, mode }) => {
|
||||||
const state = issueDetails.state_detail;
|
const state = issueDetails.state_detail;
|
||||||
const stateGroup = issueGroupFilter(state.group);
|
|
||||||
|
|
||||||
const priority = issueDetails.priority ? issuePriorityFilter(issueDetails.priority) : null;
|
const priority = issueDetails.priority ? issuePriorityFilter(issueDetails.priority) : null;
|
||||||
|
|
||||||
@ -50,28 +49,22 @@ export const PeekOverviewIssueProperties: React.FC<Props> = ({ issueDetails, mod
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={`space-y-4 ${mode === "full" ? "pt-3" : ""}`}>
|
<div className={`space-y-2 ${mode === "full" ? "pt-3" : ""}`}>
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-3 h-8">
|
||||||
<div className="flex w-1/4 flex-shrink-0 items-center gap-2 font-medium">
|
<div className="flex items-center gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||||
<Icon iconName="radio_button_checked" className="flex-shrink-0 !text-base" />
|
<DoubleCircleIcon className="size-4 flex-shrink-0" />
|
||||||
<span className="flex-grow truncate">State</span>
|
<span>State</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-3/4">
|
<div className="w-3/4 flex items-center gap-1.5 py-0.5 text-sm">
|
||||||
{stateGroup && (
|
<StateGroupIcon stateGroup={state.group} color={state.color} />
|
||||||
<div className="inline-flex rounded bg-custom-background-80 px-2.5 py-0.5 text-sm">
|
{addSpaceIfCamelCase(state?.name ?? "")}
|
||||||
<div className="flex items-center gap-1.5 text-left text-custom-text-100">
|
|
||||||
<StateGroupIcon stateGroup={state.group} color={state.color} />
|
|
||||||
{addSpaceIfCamelCase(state?.name ?? "")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-3 h-8">
|
||||||
<div className="flex w-1/4 flex-shrink-0 items-center gap-2 font-medium">
|
<div className="flex items-center gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||||
<Icon iconName="signal_cellular_alt" className="flex-shrink-0 !text-base" />
|
<Signal className="size-4 flex-shrink-0" />
|
||||||
<span className="flex-grow truncate">Priority</span>
|
<span>Priority</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-3/4">
|
<div className="w-3/4">
|
||||||
<div
|
<div
|
||||||
@ -96,23 +89,21 @@ export const PeekOverviewIssueProperties: React.FC<Props> = ({ issueDetails, mod
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm">
|
|
||||||
<div className="flex w-1/4 flex-shrink-0 items-center gap-2 font-medium">
|
<div className="flex items-center gap-3 h-8">
|
||||||
<Icon iconName="calendar_today" className="flex-shrink-0 !text-base" />
|
<div className="flex items-center gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||||
<span className="flex-grow truncate">Due date</span>
|
<CalendarCheck2 className="size-4 flex-shrink-0" />
|
||||||
|
<span>Due date</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{issueDetails.target_date ? (
|
{issueDetails.target_date ? (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn("flex items-center gap-1.5 rounded py-0.5 text-xs text-custom-text-100", {
|
||||||
"flex h-6 items-center gap-1 rounded border border-custom-border-100 bg-custom-background-80 px-2.5 py-1 text-xs text-custom-text-100",
|
"text-red-500": shouldHighlightIssueDueDate(
|
||||||
{
|
issueDetails.target_date,
|
||||||
"text-red-500": shouldHighlightIssueDueDate(
|
issueDetails.state_detail.group
|
||||||
issueDetails.target_date,
|
),
|
||||||
issueDetails.state_detail.group
|
})}
|
||||||
),
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<CalendarCheck2 className="size-3" />
|
<CalendarCheck2 className="size-3" />
|
||||||
{renderFormattedDate(issueDetails.target_date)}
|
{renderFormattedDate(issueDetails.target_date)}
|
||||||
|
Loading…
Reference in New Issue
Block a user