chore: integrate popper js (#2398)

* chore: react-popper-js added

* chore: integrate popper js in issue properties dropdown

* chore: integrate popper js in custom menu component

* chore: integrate popper js in custom select component

* chore: integrate popper js in custom search select component

* chore: popper js placement type added

* chore: popper js placement type added
This commit is contained in:
Anmol Singh Bhatia 2023-10-11 12:05:53 +05:30 committed by GitHub
parent d88eb09fad
commit 58ea4d6ec9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 449 additions and 452 deletions

View file

@ -1,9 +1,11 @@
import React from "react";
import React, { useState } from "react";
import Link from "next/link";
// react-poppper
import { usePopper } from "react-popper";
// headless ui
import { Menu, Transition } from "@headlessui/react";
import { Menu } from "@headlessui/react";
// ui
import { DropdownProps } from "components/ui";
// icons
@ -20,6 +22,7 @@ export type CustomMenuProps = DropdownProps & {
const CustomMenu = ({
buttonClassName = "",
customButtonClassName = "",
placement,
children,
className = "",
customButton,
@ -30,100 +33,106 @@ const CustomMenu = ({
noBorder = false,
noChevron = false,
optionsClassName = "",
position = "right",
selfPositioned = false,
verticalEllipsis = false,
verticalPosition = "bottom",
width = "auto",
menuButtonOnClick,
}: CustomMenuProps) => (
<Menu as="div" className={`${selfPositioned ? "" : "relative"} w-min text-left ${className}`}>
{({ open }) => (
<>
{customButton ? (
<Menu.Button
as="button"
type="button"
onClick={menuButtonOnClick}
className={customButtonClassName}
disabled={disabled}
>
{customButton}
</Menu.Button>
) : (
<>
{ellipsis || verticalEllipsis ? (
<Menu.Button
}: CustomMenuProps) => {
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = 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}
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}`}
className={customButtonClassName}
>
<MoreHorizOutlined
fontSize="small"
className={verticalEllipsis ? "rotate-90" : ""}
/>
</Menu.Button>
) : (
<Menu.Button
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 && (
<ExpandMoreOutlined
sx={{
fontSize: 14,
}}
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-300 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>
{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}`}
>
<MoreHorizOutlined
fontSize="small"
className={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 && (
<ExpandMoreOutlined
sx={{
fontSize: 14,
}}
aria-hidden="true"
/>
)}
</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}
>
<div className="py-1">{children}</div>
</div>
</Menu.Items>
</Transition>
</>
)}
</Menu>
);
</>
)}
</Menu>
);
};
type MenuItemProps = {
children: React.ReactNode;

View file

@ -1,7 +1,9 @@
import React, { useState } from "react";
// react-poppper
import { usePopper } from "react-popper";
// headless ui
import { Combobox, Transition } from "@headlessui/react";
import { Combobox } from "@headlessui/react";
// icons
import { ChevronDownIcon } from "@heroicons/react/20/solid";
import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
@ -27,9 +29,11 @@ export type CustomSearchSelectProps = DropdownProps & {
);
export const CustomSearchSelect = ({
customButtonClassName = "",
buttonClassName = "",
className = "",
customButton,
placement,
disabled = false,
footerOption,
input = false,
@ -41,14 +45,18 @@ export const CustomSearchSelect = ({
options,
onOpen,
optionsClassName = "",
position = "left",
selfPositioned = false,
value,
verticalPosition = "bottom",
width = "auto",
}: CustomSearchSelectProps) => {
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
@ -63,51 +71,54 @@ export const CustomSearchSelect = ({
if (multiple) props.multiple = true;
return (
<Combobox
as="div"
className={`${selfPositioned ? "" : "relative"} flex-shrink-0 text-left ${className}`}
{...props}
>
<Combobox as="div" className={`relative flex-shrink-0 text-left ${className}`} {...props}>
{({ open }: { open: boolean }) => {
if (open && onOpen) onOpen();
return (
<>
{customButton ? (
<Combobox.Button as="div">{customButton}</Combobox.Button>
<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
type="button"
className={`flex items-center justify-between gap-1 w-full rounded-md shadow-sm border border-custom-border-300 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 && (
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
)}
<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 && (
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
)}
</button>
</Combobox.Button>
)}
<Transition
show={open}
as={React.Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Combobox.Options
className={`absolute z-10 min-w-[10rem] border border-custom-border-300 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"} ${
<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">
<MagnifyingGlassIcon className="h-3 w-3 text-custom-text-200" />
@ -176,8 +187,8 @@ export const CustomSearchSelect = ({
)}
</div>
{footerOption}
</Combobox.Options>
</Transition>
</div>
</Combobox.Options>
</>
);
}}

View file

@ -1,7 +1,9 @@
import React from "react";
import React, { useState } from "react";
// react-popper
import { usePopper } from "react-popper";
// headless ui
import { Listbox, Transition } from "@headlessui/react";
import { Listbox } from "@headlessui/react";
// icons
import { ChevronDownIcon } from "@heroicons/react/20/solid";
import { CheckIcon } from "@heroicons/react/24/outline";
@ -15,7 +17,9 @@ export type CustomSelectProps = DropdownProps & {
};
const CustomSelect = ({
customButtonClassName = "",
buttonClassName = "",
placement,
children,
className = "",
customButton,
@ -26,68 +30,83 @@ const CustomSelect = ({
noChevron = false,
onChange,
optionsClassName = "",
position = "left",
selfPositioned = false,
value,
verticalPosition = "bottom",
width = "auto",
}: CustomSelectProps) => (
<Listbox
as="div"
value={value}
onChange={onChange}
className={`${selfPositioned ? "" : "relative"} flex-shrink-0 text-left ${className}`}
disabled={disabled}
>
<>
{customButton ? (
<Listbox.Button as={React.Fragment}>{customButton}</Listbox.Button>
) : (
<Listbox.Button
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 && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
</Listbox.Button>
)}
</>
}: CustomSelectProps) => {
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
<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"
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: placement ?? "bottom-start",
});
return (
<Listbox
as="div"
value={value}
onChange={onChange}
className={`relative flex-shrink-0 text-left ${className}`}
disabled={disabled}
>
<Listbox.Options
className={`absolute z-10 border border-custom-border-300 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"
? "max-h-48"
: maxHeight === "rg"
? "max-h-36"
: maxHeight === "sm"
? "max-h-28"
: ""
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
>
<div className="space-y-1 p-2">{children}</div>
<>
{customButton ? (
<Listbox.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>
</Listbox.Button>
) : (
<Listbox.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 && (
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
)}
</button>
</Listbox.Button>
)}
</>
<Listbox.Options>
<div
className={`z-10 border border-custom-border-300 overflow-y-auto rounded-md bg-custom-background-90 text-xs shadow-custom-shadow-rg focus:outline-none 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}
>
<div className="space-y-1 p-2">{children}</div>
</div>
</Listbox.Options>
</Transition>
</Listbox>
);
</Listbox>
);
};
type OptionProps = {
children: React.ReactNode;

View file

@ -1,4 +1,7 @@
import { Placement } from "@popperjs/core";
export type DropdownProps = {
customButtonClassName?: string;
buttonClassName?: string;
customButtonClassName?: string;
className?: string;
@ -10,8 +13,6 @@ export type DropdownProps = {
noChevron?: boolean;
onOpen?: () => void;
optionsClassName?: string;
position?: "right" | "left";
selfPositioned?: boolean;
verticalPosition?: "top" | "bottom";
width?: "auto" | string;
placement?: Placement;
};