From 911b53a1de239d4ac530e310db0c367d50ce9f5f Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Mon, 22 Jan 2024 15:58:54 +0530 Subject: [PATCH 1/6] chore: dropdowns should close on selecting an option --- web/components/dropdowns/cycle.tsx | 13 +--- web/components/dropdowns/date.tsx | 20 +++---- web/components/dropdowns/estimate.tsx | 13 +--- .../dropdowns/member/project-member.tsx | 3 + web/components/dropdowns/member/types.d.ts | 34 ++++------- .../dropdowns/member/workspace-member.tsx | 22 ++++++- web/components/dropdowns/module.tsx | 17 ++---- web/components/dropdowns/priority.tsx | 59 ++++--------------- web/components/dropdowns/project.tsx | 17 ++---- web/components/dropdowns/state.tsx | 17 ++---- web/components/dropdowns/types.d.ts | 12 ++++ 11 files changed, 89 insertions(+), 138 deletions(-) diff --git a/web/components/dropdowns/cycle.tsx b/web/components/dropdowns/cycle.tsx index 5a861a8f99f..cf3bd1cfc8e 100644 --- a/web/components/dropdowns/cycle.tsx +++ b/web/components/dropdowns/cycle.tsx @@ -2,7 +2,6 @@ import { Fragment, ReactNode, useEffect, useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { Combobox } from "@headlessui/react"; import { usePopper } from "react-popper"; -import { Placement } from "@popperjs/core"; import { Check, ChevronDown, Search } from "lucide-react"; // hooks import { useApplication, useCycle } from "hooks/store"; @@ -14,21 +13,14 @@ import { ContrastIcon } from "@plane/ui"; import { cn } from "helpers/common.helper"; // types import { ICycle } from "@plane/types"; -import { TButtonVariants } from "./types"; +import { TDropdownProps } from "./types"; -type Props = { +type Props = TDropdownProps & { button?: ReactNode; - buttonClassName?: string; - buttonContainerClassName?: string; - buttonVariant: TButtonVariants; - className?: string; - disabled?: boolean; dropdownArrow?: boolean; onChange: (val: string | null) => void; - placement?: Placement; projectId: string; value: string | null; - tabIndex?: number; }; type ButtonProps = { @@ -291,6 +283,7 @@ export const CycleDropdown: React.FC = observer((props) => { active ? "bg-custom-background-80" : "" } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` } + onClick={closeDropdown} > {({ selected }) => ( <> diff --git a/web/components/dropdowns/date.tsx b/web/components/dropdowns/date.tsx index 92f35b9105d..413b360be34 100644 --- a/web/components/dropdowns/date.tsx +++ b/web/components/dropdowns/date.tsx @@ -3,7 +3,6 @@ import { Popover } from "@headlessui/react"; import DatePicker from "react-datepicker"; import { usePopper } from "react-popper"; import { CalendarDays, X } from "lucide-react"; -// import "react-datepicker/dist/react-datepicker.css"; // hooks import { useDropdownKeyDown } from "hooks/use-dropdown-key-down"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; @@ -11,24 +10,17 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector"; import { renderFormattedDate } from "helpers/date-time.helper"; import { cn } from "helpers/common.helper"; // types -import { TButtonVariants } from "./types"; -import { Placement } from "@popperjs/core"; +import { TDropdownProps } from "./types"; -type Props = { - buttonClassName?: string; - buttonContainerClassName?: string; - buttonVariant: TButtonVariants; - disabled?: boolean; +type Props = TDropdownProps & { icon?: React.ReactNode; isClearable?: boolean; minDate?: Date; maxDate?: Date; onChange: (val: Date | null) => void; placeholder: string; - placement?: Placement; value: Date | string | null; closeOnSelect?: boolean; - tabIndex?: number; }; type ButtonProps = { @@ -118,6 +110,7 @@ export const DateDropdown: React.FC = (props) => { buttonClassName = "", buttonContainerClassName, buttonVariant, + className = "", disabled = false, icon = , isClearable = true, @@ -160,7 +153,12 @@ export const DateDropdown: React.FC = (props) => { useOutsideClickDetector(dropdownRef, closeDropdown); return ( - + {({ close }) => ( <> diff --git a/web/components/dropdowns/estimate.tsx b/web/components/dropdowns/estimate.tsx index 9138b2beac9..66b4169bf5e 100644 --- a/web/components/dropdowns/estimate.tsx +++ b/web/components/dropdowns/estimate.tsx @@ -2,7 +2,6 @@ import { Fragment, ReactNode, useEffect, useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { Combobox } from "@headlessui/react"; import { usePopper } from "react-popper"; -import { Placement } from "@popperjs/core"; import { Check, ChevronDown, Search, Triangle } from "lucide-react"; import sortBy from "lodash/sortBy"; // hooks @@ -12,21 +11,14 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector"; // helpers import { cn } from "helpers/common.helper"; // types -import { TButtonVariants } from "./types"; +import { TDropdownProps } from "./types"; -type Props = { +type Props = TDropdownProps & { button?: ReactNode; - buttonClassName?: string; - buttonContainerClassName?: string; - buttonVariant: TButtonVariants; - className?: string; - disabled?: boolean; dropdownArrow?: boolean; onChange: (val: number | null) => void; - placement?: Placement; projectId: string; value: number | null; - tabIndex?: number; }; type ButtonProps = { @@ -285,6 +277,7 @@ export const EstimateDropdown: React.FC = observer((props) => { active ? "bg-custom-background-80" : "" } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` } + onClick={closeDropdown} > {({ selected }) => ( <> diff --git a/web/components/dropdowns/member/project-member.tsx b/web/components/dropdowns/member/project-member.tsx index 1e685627494..b282fdd61ba 100644 --- a/web/components/dropdowns/member/project-member.tsx +++ b/web/components/dropdowns/member/project-member.tsx @@ -221,6 +221,9 @@ export const ProjectMemberDropdown: React.FC = observer((props) => { active ? "bg-custom-background-80" : "" } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` } + onClick={() => { + if (!multiple) closeDropdown(); + }} > {({ selected }) => ( <> diff --git a/web/components/dropdowns/member/types.d.ts b/web/components/dropdowns/member/types.d.ts index f5f81a5c69c..fbf52c969d6 100644 --- a/web/components/dropdowns/member/types.d.ts +++ b/web/components/dropdowns/member/types.d.ts @@ -1,26 +1,18 @@ -import { Placement } from "@popperjs/core"; -import { TButtonVariants } from "../types"; +import { TDropdownProps } from "../types"; -export type MemberDropdownProps = { +export type MemberDropdownProps = TDropdownProps & { button?: ReactNode; - buttonClassName?: string; - buttonContainerClassName?: string; - buttonVariant: TButtonVariants; - className?: string; - disabled?: boolean; dropdownArrow?: boolean; placeholder?: string; - placement?: Placement; - tabIndex?: number; } & ( - | { - multiple: false; - onChange: (val: string | null) => void; - value: string | null; - } - | { - multiple: true; - onChange: (val: string[]) => void; - value: string[]; - } -); + | { + multiple: false; + onChange: (val: string | null) => void; + value: string | null; + } + | { + multiple: true; + onChange: (val: string[]) => void; + value: string[]; + } + ); diff --git a/web/components/dropdowns/member/workspace-member.tsx b/web/components/dropdowns/member/workspace-member.tsx index f50da72c8c7..4089e52b9f1 100644 --- a/web/components/dropdowns/member/workspace-member.tsx +++ b/web/components/dropdowns/member/workspace-member.tsx @@ -1,4 +1,4 @@ -import { Fragment, useState } from "react"; +import { Fragment, useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { Combobox } from "@headlessui/react"; import { usePopper } from "react-popper"; @@ -13,6 +13,8 @@ import { Avatar } from "@plane/ui"; import { cn } from "helpers/common.helper"; // types import { MemberDropdownProps } from "./types"; +import { useDropdownKeyDown } from "hooks/use-dropdown-key-down"; +import useOutsideClickDetector from "hooks/use-outside-click-detector"; export const WorkspaceMemberDropdown: React.FC = observer((props) => { const { @@ -28,9 +30,13 @@ export const WorkspaceMemberDropdown: React.FC = observer(( placeholder = "Members", placement, value, + tabIndex, } = props; // states const [query, setQuery] = useState(""); + const [isOpen, setIsOpen] = useState(false); + // refs + const dropdownRef = useRef(null); // popper-js refs const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); @@ -78,13 +84,24 @@ export const WorkspaceMemberDropdown: React.FC = observer(( }; if (multiple) comboboxProps.multiple = true; + const openDropdown = () => { + setIsOpen(true); + if (referenceElement) referenceElement.focus(); + }; + const closeDropdown = () => setIsOpen(false); + const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen); + useOutsideClickDetector(dropdownRef, closeDropdown); + return ( {button ? ( @@ -186,6 +203,9 @@ export const WorkspaceMemberDropdown: React.FC = observer(( active ? "bg-custom-background-80" : "" } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` } + onClick={() => { + if (!multiple) closeDropdown(); + }} > {({ selected }) => ( <> diff --git a/web/components/dropdowns/module.tsx b/web/components/dropdowns/module.tsx index e0d6b52f73d..9bce79460a2 100644 --- a/web/components/dropdowns/module.tsx +++ b/web/components/dropdowns/module.tsx @@ -2,7 +2,6 @@ import { Fragment, ReactNode, useEffect, useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { Combobox } from "@headlessui/react"; import { usePopper } from "react-popper"; -import { Placement } from "@popperjs/core"; import { Check, ChevronDown, Search } from "lucide-react"; // hooks import { useApplication, useModule } from "hooks/store"; @@ -14,21 +13,14 @@ import { DiceIcon } from "@plane/ui"; import { cn } from "helpers/common.helper"; // types import { IModule } from "@plane/types"; -import { TButtonVariants } from "./types"; +import { TDropdownProps } from "./types"; -type Props = { +type Props = TDropdownProps & { button?: ReactNode; - buttonClassName?: string; - buttonContainerClassName?: string; - buttonVariant: TButtonVariants; - className?: string; - disabled?: boolean; dropdownArrow?: boolean; onChange: (val: string | null) => void; - placement?: Placement; projectId: string; value: string | null; - tabIndex?: number; }; type DropdownOptions = @@ -186,9 +178,7 @@ export const ModuleDropdown: React.FC = observer((props) => { as="div" ref={dropdownRef} tabIndex={tabIndex} - className={cn("h-full flex-shrink-0", { - className, - })} + className={cn("h-full flex-shrink-0", className)} value={value} onChange={onChange} disabled={disabled} @@ -291,6 +281,7 @@ export const ModuleDropdown: React.FC = observer((props) => { active ? "bg-custom-background-80" : "" } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` } + onClick={closeDropdown} > {({ selected }) => ( <> diff --git a/web/components/dropdowns/priority.tsx b/web/components/dropdowns/priority.tsx index bd20c7965c8..7c812880ad7 100644 --- a/web/components/dropdowns/priority.tsx +++ b/web/components/dropdowns/priority.tsx @@ -1,7 +1,6 @@ import { Fragment, ReactNode, useRef, useState } from "react"; import { Combobox } from "@headlessui/react"; import { usePopper } from "react-popper"; -import { Placement } from "@popperjs/core"; import { Check, ChevronDown, Search } from "lucide-react"; // hooks import { useDropdownKeyDown } from "hooks/use-dropdown-key-down"; @@ -12,23 +11,16 @@ import { PriorityIcon } from "@plane/ui"; import { cn } from "helpers/common.helper"; // types import { TIssuePriorities } from "@plane/types"; -import { TButtonVariants } from "./types"; +import { TDropdownProps } from "./types"; // constants import { ISSUE_PRIORITIES } from "constants/issue"; -type Props = { +type Props = TDropdownProps & { button?: ReactNode; - buttonClassName?: string; - buttonContainerClassName?: string; - buttonVariant: TButtonVariants; - className?: string; - disabled?: boolean; dropdownArrow?: boolean; highlightUrgent?: boolean; onChange: (val: TIssuePriorities) => void; - placement?: Placement; value: TIssuePriorities; - tabIndex?: number; }; type ButtonProps = { @@ -237,42 +229,16 @@ export const PriorityDropdown: React.FC = (props) => { ], }); - const options = ISSUE_PRIORITIES.map((priority) => { - const priorityClasses = { - urgent: "bg-red-500/20 text-red-950 border-red-500", - high: "bg-orange-500/20 text-orange-950 border-orange-500", - medium: "bg-yellow-500/20 text-yellow-950 border-yellow-500", - low: "bg-custom-primary-100/20 text-custom-primary-950 border-custom-primary-100", - none: "bg-custom-background-80 border-custom-border-300", - }; - - return { - value: priority.key, - query: priority.key, - content: ( -
-
-
- {priority.title} -
- ), - }; - }); + const options = ISSUE_PRIORITIES.map((priority) => ({ + value: priority.key, + query: priority.key, + content: ( +
+ + {priority.title} +
+ ), + })); const filteredOptions = query === "" ? options : options.filter((o) => o.query.toLowerCase().includes(query.toLowerCase())); @@ -400,6 +366,7 @@ export const PriorityDropdown: React.FC = (props) => { active ? "bg-custom-background-80" : "" } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` } + onClick={closeDropdown} > {({ selected }) => ( <> diff --git a/web/components/dropdowns/project.tsx b/web/components/dropdowns/project.tsx index 7649e1d75ab..512cba5287c 100644 --- a/web/components/dropdowns/project.tsx +++ b/web/components/dropdowns/project.tsx @@ -2,7 +2,6 @@ import { Fragment, ReactNode, useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { Combobox } from "@headlessui/react"; import { usePopper } from "react-popper"; -import { Placement } from "@popperjs/core"; import { Check, ChevronDown, Search } from "lucide-react"; // hooks import { useProject } from "hooks/store"; @@ -13,20 +12,13 @@ import { cn } from "helpers/common.helper"; import { renderEmoji } from "helpers/emoji.helper"; // types import { IProject } from "@plane/types"; -import { TButtonVariants } from "./types"; +import { TDropdownProps } from "./types"; -type Props = { +type Props = TDropdownProps & { button?: ReactNode; - buttonClassName?: string; - buttonContainerClassName?: string; - buttonVariant: TButtonVariants; - className?: string; - disabled?: boolean; dropdownArrow?: boolean; onChange: (val: string) => void; - placement?: Placement; value: string | null; - tabIndex?: number; }; type ButtonProps = { @@ -166,9 +158,7 @@ export const ProjectDropdown: React.FC = observer((props) => { as="div" ref={dropdownRef} tabIndex={tabIndex} - className={cn("h-full flex-shrink-0", { - className, - })} + className={cn("h-full flex-shrink-0", className)} value={value} onChange={onChange} disabled={disabled} @@ -271,6 +261,7 @@ export const ProjectDropdown: React.FC = observer((props) => { active ? "bg-custom-background-80" : "" } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` } + onClick={closeDropdown} > {({ selected }) => ( <> diff --git a/web/components/dropdowns/state.tsx b/web/components/dropdowns/state.tsx index ba48af4bd54..ecf084c0892 100644 --- a/web/components/dropdowns/state.tsx +++ b/web/components/dropdowns/state.tsx @@ -2,7 +2,6 @@ import { Fragment, ReactNode, useEffect, useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { Combobox } from "@headlessui/react"; import { usePopper } from "react-popper"; -import { Placement } from "@popperjs/core"; import { Check, ChevronDown, Search } from "lucide-react"; // hooks import { useApplication, useProjectState } from "hooks/store"; @@ -14,21 +13,14 @@ import { StateGroupIcon } from "@plane/ui"; import { cn } from "helpers/common.helper"; // types import { IState } from "@plane/types"; -import { TButtonVariants } from "./types"; +import { TDropdownProps } from "./types"; -type Props = { +type Props = TDropdownProps & { button?: ReactNode; - buttonClassName?: string; - buttonContainerClassName?: string; - buttonVariant: TButtonVariants; - className?: string; - disabled?: boolean; dropdownArrow?: boolean; onChange: (val: string) => void; - placement?: Placement; projectId: string; value: string; - tabIndex?: number; }; type ButtonProps = { @@ -164,9 +156,7 @@ export const StateDropdown: React.FC = observer((props) => { as="div" ref={dropdownRef} tabIndex={tabIndex} - className={cn("h-full flex-shrink-0", { - className, - })} + className={cn("h-full flex-shrink-0", className)} value={value} onChange={onChange} disabled={disabled} @@ -269,6 +259,7 @@ export const StateDropdown: React.FC = observer((props) => { active ? "bg-custom-background-80" : "" } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` } + onClick={closeDropdown} > {({ selected }) => ( <> diff --git a/web/components/dropdowns/types.d.ts b/web/components/dropdowns/types.d.ts index f23914daa10..0b8ef99071b 100644 --- a/web/components/dropdowns/types.d.ts +++ b/web/components/dropdowns/types.d.ts @@ -1,3 +1,5 @@ +import { Placement } from "@popperjs/core"; + export type TButtonVariants = | "border-with-text" | "border-without-text" @@ -5,3 +7,13 @@ export type TButtonVariants = | "background-without-text" | "transparent-with-text" | "transparent-without-text"; + +export type TDropdownProps = { + buttonClassName?: string; + buttonContainerClassName?: string; + buttonVariant: TButtonVariants; + className?: string; + disabled?: boolean; + placement?: Placement; + tabIndex?: number; +}; From 9d25dc3bed16070575eb8dfe21a8dfb728069ad6 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Mon, 22 Jan 2024 17:23:19 +0530 Subject: [PATCH 2/6] style: @plane/ui dropdown styling --- packages/ui/src/dropdowns/custom-menu.tsx | 36 ++++----- .../ui/src/dropdowns/custom-search-select.tsx | 80 ++++++++----------- packages/ui/src/dropdowns/custom-select.tsx | 52 ++++++------ packages/ui/src/dropdowns/helper.tsx | 1 - .../custom-analytics/select/project.tsx | 7 +- .../custom-analytics/select/segment.tsx | 1 - .../custom-analytics/select/x-axis.tsx | 1 - .../custom-analytics/select/y-axis.tsx | 2 +- .../automation/auto-archive-automation.tsx | 1 - .../automation/auto-close-automation.tsx | 2 - web/components/core/theme/theme-switch.tsx | 1 - .../integration/jira/give-details.tsx | 2 +- .../integration/jira/import-users.tsx | 2 +- .../issues/view-select/estimate.tsx | 2 +- web/components/modules/sidebar.tsx | 2 +- .../select-snooze-till-modal.tsx | 2 +- .../project/send-project-invitation-modal.tsx | 4 +- web/components/states/create-state-modal.tsx | 2 +- .../workspace/create-workspace-form.tsx | 2 +- .../send-workspace-invitation-modal.tsx | 2 +- .../workspace/settings/workspace-details.tsx | 2 +- web/pages/profile/index.tsx | 6 +- 22 files changed, 93 insertions(+), 119 deletions(-) diff --git a/packages/ui/src/dropdowns/custom-menu.tsx b/packages/ui/src/dropdowns/custom-menu.tsx index 08239f16abf..7ef99370f5a 100644 --- a/packages/ui/src/dropdowns/custom-menu.tsx +++ b/packages/ui/src/dropdowns/custom-menu.tsx @@ -1,17 +1,15 @@ import * as React from "react"; import ReactDOM from "react-dom"; -// react-poppper +import { Menu } from "@headlessui/react"; import { usePopper } from "react-popper"; +import { ChevronDown, MoreHorizontal } from "lucide-react"; // hooks import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down"; import useOutsideClickDetector from "../hooks/use-outside-click-detector"; -// headless ui -import { Menu } from "@headlessui/react"; -// type -import { ICustomMenuDropdownProps, ICustomMenuItemProps } from "./helper"; -// icons -import { ChevronDown, MoreHorizontal } from "lucide-react"; +// helpers import { cn } from "../../helpers"; +// types +import { ICustomMenuDropdownProps, ICustomMenuItemProps } from "./helper"; const CustomMenu = (props: ICustomMenuDropdownProps) => { const { @@ -29,7 +27,6 @@ const CustomMenu = (props: ICustomMenuDropdownProps) => { noChevron = false, optionsClassName = "", verticalEllipsis = false, - width = "auto", portalElement, menuButtonOnClick, tabIndex, @@ -63,17 +60,16 @@ const CustomMenu = (props: ICustomMenuDropdownProps) => { static >
{ as="div" ref={dropdownRef} tabIndex={tabIndex} - className={`relative w-min text-left ${className}`} + className={cn("relative w-min text-left", className)} onKeyDown={handleKeyDown} > {({ open }) => ( diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index f9af00c93ed..9cfe2af263b 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -1,14 +1,12 @@ import React, { useRef, useState } from "react"; - -// react-popper import { usePopper } from "react-popper"; +import { Combobox } from "@headlessui/react"; +import { Check, ChevronDown, Search } from "lucide-react"; // hooks import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down"; import useOutsideClickDetector from "../hooks/use-outside-click-detector"; -// headless ui -import { Combobox } from "@headlessui/react"; -// icons -import { Check, ChevronDown, Search } from "lucide-react"; +// helpers +import { cn } from "../../helpers"; // types import { ICustomSearchSelectProps } from "./helper"; @@ -31,7 +29,6 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { onOpen, optionsClassName = "", value, - width = "auto", tabIndex, } = props; const [query, setQuery] = useState(""); @@ -70,7 +67,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { as="div" ref={dropdownRef} tabIndex={tabIndex} - className={`relative flex-shrink-0 text-left ${className}`} + className={cn("relative flex-shrink-0 text-left", className)} onKeyDown={handleKeyDown} {...comboboxProps} > @@ -114,37 +111,33 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { )} {isOpen && ( - +
-
- +
+ setQuery(e.target.value)} - placeholder="Type to search..." + placeholder="Search" displayValue={(assigned: any) => assigned?.name} />
{filteredOptions ? ( filteredOptions.length > 0 ? ( @@ -152,37 +145,28 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { - `flex cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5 ${ - active || selected ? "bg-custom-background-80" : "" - } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` + className={({ active }) => + cn( + "w-full truncate flex items-center justify-between gap-2 rounded px-1 py-1.5 cursor-pointer select-none", + { + "bg-custom-background-80": active, + } + ) } > - {({ active, selected }) => ( + {({ selected }) => ( <> - {option.content} - {multiple ? ( -
- -
- ) : ( - - )} + {option.content} + {selected && } )}
)) ) : ( - -

No matching results

-
+

No matches found

) ) : ( -

Loading...

+

Loading...

)}
{footerOption} diff --git a/packages/ui/src/dropdowns/custom-select.tsx b/packages/ui/src/dropdowns/custom-select.tsx index 805368b4b06..0af8a538e4c 100644 --- a/packages/ui/src/dropdowns/custom-select.tsx +++ b/packages/ui/src/dropdowns/custom-select.tsx @@ -1,14 +1,12 @@ import React, { useRef, useState } from "react"; - -// react-popper import { usePopper } from "react-popper"; +import { Listbox } from "@headlessui/react"; +import { Check, ChevronDown } from "lucide-react"; // hooks import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down"; import useOutsideClickDetector from "../hooks/use-outside-click-detector"; -// headless ui -import { Listbox } from "@headlessui/react"; -// icons -import { Check, ChevronDown } from "lucide-react"; +// helpers +import { cn } from "../../helpers"; // types import { ICustomSelectItemProps, ICustomSelectProps } from "./helper"; @@ -28,7 +26,6 @@ const CustomSelect = (props: ICustomSelectProps) => { onChange, optionsClassName = "", value, - width = "auto", tabIndex, } = props; // states @@ -57,7 +54,7 @@ const CustomSelect = (props: ICustomSelectProps) => { tabIndex={tabIndex} value={value} onChange={onChange} - className={`relative flex-shrink-0 text-left ${className}`} + className={cn("relative flex-shrink-0 text-left", className)} onKeyDown={handleKeyDown} disabled={disabled} > @@ -94,24 +91,23 @@ const CustomSelect = (props: ICustomSelectProps) => { )} {isOpen && ( - +
-
{children}
+ {children}
)} @@ -124,16 +120,20 @@ const Option = (props: ICustomSelectItemProps) => { return ( - `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"} ${className}` + className={({ active }) => + cn( + "cursor-pointer select-none truncate rounded px-1 py-1.5 text-custom-text-200", + { + "bg-custom-background-80": active, + }, + className + ) } > {({ selected }) => (
{children}
- {selected && } + {selected && }
)}
diff --git a/packages/ui/src/dropdowns/helper.tsx b/packages/ui/src/dropdowns/helper.tsx index 9c0ae0566a3..453a33b631a 100644 --- a/packages/ui/src/dropdowns/helper.tsx +++ b/packages/ui/src/dropdowns/helper.tsx @@ -13,7 +13,6 @@ export interface IDropdownProps { noChevron?: boolean; onOpen?: () => void; optionsClassName?: string; - width?: "auto" | string; placement?: Placement; tabIndex?: number; } diff --git a/web/components/analytics/custom-analytics/select/project.tsx b/web/components/analytics/custom-analytics/select/project.tsx index ee3dce6d6f6..3c08e157473 100644 --- a/web/components/analytics/custom-analytics/select/project.tsx +++ b/web/components/analytics/custom-analytics/select/project.tsx @@ -21,9 +21,9 @@ export const SelectProject: React.FC = observer((props) => { value: projectDetails?.id, query: `${projectDetails?.name} ${projectDetails?.identifier}`, content: ( -
- {projectDetails?.identifier} - {projectDetails?.name} +
+ {projectDetails?.identifier} + {projectDetails?.name}
), }; @@ -42,7 +42,6 @@ export const SelectProject: React.FC = observer((props) => { .join(", ") : "All projects" } - optionsClassName="min-w-full max-w-[20rem]" multiple /> ); diff --git a/web/components/analytics/custom-analytics/select/segment.tsx b/web/components/analytics/custom-analytics/select/segment.tsx index b45c1fa5535..055665d9ee2 100644 --- a/web/components/analytics/custom-analytics/select/segment.tsx +++ b/web/components/analytics/custom-analytics/select/segment.tsx @@ -28,7 +28,6 @@ export const SelectSegment: React.FC = ({ value, onChange, params }) => { } onChange={onChange} - width="w-full" maxHeight="lg" > No value diff --git a/web/components/analytics/custom-analytics/select/x-axis.tsx b/web/components/analytics/custom-analytics/select/x-axis.tsx index 237582ba085..74ee99a7708 100644 --- a/web/components/analytics/custom-analytics/select/x-axis.tsx +++ b/web/components/analytics/custom-analytics/select/x-axis.tsx @@ -24,7 +24,6 @@ export const SelectXAxis: React.FC = (props) => { value={value} label={{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === value)?.label}} onChange={onChange} - width="w-full" maxHeight="lg" > {ANALYTICS_X_AXIS_VALUES.map((item) => { diff --git a/web/components/analytics/custom-analytics/select/y-axis.tsx b/web/components/analytics/custom-analytics/select/y-axis.tsx index 60445794578..9f66c6b5450 100644 --- a/web/components/analytics/custom-analytics/select/y-axis.tsx +++ b/web/components/analytics/custom-analytics/select/y-axis.tsx @@ -15,7 +15,7 @@ export const SelectYAxis: React.FC = ({ value, onChange }) => ( value={value} label={{ANALYTICS_Y_AXIS_VALUES.find((v) => v.value === value)?.label ?? "None"}} onChange={onChange} - width="w-full" + maxHeight="lg" > {ANALYTICS_Y_AXIS_VALUES.map((item) => ( diff --git a/web/components/automation/auto-archive-automation.tsx b/web/components/automation/auto-archive-automation.tsx index 3d5f6352e5f..974efff3a1a 100644 --- a/web/components/automation/auto-archive-automation.tsx +++ b/web/components/automation/auto-archive-automation.tsx @@ -79,7 +79,6 @@ export const AutoArchiveAutomation: React.FC = observer((props) => { handleChange({ archive_in: val }); }} input - width="w-full" disabled={!isAdmin} > <> diff --git a/web/components/automation/auto-close-automation.tsx b/web/components/automation/auto-close-automation.tsx index 49dd77e107d..8d6662c112d 100644 --- a/web/components/automation/auto-close-automation.tsx +++ b/web/components/automation/auto-close-automation.tsx @@ -106,7 +106,6 @@ export const AutoCloseAutomation: React.FC = observer((props) => { handleChange({ close_in: val }); }} input - width="w-full" disabled={!isAdmin} > <> @@ -161,7 +160,6 @@ export const AutoCloseAutomation: React.FC = observer((props) => { }} options={options} disabled={!multipleOptions} - width="w-full" input />
diff --git a/web/components/core/theme/theme-switch.tsx b/web/components/core/theme/theme-switch.tsx index 60ef272a295..bcd847a280a 100644 --- a/web/components/core/theme/theme-switch.tsx +++ b/web/components/core/theme/theme-switch.tsx @@ -46,7 +46,6 @@ export const ThemeSwitch: FC = (props) => { } onChange={onChange} input - width="w-full z-20" > {THEME_OPTIONS.map((themeOption) => ( diff --git a/web/components/integration/jira/give-details.tsx b/web/components/integration/jira/give-details.tsx index 90dfe404bb3..efeca487a9a 100644 --- a/web/components/integration/jira/give-details.tsx +++ b/web/components/integration/jira/give-details.tsx @@ -161,7 +161,6 @@ export const JiraGetImportDetail: React.FC = observer(() => { @@ -172,6 +171,7 @@ export const JiraGetImportDetail: React.FC = observer(() => { )} } + optionsClassName="w-full" > {workspaceProjectIds && workspaceProjectIds.length > 0 ? ( workspaceProjectIds.map((projectId) => { diff --git a/web/components/integration/jira/import-users.tsx b/web/components/integration/jira/import-users.tsx index dc23d6a7bfd..63cf84b4f7d 100644 --- a/web/components/integration/jira/import-users.tsx +++ b/web/components/integration/jira/import-users.tsx @@ -82,7 +82,7 @@ export const JiraImportUsers: FC = () => { input value={value} onChange={onChange} - width="w-full" + optionsClassName="w-full" label={{Boolean(value) ? value : ("Ignore" as any)}} > Invite by email diff --git a/web/components/issues/view-select/estimate.tsx b/web/components/issues/view-select/estimate.tsx index 45cf2db3cea..7fcb9dd9885 100644 --- a/web/components/issues/view-select/estimate.tsx +++ b/web/components/issues/view-select/estimate.tsx @@ -42,7 +42,7 @@ export const ViewEstimateSelect: React.FC = observer((props) => { maxHeight="md" noChevron disabled={disabled} - width="w-full min-w-[8rem]" + optionsClassName="w-full" > <> diff --git a/web/components/modules/sidebar.tsx b/web/components/modules/sidebar.tsx index 803e1879bf9..5b2c8f17047 100644 --- a/web/components/modules/sidebar.tsx +++ b/web/components/modules/sidebar.tsx @@ -291,7 +291,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { {isEditingAllowed && ( - + setModuleDeleteModal(true)}> diff --git a/web/components/notifications/select-snooze-till-modal.tsx b/web/components/notifications/select-snooze-till-modal.tsx index b8312d7d0d8..ab3497bb8c5 100644 --- a/web/components/notifications/select-snooze-till-modal.tsx +++ b/web/components/notifications/select-snooze-till-modal.tsx @@ -200,7 +200,7 @@ export const SnoozeNotificationModal: FC = (props) => { )}
} - width="w-full" + optionsClassName="w-full" input >
diff --git a/web/components/project/send-project-invitation-modal.tsx b/web/components/project/send-project-invitation-modal.tsx index db7572dd9a0..b165a40ae41 100644 --- a/web/components/project/send-project-invitation-modal.tsx +++ b/web/components/project/send-project-invitation-modal.tsx @@ -236,7 +236,7 @@ export const SendProjectInvitationModal: React.FC = observer((props) => { onChange(val); }} options={options} - width="w-full min-w-[12rem]" + optionsClassName="w-full" /> ); }} @@ -266,7 +266,7 @@ export const SendProjectInvitationModal: React.FC = observer((props) => {
} input - width="w-full" + optionsClassName="w-full" > {Object.entries(ROLE).map(([key, label]) => { if (parseInt(key) > (currentProjectRole ?? EUserProjectRoles.GUEST)) return null; diff --git a/web/components/states/create-state-modal.tsx b/web/components/states/create-state-modal.tsx index 95ceb894f4b..db91bb6b058 100644 --- a/web/components/states/create-state-modal.tsx +++ b/web/components/states/create-state-modal.tsx @@ -160,7 +160,7 @@ export const CreateStateModal: React.FC = observer((props) => { value={value} label={GROUP_CHOICES[value as keyof typeof GROUP_CHOICES]} onChange={onChange} - width="w-full" + optionsClassName="w-full" input > {Object.keys(GROUP_CHOICES).map((key) => ( diff --git a/web/components/workspace/create-workspace-form.tsx b/web/components/workspace/create-workspace-form.tsx index bbdd6661d00..423c80640a7 100644 --- a/web/components/workspace/create-workspace-form.tsx +++ b/web/components/workspace/create-workspace-form.tsx @@ -201,7 +201,7 @@ export const CreateWorkspaceForm: FC = observer((props) => { } buttonClassName="!border-[0.5px] !border-custom-border-200 !shadow-none" input - width="w-full" + optionsClassName="w-full" > {ORGANIZATION_SIZE.map((item) => ( diff --git a/web/components/workspace/send-workspace-invitation-modal.tsx b/web/components/workspace/send-workspace-invitation-modal.tsx index 3fd818402d2..35b5963d097 100644 --- a/web/components/workspace/send-workspace-invitation-modal.tsx +++ b/web/components/workspace/send-workspace-invitation-modal.tsx @@ -165,7 +165,7 @@ export const SendWorkspaceInvitationModal: React.FC = observer((props) => value={value} label={{ROLE[value]}} onChange={onChange} - width="w-full" + optionsClassName="w-full" input > {Object.entries(ROLE).map(([key, value]) => { diff --git a/web/components/workspace/settings/workspace-details.tsx b/web/components/workspace/settings/workspace-details.tsx index a309012d12b..3063855fd48 100644 --- a/web/components/workspace/settings/workspace-details.tsx +++ b/web/components/workspace/settings/workspace-details.tsx @@ -247,7 +247,7 @@ export const WorkspaceDetails: FC = observer(() => { value={value} onChange={onChange} label={ORGANIZATION_SIZE.find((c) => c === value) ?? "Select organization size"} - width="w-full" + optionsClassName="w-full" buttonClassName="!border-[0.5px] !border-custom-border-200 !shadow-none" input disabled={!isAdmin} diff --git a/web/pages/profile/index.tsx b/web/pages/profile/index.tsx index 88333388ad9..3174c53f3b2 100644 --- a/web/pages/profile/index.tsx +++ b/web/pages/profile/index.tsx @@ -284,7 +284,9 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => { ref={ref} hasError={Boolean(errors.email)} placeholder="Enter your email" - className={`w-full rounded-md cursor-not-allowed !bg-custom-background-80 ${errors.email ? "border-red-500" : ""}`} + className={`w-full rounded-md cursor-not-allowed !bg-custom-background-80 ${ + errors.email ? "border-red-500" : "" + }`} disabled /> )} @@ -306,7 +308,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => { label={value ? value.toString() : "Select your role"} buttonClassName={errors.role ? "border-red-500" : "border-none"} className="rounded-md border-[0.5px] !border-custom-border-200" - width="w-full" + optionsClassName="w-full" input > {USER_ROLES.map((item) => ( From de5495691c3f03b005e5ff6993871ee04b031840 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Mon, 22 Jan 2024 17:28:08 +0530 Subject: [PATCH 3/6] refactor: @plane/ui dropdowns --- packages/ui/src/dropdowns/custom-search-select.tsx | 3 +++ packages/ui/src/dropdowns/custom-select.tsx | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index 9cfe2af263b..9695eb93194 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -153,6 +153,9 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { } ) } + onClick={() => { + if (!multiple) closeDropdown(); + }} > {({ selected }) => ( <> diff --git a/packages/ui/src/dropdowns/custom-select.tsx b/packages/ui/src/dropdowns/custom-select.tsx index 0af8a538e4c..0fa183cb2ce 100644 --- a/packages/ui/src/dropdowns/custom-select.tsx +++ b/packages/ui/src/dropdowns/custom-select.tsx @@ -91,7 +91,7 @@ const CustomSelect = (props: ICustomSelectProps) => { )} {isOpen && ( - + closeDropdown()} static>
Date: Tue, 23 Jan 2024 12:44:18 +0530 Subject: [PATCH 4/6] fix: build errors --- web/components/core/activity.tsx | 28 +-- web/components/cycles/cycles-board-card.tsx | 2 +- web/components/cycles/cycles-list-item.tsx | 2 +- web/components/cycles/sidebar.tsx | 2 +- web/components/dropdowns/date.tsx | 184 +++++++++--------- web/components/headers/cycle-issues.tsx | 1 - web/components/headers/module-issues.tsx | 1 - .../issues/issue-detail/cycle-select.tsx | 1 - .../issues/issue-detail/module-select.tsx | 1 - .../kanban/headers/group-by-card.tsx | 1 - .../list/headers/group-by-card.tsx | 1 - .../spreadsheet/columns/header-column.tsx | 1 - .../issues/sub-issues/issue-list-item.tsx | 2 +- web/components/modules/module-card-item.tsx | 2 +- web/components/modules/module-list-item.tsx | 2 +- web/components/pages/pages-list/list-item.tsx | 2 +- web/components/project/member-select.tsx | 1 - web/components/views/view-list-item.tsx | 2 +- .../workspace/views/view-list-item.tsx | 2 +- 19 files changed, 102 insertions(+), 136 deletions(-) diff --git a/web/components/core/activity.tsx b/web/components/core/activity.tsx index e49205459ee..2c3038fc15e 100644 --- a/web/components/core/activity.tsx +++ b/web/components/core/activity.tsx @@ -43,7 +43,7 @@ const IssueLink = ({ activity }: { activity: IIssueActivity }) => { }`} target={activity.issue === null ? "_self" : "_blank"} rel={activity.issue === null ? "" : "noopener noreferrer"} - className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline" + className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline whitespace-nowrap" > {activity.issue_detail ? `${activity.project_detail.identifier}-${activity.issue_detail.sequence_id}` : "Issue"}{" "} {activity.issue_detail?.name} @@ -123,7 +123,6 @@ const activityDetails: { to )} - . ); else @@ -136,7 +135,6 @@ const activityDetails: { from )} - . ); }, @@ -181,7 +179,6 @@ const activityDetails: { from )} - . ); }, @@ -300,7 +297,6 @@ const activityDetails: { of )} - . ), icon:
} - width="max-w-[10rem]" noChevron disabled={disableSelect} /> diff --git a/web/components/issues/issue-detail/module-select.tsx b/web/components/issues/issue-detail/module-select.tsx index 4ac5f1fa594..05d7035b687 100644 --- a/web/components/issues/issue-detail/module-select.tsx +++ b/web/components/issues/issue-detail/module-select.tsx @@ -93,7 +93,6 @@ export const IssueModuleSelect: React.FC = observer((props)
} - width="max-w-[10rem]" noChevron disabled={disableSelect} /> diff --git a/web/components/issues/issue-layouts/kanban/headers/group-by-card.tsx b/web/components/issues/issue-layouts/kanban/headers/group-by-card.tsx index 3bb49106b36..512a071bc33 100644 --- a/web/components/issues/issue-layouts/kanban/headers/group-by-card.tsx +++ b/web/components/issues/issue-layouts/kanban/headers/group-by-card.tsx @@ -135,7 +135,6 @@ export const HeaderGroupByCard: FC = observer((props) => { {!disableIssueCreation && (renderExistingIssueModal ? ( diff --git a/web/components/issues/issue-layouts/list/headers/group-by-card.tsx b/web/components/issues/issue-layouts/list/headers/group-by-card.tsx index 8e7b0ace384..06ada23ca45 100644 --- a/web/components/issues/issue-layouts/list/headers/group-by-card.tsx +++ b/web/components/issues/issue-layouts/list/headers/group-by-card.tsx @@ -70,7 +70,6 @@ export const HeaderGroupByCard = observer( {!disableIssueCreation && (renderExistingIssueModal ? ( diff --git a/web/components/issues/issue-layouts/spreadsheet/columns/header-column.tsx b/web/components/issues/issue-layouts/spreadsheet/columns/header-column.tsx index 0400002185e..dc9f8c7c69a 100644 --- a/web/components/issues/issue-layouts/spreadsheet/columns/header-column.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/columns/header-column.tsx @@ -62,7 +62,6 @@ export const SpreadsheetHeaderColumn = (props: Props) => {
} - width="xl" placement="bottom-end" > handleOrderBy(propertyDetails.ascendingOrderKey, property)}> diff --git a/web/components/issues/sub-issues/issue-list-item.tsx b/web/components/issues/sub-issues/issue-list-item.tsx index 0e8fe76a2c9..03347347050 100644 --- a/web/components/issues/sub-issues/issue-list-item.tsx +++ b/web/components/issues/sub-issues/issue-list-item.tsx @@ -134,7 +134,7 @@ export const IssueListItem: React.FC = observer((props) => {
- + {disabled && ( handleIssueCrudState("update", parentIssueId, issue)}>
diff --git a/web/components/modules/module-card-item.tsx b/web/components/modules/module-card-item.tsx index f69e6ba2e8c..f9236ef10f0 100644 --- a/web/components/modules/module-card-item.tsx +++ b/web/components/modules/module-card-item.tsx @@ -232,7 +232,7 @@ export const ModuleCardItem: React.FC = observer((props) => { ))} - + {isEditingAllowed && ( <> diff --git a/web/components/modules/module-list-item.tsx b/web/components/modules/module-list-item.tsx index bb868d7e073..c24e75198fa 100644 --- a/web/components/modules/module-list-item.tsx +++ b/web/components/modules/module-list-item.tsx @@ -209,7 +209,7 @@ export const ModuleListItem: React.FC = observer((props) => { ))} - + {isEditingAllowed && ( <> diff --git a/web/components/pages/pages-list/list-item.tsx b/web/components/pages/pages-list/list-item.tsx index 99b50e3c018..960e222ce1e 100644 --- a/web/components/pages/pages-list/list-item.tsx +++ b/web/components/pages/pages-list/list-item.tsx @@ -235,7 +235,7 @@ export const PagesListItem: FC = observer(({ pageId, projectId } > - + {archived_at ? ( <> {userCanArchive && ( diff --git a/web/components/project/member-select.tsx b/web/components/project/member-select.tsx index e525bd6f558..e019a29e74e 100644 --- a/web/components/project/member-select.tsx +++ b/web/components/project/member-select.tsx @@ -69,7 +69,6 @@ export const MemberSelect: React.FC = observer((props) => { ] } maxHeight="md" - width="w-full" onChange={onChange} disabled={isDisabled} /> diff --git a/web/components/views/view-list-item.tsx b/web/components/views/view-list-item.tsx index 1b04dbff039..8da507539db 100644 --- a/web/components/views/view-list-item.tsx +++ b/web/components/views/view-list-item.tsx @@ -123,7 +123,7 @@ export const ProjectViewListItem: React.FC = observer((props) => { ))} - + {isEditingAllowed && ( <> = observer((props) => {

{totalFilters} {totalFilters === 1 ? "filter" : "filters"}

- + { e.preventDefault(); From fbe7a31d94792dd16bb62720369e66b1fbcc59f4 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Tue, 23 Jan 2024 13:18:06 +0530 Subject: [PATCH 5/6] fix: list layout dropdowns positioning --- web/components/issues/issue-layouts/list/block.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/issues/issue-layouts/list/block.tsx b/web/components/issues/issue-layouts/list/block.tsx index 820f98fdde9..da63a834e50 100644 --- a/web/components/issues/issue-layouts/list/block.tsx +++ b/web/components/issues/issue-layouts/list/block.tsx @@ -73,7 +73,7 @@ export const IssueBlock: React.FC = observer((props: IssueBlock {!issue?.tempId ? ( <> Date: Tue, 23 Jan 2024 14:13:55 +0530 Subject: [PATCH 6/6] fix: priority dropdown text in dark mode --- web/components/dropdowns/priority.tsx | 28 +++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/web/components/dropdowns/priority.tsx b/web/components/dropdowns/priority.tsx index 7c812880ad7..afcfaa8e494 100644 --- a/web/components/dropdowns/priority.tsx +++ b/web/components/dropdowns/priority.tsx @@ -14,6 +14,7 @@ import { TIssuePriorities } from "@plane/types"; import { TDropdownProps } from "./types"; // constants import { ISSUE_PRIORITIES } from "constants/issue"; +import { useTheme } from "next-themes"; type Props = TDropdownProps & { button?: ReactNode; @@ -228,6 +229,9 @@ export const PriorityDropdown: React.FC = (props) => { }, ], }); + // next-themes + // TODO: remove this after new theming implementation + const { resolvedTheme } = useTheme(); const options = ISSUE_PRIORITIES.map((priority) => ({ value: priority.key, @@ -291,14 +295,18 @@ export const PriorityDropdown: React.FC = (props) => { {buttonVariant === "border-with-text" ? ( ) : buttonVariant === "border-without-text" ? ( = (props) => { ) : buttonVariant === "background-with-text" ? ( ) : buttonVariant === "background-without-text" ? ( = (props) => { ) : buttonVariant === "transparent-with-text" ? ( ) : buttonVariant === "transparent-without-text" ? (