onChange(null),
+ },
+ ]
+ : []),
+ ...options
+ .filter((item) => !hiddenOptions?.includes(item.value))
+ .map((item) => ({
+ key: item.value,
+ title: item.label,
+ isChecked: value === item.value,
+ onClick: () => onChange(item.value),
+ })),
+ ]}
+ />
);
}
diff --git a/plane-src/apps/web/core/components/analytics/select/select-y-axis.tsx b/plane-src/apps/web/core/components/analytics/select/select-y-axis.tsx
index 64c69fb..5327b43 100644
--- a/plane-src/apps/web/core/components/analytics/select/select-y-axis.tsx
+++ b/plane-src/apps/web/core/components/analytics/select/select-y-axis.tsx
@@ -10,7 +10,7 @@ import { EEstimateSystem } from "@plane/constants";
import { ProjectIcon } from "@plane/propel/icons";
import type { ChartYAxisMetric } from "@plane/types";
// plane package imports
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
// hooks
import { useProjectEstimates } from "@/hooks/store/estimates";
// plane web constants
@@ -44,27 +44,22 @@ export const SelectYAxis = observer(function SelectYAxis({ value, onChange, hidd
};
return (
-
{options.find((v) => v.value === value)?.label ?? "Add Metric"}
}
- onChange={onChange}
- maxHeight="lg"
- >
- {options.map((item) => {
- if (hiddenOptions?.includes(item.value)) return null;
- return (
- isEstimateEnabled(item.value) && (
-
- {item.label}
-
- )
- );
- })}
-
+ options={options
+ .filter((item) => !hiddenOptions?.includes(item.value))
+ .filter((item) => isEstimateEnabled(item.value))
+ .map((item) => ({
+ key: item.value,
+ title: item.label,
+ isChecked: value === item.value,
+ onClick: () => onChange(item.value),
+ }))}
+ />
);
});
diff --git a/plane-src/apps/web/core/components/common/selection-dropdown.tsx b/plane-src/apps/web/core/components/common/selection-dropdown.tsx
index acecfbc..3e3014c 100644
--- a/plane-src/apps/web/core/components/common/selection-dropdown.tsx
+++ b/plane-src/apps/web/core/components/common/selection-dropdown.tsx
@@ -25,11 +25,12 @@ type Props = {
menuButtonWrapperClassName?: string;
options: TSelectionDropdownOption[];
placement?: Placement;
+ tabIndex?: number;
title?: ReactNode;
};
export function SelectionDropdown(props: Props) {
- const { disabled = false, menuButton, menuButtonWrapperClassName, options, placement = "bottom-start", title } = props;
+ const { disabled = false, menuButton, menuButtonWrapperClassName, options, placement = "bottom-start", tabIndex, title } = props;
const renderedOptions = options.filter((option) => option.shouldRender !== false);
@@ -39,6 +40,7 @@ export function SelectionDropdown(props: Props) {
menuButtonWrapperClassName={menuButtonWrapperClassName}
placement={placement}
disabled={disabled}
+ tabIndex={tabIndex}
>
{({ closeDropdown }) => (
diff --git a/plane-src/apps/web/core/components/core/filters/date-filter-select.tsx b/plane-src/apps/web/core/components/core/filters/date-filter-select.tsx
index fd106a9..f708c41 100644
--- a/plane-src/apps/web/core/components/core/filters/date-filter-select.tsx
+++ b/plane-src/apps/web/core/components/core/filters/date-filter-select.tsx
@@ -8,7 +8,7 @@ import React from "react";
import { CalendarDays } from "lucide-react";
// ui
import { CalendarAfterIcon, CalendarBeforeIcon } from "@plane/propel/icons";
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
type Props = {
title: string;
@@ -42,9 +42,8 @@ const dueDateRange: DueDate[] = [
export function DateFilterSelect({ title, value, onChange }: Props) {
return (
-
{dueDateRange.find((item) => item.value === value)?.icon}
@@ -52,16 +51,18 @@ export function DateFilterSelect({ title, value, onChange }: Props) {
}
- onChange={onChange}
- >
- {dueDateRange.map((option, index) => (
-
+ menuButtonWrapperClassName="flex items-center"
+ options={dueDateRange.map((option) => ({
+ key: option.value,
+ isChecked: value === option.value,
+ onClick: () => onChange(option.value),
+ title: (
{option.icon}
{title} {option.name}
-
- ))}
-
+ ),
+ }))}
+ />
);
}
diff --git a/plane-src/apps/web/core/components/core/theme/theme-switch.tsx b/plane-src/apps/web/core/components/core/theme/theme-switch.tsx
index b9de9bc..fc34351 100644
--- a/plane-src/apps/web/core/components/core/theme/theme-switch.tsx
+++ b/plane-src/apps/web/core/components/core/theme/theme-switch.tsx
@@ -9,7 +9,7 @@ import type { I_THEME_OPTION } from "@plane/constants";
import { THEME_OPTIONS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
// constants
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
// ui
type Props = {
@@ -22,70 +22,54 @@ export function ThemeSwitch(props: Props) {
// translation
const { t } = useTranslation();
+ const renderThemeSwatch = (themeOption: I_THEME_OPTION) => (
+
+ );
+
return (
-
-
+ {renderThemeSwatch(value)}
{t(value.key)}
) : (
t("select_your_theme")
)
}
- onChange={onChange}
- buttonClassName="border border-subtle-1"
- placement="bottom-end"
- input
- >
- {THEME_OPTIONS.map((themeOption) => (
-
+ menuButtonWrapperClassName="flex w-full items-center justify-between rounded-full border border-subtle-1 px-3 py-2 text-13"
+ options={THEME_OPTIONS.map((themeOption) => ({
+ key: themeOption.value,
+ isChecked: value?.value === themeOption.value,
+ onClick: () => onChange(themeOption),
+ title: (
-
+ {renderThemeSwatch(themeOption)}
{t(themeOption.key)}
-
- ))}
-
+ ),
+ }))}
+ />
);
}
diff --git a/plane-src/apps/web/core/components/cycles/dropdowns/estimate-type-dropdown.tsx b/plane-src/apps/web/core/components/cycles/dropdowns/estimate-type-dropdown.tsx
index 5104570..1d1cdda 100644
--- a/plane-src/apps/web/core/components/cycles/dropdowns/estimate-type-dropdown.tsx
+++ b/plane-src/apps/web/core/components/cycles/dropdowns/estimate-type-dropdown.tsx
@@ -8,7 +8,7 @@ import React from "react";
import { observer } from "mobx-react";
import type { TCycleEstimateType } from "@plane/types";
import { EEstimateSystem } from "@plane/types";
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
import { useProjectEstimates } from "@/hooks/store/estimates";
import { useCycle } from "@/hooks/store/use-cycle";
// local imports
@@ -30,19 +30,16 @@ export const EstimateTypeDropdown = observer(function EstimateTypeDropdown(props
return (getIsPointsDataAvailable(cycleId) || isCurrentProjectEstimateEnabled) &&
currentProjectEstimateType !== EEstimateSystem.CATEGORIES ? (
- {cycleEstimateOptions.find((v) => v.value === value)?.label ?? "None"}}
- onChange={onChange}
- maxHeight="lg"
- buttonClassName="bg-surface-2 border-none rounded-sm text-13 font-medium "
- >
- {cycleEstimateOptions.map((item) => (
-
- {item.label}
-
- ))}
-
+ {cycleEstimateOptions.find((v) => v.value === value)?.label ?? "None"}}
+ menuButtonWrapperClassName="flex items-center rounded-sm bg-surface-2 px-2 py-1 text-13 font-medium"
+ options={cycleEstimateOptions.map((item) => ({
+ key: item.value,
+ title: item.label,
+ isChecked: value === item.value,
+ onClick: () => onChange(item.value),
+ }))}
+ />
) : showDefault ? (
{cycleEstimateOptions.find((v) => v.value === value)?.label ?? value}
diff --git a/plane-src/apps/web/core/components/issues/peek-overview/header.tsx b/plane-src/apps/web/core/components/issues/peek-overview/header.tsx
index a4c6fd6..1176b1f 100644
--- a/plane-src/apps/web/core/components/issues/peek-overview/header.tsx
+++ b/plane-src/apps/web/core/components/issues/peek-overview/header.tsx
@@ -15,7 +15,7 @@ import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Tooltip } from "@plane/propel/tooltip";
import type { TNameDescriptionLoader } from "@plane/types";
import { EIssuesStoreType } from "@plane/types";
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
import { copyUrlToClipboard, generateWorkItemLink } from "@plane/utils";
// hooks
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
@@ -185,19 +185,20 @@ export const IssuePeekOverviewHeader = observer(function IssuePeekOverviewHeader
{currentMode && showLayoutSwitcher && (
-
setPeekMode(val)}
- customButton={
+
-
+
}
- >
- {PEEK_OPTIONS.map((mode) => (
-
+ menuButtonWrapperClassName="flex items-center"
+ options={PEEK_OPTIONS.map((mode) => ({
+ key: mode.key,
+ isChecked: currentMode.key === mode.key,
+ onClick: () => setPeekMode(mode.key),
+ title: (
{t(mode.i18n_title)}
-
- ))}
-
+ ),
+ }))}
+ />
)}
{metaSlot}
diff --git a/plane-src/apps/web/core/components/modules/module-status-dropdown.tsx b/plane-src/apps/web/core/components/modules/module-status-dropdown.tsx
index ef532e6..65886ab 100644
--- a/plane-src/apps/web/core/components/modules/module-status-dropdown.tsx
+++ b/plane-src/apps/web/core/components/modules/module-status-dropdown.tsx
@@ -11,7 +11,7 @@ import { useTranslation } from "@plane/i18n";
import type { TModuleStatus } from "@plane/propel/icons";
import { ModuleStatusIcon } from "@plane/propel/icons";
import type { IModule } from "@plane/types";
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
type Props = {
isDisabled: boolean;
@@ -27,8 +27,8 @@ export const ModuleStatusDropdown = observer(function ModuleStatusDropdown(props
if (!moduleStatus) return <>>;
return (
-
}
- value={moduleStatus?.value}
- onChange={(val: TModuleStatus) => {
- handleModuleDetailsChange({ status: val });
- }}
disabled={isDisabled}
- >
- {MODULE_STATUS.map((status) => (
-
+ menuButtonWrapperClassName="flex"
+ options={MODULE_STATUS.map((status) => ({
+ key: status.value,
+ isChecked: moduleStatus?.value === status.value,
+ onClick: () => handleModuleDetailsChange({ status: status.value as TModuleStatus }),
+ title: (
{t(status.i18n_label)}
-
- ))}
-
+ ),
+ }))}
+ />
);
});
diff --git a/plane-src/apps/web/core/components/modules/select/status.tsx b/plane-src/apps/web/core/components/modules/select/status.tsx
index e62b59a..c85a18a 100644
--- a/plane-src/apps/web/core/components/modules/select/status.tsx
+++ b/plane-src/apps/web/core/components/modules/select/status.tsx
@@ -14,7 +14,7 @@ import { useTranslation } from "@plane/i18n";
import { StatePropertyIcon, ModuleStatusIcon } from "@plane/propel/icons";
import type { IModule } from "@plane/types";
// ui
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
// types
// constants
@@ -34,9 +34,8 @@ export function ModuleStatusSelect({ control, error, tabIndex }: Props) {
render={({ field: { value, onChange } }) => {
const selectedValue = MODULE_STATUS.find((s) => s.value === value);
return (
-
@@ -50,19 +49,20 @@ export function ModuleStatusSelect({ control, error, tabIndex }: Props) {
)}
}
- onChange={onChange}
tabIndex={tabIndex}
- noChevron
- >
- {MODULE_STATUS.map((status) => (
-
+ menuButtonWrapperClassName="flex"
+ options={MODULE_STATUS.map((status) => ({
+ key: status.value,
+ isChecked: value === status.value,
+ onClick: () => onChange(status.value),
+ title: (
{t(status.i18n_label)}
-
- ))}
-
+ ),
+ }))}
+ />
);
}}
/>
diff --git a/plane-src/apps/web/core/components/modules/sidebar-select/select-status.tsx b/plane-src/apps/web/core/components/modules/sidebar-select/select-status.tsx
index 541e315..fb72cf6 100644
--- a/plane-src/apps/web/core/components/modules/sidebar-select/select-status.tsx
+++ b/plane-src/apps/web/core/components/modules/sidebar-select/select-status.tsx
@@ -14,7 +14,7 @@ import { useTranslation } from "@plane/i18n";
import { StatePropertyIcon } from "@plane/propel/icons";
import type { IModule } from "@plane/types";
// ui
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
// types
// common
// constants
@@ -38,8 +38,8 @@ export function SidebarStatusSelect({ control, submitChanges, watch }: Props) {
control={control}
name="status"
render={({ field: { value } }) => (
-
}
- value={value}
- onChange={(value: any) => {
- submitChanges({ status: value });
- }}
- >
- {MODULE_STATUS.map((option) => (
-
+ menuButtonWrapperClassName="flex"
+ options={MODULE_STATUS.map((option) => ({
+ key: option.value,
+ isChecked: value === option.value,
+ onClick: () => submitChanges({ status: option.value }),
+ title: (
{t(option.i18n_label)}
-
- ))}
-
+ ),
+ }))}
+ />
)}
/>
diff --git a/plane-src/apps/web/core/components/profile/start-of-week-preference.tsx b/plane-src/apps/web/core/components/profile/start-of-week-preference.tsx
index 93893b4..dfae795 100644
--- a/plane-src/apps/web/core/components/profile/start-of-week-preference.tsx
+++ b/plane-src/apps/web/core/components/profile/start-of-week-preference.tsx
@@ -9,7 +9,7 @@ import { observer } from "mobx-react";
import { START_OF_THE_WEEK_OPTIONS } from "@plane/constants";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { EStartOfTheWeek } from "@plane/types";
-import { CustomSelect } from "@plane/ui";
+import { SelectionDropdown } from "@/components/common/selection-dropdown";
// components
import { SettingsControlItem } from "@/components/settings/control-item";
// hooks
@@ -38,23 +38,17 @@ export const StartOfWeekPreference = observer(function StartOfWeekPreference(pro
title={props.option.title}
description={props.option.description}
control={
-
- <>
- {START_OF_THE_WEEK_OPTIONS.map((day) => (
-
- {day.label}
-
- ))}
- >
-
+ menuButton={getStartOfWeekLabel(userProfile.start_of_the_week)}
+ menuButtonWrapperClassName="flex w-full items-center justify-between rounded-full border border-subtle-1 px-3 py-2 text-13"
+ options={START_OF_THE_WEEK_OPTIONS.map((day) => ({
+ key: `${day.value}`,
+ title: day.label,
+ isChecked: userProfile.start_of_the_week === day.value,
+ onClick: () => handleStartOfWeekChange(day.value),
+ }))}
+ />
}
/>
);