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