forked from github/plane
refactor: dropdowns (#1532)
This commit is contained in:
parent
538d67dbd9
commit
dddfeb17b7
@ -28,11 +28,11 @@ export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleC
|
||||
handleClose={() => setmonthModal(false)}
|
||||
handleChange={handleChange}
|
||||
/>
|
||||
<div className="flex flex-col gap-7 px-6 py-5 rounded-[10px] border border-brand-base bg-brand-base">
|
||||
<div className="flex items-center justify-between gap-x-8 gap-y-2 ">
|
||||
<div className="flex flex-col gap-7 px-6 py-5 rounded-[10px] border border-custom-border-100 bg-custom-background-90">
|
||||
<div className="flex items-center justify-between gap-x-8 gap-y-2">
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<h4 className="text-lg font-semibold">Auto-archive closed issues</h4>
|
||||
<p className="text-sm text-brand-secondary">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
Plane will automatically archive issues that have been completed or cancelled for the
|
||||
configured time period.
|
||||
</p>
|
||||
@ -52,17 +52,12 @@ export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleC
|
||||
<div className="w-1/2 text-base font-medium">
|
||||
Auto-archive issues that are closed for
|
||||
</div>
|
||||
<div className="w-1/2 ">
|
||||
<div className="w-1/2">
|
||||
<CustomSelect
|
||||
value={projectDetails?.archive_in}
|
||||
customButton={
|
||||
<button className="flex w-full items-center justify-between gap-1 rounded-md border border-brand-base shadow-sm duration-300 text-brand-secondary hover:text-brand-base hover:bg-brand-surface-2 focus:outline-none px-3 py-2 text-sm text-left">
|
||||
{`${projectDetails?.archive_in} ${
|
||||
projectDetails?.archive_in === 1 ? "Month" : "Months"
|
||||
}`}
|
||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||
</button>
|
||||
}
|
||||
label={`${projectDetails?.archive_in} ${
|
||||
projectDetails?.archive_in === 1 ? "Month" : "Months"
|
||||
}`}
|
||||
onChange={(val: number) => {
|
||||
handleChange({ archive_in: val });
|
||||
}}
|
||||
|
@ -77,11 +77,11 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
||||
handleChange={handleChange}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col gap-7 px-6 py-5 rounded-[10px] border border-brand-base bg-brand-base">
|
||||
<div className="flex flex-col gap-7 px-6 py-5 rounded-[10px] border border-custom-border-100 bg-custom-background-90">
|
||||
<div className="flex items-center justify-between gap-x-8 gap-y-2 ">
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<h4 className="text-lg font-semibold">Auto-close inactive issues</h4>
|
||||
<p className="text-sm text-brand-secondary">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
Plane will automatically close the issues that have not been updated for the
|
||||
configured time period.
|
||||
</p>
|
||||
@ -102,17 +102,12 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
||||
<div className="w-1/2 text-base font-medium">
|
||||
Auto-close issues that are inactive for
|
||||
</div>
|
||||
<div className="w-1/2 ">
|
||||
<div className="w-1/2">
|
||||
<CustomSelect
|
||||
value={projectDetails?.close_in}
|
||||
customButton={
|
||||
<button className="flex w-full items-center justify-between gap-1 rounded-md border border-brand-base shadow-sm duration-300 text-brand-secondary hover:text-brand-base hover:bg-brand-surface-2 focus:outline-none px-3 py-2 text-sm text-left">
|
||||
{`${projectDetails?.close_in} ${
|
||||
projectDetails?.close_in === 1 ? "Month" : "Months"
|
||||
}`}
|
||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||
</button>
|
||||
}
|
||||
label={`${projectDetails?.close_in} ${
|
||||
projectDetails?.close_in === 1 ? "Month" : "Months"
|
||||
}`}
|
||||
onChange={(val: number) => {
|
||||
handleChange({ close_in: val });
|
||||
}}
|
||||
@ -143,42 +138,34 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
||||
value={
|
||||
projectDetails?.default_state ? projectDetails?.default_state : defaultState
|
||||
}
|
||||
customButton={
|
||||
<button
|
||||
className={`flex w-full items-center justify-between gap-1 rounded-md border border-brand-base shadow-sm duration-300 text-brand-secondary hover:text-brand-base hover:bg-brand-surface-2 focus:outline-none px-3 py-2 text-sm text-left ${
|
||||
!multipleOptions ? "opacity-60" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{selectedOption ? (
|
||||
getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color)
|
||||
) : currentDefaultState ? (
|
||||
getStateGroupIcon(
|
||||
currentDefaultState.group,
|
||||
"16",
|
||||
"16",
|
||||
currentDefaultState.color
|
||||
)
|
||||
) : (
|
||||
<Squares2X2Icon className="h-3.5 w-3.5 text-custom-text-200" />
|
||||
)}
|
||||
{selectedOption?.name
|
||||
? selectedOption.name
|
||||
: currentDefaultState?.name ?? (
|
||||
<span className="text-custom-text-200">State</span>
|
||||
)}
|
||||
</div>
|
||||
{multipleOptions && (
|
||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||
label={
|
||||
<div className="flex items-center gap-2">
|
||||
{selectedOption ? (
|
||||
getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color)
|
||||
) : currentDefaultState ? (
|
||||
getStateGroupIcon(
|
||||
currentDefaultState.group,
|
||||
"16",
|
||||
"16",
|
||||
currentDefaultState.color
|
||||
)
|
||||
) : (
|
||||
<Squares2X2Icon className="h-3.5 w-3.5 text-custom-text-200" />
|
||||
)}
|
||||
</button>
|
||||
{selectedOption?.name
|
||||
? selectedOption.name
|
||||
: currentDefaultState?.name ?? (
|
||||
<span className="text-custom-text-200">State</span>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
onChange={(val: string) => {
|
||||
handleChange({ default_state: val });
|
||||
}}
|
||||
options={options}
|
||||
disabled={!multipleOptions}
|
||||
dropdownWidth="w-full"
|
||||
width="w-full"
|
||||
input
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -56,7 +56,7 @@ export const SpreadsheetColumns: React.FC<Props> = ({ columnData, gridTemplateCo
|
||||
className="!w-full"
|
||||
customButton={
|
||||
<div
|
||||
className={`relative group flex items-center justify-start gap-1.5 cursor-pointer text-sm text-custom-text-200 text-current hover:text-custom-text-100 w-full py-3 px-2 ${
|
||||
className={`relative group flex items-center justify-start gap-1.5 cursor-pointer text-sm text-custom-text-200 hover:text-custom-text-100 w-full py-3 px-2 ${
|
||||
activeSortingProperty === col.propertyName ? "bg-custom-background-80" : ""
|
||||
}`}
|
||||
>
|
||||
@ -90,16 +90,9 @@ export const SpreadsheetColumns: React.FC<Props> = ({ columnData, gridTemplateCo
|
||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||
</div>
|
||||
}
|
||||
menuItemsWhiteBg
|
||||
width="xl"
|
||||
>
|
||||
<CustomMenu.MenuItem
|
||||
className={`${
|
||||
selectedMenuItem === `${col.ascendingOrder}_${col.propertyName}`
|
||||
? "bg-custom-background-80"
|
||||
: ""
|
||||
}`}
|
||||
key={col.propertyName}
|
||||
onClick={() => {
|
||||
handleOrderBy(col.ascendingOrder, col.propertyName);
|
||||
}}
|
||||
|
@ -112,7 +112,7 @@ export const SpreadsheetView: React.FC<Props> = ({
|
||||
</button>
|
||||
}
|
||||
position="left"
|
||||
menuItemsClassName="left-5 !w-36"
|
||||
optionsClassName="left-5 !w-36"
|
||||
noBorder
|
||||
>
|
||||
<CustomMenu.MenuItem
|
||||
|
@ -130,7 +130,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
|
||||
position={position}
|
||||
disabled={isNotAllowed}
|
||||
selfPositioned={selfPositioned}
|
||||
dropdownWidth="w-full min-w-[12rem]"
|
||||
width="w-full min-w-[12rem]"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -127,8 +127,6 @@ export const ViewLabelSelect: React.FC<Props> = ({
|
||||
</button>
|
||||
);
|
||||
|
||||
const noResultIcon = <TagIcon className="h-3.5 w-3.5 text-custom-text-200" />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{projectId && (
|
||||
@ -152,8 +150,7 @@ export const ViewLabelSelect: React.FC<Props> = ({
|
||||
disabled={isNotAllowed}
|
||||
selfPositioned={selfPositioned}
|
||||
footerOption={footerOption}
|
||||
noResultIcon={noResultIcon}
|
||||
dropdownWidth="w-full min-w-[12rem]"
|
||||
width="w-full min-w-[12rem]"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -66,7 +66,7 @@ export const ModuleMembersSelect: React.FC<Props> = ({ value, onChange }) => {
|
||||
}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
height="md"
|
||||
maxHeight="md"
|
||||
multiple
|
||||
noChevron
|
||||
/>
|
||||
|
@ -75,7 +75,7 @@ export const SidebarLeadSelect: React.FC<Props> = ({ value, onChange }) => {
|
||||
</div>
|
||||
}
|
||||
options={options}
|
||||
height="md"
|
||||
maxHeight="md"
|
||||
position="right"
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
@ -70,7 +70,7 @@ export const SidebarMembersSelect: React.FC<Props> = ({ value, onChange }) => {
|
||||
}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
height="md"
|
||||
maxHeight="md"
|
||||
position="right"
|
||||
multiple
|
||||
/>
|
||||
|
@ -223,7 +223,7 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
|
||||
}}
|
||||
options={options}
|
||||
position="left"
|
||||
dropdownWidth="w-full min-w-[12rem]"
|
||||
width="w-full min-w-[12rem]"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -4,7 +4,7 @@ import Link from "next/link";
|
||||
// headless ui
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { ChevronDownIcon, EllipsisHorizontalIcon } from "@heroicons/react/24/outline";
|
||||
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
type Props = {
|
||||
|
153
apps/app/components/ui/dropdowns/custom-menu.tsx
Normal file
153
apps/app/components/ui/dropdowns/custom-menu.tsx
Normal file
@ -0,0 +1,153 @@
|
||||
import React from "react";
|
||||
|
||||
import Link from "next/link";
|
||||
|
||||
// headless ui
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { DropdownProps, Icon } from "components/ui";
|
||||
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
export type CustomMenuProps = DropdownProps & {
|
||||
children: React.ReactNode;
|
||||
ellipsis?: boolean;
|
||||
noBorder?: boolean;
|
||||
verticalEllipsis?: boolean;
|
||||
};
|
||||
|
||||
const CustomMenu = ({
|
||||
buttonClassName = "",
|
||||
children,
|
||||
className = "",
|
||||
customButton,
|
||||
disabled = false,
|
||||
ellipsis = false,
|
||||
label,
|
||||
maxHeight = "md",
|
||||
noBorder = false,
|
||||
noChevron = false,
|
||||
optionsClassName = "",
|
||||
position = "right",
|
||||
selfPositioned = false,
|
||||
verticalEllipsis = false,
|
||||
verticalPosition = "bottom",
|
||||
width = "auto",
|
||||
}: CustomMenuProps) => (
|
||||
<Menu as="div" className={`${selfPositioned ? "" : "relative"} w-min text-left ${className}`}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
{customButton ? (
|
||||
<Menu.Button as="div">{customButton}</Menu.Button>
|
||||
) : (
|
||||
<>
|
||||
{ellipsis || verticalEllipsis ? (
|
||||
<Menu.Button
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
className={`relative grid place-items-center rounded p-1 text-custom-text-200 outline-none ${
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-80"
|
||||
} ${buttonClassName}`}
|
||||
>
|
||||
<Icon
|
||||
iconName="more_horiz"
|
||||
className={`${verticalEllipsis ? "rotate-90" : ""} text-custom-text-200`}
|
||||
/>
|
||||
</Menu.Button>
|
||||
) : (
|
||||
<Menu.Button
|
||||
type="button"
|
||||
className={`flex items-center justify-between gap-1 rounded-md px-2.5 py-1 text-xs duration-300 ${
|
||||
open ? "bg-custom-background-90 text-custom-text-100" : "text-custom-text-200"
|
||||
} ${
|
||||
noBorder ? "" : "border border-custom-border-100 shadow-sm focus:outline-none"
|
||||
} ${
|
||||
disabled
|
||||
? "cursor-not-allowed text-custom-text-200"
|
||||
: "cursor-pointer hover:bg-custom-background-80"
|
||||
} ${buttonClassName}`}
|
||||
>
|
||||
{label}
|
||||
{!noChevron && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
|
||||
</Menu.Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
className={`absolute z-10 overflow-y-scroll whitespace-nowrap rounded-md border border-custom-border-100 p-1 text-xs shadow-lg focus:outline-none bg-custom-background-90 ${
|
||||
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
||||
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
|
||||
maxHeight === "lg"
|
||||
? "max-h-60"
|
||||
: maxHeight === "md"
|
||||
? "max-h-48"
|
||||
: maxHeight === "rg"
|
||||
? "max-h-36"
|
||||
: maxHeight === "sm"
|
||||
? "max-h-28"
|
||||
: ""
|
||||
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
||||
>
|
||||
<div className="py-1">{children}</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
type MenuItemProps = {
|
||||
children: JSX.Element | string;
|
||||
renderAs?: "button" | "a";
|
||||
href?: string;
|
||||
onClick?: (args?: any) => void;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const MenuItem: React.FC<MenuItemProps> = ({
|
||||
children,
|
||||
renderAs,
|
||||
href,
|
||||
onClick,
|
||||
className = "",
|
||||
}) => (
|
||||
<Menu.Item as="div">
|
||||
{({ active, close }) =>
|
||||
renderAs === "a" ? (
|
||||
<Link href={href ?? ""}>
|
||||
<a
|
||||
className={`inline-block w-full select-none truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80 ${
|
||||
active ? "bg-custom-background-80" : ""
|
||||
} ${className}`}
|
||||
onClick={close}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={`w-full select-none truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80 ${
|
||||
active ? "bg-custom-background-80" : ""
|
||||
} ${className}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
CustomMenu.MenuItem = MenuItem;
|
||||
|
||||
export { CustomMenu };
|
@ -5,8 +5,12 @@ import { Combobox, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
||||
import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { DropdownProps } from "./types";
|
||||
|
||||
type CustomSearchSelectProps = {
|
||||
export type CustomSearchSelectProps = DropdownProps & {
|
||||
footerOption?: JSX.Element;
|
||||
multiple?: boolean;
|
||||
value: any;
|
||||
onChange: any;
|
||||
options:
|
||||
@ -16,43 +20,27 @@ type CustomSearchSelectProps = {
|
||||
content: JSX.Element;
|
||||
}[]
|
||||
| undefined;
|
||||
label?: string | JSX.Element;
|
||||
textAlignment?: "left" | "center" | "right";
|
||||
height?: "sm" | "md" | "rg" | "lg";
|
||||
position?: "right" | "left";
|
||||
verticalPosition?: "top" | "bottom";
|
||||
noChevron?: boolean;
|
||||
customButton?: JSX.Element;
|
||||
className?: string;
|
||||
optionsClassName?: string;
|
||||
input?: boolean;
|
||||
disabled?: boolean;
|
||||
selfPositioned?: boolean;
|
||||
multiple?: boolean;
|
||||
footerOption?: JSX.Element;
|
||||
noResultIcon?: JSX.Element;
|
||||
dropdownWidth?: string;
|
||||
};
|
||||
|
||||
export const CustomSearchSelect = ({
|
||||
buttonClassName = "",
|
||||
className = "",
|
||||
customButton,
|
||||
disabled = false,
|
||||
footerOption,
|
||||
input = false,
|
||||
label,
|
||||
textAlignment,
|
||||
height = "md",
|
||||
value,
|
||||
maxHeight = "md",
|
||||
multiple = false,
|
||||
noChevron = false,
|
||||
onChange,
|
||||
options,
|
||||
position = "left",
|
||||
verticalPosition = "bottom",
|
||||
noChevron = false,
|
||||
customButton,
|
||||
className = "",
|
||||
optionsClassName = "",
|
||||
input = false,
|
||||
disabled = false,
|
||||
position = "left",
|
||||
selfPositioned = false,
|
||||
multiple = false,
|
||||
noResultIcon,
|
||||
footerOption,
|
||||
dropdownWidth,
|
||||
value,
|
||||
verticalPosition = "bottom",
|
||||
width = "auto",
|
||||
}: CustomSearchSelectProps) => {
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
@ -72,7 +60,7 @@ export const CustomSearchSelect = ({
|
||||
return (
|
||||
<Combobox
|
||||
as="div"
|
||||
className={`${!selfPositioned ? "relative" : ""} flex-shrink-0 text-left ${className}`}
|
||||
className={`${selfPositioned ? "" : "relative"} flex-shrink-0 text-left ${className}`}
|
||||
{...props}
|
||||
>
|
||||
{({ open }: any) => (
|
||||
@ -81,17 +69,13 @@ export const CustomSearchSelect = ({
|
||||
<Combobox.Button as="div">{customButton}</Combobox.Button>
|
||||
) : (
|
||||
<Combobox.Button
|
||||
className={`flex w-full border border-custom-border-200 ${
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-80"
|
||||
} ${
|
||||
className={`flex items-center justify-between gap-1 w-full rounded-md shadow-sm border border-custom-border-100 duration-300 focus:outline-none ${
|
||||
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
||||
} items-center justify-between gap-1 rounded-md shadow-sm duration-300 focus:outline-none focus:ring-1 focus:ring-custom-border-100 ${
|
||||
textAlignment === "right"
|
||||
? "text-right"
|
||||
: textAlignment === "center"
|
||||
? "text-center"
|
||||
: "text-left"
|
||||
}`}
|
||||
} ${
|
||||
disabled
|
||||
? "cursor-not-allowed text-custom-text-200"
|
||||
: "cursor-pointer hover:bg-custom-background-80"
|
||||
} ${buttonClassName}`}
|
||||
>
|
||||
{label}
|
||||
{!noChevron && !disabled && (
|
||||
@ -110,13 +94,11 @@ export const CustomSearchSelect = ({
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Combobox.Options
|
||||
className={`${optionsClassName} absolute min-w-[10rem] border border-custom-border-200 p-2 ${
|
||||
position === "right" ? "right-0" : "left-0"
|
||||
} ${
|
||||
verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"
|
||||
} z-10 origin-top-right rounded-md bg-custom-background-90 text-xs shadow-lg focus:outline-none ${
|
||||
dropdownWidth ? dropdownWidth : ``
|
||||
} `}
|
||||
className={`absolute z-10 min-w-[10rem] border border-custom-border-100 p-2 rounded-md bg-custom-background-90 text-xs shadow-lg focus:outline-none ${
|
||||
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
||||
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
|
||||
width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width
|
||||
} ${optionsClassName}`}
|
||||
>
|
||||
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] border-custom-border-200 bg-custom-background-90 px-2">
|
||||
<MagnifyingGlassIcon className="h-3 w-3 text-custom-text-200" />
|
||||
@ -129,14 +111,14 @@ export const CustomSearchSelect = ({
|
||||
</div>
|
||||
<div
|
||||
className={`mt-2 space-y-1 ${
|
||||
height === "sm"
|
||||
maxHeight === "lg"
|
||||
? "max-h-60"
|
||||
: maxHeight === "md"
|
||||
? "max-h-48"
|
||||
: maxHeight === "rg"
|
||||
? "max-h-36"
|
||||
: maxHeight === "sm"
|
||||
? "max-h-28"
|
||||
: height === "md"
|
||||
? "max-h-44"
|
||||
: height === "rg"
|
||||
? "max-h-56"
|
||||
: height === "lg"
|
||||
? "max-h-80"
|
||||
: ""
|
||||
} overflow-y-scroll`}
|
||||
>
|
||||
@ -147,9 +129,9 @@ export const CustomSearchSelect = ({
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
className={({ active, selected }) =>
|
||||
`${active || selected ? "bg-custom-background-80" : ""} ${
|
||||
selected ? "text-custom-text-100" : "text-custom-text-200"
|
||||
} flex cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5`
|
||||
`flex items-center justify-between gap-2 cursor-pointer select-none truncate rounded px-1 py-1.5 ${
|
||||
active || selected ? "bg-custom-background-80" : ""
|
||||
} ${selected ? "text-custom-text-100" : "text-custom-text-200"}`
|
||||
}
|
||||
>
|
||||
{({ active, selected }) => (
|
||||
@ -176,7 +158,6 @@ export const CustomSearchSelect = ({
|
||||
))
|
||||
) : (
|
||||
<span className="flex items-center gap-2 p-1">
|
||||
{noResultIcon && noResultIcon}
|
||||
<p className="text-left text-custom-text-200 ">No matching results</p>
|
||||
</span>
|
||||
)
|
@ -5,73 +5,58 @@ import { Listbox, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
||||
import { CheckIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { DropdownProps } from "./types";
|
||||
|
||||
type CustomSelectProps = {
|
||||
export type CustomSelectProps = DropdownProps & {
|
||||
children: React.ReactNode;
|
||||
value: any;
|
||||
onChange: any;
|
||||
children: React.ReactNode;
|
||||
label?: string | JSX.Element;
|
||||
textAlignment?: "left" | "center" | "right";
|
||||
maxHeight?: "sm" | "rg" | "md" | "lg" | "none";
|
||||
position?: "right" | "left";
|
||||
verticalPosition?: "top" | "bottom";
|
||||
width?: "auto" | string;
|
||||
input?: boolean;
|
||||
noChevron?: boolean;
|
||||
customButton?: JSX.Element;
|
||||
optionsClassName?: string;
|
||||
disabled?: boolean;
|
||||
selfPositioned?: boolean;
|
||||
};
|
||||
|
||||
const CustomSelect = ({
|
||||
buttonClassName = "",
|
||||
children,
|
||||
className = "",
|
||||
customButton,
|
||||
disabled = false,
|
||||
input = false,
|
||||
label,
|
||||
textAlignment,
|
||||
value,
|
||||
maxHeight = "md",
|
||||
noChevron = false,
|
||||
onChange,
|
||||
maxHeight = "none",
|
||||
optionsClassName = "",
|
||||
position = "left",
|
||||
selfPositioned = false,
|
||||
value,
|
||||
verticalPosition = "bottom",
|
||||
width = "auto",
|
||||
input = false,
|
||||
noChevron = false,
|
||||
customButton,
|
||||
optionsClassName = "",
|
||||
disabled = false,
|
||||
selfPositioned = false,
|
||||
}: CustomSelectProps) => (
|
||||
<Listbox
|
||||
as="div"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
className={`${!selfPositioned ? "relative" : ""} flex-shrink-0 text-left`}
|
||||
className={`${selfPositioned ? "" : "relative"} flex-shrink-0 text-left ${className}`}
|
||||
disabled={disabled}
|
||||
>
|
||||
<div>
|
||||
<>
|
||||
{customButton ? (
|
||||
<Listbox.Button as="div">{customButton}</Listbox.Button>
|
||||
) : (
|
||||
<Listbox.Button
|
||||
className={`flex w-full ${
|
||||
className={`flex items-center justify-between gap-1 w-full rounded-md border border-custom-border-100 shadow-sm duration-300 focus:outline-none ${
|
||||
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
||||
} ${
|
||||
disabled
|
||||
? "cursor-not-allowed text-custom-text-200"
|
||||
: "cursor-pointer hover:bg-custom-background-80"
|
||||
} items-center justify-between gap-1 rounded-md border border-custom-border-200 shadow-sm duration-300 focus:outline-none ${
|
||||
input ? "border-custom-border-200 px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
||||
} ${
|
||||
textAlignment === "right"
|
||||
? "text-right"
|
||||
: textAlignment === "center"
|
||||
? "text-center"
|
||||
: "text-left"
|
||||
}`}
|
||||
} ${buttonClassName}`}
|
||||
>
|
||||
{label}
|
||||
{!noChevron && !disabled && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
|
||||
</Listbox.Button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
@ -83,13 +68,9 @@ const CustomSelect = ({
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Listbox.Options
|
||||
className={`${optionsClassName} absolute border border-custom-border-200 ${
|
||||
position === "right" ? "right-0" : "left-0"
|
||||
} ${
|
||||
verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"
|
||||
} z-10 mt-1 origin-top-right overflow-y-auto rounded-md bg-custom-background-90 text-xs shadow-lg focus:outline-none ${
|
||||
width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width
|
||||
} ${input ? "max-h-48" : ""} ${
|
||||
className={`absolute z-10 border border-custom-border-100 mt-1 origin-top-right overflow-y-auto rounded-md bg-custom-background-90 text-xs shadow-lg focus:outline-none ${
|
||||
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
||||
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
|
||||
maxHeight === "lg"
|
||||
? "max-h-60"
|
||||
: maxHeight === "md"
|
||||
@ -99,7 +80,7 @@ const CustomSelect = ({
|
||||
: maxHeight === "sm"
|
||||
? "max-h-28"
|
||||
: ""
|
||||
}`}
|
||||
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
||||
>
|
||||
<div className="space-y-1 p-2">{children}</div>
|
||||
</Listbox.Options>
|
||||
@ -117,9 +98,9 @@ const Option: React.FC<OptionProps> = ({ children, value, className }) => (
|
||||
<Listbox.Option
|
||||
value={value}
|
||||
className={({ active, selected }) =>
|
||||
`${className} ${active || selected ? "bg-custom-background-80" : ""} ${
|
||||
selected ? "text-custom-text-100" : "text-custom-text-200"
|
||||
} cursor-pointer select-none truncate rounded px-1 py-1.5`
|
||||
`cursor-pointer select-none truncate rounded px-1 py-1.5 ${
|
||||
active || selected ? "bg-custom-background-80" : ""
|
||||
} ${selected ? "text-custom-text-100" : "text-custom-text-200"} ${className}`
|
||||
}
|
||||
>
|
||||
{({ selected }) => (
|
5
apps/app/components/ui/dropdowns/index.ts
Normal file
5
apps/app/components/ui/dropdowns/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from "./context-menu";
|
||||
export * from "./custom-menu";
|
||||
export * from "./custom-search-select";
|
||||
export * from "./custom-select";
|
||||
export * from "./types.d";
|
15
apps/app/components/ui/dropdowns/types.d.ts
vendored
Normal file
15
apps/app/components/ui/dropdowns/types.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
export type DropdownProps = {
|
||||
buttonClassName?: string;
|
||||
className?: string;
|
||||
customButton?: JSX.Element;
|
||||
disabled?: boolean;
|
||||
input?: boolean;
|
||||
label?: string | JSX.Element;
|
||||
maxHeight?: "sm" | "rg" | "md" | "lg";
|
||||
noChevron?: boolean;
|
||||
optionsClassName?: string;
|
||||
position?: "right" | "left";
|
||||
selfPositioned?: boolean;
|
||||
verticalPosition?: "top" | "bottom";
|
||||
width?: "auto" | string;
|
||||
};
|
@ -1,16 +1,14 @@
|
||||
export * from "./buttons";
|
||||
export * from "./dropdowns";
|
||||
export * from "./graphs";
|
||||
export * from "./input";
|
||||
export * from "./text-area";
|
||||
export * from "./avatar";
|
||||
export * from "./context-menu";
|
||||
export * from "./custom-menu";
|
||||
export * from "./custom-search-select";
|
||||
export * from "./custom-select";
|
||||
export * from "./date";
|
||||
export * from "./datepicker";
|
||||
export * from "./empty-space";
|
||||
export * from "./empty-state";
|
||||
export * from "./icon";
|
||||
export * from "./labels-list";
|
||||
export * from "./linear-progress-indicator";
|
||||
export * from "./loader";
|
||||
@ -24,5 +22,4 @@ export * from "./markdown-to-component";
|
||||
export * from "./product-updates-modal";
|
||||
export * from "./integration-and-import-export-banner";
|
||||
export * from "./range-datepicker";
|
||||
export * from "./icon";
|
||||
export * from "./circular-progress";
|
||||
|
Loading…
Reference in New Issue
Block a user