Skip to content

Commit

Permalink
chore: swap dropdown component with plane/ui component (#2480)
Browse files Browse the repository at this point in the history
* chore: swap custom menu component with plane/ui component

* chore: swap custom select component with plane/ui component

* chore: swap custom search select component with plane/ui component
  • Loading branch information
anmolsinghbhatia authored Oct 19, 2023
1 parent 0b8367a commit c0793ec
Show file tree
Hide file tree
Showing 91 changed files with 681 additions and 194 deletions.
152 changes: 152 additions & 0 deletions packages/ui/src/dropdowns/custom-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import * as React from "react";

// react-poppper
import { usePopper } from "react-popper";
// headless ui
import { Menu } from "@headlessui/react";
// type
import { ICustomMenuDropdownProps, ICustomMenuItemProps } from "./helper";
// icons
import { ChevronDown, MoreHorizontal } from "lucide-react";

const CustomMenu = (props: ICustomMenuDropdownProps) => {
const {
buttonClassName = "",
customButtonClassName = "",
placement,
children,
className = "",
customButton,
disabled = false,
ellipsis = false,
label,
maxHeight = "md",
noBorder = false,
noChevron = false,
optionsClassName = "",
verticalEllipsis = false,
width = "auto",
menuButtonOnClick,
} = props;

const [referenceElement, setReferenceElement] =
React.useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] =
React.useState<HTMLDivElement | null>(null);

const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: placement ?? "bottom-start",
});
return (
<Menu as="div" className={`relative w-min text-left ${className}`}>
{({ open }) => (
<>
{customButton ? (
<Menu.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
onClick={menuButtonOnClick}
className={customButtonClassName}
>
{customButton}
</button>
</Menu.Button>
) : (
<>
{ellipsis || verticalEllipsis ? (
<Menu.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
onClick={menuButtonOnClick}
disabled={disabled}
className={`relative grid place-items-center rounded p-1 text-custom-text-200 hover:text-custom-text-100 outline-none ${
disabled
? "cursor-not-allowed"
: "cursor-pointer hover:bg-custom-background-80"
} ${buttonClassName}`}
>
<MoreHorizontal
className={`h-3.5 w-3.5 ${
verticalEllipsis ? "rotate-90" : ""
}`}
/>
</button>
</Menu.Button>
) : (
<Menu.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
className={`flex items-center justify-between gap-1 rounded-md px-2.5 py-1 text-xs whitespace-nowrap duration-300 ${
open
? "bg-custom-background-90 text-custom-text-100"
: "text-custom-text-200"
} ${
noBorder
? ""
: "border border-custom-border-300 shadow-sm focus:outline-none"
} ${
disabled
? "cursor-not-allowed text-custom-text-200"
: "cursor-pointer hover:bg-custom-background-80"
} ${buttonClassName}`}
>
{label}
{!noChevron && <ChevronDown className="h-3.5 w-3.5" />}
</button>
</Menu.Button>
)}
</>
)}
<Menu.Items>
<div
className={`z-10 overflow-y-scroll whitespace-nowrap rounded-md border border-custom-border-300 p-1 text-xs shadow-custom-shadow-rg focus:outline-none bg-custom-background-90 my-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}`}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
{children}
</div>
</Menu.Items>
</>
)}
</Menu>
);
};

const MenuItem: React.FC<ICustomMenuItemProps> = (props) => {
const { children, onClick, className = "" } = props;
return (
<Menu.Item as="div">
{({ active, close }) => (
<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 };
204 changes: 204 additions & 0 deletions packages/ui/src/dropdowns/custom-search-select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import React, { useState } from "react";

// react-popper
import { usePopper } from "react-popper";
// headless ui
import { Combobox } from "@headlessui/react";
// icons
import { Check, ChevronDown, Search } from "lucide-react";
// types
import { ICustomSearchSelectProps } from "./helper";

export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
const {
customButtonClassName = "",
buttonClassName = "",
className = "",
customButton,
placement,
disabled = false,
footerOption,
input = false,
label,
maxHeight = "md",
multiple = false,
noChevron = false,
onChange,
options,
onOpen,
optionsClassName = "",
value,
width = "auto",
} = props;
const [query, setQuery] = useState("");

const [referenceElement, setReferenceElement] =
useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
null
);

const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: placement ?? "bottom-start",
});

const filteredOptions =
query === ""
? options
: options?.filter((option) =>
option.query.toLowerCase().includes(query.toLowerCase())
);

const comboboxProps: any = {
value,
onChange,
disabled,
};

if (multiple) comboboxProps.multiple = true;

return (
<Combobox
as="div"
className={`relative flex-shrink-0 text-left ${className}`}
{...comboboxProps}
>
{({ open }: { open: boolean }) => {
if (open && onOpen) onOpen();

return (
<>
{customButton ? (
<Combobox.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
className={`flex items-center justify-between gap-1 w-full text-xs ${
disabled
? "cursor-not-allowed text-custom-text-200"
: "cursor-pointer hover:bg-custom-background-80"
} ${customButtonClassName}`}
>
{customButton}
</button>
</Combobox.Button>
) : (
<Combobox.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
className={`flex items-center justify-between gap-1 w-full rounded-md border border-custom-border-300 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"
} ${buttonClassName}`}
>
{label}
{!noChevron && !disabled && (
<ChevronDown className="h-3 w-3" aria-hidden="true" />
)}
</button>
</Combobox.Button>
)}
<Combobox.Options as={React.Fragment}>
<div
className={`z-10 min-w-[10rem] border border-custom-border-300 p-2 rounded-md bg-custom-background-90 text-xs shadow-custom-shadow-rg focus:outline-none my-1 ${
width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width
} ${optionsClassName}`}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] border-custom-border-200 bg-custom-background-90 px-2">
<Search className="h-3 w-3 text-custom-text-200" />
<Combobox.Input
className="w-full bg-transparent py-1 px-2 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Type to search..."
displayValue={(assigned: any) => assigned?.name}
/>
</div>
<div
className={`mt-2 space-y-1 ${
maxHeight === "lg"
? "max-h-60"
: maxHeight === "md"
? "max-h-48"
: maxHeight === "rg"
? "max-h-36"
: maxHeight === "sm"
? "max-h-28"
: ""
} overflow-y-scroll`}
>
{filteredOptions ? (
filteredOptions.length > 0 ? (
filteredOptions.map((option) => (
<Combobox.Option
key={option.value}
value={option.value}
className={({ active, selected }) =>
`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 }) => (
<>
{option.content}
{multiple ? (
<div
className={`flex items-center justify-center rounded border border-custom-border-400 p-0.5 ${
active || selected
? "opacity-100"
: "opacity-0"
}`}
>
<Check
className={`h-3 w-3 ${
selected ? "opacity-100" : "opacity-0"
}`}
/>
</div>
) : (
<Check
className={`h-3 w-3 ${
selected ? "opacity-100" : "opacity-0"
}`}
/>
)}
</>
)}
</Combobox.Option>
))
) : (
<span className="flex items-center gap-2 p-1">
<p className="text-left text-custom-text-200 ">
No matching results
</p>
</span>
)
) : (
<p className="text-center text-custom-text-200">
Loading...
</p>
)}
</div>
{footerOption}
</div>
</Combobox.Options>
</>
);
}}
</Combobox>
);
};
Loading

1 comment on commit c0793ec

@vercel
Copy link

@vercel vercel bot commented on c0793ec Oct 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

plane-dev – ./web/

crew42.plane.so
plane-dev-git-develop-plane.vercel.app
plane-dev.vercel.app
plane-dev-plane.vercel.app

Please sign in to comment.