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

@ -74,7 +74,6 @@ export const AutoArchiveAutomation: React.FC<Props> = ({
handleChange({ archive_in: val });
}}
input
verticalPosition="bottom"
width="w-full"
disabled={disabled}
>

View File

@ -100,7 +100,6 @@ export const ThemeSwitch: React.FC<Props> = observer(
}}
input
width="w-full"
position="right"
>
{THEMES_OBJ.map(({ value, label, type, icon }) => (
<CustomSelect.Option key={value} value={{ value, type }}>

View File

@ -255,15 +255,11 @@ export const SingleBoard: React.FC<Props> = (props) => {
!isDraftIssuesPage && (
<CustomMenu
customButton={
<button
type="button"
className="flex items-center gap-2 font-medium text-custom-primary outline-none whitespace-nowrap"
>
<div className="flex items-center gap-2 font-medium text-custom-primary outline-none whitespace-nowrap">
<PlusIcon className="h-4 w-4" />
Add Issue
</button>
</div>
}
position="left"
noBorder
>
<CustomMenu.MenuItem

View File

@ -369,12 +369,12 @@ export const SingleBoardIssue: React.FC<Props> = ({
{type && !isNotAllowed && (
<CustomMenu
customButton={
<button
<div
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded p-1 text-left text-xs duration-300 hover:bg-custom-background-80"
onClick={() => setIsMenuActive(!isMenuActive)}
>
<EllipsisHorizontalIcon className="h-4 w-4" />
</button>
</div>
}
>
<CustomMenu.MenuItem

View File

@ -272,7 +272,6 @@ export const SingleList: React.FC<Props> = (props) => {
<PlusIcon className="h-4 w-4" />
</div>
}
position="right"
noBorder
>
<CustomMenu.MenuItem onClick={() => setIsCreateIssueFormOpen(true)}>

View File

@ -255,7 +255,6 @@ export const SpreadsheetView: React.FC<Props> = ({
<CustomMenu
customButtonClassName="!w-full"
className="!w-full"
position="left"
customButton={
<div
className={`relative group flex items-center justify-between gap-1.5 cursor-pointer text-sm text-custom-text-200 hover:text-custom-text-100 w-full py-3 px-2 ${
@ -641,16 +640,11 @@ export const SpreadsheetView: React.FC<Props> = ({
<CustomMenu
className="sticky left-0 z-10"
customButton={
<button
className="flex gap-1.5 items-center text-custom-primary-100 pl-4 py-2.5 text-sm sticky left-0 z-[1] border-custom-border-200 w-full"
type="button"
>
<div className="flex gap-1.5 items-center text-custom-primary-100 pl-4 py-2.5 text-sm sticky left-0 z-[1] border-custom-border-200 w-full">
<PlusIcon className="h-4 w-4" />
New Issue
</button>
</div>
}
position="left"
verticalPosition="top"
optionsClassName="left-5 !w-36"
noBorder
>

View File

@ -92,7 +92,6 @@ export const SelectRepository: React.FC<Props> = ({
)}
</>
}
position="right"
optionsClassName="w-full"
/>
);

View File

@ -143,7 +143,6 @@ export const JiraGetImportDetail: React.FC = () => {
)}
</span>
}
verticalPosition="top"
>
{projects && projects.length > 0 ? (
projects.map((project) => (

View File

@ -158,7 +158,7 @@ export const IssueMainContent: React.FC<Props> = ({
</a>
</Link>
<CustomMenu position="left" ellipsis optionsClassName="px-1.5">
<CustomMenu ellipsis optionsClassName="px-1.5">
{siblingIssuesList ? (
siblingIssuesList.length > 0 ? (
<>

View File

@ -93,7 +93,6 @@ export const PeekOverviewHeader: React.FC<Props> = ({
<Icon iconName={peekModes.find((m) => m.key === mode)?.icon ?? ""} />
</button>
}
position="left"
>
{peekModes.map((mode) => (
<CustomSelect.Option key={mode.key} value={mode.key}>

View File

@ -33,7 +33,6 @@ export const IssueEstimateSelect: React.FC<Props> = ({ value, onChange }) => {
</div>
}
onChange={onChange}
position="right"
width="w-full min-w-[8rem]"
noChevron
>

View File

@ -91,7 +91,6 @@ export const SidebarCycleSelect: React.FC<Props> = ({
: handleCycleChange(incompleteCycles?.find((c) => c.id === value) as ICycle);
}}
width="w-full"
position="right"
maxHeight="rg"
disabled={disabled}
>

View File

@ -20,17 +20,14 @@ export const SidebarEstimateSelect: React.FC<Props> = ({ value, onChange, disabl
<CustomSelect
value={value}
customButton={
<button
type="button"
className="flex items-center gap-1.5 !text-sm bg-custom-background-80 rounded px-2.5 py-0.5"
>
<div className="flex items-center gap-1.5 !text-sm bg-custom-background-80 rounded px-2.5 py-0.5">
<PlayIcon
className={`h-4 w-4 -rotate-90 ${
value !== null ? "text-custom-text-100" : "text-custom-text-200"
}`}
/>
{estimatePoints?.find((e) => e.key === value)?.value ?? "No estimate"}
</button>
</div>
}
onChange={onChange}
disabled={disabled}

View File

@ -87,7 +87,6 @@ export const SidebarModuleSelect: React.FC<Props> = ({
: handleModuleChange(modules?.find((m) => m.id === value) as IModule);
}}
width="w-full"
position="right"
maxHeight="rg"
disabled={disabled}
>

View File

@ -18,8 +18,7 @@ type Props = {
export const SidebarPrioritySelect: React.FC<Props> = ({ value, onChange, disabled = false }) => (
<CustomSelect
customButton={
<button
type="button"
<div
className={`flex items-center gap-1.5 text-left text-xs capitalize rounded px-2.5 py-0.5 ${
value === "urgent"
? "border-red-500/20 bg-red-500/20 text-red-500"
@ -36,7 +35,7 @@ export const SidebarPrioritySelect: React.FC<Props> = ({ value, onChange, disabl
<PriorityIcon priority={value} className="!text-sm" />
</span>
<span>{value ?? "None"}</span>
</button>
</div>
}
value={value}
onChange={onChange}

View File

@ -39,7 +39,7 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, disabled
return (
<CustomSelect
customButton={
<button type="button" className="bg-custom-background-80 text-xs rounded px-2.5 py-0.5">
<div className="bg-custom-background-80 text-xs rounded px-2.5 py-0.5">
{selectedState ? (
<div className="flex items-center gap-1.5 text-left text-custom-text-100">
<StateGroupIcon stateGroup={selectedState.group} color={selectedState.color} />
@ -53,12 +53,11 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, disabled
) : (
"None"
)}
</button>
</div>
}
value={value}
onChange={onChange}
optionsClassName="w-min"
position="left"
disabled={disabled}
>
{states ? (

View File

@ -247,7 +247,6 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
</>
}
buttonClassName="whitespace-nowrap"
position="left"
noBorder
noChevron
>
@ -283,7 +282,6 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
</>
}
buttonClassName="whitespace-nowrap"
position="left"
noBorder
noChevron
>

View File

@ -113,10 +113,8 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
{...(customButton ? { customButton: assigneeLabel } : { label: assigneeLabel })}
multiple
noChevron
position={position}
disabled={isNotAllowed}
onOpen={() => setFetchAssignees(true)}
selfPositioned={selfPositioned}
width="w-full min-w-[12rem]"
/>
);

View File

@ -74,8 +74,6 @@ export const ViewEstimateSelect: React.FC<Props> = ({
maxHeight="md"
noChevron
disabled={isNotAllowed}
position={position}
selfPositioned={selfPositioned}
width="w-full min-w-[8rem]"
>
<CustomSelect.Option value={null}>

View File

@ -146,9 +146,7 @@ export const ViewLabelSelect: React.FC<Props> = ({
{...(customButton ? { customButton: labelsLabel } : { label: labelsLabel })}
multiple
noChevron
position={position}
disabled={isNotAllowed}
selfPositioned={selfPositioned}
footerOption={footerOption}
width="w-full min-w-[12rem]"
/>

View File

@ -59,8 +59,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
}}
maxHeight="md"
customButton={
<button
type="button"
<div
className={`grid place-items-center rounded ${
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
} ${noBorder ? "" : "h-6 w-6 border shadow-sm"} ${
@ -94,12 +93,10 @@ export const ViewPrioritySelect: React.FC<Props> = ({
{noBorder ? capitalizeFirstLetter(issue.priority ?? "None") : ""}
</span>
</Tooltip>
</button>
</div>
}
noChevron
disabled={isNotAllowed}
position={position}
selfPositioned={selfPositioned}
>
{PRIORITIES?.map((priority) => (
<CustomSelect.Option key={priority} value={priority} className="capitalize">

View File

@ -65,7 +65,6 @@ export const SidebarLeadSelect: React.FC<Props> = ({ value, onChange }) => {
}
options={options}
maxHeight="md"
position="right"
onChange={onChange}
/>
</div>

View File

@ -64,7 +64,6 @@ export const SidebarMembersSelect: React.FC<Props> = ({ value, onChange }) => {
options={options}
onChange={onChange}
maxHeight="md"
position="right"
multiple
/>
</div>

View File

@ -214,12 +214,9 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
e.stopPropagation();
}}
customButton={
<button
type="button"
className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded"
>
<div className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded">
<Icon iconName="schedule" className="h-5 w-5 text-custom-text-300" />
</button>
</div>
}
optionsClassName="!z-20"
>

View File

@ -167,7 +167,6 @@ export const UserDetails: React.FC<Props> = ({ user }) => {
}
input
width="w-full"
verticalPosition="top"
>
{USER_ROLES.map((item) => (
<CustomSelect.Option key={item.value} value={item.value}>
@ -197,7 +196,6 @@ export const UserDetails: React.FC<Props> = ({ user }) => {
}
options={timeZoneOptions}
onChange={onChange}
verticalPosition="top"
optionsClassName="w-full"
input
/>

View File

@ -384,12 +384,12 @@ export const SinglePageBlock: React.FC<Props> = ({
</button>
<CustomMenu
customButton={
<button
<div
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded px-2.5 py-1 text-left text-xs duration-300 hover:bg-custom-background-90"
onClick={() => setIsMenuActive(!isMenuActive)}
>
<BoltIcon className="h-4.5 w-3.5" />
</button>
</div>
}
>
{block.issue ? (

View File

@ -418,7 +418,6 @@ export const CreateProjectModal: React.FC<Props> = ({
)}
</div>
}
verticalPosition="top"
noChevron
/>
);

View File

@ -1,13 +1,13 @@
import React, { useRef, useState } from "react";
import React, { useState } from "react";
import useSWR from "swr";
import { useRouter } from "next/router";
// react-popper
import { usePopper } from "react-popper";
// services
import issuesService from "services/issues.service";
// hooks
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
// headless ui
import { Combobox } from "@headlessui/react";
// component
@ -19,6 +19,7 @@ import { PlusIcon } from "lucide-react";
// types
import { Tooltip } from "components/ui";
import { ICurrentUserResponse, IIssueLabels } from "types";
import { Placement } from "@popperjs/core";
// constants
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
@ -31,6 +32,7 @@ type Props = {
buttonClassName?: string;
optionsClassName?: string;
maxRender?: number;
placement?: Placement;
hideDropdownArrow?: boolean;
disabled?: boolean;
user: ICurrentUserResponse | undefined;
@ -45,21 +47,25 @@ export const LabelSelect: React.FC<Props> = ({
buttonClassName = "",
optionsClassName = "",
maxRender = 2,
placement,
hideDropdownArrow = false,
disabled = false,
user,
}) => {
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [fetchStates, setFetchStates] = useState(false);
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const [labelModal, setLabelModal] = useState(false);
const router = useRouter();
const { workspaceSlug } = router.query;
const dropdownBtn = useRef<any>(null);
const dropdownOptions = useRef<any>(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: placement ?? "bottom-start",
});
const { data: issueLabels } = useSWR<IIssueLabels[]>(
projectId && fetchStates ? PROJECT_ISSUE_LABELS(projectId) : null,
@ -131,8 +137,6 @@ export const LabelSelect: React.FC<Props> = ({
</div>
);
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
const footerOption = (
<button
type="button"
@ -165,15 +169,13 @@ export const LabelSelect: React.FC<Props> = ({
multiple
>
{({ open }: { open: boolean }) => {
if (open) {
if (!isOpen) setIsOpen(true);
setFetchStates(true);
} else if (isOpen) setIsOpen(false);
if (open) setFetchStates(true);
return (
<>
<Combobox.Button
ref={dropdownBtn}
<Combobox.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
className={`flex items-center justify-between gap-1 w-full text-xs ${
disabled
@ -187,11 +189,15 @@ export const LabelSelect: React.FC<Props> = ({
{!hideDropdownArrow && !disabled && (
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
)}
</button>
</Combobox.Button>
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
<Combobox.Options
ref={dropdownOptions}
className={`absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap mt-1 ${optionsClassName}`}
<Combobox.Options>
<div
className={`z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-custom-shadow-rg focus:outline-none w-48 whitespace-nowrap my-1 ${optionsClassName}`}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
@ -234,8 +240,8 @@ export const LabelSelect: React.FC<Props> = ({
)}
</div>
{footerOption}
</Combobox.Options>
</div>
</Combobox.Options>
</>
);
}}

View File

@ -77,7 +77,6 @@ export const MemberSelect: React.FC<Props> = ({ value, onChange, isDisabled = fa
]
}
maxHeight="md"
position="right"
width="w-full"
onChange={onChange}
disabled={isDisabled}

View File

@ -1,9 +1,10 @@
import React, { useRef, useState } from "react";
import React, { useState } from "react";
import { useRouter } from "next/router";
// react-popper
import { usePopper } from "react-popper";
// hooks
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
import useProjectMembers from "hooks/use-project-members";
import useWorkspaceMembers from "hooks/use-workspace-members";
// headless ui
@ -15,6 +16,7 @@ import { ChevronDownIcon } from "@heroicons/react/20/solid";
import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
// types
import { IUser } from "types";
import { Placement } from "@popperjs/core";
type Props = {
value: string | string[];
@ -25,6 +27,7 @@ type Props = {
className?: string;
buttonClassName?: string;
optionsClassName?: string;
placement?: Placement;
hideDropdownArrow?: boolean;
disabled?: boolean;
};
@ -38,18 +41,22 @@ export const MembersSelect: React.FC<Props> = ({
className = "",
buttonClassName = "",
optionsClassName = "",
placement,
hideDropdownArrow = false,
disabled = false,
}) => {
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [fetchStates, setFetchStates] = useState(false);
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const router = useRouter();
const { workspaceSlug } = router.query;
const dropdownBtn = useRef<any>(null);
const dropdownOptions = useRef<any>(null);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: placement ?? "bottom-start",
});
const { members } = useProjectMembers(
workspaceSlug?.toString(),
@ -105,8 +112,6 @@ export const MembersSelect: React.FC<Props> = ({
</Tooltip>
);
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
return (
<Combobox
as="div"
@ -117,15 +122,13 @@ export const MembersSelect: React.FC<Props> = ({
multiple
>
{({ open }: { open: boolean }) => {
if (open) {
if (!isOpen) setIsOpen(true);
setFetchStates(true);
} else if (isOpen) setIsOpen(false);
if (open) setFetchStates(true);
return (
<>
<Combobox.Button
ref={dropdownBtn}
<Combobox.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
className={`flex items-center justify-between gap-1 w-full text-xs ${
disabled
@ -137,11 +140,14 @@ export const MembersSelect: React.FC<Props> = ({
{!hideDropdownArrow && !disabled && (
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
)}
</button>
</Combobox.Button>
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
<Combobox.Options
ref={dropdownOptions}
className={`absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap mt-1 ${optionsClassName}`}
<Combobox.Options>
<div
className={`z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-custom-shadow-rg focus:outline-none w-48 whitespace-nowrap my-1 ${optionsClassName}`}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
@ -183,8 +189,8 @@ export const MembersSelect: React.FC<Props> = ({
<p className="text-center text-custom-text-200">Loading...</p>
)}
</div>
</Combobox.Options>
</div>
</Combobox.Options>
</>
);
}}

View File

@ -1,7 +1,7 @@
import React, { useRef, useState } from "react";
import React, { useState } from "react";
// hooks
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
// react-popper
import { usePopper } from "react-popper";
// headless ui
import { Combobox } from "@headlessui/react";
// icons
@ -12,6 +12,7 @@ import { PriorityIcon } from "components/icons";
import { Tooltip } from "components/ui";
// types
import { TIssuePriorities } from "types";
import { Placement } from "@popperjs/core";
// constants
import { PRIORITIES } from "constants/project";
@ -21,6 +22,7 @@ type Props = {
className?: string;
buttonClassName?: string;
optionsClassName?: string;
placement?: Placement;
hideDropdownArrow?: boolean;
disabled?: boolean;
};
@ -31,14 +33,18 @@ export const PrioritySelect: React.FC<Props> = ({
className = "",
buttonClassName = "",
optionsClassName = "",
placement,
hideDropdownArrow = false,
disabled = false,
}) => {
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);
const dropdownBtn = useRef<any>(null);
const dropdownOptions = useRef<any>(null);
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 options = PRIORITIES?.map((priority) => ({
value: priority,
@ -87,8 +93,6 @@ export const PrioritySelect: React.FC<Props> = ({
</Tooltip>
);
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
return (
<Combobox
as="div"
@ -97,15 +101,9 @@ export const PrioritySelect: React.FC<Props> = ({
onChange={onChange}
disabled={disabled}
>
{({ open }: { open: boolean }) => {
if (open) {
if (!isOpen) setIsOpen(true);
} else if (isOpen) setIsOpen(false);
return (
<>
<Combobox.Button
ref={dropdownBtn}
<Combobox.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
className={`flex items-center justify-between gap-1 w-full text-xs ${
disabled
@ -117,11 +115,14 @@ export const PrioritySelect: React.FC<Props> = ({
{!hideDropdownArrow && !disabled && (
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
)}
</button>
</Combobox.Button>
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
<Combobox.Options
ref={dropdownOptions}
className={`absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap mt-1 ${optionsClassName}`}
<Combobox.Options>
<div
className={`z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-custom-shadow-rg focus:outline-none w-48 whitespace-nowrap my-1 ${optionsClassName}`}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
@ -163,11 +164,8 @@ export const PrioritySelect: React.FC<Props> = ({
<p className="text-center text-custom-text-200">Loading...</p>
)}
</div>
</Combobox.Options>
</div>
</>
);
}}
</Combobox.Options>
</Combobox>
);
};

View File

@ -230,7 +230,6 @@ const SendProjectInvitationModal: React.FC<Props> = (props) => {
onChange(val);
}}
options={options}
position="left"
width="w-full min-w-[12rem]"
/>
)}
@ -252,12 +251,12 @@ const SendProjectInvitationModal: React.FC<Props> = (props) => {
<CustomSelect
{...field}
customButton={
<button className="flex w-full items-center justify-between gap-1 rounded-md border border-custom-border-200 shadow-sm duration-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80 focus:outline-none px-3 py-2.5 text-sm text-left">
<div className="flex w-full items-center justify-between gap-1 rounded-md border border-custom-border-200 shadow-sm duration-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80 focus:outline-none px-3 py-2.5 text-sm text-left">
<span className="capitalize">
{field.value ? ROLE[field.value] : "Select role"}
</span>
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
</button>
</div>
}
input
width="w-full"

View File

@ -1,11 +1,11 @@
import React, { useRef, useState } from "react";
import React, { useState } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// hooks
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
// react-popper
import { usePopper } from "react-popper";
// services
import stateService from "services/state.service";
// headless ui
@ -16,6 +16,7 @@ import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import { StateGroupIcon } from "components/icons";
// types
import { Tooltip } from "components/ui";
import { Placement } from "@popperjs/core";
// constants
import { IState } from "types";
import { STATES_LIST } from "constants/fetch-keys";
@ -29,6 +30,7 @@ type Props = {
className?: string;
buttonClassName?: string;
optionsClassName?: string;
placement?: Placement;
hideDropdownArrow?: boolean;
disabled?: boolean;
};
@ -40,17 +42,21 @@ export const StateSelect: React.FC<Props> = ({
className = "",
buttonClassName = "",
optionsClassName = "",
placement,
hideDropdownArrow = false,
disabled = false,
}) => {
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);
const dropdownBtn = useRef<any>(null);
const dropdownOptions = useRef<any>(null);
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const [fetchStates, setFetchStates] = useState<boolean>(false);
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: placement ?? "bottom-start",
});
const router = useRouter();
const { workspaceSlug } = router.query;
@ -90,8 +96,6 @@ export const StateSelect: React.FC<Props> = ({
</Tooltip>
);
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
return (
<Combobox
as="div"
@ -103,15 +107,13 @@ export const StateSelect: React.FC<Props> = ({
disabled={disabled}
>
{({ open }: { open: boolean }) => {
if (open) {
if (!isOpen) setIsOpen(true);
setFetchStates(true);
} else if (isOpen) setIsOpen(false);
if (open) setFetchStates(true);
return (
<>
<Combobox.Button
ref={dropdownBtn}
<Combobox.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
className={`flex items-center justify-between gap-1 w-full text-xs px-2.5 py-1 rounded-md shadow-sm border border-custom-border-300 duration-300 focus:outline-none ${
disabled
@ -123,11 +125,14 @@ export const StateSelect: React.FC<Props> = ({
{!hideDropdownArrow && !disabled && (
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
)}
</button>
</Combobox.Button>
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
<Combobox.Options
ref={dropdownOptions}
className={`absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap mt-1 ${optionsClassName}`}
<Combobox.Options>
<div
className={`z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-custom-shadow-rg focus:outline-none w-48 whitespace-nowrap my-1 ${optionsClassName}`}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
@ -169,8 +174,8 @@ export const StateSelect: React.FC<Props> = ({
<p className="text-center text-custom-text-200">Loading...</p>
)}
</div>
</Combobox.Options>
</div>
</Combobox.Options>
</>
);
}}

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,44 +33,56 @@ 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}`}>
}: 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="button"
<Menu.Button as={React.Fragment}>
<button
ref={setReferenceElement}
type="button"
onClick={menuButtonOnClick}
className={customButtonClassName}
disabled={disabled}
>
{customButton}
</button>
</Menu.Button>
) : (
<>
{ellipsis || verticalEllipsis ? (
<Menu.Button
<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"
disabled
? "cursor-not-allowed"
: "cursor-pointer hover:bg-custom-background-80"
} ${buttonClassName}`}
>
<MoreHorizOutlined
fontSize="small"
className={verticalEllipsis ? "rotate-90" : ""}
/>
</button>
</Menu.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"
@ -88,24 +103,14 @@ const CustomMenu = ({
aria-hidden="true"
/>
)}
</button>
</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"} ${
<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"
@ -116,14 +121,18 @@ const CustomMenu = ({
? "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>
);
);
};
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,22 +71,32 @@ 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
<Combobox.Button as={React.Fragment}>
<button
ref={setReferenceElement}
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 ${
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
@ -90,24 +108,17 @@ export const CustomSearchSelect = ({
{!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}
</div>
</Combobox.Options>
</Transition>
</>
);
}}

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,24 +30,43 @@ const CustomSelect = ({
noChevron = false,
onChange,
optionsClassName = "",
position = "left",
selfPositioned = false,
value,
verticalPosition = "bottom",
width = "auto",
}: CustomSelectProps) => (
}: CustomSelectProps) => {
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 (
<Listbox
as="div"
value={value}
onChange={onChange}
className={`${selfPositioned ? "" : "relative"} flex-shrink-0 text-left ${className}`}
className={`relative flex-shrink-0 text-left ${className}`}
disabled={disabled}
>
<>
{customButton ? (
<Listbox.Button as={React.Fragment}>{customButton}</Listbox.Button>
<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
<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"
@ -54,24 +77,16 @@ const CustomSelect = ({
} ${buttonClassName}`}
>
{label}
{!noChevron && !disabled && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
{!noChevron && !disabled && (
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
)}
</button>
</Listbox.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"
>
<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"} ${
<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"
@ -82,12 +97,16 @@ const CustomSelect = ({
? "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>
);
);
};
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;
};

View File

@ -25,6 +25,7 @@
"@nivo/line": "0.80.0",
"@nivo/pie": "0.80.0",
"@nivo/scatterplot": "0.80.0",
"@popperjs/core": "^2.11.8",
"@sentry/nextjs": "^7.36.0",
"@tiptap/extension-code-block-lowlight": "^2.0.4",
"@tiptap/extension-color": "^2.0.4",
@ -72,6 +73,7 @@
"react-hook-form": "^7.38.0",
"react-markdown": "^8.0.7",
"react-moveable": "^0.54.1",
"react-popper": "^2.3.0",
"sharp": "^0.32.1",
"sonner": "^0.6.2",
"swr": "^2.1.3",

View File

@ -298,8 +298,6 @@ const Profile: NextPage = () => {
buttonClassName={errors.role ? "border-red-500 bg-red-500/10" : ""}
width="w-full"
input
verticalPosition="top"
position="right"
>
{USER_ROLES.map((item) => (
<CustomSelect.Option key={item.value} value={item.value}>
@ -362,7 +360,6 @@ const Profile: NextPage = () => {
}
options={timeZoneOptions}
onChange={onChange}
verticalPosition="top"
optionsClassName="w-full"
input
/>

View File

@ -427,13 +427,10 @@ const SinglePage: NextPage = () => {
)}
<CustomSearchSelect
customButton={
<button
type="button"
className="flex items-center gap-1 rounded-sm bg-custom-background-80 p-1.5 text-xs"
>
<div className="flex items-center gap-1 rounded-sm bg-custom-background-80 p-1.5 text-xs">
<PlusIcon className="h-3.5 w-3.5" />
{pageDetails.labels.length <= 0 && <span>Add Label</span>}
</button>
</div>
}
value={pageDetails.labels}
footerOption={

View File

@ -412,7 +412,7 @@ const MembersSettings: NextPage = () => {
)}
<CustomSelect
customButton={
<button className="flex item-center gap-1">
<div className="flex item-center gap-1">
<span
className={`flex items-center text-sm font-medium ${
member.memberId !== user?.id ? "" : "text-custom-sidebar-text-400"
@ -423,7 +423,7 @@ const MembersSettings: NextPage = () => {
{member.memberId !== user?.id && (
<Icon iconName="expand_more" className="text-lg font-medium" />
)}
</button>
</div>
}
value={member.role}
onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
@ -455,7 +455,6 @@ const MembersSettings: NextPage = () => {
});
});
}}
position="right"
disabled={
member.memberId === user?.id ||
!member.member ||

View File

@ -269,7 +269,7 @@ const MembersSettings: NextPage = () => {
)}
<CustomSelect
customButton={
<button className="flex item-center gap-1">
<div className="flex item-center gap-1">
<span
className={`flex items-center text-sm font-medium ${
member.memberId !== user?.id ? "" : "text-custom-sidebar-text-400"
@ -280,7 +280,7 @@ const MembersSettings: NextPage = () => {
{member.memberId !== user?.id && (
<Icon iconName="expand_more" className="text-lg font-medium" />
)}
</button>
</div>
}
value={member.role}
onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
@ -307,7 +307,6 @@ const MembersSettings: NextPage = () => {
});
});
}}
position="right"
disabled={
member.memberId === currentUser?.member.id ||
!member.status ||

View File

@ -6628,20 +6628,13 @@ prosemirror-menu@^1.2.1:
prosemirror-history "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.8.1:
prosemirror-model@1.18.1, prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.19.0, prosemirror-model@^1.8.1:
version "1.18.1"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd"
integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==
dependencies:
orderedmap "^2.0.0"
prosemirror-model@^1.19.0:
version "1.19.3"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.3.tgz#f0d55285487fefd962d0ac695f716f4ec6705006"
integrity sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==
dependencies:
orderedmap "^2.0.0"
prosemirror-schema-basic@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.2.tgz#6695f5175e4628aab179bf62e5568628b9cfe6c7"