[WEB-1201] chore: dropdown options hierarchy improvements (#8501)
* chore: sortBySelectedFirst and sortByCurrentUserThenSelected utils added * chore: members dropdown updated * chore: module dropdown updated * chore: project and label dropdown updated * chore: code refactor
This commit is contained in:
parent
7607cc9b10
commit
bf521b7b03
7 changed files with 107 additions and 14 deletions
|
|
@ -183,6 +183,7 @@ export const MemberDropdownBase = observer(function MemberDropdownBase(props: TM
|
|||
optionsClassName={optionsClassName}
|
||||
placement={placement}
|
||||
referenceElement={referenceElement}
|
||||
value={value}
|
||||
/>
|
||||
)}
|
||||
</ComboDropDown>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { CheckIcon, SearchIcon, SuspendedUserIcon } from "@plane/propel/icons";
|
|||
import { EPillSize, EPillVariant, Pill } from "@plane/propel/pill";
|
||||
import type { IUserLite } from "@plane/types";
|
||||
import { Avatar } from "@plane/ui";
|
||||
import { cn, getFileURL } from "@plane/utils";
|
||||
import { cn, getFileURL, sortByCurrentUserThenSelected } from "@plane/utils";
|
||||
// hooks
|
||||
import { useMember } from "@/hooks/store/use-member";
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
|
|
@ -32,6 +32,7 @@ interface Props {
|
|||
optionsClassName?: string;
|
||||
placement: Placement | undefined;
|
||||
referenceElement: HTMLButtonElement | null;
|
||||
value?: string[] | string | null;
|
||||
}
|
||||
|
||||
export const MemberOptions = observer(function MemberOptions(props: Props) {
|
||||
|
|
@ -43,6 +44,7 @@ export const MemberOptions = observer(function MemberOptions(props: Props) {
|
|||
optionsClassName = "",
|
||||
placement,
|
||||
referenceElement,
|
||||
value,
|
||||
} = props;
|
||||
// router
|
||||
const { workspaceSlug } = useParams();
|
||||
|
|
@ -117,8 +119,11 @@ export const MemberOptions = observer(function MemberOptions(props: Props) {
|
|||
})
|
||||
.filter((o) => !!o);
|
||||
|
||||
const filteredOptions =
|
||||
query === "" ? options : options?.filter((o) => o?.query.toLowerCase().includes(query.toLowerCase()));
|
||||
const filteredOptions = sortByCurrentUserThenSelected(
|
||||
query === "" ? options : options?.filter((o) => o?.query.toLowerCase().includes(query.toLowerCase())),
|
||||
value,
|
||||
currentUser?.id
|
||||
);
|
||||
|
||||
return createPortal(
|
||||
<Combobox.Options data-prevent-outside-click static>
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ export const ModuleDropdownBase = observer(function ModuleDropdownBase(props: TM
|
|||
multiple={multiple}
|
||||
getModuleById={getModuleById}
|
||||
moduleIds={moduleIds}
|
||||
value={value}
|
||||
/>
|
||||
)}
|
||||
</ComboDropDown>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { Combobox } from "@headlessui/react";
|
|||
import { useTranslation } from "@plane/i18n";
|
||||
import { CheckIcon, SearchIcon, ModuleIcon } from "@plane/propel/icons";
|
||||
import type { IModule } from "@plane/types";
|
||||
import { cn } from "@plane/utils";
|
||||
import { cn, sortBySelectedFirst } from "@plane/utils";
|
||||
// hooks
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
|
||||
|
|
@ -33,10 +33,11 @@ interface Props {
|
|||
onDropdownOpen?: () => void;
|
||||
placement: Placement | undefined;
|
||||
referenceElement: HTMLButtonElement | null;
|
||||
value?: string[] | string | null;
|
||||
}
|
||||
|
||||
export const ModuleOptions = observer(function ModuleOptions(props: Props) {
|
||||
const { getModuleById, isOpen, moduleIds, multiple, onDropdownOpen, placement, referenceElement } = props;
|
||||
const { getModuleById, isOpen, moduleIds, multiple, onDropdownOpen, placement, referenceElement, value } = props;
|
||||
// refs
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
// states
|
||||
|
|
@ -106,8 +107,10 @@ export const ModuleOptions = observer(function ModuleOptions(props: Props) {
|
|||
),
|
||||
});
|
||||
|
||||
const filteredOptions =
|
||||
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase()));
|
||||
const filteredOptions = sortBySelectedFirst(
|
||||
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase())),
|
||||
value
|
||||
);
|
||||
|
||||
return (
|
||||
<Combobox.Options className="fixed z-10" static>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { useTranslation } from "@plane/i18n";
|
|||
import { Logo } from "@plane/propel/emoji-icon-picker";
|
||||
import { CheckIcon, SearchIcon, ProjectIcon, ChevronDownIcon } from "@plane/propel/icons";
|
||||
import { ComboDropDown } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
import { cn, sortBySelectedFirst } from "@plane/utils";
|
||||
// components
|
||||
// hooks
|
||||
import { useDropdown } from "@/hooks/use-dropdown";
|
||||
|
|
@ -116,10 +116,13 @@ export const ProjectDropdownBase = observer(function ProjectDropdownBase(props:
|
|||
};
|
||||
});
|
||||
|
||||
const filteredOptions =
|
||||
query === ""
|
||||
const filteredOptions = sortBySelectedFirst(
|
||||
(query === ""
|
||||
? options?.filter((o) => o?.value !== currentProjectId)
|
||||
: options?.filter((o) => o?.value !== currentProjectId && o?.query.toLowerCase().includes(query.toLowerCase()));
|
||||
: options?.filter((o) => o?.value !== currentProjectId && o?.query.toLowerCase().includes(query.toLowerCase()))
|
||||
)?.filter((o): o is NonNullable<typeof o> => o !== undefined),
|
||||
value
|
||||
);
|
||||
|
||||
const { handleClose, handleKeyDown, handleOnClick, searchInputKeyDown } = useDropdown({
|
||||
dropdownRef,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import type { IIssueLabel } from "@plane/types";
|
|||
import { EUserProjectRoles } from "@plane/types";
|
||||
// components
|
||||
import { ComboDropDown } from "@plane/ui";
|
||||
import { sortBySelectedFirst } from "@plane/utils";
|
||||
// hooks
|
||||
import { useLabel } from "@/hooks/store/use-label";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
|
|
@ -118,8 +119,11 @@ export function LabelDropdown(props: ILabelDropdownProps) {
|
|||
|
||||
const filteredOptions = useMemo(
|
||||
() =>
|
||||
query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())),
|
||||
[options, query]
|
||||
sortBySelectedFirst(
|
||||
query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())),
|
||||
value
|
||||
),
|
||||
[options, query, value]
|
||||
);
|
||||
|
||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||
|
|
@ -270,7 +274,7 @@ export function LabelDropdown(props: ILabelDropdownProps) {
|
|||
<div className={`mt-2 max-h-48 space-y-1 overflow-y-scroll`}>
|
||||
{isLoading ? (
|
||||
<p className="text-center text-secondary">{t("common.loading")}</p>
|
||||
) : filteredOptions.length > 0 ? (
|
||||
) : filteredOptions && filteredOptions.length > 0 ? (
|
||||
filteredOptions.map((option) => (
|
||||
<Combobox.Option
|
||||
key={option.value}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue