АРХ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: миграция searchable select-пикеров на общий канон
This commit is contained in:
parent
a49e18d0e5
commit
8fa5de24eb
|
|
@ -11,8 +11,8 @@ import { Calendar } from "lucide-react";
|
||||||
// plane package imports
|
// plane package imports
|
||||||
import { ANALYTICS_DURATION_FILTER_OPTIONS } from "@plane/constants";
|
import { ANALYTICS_DURATION_FILTER_OPTIONS } from "@plane/constants";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { CustomSearchSelect } from "@plane/ui";
|
|
||||||
// types
|
// types
|
||||||
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
import type { TDropdownProps } from "@/components/dropdowns/types";
|
import type { TDropdownProps } from "@/components/dropdowns/types";
|
||||||
|
|
||||||
type Props = TDropdownProps & {
|
type Props = TDropdownProps & {
|
||||||
|
|
@ -31,25 +31,21 @@ function DurationDropdown({ placeholder = "Duration", onChange, value }: Props)
|
||||||
useTranslation();
|
useTranslation();
|
||||||
|
|
||||||
const options = ANALYTICS_DURATION_FILTER_OPTIONS.map((option) => ({
|
const options = ANALYTICS_DURATION_FILTER_OPTIONS.map((option) => ({
|
||||||
value: option.value,
|
key: option.value,
|
||||||
query: option.name,
|
title: option.name,
|
||||||
content: (
|
isChecked: value === option.value,
|
||||||
<div className="flex max-w-[300px] items-center gap-2">
|
onClick: () => onChange(option.value),
|
||||||
<span className="flex-grow truncate">{option.name}</span>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
}));
|
}));
|
||||||
return (
|
return (
|
||||||
<CustomSearchSelect
|
<SelectionDropdown
|
||||||
value={value ? [value] : []}
|
|
||||||
onChange={onChange}
|
|
||||||
options={options}
|
options={options}
|
||||||
label={
|
menuButton={
|
||||||
<div className="flex items-center gap-2 p-1">
|
<div className="flex items-center gap-2 p-1">
|
||||||
<Calendar className="h-4 w-4" />
|
<Calendar className="h-4 w-4" />
|
||||||
{value ? ANALYTICS_DURATION_FILTER_OPTIONS.find((opt) => opt.value === value)?.name : placeholder}
|
{value ? ANALYTICS_DURATION_FILTER_OPTIONS.find((opt) => opt.value === value)?.name : placeholder}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
menuButtonWrapperClassName="flex items-center rounded-full border-0 outline-none"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { observer } from "mobx-react";
|
||||||
import { getButtonStyling } from "@plane/propel/button";
|
import { getButtonStyling } from "@plane/propel/button";
|
||||||
import { Logo } from "@plane/propel/emoji-icon-picker";
|
import { Logo } from "@plane/propel/emoji-icon-picker";
|
||||||
import { ChevronDownIcon, ProjectIcon } from "@plane/propel/icons";
|
import { ChevronDownIcon, ProjectIcon } from "@plane/propel/icons";
|
||||||
import { CustomSearchSelect } from "@plane/ui";
|
import { SearchSelectionDropdown } from "@plane/ui";
|
||||||
import { cn } from "@plane/utils";
|
import { cn } from "@plane/utils";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject } from "@/hooks/store/use-project";
|
import { useProject } from "@/hooks/store/use-project";
|
||||||
|
|
@ -44,12 +44,12 @@ export const ProjectSelect = observer(function ProjectSelect(props: Props) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
value={value ?? []}
|
value={value ?? []}
|
||||||
onChange={(val: string[]) => onChange(val)}
|
onChange={(val: string[]) => onChange(val)}
|
||||||
options={options}
|
options={options}
|
||||||
className="border-none p-0"
|
className="border-none p-0"
|
||||||
customButton={
|
menuButton={
|
||||||
<div className={cn(getButtonStyling("secondary", "lg"), "gap-2")}>
|
<div className={cn(getButtonStyling("secondary", "lg"), "gap-2")}>
|
||||||
<ProjectIcon className="h-4 w-4" />
|
<ProjectIcon className="h-4 w-4" />
|
||||||
{value && value.length > 3
|
{value && value.length > 3
|
||||||
|
|
@ -63,7 +63,7 @@ export const ProjectSelect = observer(function ProjectSelect(props: Props) {
|
||||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
customButtonClassName="border-none p-0 bg-transparent hover:bg-transparent w-auto h-auto"
|
menuButtonWrapperClassName="h-auto w-auto border-none bg-transparent p-0 hover:bg-transparent"
|
||||||
multiple
|
multiple
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { PROJECT_AUTOMATION_MONTHS, EUserPermissions, EUserPermissionsLevel, EIc
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { StateGroupIcon, StatePropertyIcon } from "@plane/propel/icons";
|
import { StateGroupIcon, StatePropertyIcon } from "@plane/propel/icons";
|
||||||
import type { IProject } from "@plane/types";
|
import type { IProject } from "@plane/types";
|
||||||
import { CustomSelect, CustomSearchSelect, ToggleSwitch, Loader } from "@plane/ui";
|
import { CustomSelect, Loader, SearchSelectionDropdown, ToggleSwitch } from "@plane/ui";
|
||||||
import { SelectMonthModal } from "@/components/automation";
|
import { SelectMonthModal } from "@/components/automation";
|
||||||
import { SettingsControlItem } from "@/components/settings/control-item";
|
import { SettingsControlItem } from "@/components/settings/control-item";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -151,7 +151,7 @@ export const AutoCloseAutomation = observer(function AutoCloseAutomation(props:
|
||||||
{t("project_settings.automations.auto-close.auto_close_status")}
|
{t("project_settings.automations.auto-close.auto_close_status")}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-1/2">
|
<div className="w-1/2">
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
value={currentProjectDetails?.default_state ?? defaultState}
|
value={currentProjectDetails?.default_state ?? defaultState}
|
||||||
label={
|
label={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
// import { Tooltip } from "@plane/propel/tooltip";
|
// import { Tooltip } from "@plane/propel/tooltip";
|
||||||
// import { EIssuesStoreType } from "@plane/types";
|
// import { EIssuesStoreType } from "@plane/types";
|
||||||
import type { TWorkItemFilterExpression } from "@plane/types";
|
import type { TWorkItemFilterExpression } from "@plane/types";
|
||||||
import { CustomSearchSelect, CustomSelect } from "@plane/ui";
|
import { CustomSelect, SearchSelectionDropdown } from "@plane/ui";
|
||||||
// import { WorkspaceLevelWorkItemFiltersHOC } from "@/components/work-item-filters/filters-hoc/workspace-level";
|
// import { WorkspaceLevelWorkItemFiltersHOC } from "@/components/work-item-filters/filters-hoc/workspace-level";
|
||||||
// import { WorkItemFiltersRow } from "@/components/work-item-filters/filters-row";
|
// import { WorkItemFiltersRow } from "@/components/work-item-filters/filters-row";
|
||||||
import { useProject } from "@/hooks/store/use-project";
|
import { useProject } from "@/hooks/store/use-project";
|
||||||
|
|
@ -155,7 +155,7 @@ export const ExportForm = observer(function ExportForm(props: Props) {
|
||||||
name="project"
|
name="project"
|
||||||
disabled={!isMember && (!hasProjects || !canPerformAnyCreateAction)}
|
disabled={!isMember && (!hasProjects || !canPerformAnyCreateAction)}
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
value={value ?? []}
|
value={value ?? []}
|
||||||
onChange={(val: string[]) => onChange(val)}
|
onChange={(val: string[]) => onChange(val)}
|
||||||
options={options}
|
options={options}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { Button } from "@plane/propel/button";
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { IUser, IImporterService } from "@plane/types";
|
import type { IUser, IImporterService } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import { Checkbox, CustomSearchSelect, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
import { Checkbox, EModalPosition, EModalWidth, ModalCore, SearchSelectionDropdown } from "@plane/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject } from "@/hooks/store/use-project";
|
import { useProject } from "@/hooks/store/use-project";
|
||||||
import { useUser } from "@/hooks/store/user";
|
import { useUser } from "@/hooks/store/user";
|
||||||
|
|
@ -122,7 +122,7 @@ export const Exporter = observer(function Exporter(props: Props) {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
value={value ?? []}
|
value={value ?? []}
|
||||||
onChange={(val: string[]) => onChange(val)}
|
onChange={(val: string[]) => onChange(val)}
|
||||||
options={options}
|
options={options}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// plane imports
|
// plane imports
|
||||||
import { CustomSearchSelect } from "@plane/ui";
|
import { SearchSelectionDropdown } from "@plane/ui";
|
||||||
import { cn } from "@plane/utils";
|
import { cn } from "@plane/utils";
|
||||||
// hooks
|
// hooks
|
||||||
import useTimezone from "@/hooks/use-timezone";
|
import useTimezone from "@/hooks/use-timezone";
|
||||||
|
|
@ -39,7 +39,7 @@ export const TimezoneSelect = observer(function TimezoneSelect(props: TTimezoneS
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
value={value}
|
value={value}
|
||||||
label={value && selectedValue ? selectedValue(value) : label}
|
label={value && selectedValue ? selectedValue(value) : label}
|
||||||
options={isDisabled || disabled ? [] : timezones}
|
options={isDisabled || disabled ? [] : timezones}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import useSWRInfinite from "swr/infinite";
|
||||||
import type { IWorkspaceIntegration } from "@plane/types";
|
import type { IWorkspaceIntegration } from "@plane/types";
|
||||||
// services
|
// services
|
||||||
// ui
|
// ui
|
||||||
import { CustomSearchSelect } from "@plane/ui";
|
import { SearchSelectionDropdown } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "@plane/utils";
|
import { truncateText } from "@plane/utils";
|
||||||
import { ProjectService } from "@/services/project";
|
import { ProjectService } from "@/services/project";
|
||||||
|
|
@ -62,7 +62,7 @@ export function SelectRepository(props: Props) {
|
||||||
if (userRepositories.length < 1) return null;
|
if (userRepositories.length < 1) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
value={value}
|
value={value}
|
||||||
options={options}
|
options={options}
|
||||||
onChange={(val: string) => {
|
onChange={(val: string) => {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { observer } from "mobx-react";
|
||||||
// plane imports
|
// plane imports
|
||||||
import { ProjectIcon } from "@plane/propel/icons";
|
import { ProjectIcon } from "@plane/propel/icons";
|
||||||
import type { ICustomSearchSelectOption } from "@plane/types";
|
import type { ICustomSearchSelectOption } from "@plane/types";
|
||||||
import { CustomSearchSelect } from "@plane/ui";
|
import { SearchSelectionDropdown } from "@plane/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject } from "@/hooks/store/use-project";
|
import { useProject } from "@/hooks/store/use-project";
|
||||||
import { useUserPermissions } from "@/hooks/store/user";
|
import { useUserPermissions } from "@/hooks/store/user";
|
||||||
|
|
@ -100,13 +100,13 @@ export const ProjectHeader = observer(function ProjectHeader(props: TProjectHead
|
||||||
if (!currentProjectDetails) return null;
|
if (!currentProjectDetails) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
options={switcherOptions}
|
options={switcherOptions}
|
||||||
value={currentProjectDetails.id}
|
value={currentProjectDetails.id}
|
||||||
onChange={handleProjectChange}
|
onChange={handleProjectChange}
|
||||||
customButton={currentProjectDetails ? <ProjectHeaderButton project={currentProjectDetails} /> : null}
|
menuButton={currentProjectDetails ? <ProjectHeaderButton project={currentProjectDetails} /> : null}
|
||||||
className="h-full rounded"
|
className="h-full rounded"
|
||||||
customButtonClassName="group flex items-center gap-0.5 rounded-sm hover:bg-surface-2 outline-none cursor-pointer h-full"
|
menuButtonWrapperClassName="group flex h-full cursor-pointer items-center gap-0.5 rounded-sm outline-none hover:bg-surface-2"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { Ban } from "lucide-react";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { EUserProjectRoles } from "@plane/types";
|
import { EUserProjectRoles } from "@plane/types";
|
||||||
// plane ui
|
// plane ui
|
||||||
import { Avatar, CustomSearchSelect } from "@plane/ui";
|
import { Avatar, SearchSelectionDropdown } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { getFileURL } from "@plane/utils";
|
import { getFileURL } from "@plane/utils";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -62,7 +62,7 @@ export const MemberSelect = observer(function MemberSelect(props: Props) {
|
||||||
const selectedOption = projectId ? getProjectMemberDetails(value, projectId.toString()) : null;
|
const selectedOption = projectId ? getProjectMemberDetails(value, projectId.toString()) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
value={value}
|
value={value}
|
||||||
label={
|
label={
|
||||||
<div className="flex h-3.5 items-center gap-2">
|
<div className="flex h-3.5 items-center gap-2">
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { useTranslation } from "@plane/i18n";
|
||||||
import { Button } from "@plane/propel/button";
|
import { Button } from "@plane/propel/button";
|
||||||
import { PlusIcon, CloseIcon, ChevronDownIcon } from "@plane/propel/icons";
|
import { PlusIcon, CloseIcon, ChevronDownIcon } from "@plane/propel/icons";
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import { Avatar, CustomSelect, CustomSearchSelect, EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
import { Avatar, CustomSelect, EModalPosition, EModalWidth, ModalCore, SearchSelectionDropdown } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { getFileURL } from "@plane/utils";
|
import { getFileURL } from "@plane/utils";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -193,10 +193,10 @@ export const SendProjectInvitationModal = observer(function SendProjectInvitatio
|
||||||
render={({ field: { value, onChange } }) => {
|
render={({ field: { value, onChange } }) => {
|
||||||
const selectedMember = getWorkspaceMemberDetails(value);
|
const selectedMember = getWorkspaceMemberDetails(value);
|
||||||
return (
|
return (
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
value={value}
|
value={value}
|
||||||
customButton={
|
menuButton={
|
||||||
<button className="shadow-sm flex w-full items-center justify-between gap-1 rounded-md border border-subtle px-3 py-2 text-left text-13 text-secondary duration-300 hover:bg-layer-1 hover:text-primary focus:outline-none">
|
<>
|
||||||
{value && value !== "" ? (
|
{value && value !== "" ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Avatar
|
<Avatar
|
||||||
|
|
@ -211,8 +211,9 @@ export const SendProjectInvitationModal = observer(function SendProjectInvitatio
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
</button>
|
</>
|
||||||
}
|
}
|
||||||
|
menuButtonWrapperClassName="shadow-sm flex w-full items-center justify-between gap-1 rounded-md border border-subtle px-3 py-2 text-left text-13 text-secondary duration-300 hover:bg-layer-1 hover:text-primary focus:outline-none"
|
||||||
onChange={(val: string) => {
|
onChange={(val: string) => {
|
||||||
onChange(val);
|
onChange(val);
|
||||||
// Update the role to the workspace role when member ID changes
|
// Update the role to the workspace role when member ID changes
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useState } from "react";
|
|
||||||
import type { ICustomSearchSelectOption } from "@plane/types";
|
import type { ICustomSearchSelectOption } from "@plane/types";
|
||||||
import { CustomSearchSelect } from "../dropdowns";
|
import { SearchSelectionDropdown } from "../dropdowns";
|
||||||
import { cn } from "../utils";
|
import { cn } from "../utils";
|
||||||
import { Breadcrumbs } from "./breadcrumbs";
|
import { Breadcrumbs } from "./breadcrumbs";
|
||||||
|
|
||||||
|
|
@ -42,18 +41,10 @@ export function BreadcrumbNavigationSearchDropdown(props: TBreadcrumbNavigationS
|
||||||
rotateChevronWhenLast = true,
|
rotateChevronWhenLast = true,
|
||||||
showLastChevron = true,
|
showLastChevron = true,
|
||||||
} = props;
|
} = props;
|
||||||
// state
|
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
||||||
const shouldOpenOnItemClick = openOnLabelClick || !handleOnClick;
|
const shouldOpenOnItemClick = openOnLabelClick || !handleOnClick;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSearchSelect
|
<SearchSelectionDropdown
|
||||||
onOpen={() => {
|
|
||||||
setIsDropdownOpen(true);
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setIsDropdownOpen(false);
|
|
||||||
}}
|
|
||||||
options={navigationItems}
|
options={navigationItems}
|
||||||
value={selectedItem}
|
value={selectedItem}
|
||||||
onChange={(value: string) => {
|
onChange={(value: string) => {
|
||||||
|
|
@ -61,7 +52,7 @@ export function BreadcrumbNavigationSearchDropdown(props: TBreadcrumbNavigationS
|
||||||
onChange?.(value);
|
onChange?.(value);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
customButton={
|
menuButton={({ open }) => (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|
@ -92,27 +83,26 @@ export function BreadcrumbNavigationSearchDropdown(props: TBreadcrumbNavigationS
|
||||||
{(!isLast || showLastChevron) && (
|
{(!isLast || showLastChevron) && (
|
||||||
<Breadcrumbs.Separator
|
<Breadcrumbs.Separator
|
||||||
className={cn("rounded-r-sm", {
|
className={cn("rounded-r-sm", {
|
||||||
"bg-layer-1": isDropdownOpen && !isLast,
|
"bg-layer-1": open && !isLast,
|
||||||
"hover:bg-layer-1": !isLast,
|
"hover:bg-layer-1": !isLast,
|
||||||
})}
|
})}
|
||||||
containerClassName="p-0"
|
containerClassName="p-0"
|
||||||
iconClassName={cn("group-hover:rotate-90 hover:text-primary", {
|
iconClassName={cn("group-hover:rotate-90 hover:text-primary", {
|
||||||
"text-primary": isDropdownOpen,
|
"text-primary": open,
|
||||||
"rotate-90": isDropdownOpen || (isLast && rotateChevronWhenLast),
|
"rotate-90": open || (isLast && rotateChevronWhenLast),
|
||||||
})}
|
})}
|
||||||
showDivider={!isLast}
|
showDivider={!isLast}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
disabled={navigationDisabled}
|
disabled={navigationDisabled}
|
||||||
className="h-full rounded-sm"
|
className="h-full rounded-sm"
|
||||||
customButtonClassName={cn(
|
menuButtonWrapperClassName={({ open }) =>
|
||||||
"group flex h-full cursor-pointer items-center gap-0.5 rounded-sm outline-none hover:bg-surface-2",
|
cn("group flex h-full cursor-pointer items-center gap-0.5 rounded-sm outline-none hover:bg-surface-2", {
|
||||||
{
|
"bg-surface-2": open,
|
||||||
"bg-surface-2": isDropdownOpen,
|
})
|
||||||
}
|
}
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
export * from "./context-menu";
|
export * from "./context-menu";
|
||||||
export * from "./action-dropdown";
|
export * from "./action-dropdown";
|
||||||
|
export * from "./search-selection-dropdown";
|
||||||
export * from "./custom-menu";
|
export * from "./custom-menu";
|
||||||
export * from "./custom-select";
|
export * from "./custom-select";
|
||||||
export * from "./custom-search-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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue