АРХ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: миграция searchable select-пикеров на общий канон

This commit is contained in:
DCCONSTRUCTIONS
2026-04-22 13:48:28 +03:00
parent a49e18d0e5
commit 8fa5de24eb
13 changed files with 127 additions and 59 deletions
@@ -5,9 +5,8 @@
*/
import * as React from "react";
import { useState } from "react";
import type { ICustomSearchSelectOption } from "@plane/types";
import { CustomSearchSelect } from "../dropdowns";
import { SearchSelectionDropdown } from "../dropdowns";
import { cn } from "../utils";
import { Breadcrumbs } from "./breadcrumbs";
@@ -42,18 +41,10 @@ export function BreadcrumbNavigationSearchDropdown(props: TBreadcrumbNavigationS
rotateChevronWhenLast = true,
showLastChevron = true,
} = props;
// state
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const shouldOpenOnItemClick = openOnLabelClick || !handleOnClick;
return (
<CustomSearchSelect
onOpen={() => {
setIsDropdownOpen(true);
}}
onClose={() => {
setIsDropdownOpen(false);
}}
<SearchSelectionDropdown
options={navigationItems}
value={selectedItem}
onChange={(value: string) => {
@@ -61,7 +52,7 @@ export function BreadcrumbNavigationSearchDropdown(props: TBreadcrumbNavigationS
onChange?.(value);
}
}}
customButton={
menuButton={({ open }) => (
<>
<div
onClick={(e) => {
@@ -92,27 +83,26 @@ export function BreadcrumbNavigationSearchDropdown(props: TBreadcrumbNavigationS
{(!isLast || showLastChevron) && (
<Breadcrumbs.Separator
className={cn("rounded-r-sm", {
"bg-layer-1": isDropdownOpen && !isLast,
"bg-layer-1": open && !isLast,
"hover:bg-layer-1": !isLast,
})}
containerClassName="p-0"
iconClassName={cn("group-hover:rotate-90 hover:text-primary", {
"text-primary": isDropdownOpen,
"rotate-90": isDropdownOpen || (isLast && rotateChevronWhenLast),
"text-primary": open,
"rotate-90": open || (isLast && rotateChevronWhenLast),
})}
showDivider={!isLast}
/>
)}
</>
}
)}
disabled={navigationDisabled}
className="h-full rounded-sm"
customButtonClassName={cn(
"group flex h-full cursor-pointer items-center gap-0.5 rounded-sm outline-none hover:bg-surface-2",
{
"bg-surface-2": isDropdownOpen,
}
)}
menuButtonWrapperClassName={({ open }) =>
cn("group flex h-full cursor-pointer items-center gap-0.5 rounded-sm outline-none hover:bg-surface-2", {
"bg-surface-2": open,
})
}
/>
);
}
@@ -6,6 +6,7 @@
export * from "./context-menu";
export * from "./action-dropdown";
export * from "./search-selection-dropdown";
export * from "./custom-menu";
export * from "./custom-select";
export * from "./custom-search-select";
@@ -0,0 +1,80 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import type { ReactNode } from "react";
import { useMemo, useState } from "react";
import type { ICustomSearchSelectOption } from "@plane/types";
import { CustomSearchSelect } from "./custom-search-select";
import type { IDropdownProps } from "./helper";
export type TSearchSelectionDropdownOption = ICustomSearchSelectOption & {
shouldRender?: boolean;
};
type TSearchSelectionDropdownBaseProps = Omit<IDropdownProps, "customButton" | "customButtonClassName"> & {
footerOption?: ReactNode;
menuButton?: ReactNode | ((props: { open: boolean }) => ReactNode);
menuButtonWrapperClassName?: string | ((props: { open: boolean }) => string);
noResultsMessage?: string;
onChange: (value: any) => void;
onClose?: () => void;
options?: TSearchSelectionDropdownOption[];
};
type TSingleValueProps = {
multiple?: false;
value: any;
};
type TMultipleValuesProps = {
multiple: true;
value: any[] | null;
};
type Props = TSearchSelectionDropdownBaseProps & (TSingleValueProps | TMultipleValuesProps);
export function SearchSelectionDropdown(props: Props) {
const {
defaultOpen = false,
menuButton,
menuButtonWrapperClassName,
onOpen,
onClose,
options,
...rest
} = props;
const [isOpen, setIsOpen] = useState(defaultOpen);
const renderedOptions = useMemo(
() => options?.filter((option) => option.shouldRender !== false),
[options]
);
const resolvedMenuButton = typeof menuButton === "function" ? menuButton({ open: isOpen }) : menuButton;
const resolvedMenuButtonWrapperClassName =
typeof menuButtonWrapperClassName === "function"
? menuButtonWrapperClassName({ open: isOpen })
: menuButtonWrapperClassName;
return (
<CustomSearchSelect
{...rest}
customButton={resolvedMenuButton}
customButtonClassName={menuButton ? resolvedMenuButtonWrapperClassName : undefined}
defaultOpen={defaultOpen}
onClose={() => {
setIsOpen(false);
onClose?.();
}}
onOpen={() => {
setIsOpen(true);
onOpen?.();
}}
options={renderedOptions}
/>
);
}