mirror of
https://github.com/makeplane/plane.git
synced 2025-12-23 23:29:37 +01:00
[WEB-2316] chore: Render Tooltips and Drop downs in certain places on hover hover to improve rendering performance (#5456)
* render tooltips and dropdowns in certain places post hover to improve performance * fix useEffect hooks
This commit is contained in:
73
packages/ui/src/dropdowns/combo-box.tsx
Normal file
73
packages/ui/src/dropdowns/combo-box.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { Combobox } from "@headlessui/react";
|
||||||
|
import React, {
|
||||||
|
ElementType,
|
||||||
|
Fragment,
|
||||||
|
KeyboardEventHandler,
|
||||||
|
ReactNode,
|
||||||
|
Ref,
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
as?: ElementType | undefined;
|
||||||
|
ref?: Ref<HTMLElement> | undefined;
|
||||||
|
tabIndex?: number | undefined;
|
||||||
|
className?: string | undefined;
|
||||||
|
value?: string | string[] | null;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
onKeyDown?: KeyboardEventHandler<HTMLDivElement> | undefined;
|
||||||
|
multiple?: boolean;
|
||||||
|
renderByDefault?: boolean;
|
||||||
|
button: ReactNode;
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ComboDropDown = forwardRef((props: Props, ref) => {
|
||||||
|
const { button, renderByDefault = true, children, ...rest } = props;
|
||||||
|
|
||||||
|
const dropDownButtonRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
const [shouldRender, setShouldRender] = useState(renderByDefault);
|
||||||
|
|
||||||
|
const onHover = () => {
|
||||||
|
setShouldRender(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const element = dropDownButtonRef.current;
|
||||||
|
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
element.addEventListener("mouseenter", onHover);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
element?.removeEventListener("mouseenter", onHover);
|
||||||
|
};
|
||||||
|
}, [dropDownButtonRef, shouldRender]);
|
||||||
|
|
||||||
|
if (!shouldRender) {
|
||||||
|
return (
|
||||||
|
<div ref={dropDownButtonRef} className="h-full flex items-center">
|
||||||
|
{button}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
//@ts-ignore
|
||||||
|
<Combobox {...rest} ref={ref}>
|
||||||
|
<Combobox.Button as={Fragment}>{button}</Combobox.Button>
|
||||||
|
{children}
|
||||||
|
</Combobox>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const ComboOptions = Combobox.Options;
|
||||||
|
const ComboOption = Combobox.Option;
|
||||||
|
const ComboInput = Combobox.Input;
|
||||||
|
|
||||||
|
export { ComboDropDown, ComboOptions, ComboOption, ComboInput };
|
||||||
@@ -2,3 +2,4 @@ export * from "./context-menu";
|
|||||||
export * from "./custom-menu";
|
export * from "./custom-menu";
|
||||||
export * from "./custom-select";
|
export * from "./custom-select";
|
||||||
export * from "./custom-search-select";
|
export * from "./custom-search-select";
|
||||||
|
export * from "./combo-box";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { Tooltip2 } from "@blueprintjs/popover2";
|
import { Tooltip2 } from "@blueprintjs/popover2";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "../../helpers";
|
import { cn } from "../../helpers";
|
||||||
@@ -42,37 +42,67 @@ export const Tooltip: React.FC<ITooltipProps> = ({
|
|||||||
openDelay = 200,
|
openDelay = 200,
|
||||||
closeDelay,
|
closeDelay,
|
||||||
isMobile = false,
|
isMobile = false,
|
||||||
}) => (
|
}) => {
|
||||||
<Tooltip2
|
const toolTipRef = useRef<HTMLDivElement | null>(null);
|
||||||
disabled={disabled}
|
|
||||||
hoverOpenDelay={openDelay}
|
const [shouldRender, setShouldRender] = useState(false);
|
||||||
hoverCloseDelay={closeDelay}
|
|
||||||
content={
|
const onHover = () => {
|
||||||
<div
|
setShouldRender(true);
|
||||||
className={cn(
|
};
|
||||||
"relative block z-50 max-w-xs gap-1 overflow-hidden break-words rounded-md bg-custom-background-100 p-2 text-xs text-custom-text-200 shadow-md",
|
|
||||||
{
|
useEffect(() => {
|
||||||
hidden: isMobile,
|
const element = toolTipRef.current;
|
||||||
},
|
|
||||||
className
|
if (!element) return;
|
||||||
)}
|
|
||||||
>
|
element.addEventListener("mouseenter", onHover);
|
||||||
{tooltipHeading && <h5 className="font-medium text-custom-text-100">{tooltipHeading}</h5>}
|
|
||||||
{tooltipContent}
|
return () => {
|
||||||
|
element?.removeEventListener("mouseenter", onHover);
|
||||||
|
};
|
||||||
|
}, [toolTipRef, shouldRender]);
|
||||||
|
|
||||||
|
if (!shouldRender) {
|
||||||
|
return (
|
||||||
|
<div ref={toolTipRef} className="h-full flex items-center">
|
||||||
|
{children}
|
||||||
</div>
|
</div>
|
||||||
}
|
);
|
||||||
position={position}
|
}
|
||||||
renderTarget={({
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
return (
|
||||||
isOpen: isTooltipOpen,
|
<Tooltip2
|
||||||
ref: eleReference,
|
disabled={disabled}
|
||||||
...tooltipProps
|
hoverOpenDelay={openDelay}
|
||||||
}) =>
|
hoverCloseDelay={closeDelay}
|
||||||
React.cloneElement(children, {
|
content={
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"relative block z-50 max-w-xs gap-1 overflow-hidden break-words rounded-md bg-custom-background-100 p-2 text-xs text-custom-text-200 shadow-md",
|
||||||
|
{
|
||||||
|
hidden: isMobile,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{tooltipHeading && <h5 className="font-medium text-custom-text-100">{tooltipHeading}</h5>}
|
||||||
|
{tooltipContent}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
position={position}
|
||||||
|
renderTarget={({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
isOpen: isTooltipOpen,
|
||||||
ref: eleReference,
|
ref: eleReference,
|
||||||
...tooltipProps,
|
...tooltipProps
|
||||||
...children.props,
|
}) =>
|
||||||
})
|
React.cloneElement(children, {
|
||||||
}
|
ref: eleReference,
|
||||||
/>
|
...tooltipProps,
|
||||||
);
|
...children.props,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Fragment, ReactNode, useRef, useState } from "react";
|
import { ReactNode, useRef, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
import { Combobox } from "@headlessui/react";
|
|
||||||
// ui
|
// ui
|
||||||
import { ContrastIcon } from "@plane/ui";
|
import { ComboDropDown, ContrastIcon } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@@ -26,6 +25,7 @@ type Props = TDropdownProps & {
|
|||||||
projectId: string | undefined;
|
projectId: string | undefined;
|
||||||
value: string | null;
|
value: string | null;
|
||||||
canRemoveCycle?: boolean;
|
canRemoveCycle?: boolean;
|
||||||
|
renderByDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CycleDropdown: React.FC<Props> = observer((props) => {
|
export const CycleDropdown: React.FC<Props> = observer((props) => {
|
||||||
@@ -48,6 +48,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
|
|||||||
tabIndex,
|
tabIndex,
|
||||||
value,
|
value,
|
||||||
canRemoveCycle = true,
|
canRemoveCycle = true,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
|
|
||||||
@@ -72,8 +73,57 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
|
|||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<>
|
||||||
|
{button ? (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full w-full outline-none hover:bg-custom-background-80",
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none hover:bg-custom-background-80",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
className={buttonClassName}
|
||||||
|
isActive={isOpen}
|
||||||
|
tooltipHeading="Cycle"
|
||||||
|
tooltipContent={selectedName ?? placeholder}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
variant={buttonVariant}
|
||||||
|
>
|
||||||
|
{!hideIcon && <ContrastIcon className="h-3 w-3 flex-shrink-0" />}
|
||||||
|
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (!!selectedName || !!placeholder) && (
|
||||||
|
<span className="max-w-40 flex-grow truncate">{selectedName ?? placeholder}</span>
|
||||||
|
)}
|
||||||
|
{dropdownArrow && (
|
||||||
|
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</DropdownButton>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
@@ -82,53 +132,9 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
|
|||||||
onChange={dropdownOnChange}
|
onChange={dropdownOnChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
button={comboButton}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={Fragment}>
|
|
||||||
{button ? (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full w-full outline-none hover:bg-custom-background-80",
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
{button}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none hover:bg-custom-background-80",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
className={buttonClassName}
|
|
||||||
isActive={isOpen}
|
|
||||||
tooltipHeading="Cycle"
|
|
||||||
tooltipContent={selectedName ?? placeholder}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
|
||||||
{!hideIcon && <ContrastIcon className="h-3 w-3 flex-shrink-0" />}
|
|
||||||
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (!!selectedName || !!placeholder) && (
|
|
||||||
<span className="max-w-40 flex-grow truncate">{selectedName ?? placeholder}</span>
|
|
||||||
)}
|
|
||||||
{dropdownArrow && (
|
|
||||||
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
|
||||||
)}
|
|
||||||
</DropdownButton>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen && projectId && (
|
{isOpen && projectId && (
|
||||||
<CycleOptions
|
<CycleOptions
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
@@ -138,6 +144,6 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
|
|||||||
canRemoveCycle={canRemoveCycle}
|
canRemoveCycle={canRemoveCycle}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { usePopper } from "react-popper";
|
|||||||
import { ArrowRight, CalendarDays } from "lucide-react";
|
import { ArrowRight, CalendarDays } from "lucide-react";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { Button } from "@plane/ui";
|
import { Button, ComboDropDown } from "@plane/ui";
|
||||||
// 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";
|
||||||
@@ -49,6 +49,7 @@ type Props = {
|
|||||||
from: Date | undefined;
|
from: Date | undefined;
|
||||||
to: Date | undefined;
|
to: Date | undefined;
|
||||||
};
|
};
|
||||||
|
renderByDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateRangeDropdown: React.FC<Props> = (props) => {
|
export const DateRangeDropdown: React.FC<Props> = (props) => {
|
||||||
@@ -80,6 +81,7 @@ export const DateRangeDropdown: React.FC<Props> = (props) => {
|
|||||||
showTooltip = false,
|
showTooltip = false,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
value,
|
value,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@@ -131,8 +133,53 @@ export const DateRangeDropdown: React.FC<Props> = (props) => {
|
|||||||
setDateRange(value);
|
setDateRange(value);
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
className={buttonClassName}
|
||||||
|
isActive={isOpen}
|
||||||
|
tooltipHeading="Date range"
|
||||||
|
tooltipContent={
|
||||||
|
<>
|
||||||
|
{dateRange.from ? renderFormattedDate(dateRange.from) : "N/A"}
|
||||||
|
{" - "}
|
||||||
|
{dateRange.to ? renderFormattedDate(dateRange.to) : "N/A"}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
variant={buttonVariant}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={cn("h-full flex items-center justify-center gap-1 rounded-sm flex-grow", buttonFromDateClassName)}
|
||||||
|
>
|
||||||
|
{!hideIcon.from && icon}
|
||||||
|
{dateRange.from ? renderFormattedDate(dateRange.from) : placeholder.from}
|
||||||
|
</span>
|
||||||
|
<ArrowRight className="h-3 w-3 flex-shrink-0" />
|
||||||
|
<span
|
||||||
|
className={cn("h-full flex items-center justify-center gap-1 rounded-sm flex-grow", buttonToDateClassName)}
|
||||||
|
>
|
||||||
|
{!hideIcon.to && icon}
|
||||||
|
{dateRange.to ? renderFormattedDate(dateRange.to) : placeholder.to}
|
||||||
|
</span>
|
||||||
|
</DropdownButton>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
@@ -142,58 +189,10 @@ export const DateRangeDropdown: React.FC<Props> = (props) => {
|
|||||||
if (!isOpen) handleKeyDown(e);
|
if (!isOpen) handleKeyDown(e);
|
||||||
} else handleKeyDown(e);
|
} else handleKeyDown(e);
|
||||||
}}
|
}}
|
||||||
|
button={comboButton}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
className={buttonClassName}
|
|
||||||
isActive={isOpen}
|
|
||||||
tooltipHeading="Date range"
|
|
||||||
tooltipContent={
|
|
||||||
<>
|
|
||||||
{dateRange.from ? renderFormattedDate(dateRange.from) : "N/A"}
|
|
||||||
{" - "}
|
|
||||||
{dateRange.to ? renderFormattedDate(dateRange.to) : "N/A"}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={cn(
|
|
||||||
"h-full flex items-center justify-center gap-1 rounded-sm flex-grow",
|
|
||||||
buttonFromDateClassName
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{!hideIcon.from && icon}
|
|
||||||
{dateRange.from ? renderFormattedDate(dateRange.from) : placeholder.from}
|
|
||||||
</span>
|
|
||||||
<ArrowRight className="h-3 w-3 flex-shrink-0" />
|
|
||||||
<span
|
|
||||||
className={cn(
|
|
||||||
"h-full flex items-center justify-center gap-1 rounded-sm flex-grow",
|
|
||||||
buttonToDateClassName
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{!hideIcon.to && icon}
|
|
||||||
{dateRange.to ? renderFormattedDate(dateRange.to) : placeholder.to}
|
|
||||||
</span>
|
|
||||||
</DropdownButton>
|
|
||||||
</button>
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<Combobox.Options className="fixed z-10" static>
|
<Combobox.Options className="fixed z-10" static>
|
||||||
<div
|
<div
|
||||||
@@ -250,6 +249,6 @@ export const DateRangeDropdown: React.FC<Props> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { createPortal } from "react-dom";
|
|||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
import { CalendarDays, X } from "lucide-react";
|
import { CalendarDays, X } from "lucide-react";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
|
// ui
|
||||||
|
import { ComboDropDown } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { renderFormattedDate, getDate } from "@/helpers/date-time.helper";
|
import { renderFormattedDate, getDate } from "@/helpers/date-time.helper";
|
||||||
@@ -27,6 +29,7 @@ type Props = TDropdownProps & {
|
|||||||
value: Date | string | null;
|
value: Date | string | null;
|
||||||
closeOnSelect?: boolean;
|
closeOnSelect?: boolean;
|
||||||
formatToken?: string;
|
formatToken?: string;
|
||||||
|
renderByDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateDropdown: React.FC<Props> = (props) => {
|
export const DateDropdown: React.FC<Props> = (props) => {
|
||||||
@@ -51,6 +54,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
|||||||
tabIndex,
|
tabIndex,
|
||||||
value,
|
value,
|
||||||
formatToken,
|
formatToken,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@@ -98,8 +102,48 @@ export const DateDropdown: React.FC<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 comboButton = (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
ref={setReferenceElement}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
className={buttonClassName}
|
||||||
|
isActive={isOpen}
|
||||||
|
tooltipHeading={placeholder}
|
||||||
|
tooltipContent={value ? renderFormattedDate(value, formatToken) : "None"}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
variant={buttonVariant}
|
||||||
|
>
|
||||||
|
{!hideIcon && icon}
|
||||||
|
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
||||||
|
<span className="flex-grow truncate">{value ? renderFormattedDate(value, formatToken) : placeholder}</span>
|
||||||
|
)}
|
||||||
|
{isClearable && !disabled && isDateSelected && (
|
||||||
|
<X
|
||||||
|
className={cn("h-2.5 w-2.5 flex-shrink-0", clearIconClassName)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
onChange(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DropdownButton>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
@@ -109,49 +153,10 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
|||||||
if (!isOpen) handleKeyDown(e);
|
if (!isOpen) handleKeyDown(e);
|
||||||
} else handleKeyDown(e);
|
} else handleKeyDown(e);
|
||||||
}}
|
}}
|
||||||
|
button={comboButton}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={React.Fragment}>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
ref={setReferenceElement}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
className={buttonClassName}
|
|
||||||
isActive={isOpen}
|
|
||||||
tooltipHeading={placeholder}
|
|
||||||
tooltipContent={value ? renderFormattedDate(value, formatToken) : "None"}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
|
||||||
{!hideIcon && icon}
|
|
||||||
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
|
||||||
<span className="flex-grow truncate">
|
|
||||||
{value ? renderFormattedDate(value, formatToken) : placeholder}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{isClearable && !disabled && isDateSelected && (
|
|
||||||
<X
|
|
||||||
className={cn("h-2.5 w-2.5 flex-shrink-0", clearIconClassName)}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
onChange(null);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DropdownButton>
|
|
||||||
</button>
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen &&
|
{isOpen &&
|
||||||
createPortal(
|
createPortal(
|
||||||
<Combobox.Options data-prevent-outside-click static>
|
<Combobox.Options data-prevent-outside-click static>
|
||||||
@@ -176,6 +181,6 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
|||||||
</Combobox.Options>,
|
</Combobox.Options>,
|
||||||
document.body
|
document.body
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Fragment, ReactNode, useRef, useState } from "react";
|
import { ReactNode, useRef, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
import { Check, ChevronDown, Search, Triangle } from "lucide-react";
|
import { Check, ChevronDown, Search, Triangle } from "lucide-react";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
|
// ui
|
||||||
|
import { ComboDropDown } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@@ -27,6 +29,7 @@ type Props = TDropdownProps & {
|
|||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
projectId: string | undefined;
|
projectId: string | undefined;
|
||||||
value: string | undefined | null;
|
value: string | undefined | null;
|
||||||
|
renderByDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DropdownOptions =
|
type DropdownOptions =
|
||||||
@@ -56,6 +59,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
showTooltip = false,
|
showTooltip = false,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
value,
|
value,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
@@ -142,8 +146,54 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<>
|
||||||
|
{button ? (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
className={buttonClassName}
|
||||||
|
isActive={isOpen}
|
||||||
|
tooltipHeading="Estimate"
|
||||||
|
tooltipContent={selectedEstimate ? selectedEstimate?.value : placeholder}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
variant={buttonVariant}
|
||||||
|
>
|
||||||
|
{!hideIcon && <Triangle className="h-3 w-3 flex-shrink-0" />}
|
||||||
|
{(selectedEstimate || placeholder) && BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
||||||
|
<span className="flex-grow truncate">{selectedEstimate ? selectedEstimate?.value : placeholder}</span>
|
||||||
|
)}
|
||||||
|
{dropdownArrow && (
|
||||||
|
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</DropdownButton>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
@@ -152,50 +202,9 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
onChange={dropdownOnChange}
|
onChange={dropdownOnChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
button={comboButton}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={Fragment}>
|
|
||||||
{button ? (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
{button}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
className={buttonClassName}
|
|
||||||
isActive={isOpen}
|
|
||||||
tooltipHeading="Estimate"
|
|
||||||
tooltipContent={selectedEstimate ? selectedEstimate?.value : placeholder}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
|
||||||
{!hideIcon && <Triangle className="h-3 w-3 flex-shrink-0" />}
|
|
||||||
{(selectedEstimate || placeholder) && BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
|
||||||
<span className="flex-grow truncate">{selectedEstimate ? selectedEstimate?.value : placeholder}</span>
|
|
||||||
)}
|
|
||||||
{dropdownArrow && (
|
|
||||||
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
|
||||||
)}
|
|
||||||
</DropdownButton>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<Combobox.Options className="fixed z-10" static>
|
<Combobox.Options className="fixed z-10" static>
|
||||||
<div
|
<div
|
||||||
@@ -263,6 +272,6 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Fragment, useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { ChevronDown, LucideIcon } from "lucide-react";
|
import { ChevronDown, LucideIcon } from "lucide-react";
|
||||||
// headless ui
|
// ui
|
||||||
import { Combobox } from "@headlessui/react";
|
import { ComboDropDown } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@@ -21,6 +21,7 @@ type Props = {
|
|||||||
projectId?: string;
|
projectId?: string;
|
||||||
icon?: LucideIcon;
|
icon?: LucideIcon;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
|
renderByDefault?: boolean;
|
||||||
} & MemberDropdownProps;
|
} & MemberDropdownProps;
|
||||||
|
|
||||||
export const MemberDropdown: React.FC<Props> = observer((props) => {
|
export const MemberDropdown: React.FC<Props> = observer((props) => {
|
||||||
@@ -46,6 +47,7 @@ export const MemberDropdown: React.FC<Props> = observer((props) => {
|
|||||||
tabIndex,
|
tabIndex,
|
||||||
value,
|
value,
|
||||||
icon,
|
icon,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@@ -96,61 +98,66 @@ export const MemberDropdown: React.FC<Props> = observer((props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<>
|
||||||
|
{button ? (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
className={cn("text-xs", buttonClassName)}
|
||||||
|
isActive={isOpen}
|
||||||
|
tooltipHeading={placeholder}
|
||||||
|
tooltipContent={tooltipContent ?? `${value?.length ?? 0} assignee${value?.length !== 1 ? "s" : ""}`}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
variant={buttonVariant}
|
||||||
|
>
|
||||||
|
{!hideIcon && <ButtonAvatars showTooltip={showTooltip} userIds={value} icon={icon} />}
|
||||||
|
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
||||||
|
<span className="flex-grow truncate leading-5">
|
||||||
|
{getDisplayName(value, showUserDetails, placeholder)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{dropdownArrow && (
|
||||||
|
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</DropdownButton>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
className={cn("h-full", className)}
|
className={cn("h-full", className)}
|
||||||
onChange={dropdownOnChange}
|
onChange={dropdownOnChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
button={comboButton}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
{...comboboxProps}
|
{...comboboxProps}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={Fragment}>
|
|
||||||
{button ? (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
{button}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
className={cn("text-xs", buttonClassName)}
|
|
||||||
isActive={isOpen}
|
|
||||||
tooltipHeading={placeholder}
|
|
||||||
tooltipContent={tooltipContent ?? `${value?.length ?? 0} assignee${value?.length !== 1 ? "s" : ""}`}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
|
||||||
{!hideIcon && <ButtonAvatars showTooltip={showTooltip} userIds={value} icon={icon} />}
|
|
||||||
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
|
||||||
<span className="flex-grow truncate leading-5">
|
|
||||||
{getDisplayName(value, showUserDetails, placeholder)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{dropdownArrow && (
|
|
||||||
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
|
||||||
)}
|
|
||||||
</DropdownButton>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<MemberOptions
|
<MemberOptions
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
@@ -159,6 +166,6 @@ export const MemberDropdown: React.FC<Props> = observer((props) => {
|
|||||||
referenceElement={referenceElement}
|
referenceElement={referenceElement}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Fragment, ReactNode, useEffect, useRef, useState } from "react";
|
import { ReactNode, useEffect, useRef, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { ChevronDown, X } from "lucide-react";
|
import { ChevronDown, X } from "lucide-react";
|
||||||
import { Combobox } from "@headlessui/react";
|
|
||||||
// ui
|
// ui
|
||||||
import { DiceIcon, Tooltip } from "@plane/ui";
|
import { ComboDropDown, DiceIcon, Tooltip } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@@ -27,6 +26,7 @@ type Props = TDropdownProps & {
|
|||||||
projectId: string | undefined;
|
projectId: string | undefined;
|
||||||
showCount?: boolean;
|
showCount?: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
|
renderByDefault?: boolean;
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
multiple: false;
|
multiple: false;
|
||||||
@@ -170,6 +170,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
|
|||||||
showTooltip = false,
|
showTooltip = false,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
value,
|
value,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@@ -207,73 +208,78 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
|
|||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<>
|
||||||
|
{button ? (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full w-full outline-none hover:bg-custom-background-80",
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none hover:bg-custom-background-80",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
className={buttonClassName}
|
||||||
|
isActive={isOpen}
|
||||||
|
tooltipHeading="Module"
|
||||||
|
tooltipContent={
|
||||||
|
Array.isArray(value)
|
||||||
|
? `${value
|
||||||
|
.map((moduleId) => getModuleNameById(moduleId))
|
||||||
|
.toString()
|
||||||
|
.replaceAll(",", ", ")}`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
variant={buttonVariant}
|
||||||
|
>
|
||||||
|
<ButtonContent
|
||||||
|
disabled={disabled}
|
||||||
|
dropdownArrow={dropdownArrow}
|
||||||
|
dropdownArrowClassName={dropdownArrowClassName}
|
||||||
|
hideIcon={hideIcon}
|
||||||
|
hideText={BUTTON_VARIANTS_WITHOUT_TEXT.includes(buttonVariant)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
showCount={showCount}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange as any}
|
||||||
|
/>
|
||||||
|
</DropdownButton>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
className={cn("h-full", className)}
|
className={cn("h-full", className)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
button={comboButton}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
{...comboboxProps}
|
{...comboboxProps}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={Fragment}>
|
|
||||||
{button ? (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full w-full outline-none hover:bg-custom-background-80",
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
{button}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none hover:bg-custom-background-80",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
className={buttonClassName}
|
|
||||||
isActive={isOpen}
|
|
||||||
tooltipHeading="Module"
|
|
||||||
tooltipContent={
|
|
||||||
Array.isArray(value)
|
|
||||||
? `${value
|
|
||||||
.map((moduleId) => getModuleNameById(moduleId))
|
|
||||||
.toString()
|
|
||||||
.replaceAll(",", ", ")}`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
|
||||||
<ButtonContent
|
|
||||||
disabled={disabled}
|
|
||||||
dropdownArrow={dropdownArrow}
|
|
||||||
dropdownArrowClassName={dropdownArrowClassName}
|
|
||||||
hideIcon={hideIcon}
|
|
||||||
hideText={BUTTON_VARIANTS_WITHOUT_TEXT.includes(buttonVariant)}
|
|
||||||
placeholder={placeholder}
|
|
||||||
showCount={showCount}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange as any}
|
|
||||||
/>
|
|
||||||
</DropdownButton>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen && projectId && (
|
{isOpen && projectId && (
|
||||||
<ModuleOptions
|
<ModuleOptions
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
@@ -283,6 +289,6 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
|
|||||||
multiple={multiple}
|
multiple={multiple}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Combobox } from "@headlessui/react";
|
|||||||
// types
|
// types
|
||||||
import { TIssuePriorities } from "@plane/types";
|
import { TIssuePriorities } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import { PriorityIcon, Tooltip } from "@plane/ui";
|
import { ComboDropDown, PriorityIcon, Tooltip } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_PRIORITIES } from "@/constants/issue";
|
import { ISSUE_PRIORITIES } from "@/constants/issue";
|
||||||
// helpers
|
// helpers
|
||||||
@@ -29,6 +29,7 @@ type Props = TDropdownProps & {
|
|||||||
onChange: (val: TIssuePriorities) => void;
|
onChange: (val: TIssuePriorities) => void;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
value: TIssuePriorities | undefined | null;
|
value: TIssuePriorities | undefined | null;
|
||||||
|
renderByDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ButtonProps = {
|
type ButtonProps = {
|
||||||
@@ -305,6 +306,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
|
|||||||
showTooltip = false,
|
showTooltip = false,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
value = "none",
|
value = "none",
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
@@ -363,11 +365,54 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
|
|||||||
const ButtonToRender = BORDER_BUTTON_VARIANTS.includes(buttonVariant)
|
const ButtonToRender = BORDER_BUTTON_VARIANTS.includes(buttonVariant)
|
||||||
? BorderButton
|
? BorderButton
|
||||||
: BACKGROUND_BUTTON_VARIANTS.includes(buttonVariant)
|
: BACKGROUND_BUTTON_VARIANTS.includes(buttonVariant)
|
||||||
? BackgroundButton
|
? BackgroundButton
|
||||||
: TransparentButton;
|
: TransparentButton;
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<>
|
||||||
|
{button ? (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<ButtonToRender
|
||||||
|
priority={value ?? undefined}
|
||||||
|
className={cn(buttonClassName, {
|
||||||
|
"text-custom-text-200": resolvedTheme?.includes("dark") || resolvedTheme === "custom",
|
||||||
|
})}
|
||||||
|
highlightUrgent={highlightUrgent}
|
||||||
|
dropdownArrow={dropdownArrow && !disabled}
|
||||||
|
dropdownArrowClassName={dropdownArrowClassName}
|
||||||
|
hideIcon={hideIcon}
|
||||||
|
placeholder={placeholder}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
hideText={BUTTON_VARIANTS_WITHOUT_TEXT.includes(buttonVariant)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
@@ -382,47 +427,9 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
|
|||||||
onChange={dropdownOnChange}
|
onChange={dropdownOnChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
button={comboButton}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={Fragment}>
|
|
||||||
{button ? (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
{button}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<ButtonToRender
|
|
||||||
priority={value ?? undefined}
|
|
||||||
className={cn(buttonClassName, {
|
|
||||||
"text-custom-text-200": resolvedTheme?.includes("dark") || resolvedTheme === "custom",
|
|
||||||
})}
|
|
||||||
highlightUrgent={highlightUrgent}
|
|
||||||
dropdownArrow={dropdownArrow && !disabled}
|
|
||||||
dropdownArrowClassName={dropdownArrowClassName}
|
|
||||||
hideIcon={hideIcon}
|
|
||||||
placeholder={placeholder}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
hideText={BUTTON_VARIANTS_WITHOUT_TEXT.includes(buttonVariant)}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<Combobox.Options className="fixed z-10" static>
|
<Combobox.Options className="fixed z-10" static>
|
||||||
<div
|
<div
|
||||||
@@ -471,6 +478,6 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { Check, ChevronDown, Search } from "lucide-react";
|
|||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
// types
|
// types
|
||||||
import { IProject } from "@plane/types";
|
import { IProject } from "@plane/types";
|
||||||
|
// ui
|
||||||
|
import { ComboDropDown } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { Logo } from "@/components/common";
|
import { Logo } from "@/components/common";
|
||||||
// helpers
|
// helpers
|
||||||
@@ -27,6 +29,7 @@ type Props = TDropdownProps & {
|
|||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
renderCondition?: (project: IProject) => boolean;
|
renderCondition?: (project: IProject) => boolean;
|
||||||
value: string | null;
|
value: string | null;
|
||||||
|
renderByDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProjectDropdown: React.FC<Props> = observer((props) => {
|
export const ProjectDropdown: React.FC<Props> = observer((props) => {
|
||||||
@@ -48,6 +51,7 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
|
|||||||
showTooltip = false,
|
showTooltip = false,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
value,
|
value,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
@@ -112,8 +116,58 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
|
|||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<>
|
||||||
|
{button ? (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
className={buttonClassName}
|
||||||
|
isActive={isOpen}
|
||||||
|
tooltipHeading="Project"
|
||||||
|
tooltipContent={selectedProject?.name ?? placeholder}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
variant={buttonVariant}
|
||||||
|
>
|
||||||
|
{!hideIcon && selectedProject && (
|
||||||
|
<span className="grid place-items-center flex-shrink-0 h-4 w-4">
|
||||||
|
<Logo logo={selectedProject.logo_props} size={12} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
||||||
|
<span className="flex-grow truncate max-w-40">{selectedProject?.name ?? placeholder}</span>
|
||||||
|
)}
|
||||||
|
{dropdownArrow && (
|
||||||
|
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</DropdownButton>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
@@ -122,54 +176,9 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
|
|||||||
onChange={dropdownOnChange}
|
onChange={dropdownOnChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
button={comboButton}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={Fragment}>
|
|
||||||
{button ? (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
{button}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
className={buttonClassName}
|
|
||||||
isActive={isOpen}
|
|
||||||
tooltipHeading="Project"
|
|
||||||
tooltipContent={selectedProject?.name ?? placeholder}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
|
||||||
{!hideIcon && selectedProject && (
|
|
||||||
<span className="grid place-items-center flex-shrink-0 h-4 w-4">
|
|
||||||
<Logo logo={selectedProject.logo_props} size={12} />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
|
||||||
<span className="flex-grow truncate max-w-40">{selectedProject?.name ?? placeholder}</span>
|
|
||||||
)}
|
|
||||||
{dropdownArrow && (
|
|
||||||
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
|
||||||
)}
|
|
||||||
</DropdownButton>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<Combobox.Options className="fixed z-10" static>
|
<Combobox.Options className="fixed z-10" static>
|
||||||
<div
|
<div
|
||||||
@@ -225,6 +234,6 @@ export const ProjectDropdown: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { usePopper } from "react-popper";
|
|||||||
import { Check, ChevronDown, Search } from "lucide-react";
|
import { Check, ChevronDown, Search } from "lucide-react";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner, StateGroupIcon } from "@plane/ui";
|
import { ComboDropDown, Spinner, StateGroupIcon } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@@ -29,6 +29,7 @@ type Props = TDropdownProps & {
|
|||||||
projectId: string | undefined;
|
projectId: string | undefined;
|
||||||
showDefaultState?: boolean;
|
showDefaultState?: boolean;
|
||||||
value: string | undefined | null;
|
value: string | undefined | null;
|
||||||
|
renderByDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StateDropdown: React.FC<Props> = observer((props) => {
|
export const StateDropdown: React.FC<Props> = observer((props) => {
|
||||||
@@ -50,6 +51,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
showTooltip = false,
|
showTooltip = false,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
value,
|
value,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
@@ -125,8 +127,66 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<>
|
||||||
|
{button ? (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
"clickable block h-full max-w-full outline-none",
|
||||||
|
{
|
||||||
|
"cursor-not-allowed text-custom-text-200": disabled,
|
||||||
|
"cursor-pointer": !disabled,
|
||||||
|
},
|
||||||
|
buttonContainerClassName
|
||||||
|
)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
className={buttonClassName}
|
||||||
|
isActive={isOpen}
|
||||||
|
tooltipHeading="State"
|
||||||
|
tooltipContent={selectedState?.name ?? "State"}
|
||||||
|
showTooltip={showTooltip}
|
||||||
|
variant={buttonVariant}
|
||||||
|
>
|
||||||
|
{stateLoader ? (
|
||||||
|
<Spinner className="h-3.5 w-3.5" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{!hideIcon && (
|
||||||
|
<StateGroupIcon
|
||||||
|
stateGroup={selectedState?.group ?? "backlog"}
|
||||||
|
color={selectedState?.color ?? "rgba(var(--color-text-300))"}
|
||||||
|
className="h-3 w-3 flex-shrink-0"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
||||||
|
<span className="flex-grow truncate">{selectedState?.name ?? "State"}</span>
|
||||||
|
)}
|
||||||
|
{dropdownArrow && (
|
||||||
|
<ChevronDown className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)} aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</DropdownButton>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
@@ -135,65 +195,9 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
onChange={dropdownOnChange}
|
onChange={dropdownOnChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
button={comboButton}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
>
|
>
|
||||||
<Combobox.Button as={Fragment}>
|
|
||||||
{button ? (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn("clickable block h-full w-full outline-none", buttonContainerClassName)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
{button}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"clickable block h-full max-w-full outline-none",
|
|
||||||
{
|
|
||||||
"cursor-not-allowed text-custom-text-200": disabled,
|
|
||||||
"cursor-pointer": !disabled,
|
|
||||||
},
|
|
||||||
buttonContainerClassName
|
|
||||||
)}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
className={buttonClassName}
|
|
||||||
isActive={isOpen}
|
|
||||||
tooltipHeading="State"
|
|
||||||
tooltipContent={selectedState?.name ?? "State"}
|
|
||||||
showTooltip={showTooltip}
|
|
||||||
variant={buttonVariant}
|
|
||||||
>
|
|
||||||
{stateLoader ? (
|
|
||||||
<Spinner className="h-3.5 w-3.5" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{!hideIcon && (
|
|
||||||
<StateGroupIcon
|
|
||||||
stateGroup={selectedState?.group ?? "backlog"}
|
|
||||||
color={selectedState?.color ?? "rgba(var(--color-text-300))"}
|
|
||||||
className="h-3 w-3 flex-shrink-0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && (
|
|
||||||
<span className="flex-grow truncate">{selectedState?.name ?? "State"}</span>
|
|
||||||
)}
|
|
||||||
{dropdownArrow && (
|
|
||||||
<ChevronDown
|
|
||||||
className={cn("h-2.5 w-2.5 flex-shrink-0", dropdownArrowClassName)}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DropdownButton>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Combobox.Button>
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<Combobox.Options className="fixed z-10" static>
|
<Combobox.Options className="fixed z-10" static>
|
||||||
<div
|
<div
|
||||||
@@ -246,6 +250,6 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
projectId={issue.project_id}
|
projectId={issue.project_id}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
buttonVariant="border-with-text"
|
buttonVariant="border-with-text"
|
||||||
|
renderByDefault={isMobile}
|
||||||
showTooltip
|
showTooltip
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -298,6 +299,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
buttonVariant="border-without-text"
|
buttonVariant="border-without-text"
|
||||||
buttonClassName="border"
|
buttonClassName="border"
|
||||||
|
renderByDefault={isMobile}
|
||||||
showTooltip
|
showTooltip
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -312,6 +314,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
defaultOptions={defaultLabelOptions}
|
defaultOptions={defaultLabelOptions}
|
||||||
onChange={handleLabel}
|
onChange={handleLabel}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
|
renderByDefault={isMobile}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -328,6 +331,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
||||||
buttonVariant={issue.start_date ? "border-with-text" : "border-without-text"}
|
buttonVariant={issue.start_date ? "border-with-text" : "border-without-text"}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
|
renderByDefault={isMobile}
|
||||||
showTooltip
|
showTooltip
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -346,6 +350,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
buttonClassName={shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group) ? "text-red-500" : ""}
|
buttonClassName={shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group) ? "text-red-500" : ""}
|
||||||
clearIconClassName="!text-custom-text-100"
|
clearIconClassName="!text-custom-text-100"
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
|
renderByDefault={isMobile}
|
||||||
showTooltip
|
showTooltip
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -365,6 +370,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
showTooltip={issue?.assignee_ids?.length === 0}
|
showTooltip={issue?.assignee_ids?.length === 0}
|
||||||
placeholder="Assignees"
|
placeholder="Assignees"
|
||||||
tooltipContent=""
|
tooltipContent=""
|
||||||
|
renderByDefault={isMobile}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</WithDisplayPropertiesHOC>
|
</WithDisplayPropertiesHOC>
|
||||||
@@ -379,6 +385,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
value={issue?.module_ids ?? []}
|
value={issue?.module_ids ?? []}
|
||||||
onChange={handleModule}
|
onChange={handleModule}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
|
renderByDefault={isMobile}
|
||||||
multiple
|
multiple
|
||||||
buttonVariant="border-with-text"
|
buttonVariant="border-with-text"
|
||||||
showCount
|
showCount
|
||||||
@@ -399,6 +406,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
onChange={handleCycle}
|
onChange={handleCycle}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
buttonVariant="border-with-text"
|
buttonVariant="border-with-text"
|
||||||
|
renderByDefault={isMobile}
|
||||||
showTooltip
|
showTooltip
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -415,6 +423,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
projectId={issue.project_id}
|
projectId={issue.project_id}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
buttonVariant="border-with-text"
|
buttonVariant="border-with-text"
|
||||||
|
renderByDefault={isMobile}
|
||||||
showTooltip
|
showTooltip
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { Combobox } from "@headlessui/react";
|
|||||||
// types
|
// types
|
||||||
import { IIssueLabel } from "@plane/types";
|
import { IIssueLabel } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import { Tooltip } from "@plane/ui";
|
import { ComboDropDown, Tooltip } from "@plane/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import { useLabel } from "@/hooks/store";
|
import { useLabel } from "@/hooks/store";
|
||||||
import { useDropdownKeyDown } from "@/hooks/use-dropdown-key-down";
|
import { useDropdownKeyDown } from "@/hooks/use-dropdown-key-down";
|
||||||
@@ -32,6 +32,7 @@ export interface IIssuePropertyLabels {
|
|||||||
noLabelBorder?: boolean;
|
noLabelBorder?: boolean;
|
||||||
placeholderText?: string;
|
placeholderText?: string;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
|
renderByDefault?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((props) => {
|
export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((props) => {
|
||||||
@@ -50,6 +51,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||||||
maxRender = 2,
|
maxRender = 2,
|
||||||
noLabelBorder = false,
|
noLabelBorder = false,
|
||||||
placeholderText,
|
placeholderText,
|
||||||
|
renderByDefault = true,
|
||||||
} = props;
|
} = props;
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug: routerWorkspaceSlug } = useParams();
|
const { workspaceSlug: routerWorkspaceSlug } = useParams();
|
||||||
@@ -217,8 +219,26 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const comboButton = (
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={`clickable flex w-full items-center justify-between gap-1 text-xs ${
|
||||||
|
disabled
|
||||||
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
|
: value.length <= maxRender
|
||||||
|
? "cursor-pointer"
|
||||||
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
|
} ${buttonClassName}`}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
{!hideDropdownArrow && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<ComboDropDown
|
||||||
as="div"
|
as="div"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
className={`w-auto max-w-full flex-shrink-0 text-left ${className}`}
|
className={`w-auto max-w-full flex-shrink-0 text-left ${className}`}
|
||||||
@@ -226,26 +246,10 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
button={comboButton}
|
||||||
|
renderByDefault={renderByDefault}
|
||||||
multiple
|
multiple
|
||||||
>
|
>
|
||||||
<Combobox.Button as={Fragment}>
|
|
||||||
<button
|
|
||||||
ref={setReferenceElement}
|
|
||||||
type="button"
|
|
||||||
className={`clickable flex w-full items-center justify-between gap-1 text-xs ${
|
|
||||||
disabled
|
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
|
||||||
: value.length <= maxRender
|
|
||||||
? "cursor-pointer"
|
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${buttonClassName}`}
|
|
||||||
onClick={handleOnClick}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
{!hideDropdownArrow && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
|
||||||
</button>
|
|
||||||
</Combobox.Button>
|
|
||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<Combobox.Options className="fixed z-10" static>
|
<Combobox.Options className="fixed z-10" static>
|
||||||
<div
|
<div
|
||||||
@@ -307,6 +311,6 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||||||
</div>
|
</div>
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</ComboDropDown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user