АРХ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: миграция searchable select-пикеров на общий канон
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user