-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: swap dropdown component with plane/ui component (#2480)
* 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
1 parent
0b8367a
commit c0793ec
Showing
91 changed files
with
681 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
Oops, something went wrong.
c0793ec
There was a problem hiding this comment.
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