АРХ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: миграция простых CustomSelect-пикеров на SelectionDropdown
This commit is contained in:
parent
305357478e
commit
a49e18d0e5
|
|
@ -23,8 +23,9 @@ import {
|
||||||
} from "@plane/propel/icons";
|
} from "@plane/propel/icons";
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { TExternalContourRequest, TNameDescriptionLoader } from "@plane/types";
|
import type { TExternalContourRequest, TNameDescriptionLoader } from "@plane/types";
|
||||||
import { ControlLink, CustomSelect, Header, Row, Tooltip } from "@plane/ui";
|
import { ControlLink, Header, Row, Tooltip } from "@plane/ui";
|
||||||
import { copyUrlToClipboard, generateWorkItemLink } from "@plane/utils";
|
import { copyUrlToClipboard, generateWorkItemLink } from "@plane/utils";
|
||||||
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
import { NameDescriptionUpdateStatus } from "@/components/issues/issue-update-status";
|
import { NameDescriptionUpdateStatus } from "@/components/issues/issue-update-status";
|
||||||
import { useProject } from "@/hooks/store/use-project";
|
import { useProject } from "@/hooks/store/use-project";
|
||||||
import { useProjectExternalContoursBoard } from "@/hooks/store/use-project-external-contours-board";
|
import { useProjectExternalContoursBoard } from "@/hooks/store/use-project-external-contours-board";
|
||||||
|
|
@ -228,19 +229,20 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{currentMode && !embedIssue && (
|
{currentMode && !embedIssue && (
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
value={currentMode}
|
menuButton={
|
||||||
onChange={(value: TExternalContourPeekMode) => setPeekMode(value)}
|
|
||||||
customButton={
|
|
||||||
<Tooltip tooltipContent={t("common.toggle_peek_view_layout")}>
|
<Tooltip tooltipContent={t("common.toggle_peek_view_layout")}>
|
||||||
<button type="button">
|
<span>
|
||||||
<currentMode.icon className="h-4 w-4 text-tertiary hover:text-secondary" />
|
<currentMode.icon className="h-4 w-4 text-tertiary hover:text-secondary" />
|
||||||
</button>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
>
|
menuButtonWrapperClassName="flex items-center"
|
||||||
{PEEK_OPTIONS.map((mode) => (
|
options={PEEK_OPTIONS.map((mode) => ({
|
||||||
<CustomSelect.Option key={mode.key} value={mode.key}>
|
key: mode.key,
|
||||||
|
isChecked: currentMode.key === mode.key,
|
||||||
|
onClick: () => setPeekMode(mode.key),
|
||||||
|
title: (
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-1.5 ${
|
className={`flex items-center gap-1.5 ${
|
||||||
currentMode.key === mode.key ? "text-secondary" : "text-placeholder hover:text-secondary"
|
currentMode.key === mode.key ? "text-secondary" : "text-placeholder hover:text-secondary"
|
||||||
|
|
@ -249,9 +251,9 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
|
||||||
<mode.icon className="-my-1 h-4 w-4 flex-shrink-0" />
|
<mode.icon className="-my-1 h-4 w-4 flex-shrink-0" />
|
||||||
{t(mode.i18n_title)}
|
{t(mode.i18n_title)}
|
||||||
</div>
|
</div>
|
||||||
</CustomSelect.Option>
|
),
|
||||||
))}
|
}))}
|
||||||
</CustomSelect>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{issue?.project_id && issue.sequence_id && (
|
{issue?.project_id && issue.sequence_id && (
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
// plane package imports
|
// plane package imports
|
||||||
import type { ChartXAxisProperty } from "@plane/types";
|
import type { ChartXAxisProperty } from "@plane/types";
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value?: ChartXAxisProperty;
|
value?: ChartXAxisProperty;
|
||||||
|
|
@ -21,16 +21,28 @@ type Props = {
|
||||||
export function SelectXAxis(props: Props) {
|
export function SelectXAxis(props: Props) {
|
||||||
const { value, onChange, options, hiddenOptions, allowNoValue, label } = props;
|
const { value, onChange, options, hiddenOptions, allowNoValue, label } = props;
|
||||||
return (
|
return (
|
||||||
<CustomSelect value={value} label={label} onChange={onChange} maxHeight="lg">
|
<SelectionDropdown
|
||||||
{allowNoValue && <CustomSelect.Option value={null}>No value</CustomSelect.Option>}
|
menuButton={label ?? "Select"}
|
||||||
{options.map((item) => {
|
options={[
|
||||||
if (hiddenOptions?.includes(item.value)) return null;
|
...(allowNoValue
|
||||||
return (
|
? [
|
||||||
<CustomSelect.Option key={item.value} value={item.value}>
|
{
|
||||||
{item.label}
|
key: "__none__",
|
||||||
</CustomSelect.Option>
|
title: "No value",
|
||||||
);
|
isChecked: value == null,
|
||||||
})}
|
onClick: () => onChange(null),
|
||||||
</CustomSelect>
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...options
|
||||||
|
.filter((item) => !hiddenOptions?.includes(item.value))
|
||||||
|
.map((item) => ({
|
||||||
|
key: item.value,
|
||||||
|
title: item.label,
|
||||||
|
isChecked: value === item.value,
|
||||||
|
onClick: () => onChange(item.value),
|
||||||
|
})),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { EEstimateSystem } from "@plane/constants";
|
||||||
import { ProjectIcon } from "@plane/propel/icons";
|
import { ProjectIcon } from "@plane/propel/icons";
|
||||||
import type { ChartYAxisMetric } from "@plane/types";
|
import type { ChartYAxisMetric } from "@plane/types";
|
||||||
// plane package imports
|
// plane package imports
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProjectEstimates } from "@/hooks/store/estimates";
|
import { useProjectEstimates } from "@/hooks/store/estimates";
|
||||||
// plane web constants
|
// plane web constants
|
||||||
|
|
@ -44,27 +44,22 @@ export const SelectYAxis = observer(function SelectYAxis({ value, onChange, hidd
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
value={value}
|
menuButton={
|
||||||
label={
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<ProjectIcon className="h-3 w-3" />
|
<ProjectIcon className="h-3 w-3" />
|
||||||
<span>{options.find((v) => v.value === value)?.label ?? "Add Metric"}</span>
|
<span>{options.find((v) => v.value === value)?.label ?? "Add Metric"}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
onChange={onChange}
|
options={options
|
||||||
maxHeight="lg"
|
.filter((item) => !hiddenOptions?.includes(item.value))
|
||||||
>
|
.filter((item) => isEstimateEnabled(item.value))
|
||||||
{options.map((item) => {
|
.map((item) => ({
|
||||||
if (hiddenOptions?.includes(item.value)) return null;
|
key: item.value,
|
||||||
return (
|
title: item.label,
|
||||||
isEstimateEnabled(item.value) && (
|
isChecked: value === item.value,
|
||||||
<CustomSelect.Option key={item.value} value={item.value}>
|
onClick: () => onChange(item.value),
|
||||||
{item.label}
|
}))}
|
||||||
</CustomSelect.Option>
|
/>
|
||||||
)
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CustomSelect>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,12 @@ type Props = {
|
||||||
menuButtonWrapperClassName?: string;
|
menuButtonWrapperClassName?: string;
|
||||||
options: TSelectionDropdownOption[];
|
options: TSelectionDropdownOption[];
|
||||||
placement?: Placement;
|
placement?: Placement;
|
||||||
|
tabIndex?: number;
|
||||||
title?: ReactNode;
|
title?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SelectionDropdown(props: Props) {
|
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);
|
const renderedOptions = options.filter((option) => option.shouldRender !== false);
|
||||||
|
|
||||||
|
|
@ -39,6 +40,7 @@ export function SelectionDropdown(props: Props) {
|
||||||
menuButtonWrapperClassName={menuButtonWrapperClassName}
|
menuButtonWrapperClassName={menuButtonWrapperClassName}
|
||||||
placement={placement}
|
placement={placement}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
tabIndex={tabIndex}
|
||||||
>
|
>
|
||||||
{({ closeDropdown }) => (
|
{({ closeDropdown }) => (
|
||||||
<div className="vertical-scrollbar relative scrollbar-sm h-full w-full overflow-y-auto px-2.5 py-2">
|
<div className="vertical-scrollbar relative scrollbar-sm h-full w-full overflow-y-auto px-2.5 py-2">
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import React from "react";
|
||||||
import { CalendarDays } from "lucide-react";
|
import { CalendarDays } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { CalendarAfterIcon, CalendarBeforeIcon } from "@plane/propel/icons";
|
import { CalendarAfterIcon, CalendarBeforeIcon } from "@plane/propel/icons";
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -42,9 +42,8 @@ const dueDateRange: DueDate[] = [
|
||||||
|
|
||||||
export function DateFilterSelect({ title, value, onChange }: Props) {
|
export function DateFilterSelect({ title, value, onChange }: Props) {
|
||||||
return (
|
return (
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
value={value}
|
menuButton={
|
||||||
label={
|
|
||||||
<div className="flex items-center gap-2 text-11">
|
<div className="flex items-center gap-2 text-11">
|
||||||
{dueDateRange.find((item) => item.value === value)?.icon}
|
{dueDateRange.find((item) => item.value === value)?.icon}
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -52,16 +51,18 @@ export function DateFilterSelect({ title, value, onChange }: Props) {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
onChange={onChange}
|
menuButtonWrapperClassName="flex items-center"
|
||||||
>
|
options={dueDateRange.map((option) => ({
|
||||||
{dueDateRange.map((option, index) => (
|
key: option.value,
|
||||||
<CustomSelect.Option key={index} value={option.value}>
|
isChecked: value === option.value,
|
||||||
|
onClick: () => onChange(option.value),
|
||||||
|
title: (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span>{option.icon}</span>
|
<span>{option.icon}</span>
|
||||||
{title} {option.name}
|
{title} {option.name}
|
||||||
</div>
|
</div>
|
||||||
</CustomSelect.Option>
|
),
|
||||||
))}
|
}))}
|
||||||
</CustomSelect>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import type { I_THEME_OPTION } from "@plane/constants";
|
||||||
import { THEME_OPTIONS } from "@plane/constants";
|
import { THEME_OPTIONS } from "@plane/constants";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
// constants
|
// constants
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
// ui
|
// ui
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -22,70 +22,54 @@ export function ThemeSwitch(props: Props) {
|
||||||
// translation
|
// translation
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const renderThemeSwatch = (themeOption: I_THEME_OPTION) => (
|
||||||
|
<div
|
||||||
|
className="relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border-1"
|
||||||
|
style={{
|
||||||
|
borderColor: themeOption.icon.border,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="h-full w-1/2 rounded-l-full"
|
||||||
|
style={{
|
||||||
|
background: themeOption.icon.color1,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="h-full w-1/2 rounded-r-full border-l"
|
||||||
|
style={{
|
||||||
|
borderLeftColor: themeOption.icon.border,
|
||||||
|
background: themeOption.icon.color2,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
value={value}
|
placement="bottom-end"
|
||||||
label={
|
menuButton={
|
||||||
value ? (
|
value ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div
|
{renderThemeSwatch(value)}
|
||||||
className="relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border-1"
|
|
||||||
style={{
|
|
||||||
borderColor: value.icon.border,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="h-full w-1/2 rounded-l-full"
|
|
||||||
style={{
|
|
||||||
background: value.icon.color1,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="h-full w-1/2 rounded-r-full border-l"
|
|
||||||
style={{
|
|
||||||
borderLeftColor: value.icon.border,
|
|
||||||
background: value.icon.color2,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{t(value.key)}
|
{t(value.key)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
t("select_your_theme")
|
t("select_your_theme")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onChange={onChange}
|
menuButtonWrapperClassName="flex w-full items-center justify-between rounded-full border border-subtle-1 px-3 py-2 text-13"
|
||||||
buttonClassName="border border-subtle-1"
|
options={THEME_OPTIONS.map((themeOption) => ({
|
||||||
placement="bottom-end"
|
key: themeOption.value,
|
||||||
input
|
isChecked: value?.value === themeOption.value,
|
||||||
>
|
onClick: () => onChange(themeOption),
|
||||||
{THEME_OPTIONS.map((themeOption) => (
|
title: (
|
||||||
<CustomSelect.Option key={themeOption.value} value={themeOption}>
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div
|
{renderThemeSwatch(themeOption)}
|
||||||
className="relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border border-1"
|
|
||||||
style={{
|
|
||||||
borderColor: themeOption.icon.border,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="h-full w-1/2 rounded-l-full"
|
|
||||||
style={{
|
|
||||||
background: themeOption.icon.color1,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="h-full w-1/2 rounded-r-full border-l"
|
|
||||||
style={{
|
|
||||||
borderLeftColor: themeOption.icon.border,
|
|
||||||
background: themeOption.icon.color2,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{t(themeOption.key)}
|
{t(themeOption.key)}
|
||||||
</div>
|
</div>
|
||||||
</CustomSelect.Option>
|
),
|
||||||
))}
|
}))}
|
||||||
</CustomSelect>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { TCycleEstimateType } from "@plane/types";
|
import type { TCycleEstimateType } from "@plane/types";
|
||||||
import { EEstimateSystem } 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 { useProjectEstimates } from "@/hooks/store/estimates";
|
||||||
import { useCycle } from "@/hooks/store/use-cycle";
|
import { useCycle } from "@/hooks/store/use-cycle";
|
||||||
// local imports
|
// local imports
|
||||||
|
|
@ -30,19 +30,16 @@ export const EstimateTypeDropdown = observer(function EstimateTypeDropdown(props
|
||||||
return (getIsPointsDataAvailable(cycleId) || isCurrentProjectEstimateEnabled) &&
|
return (getIsPointsDataAvailable(cycleId) || isCurrentProjectEstimateEnabled) &&
|
||||||
currentProjectEstimateType !== EEstimateSystem.CATEGORIES ? (
|
currentProjectEstimateType !== EEstimateSystem.CATEGORIES ? (
|
||||||
<div className="relative flex items-center gap-2">
|
<div className="relative flex items-center gap-2">
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
value={value}
|
menuButton={<span>{cycleEstimateOptions.find((v) => v.value === value)?.label ?? "None"}</span>}
|
||||||
label={<span>{cycleEstimateOptions.find((v) => v.value === value)?.label ?? "None"}</span>}
|
menuButtonWrapperClassName="flex items-center rounded-sm bg-surface-2 px-2 py-1 text-13 font-medium"
|
||||||
onChange={onChange}
|
options={cycleEstimateOptions.map((item) => ({
|
||||||
maxHeight="lg"
|
key: item.value,
|
||||||
buttonClassName="bg-surface-2 border-none rounded-sm text-13 font-medium "
|
title: item.label,
|
||||||
>
|
isChecked: value === item.value,
|
||||||
{cycleEstimateOptions.map((item) => (
|
onClick: () => onChange(item.value),
|
||||||
<CustomSelect.Option key={item.value} value={item.value}>
|
}))}
|
||||||
{item.label}
|
/>
|
||||||
</CustomSelect.Option>
|
|
||||||
))}
|
|
||||||
</CustomSelect>
|
|
||||||
</div>
|
</div>
|
||||||
) : showDefault ? (
|
) : showDefault ? (
|
||||||
<span className="capitalize">{cycleEstimateOptions.find((v) => v.value === value)?.label ?? value}</span>
|
<span className="capitalize">{cycleEstimateOptions.find((v) => v.value === value)?.label ?? value}</span>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import { Tooltip } from "@plane/propel/tooltip";
|
import { Tooltip } from "@plane/propel/tooltip";
|
||||||
import type { TNameDescriptionLoader } from "@plane/types";
|
import type { TNameDescriptionLoader } from "@plane/types";
|
||||||
import { EIssuesStoreType } 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";
|
import { copyUrlToClipboard, generateWorkItemLink } from "@plane/utils";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||||
|
|
@ -185,19 +185,20 @@ export const IssuePeekOverviewHeader = observer(function IssuePeekOverviewHeader
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{currentMode && showLayoutSwitcher && (
|
{currentMode && showLayoutSwitcher && (
|
||||||
<div className="flex flex-shrink-0 items-center gap-2">
|
<div className="flex flex-shrink-0 items-center gap-2">
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
value={currentMode}
|
menuButton={
|
||||||
onChange={(val: any) => setPeekMode(val)}
|
|
||||||
customButton={
|
|
||||||
<Tooltip tooltipContent={t("common.toggle_peek_view_layout")} isMobile={isMobile}>
|
<Tooltip tooltipContent={t("common.toggle_peek_view_layout")} isMobile={isMobile}>
|
||||||
<button type="button" className="">
|
<span>
|
||||||
<currentMode.icon className="h-4 w-4 text-tertiary hover:text-secondary" />
|
<currentMode.icon className="h-4 w-4 text-tertiary hover:text-secondary" />
|
||||||
</button>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
>
|
menuButtonWrapperClassName="flex items-center"
|
||||||
{PEEK_OPTIONS.map((mode) => (
|
options={PEEK_OPTIONS.map((mode) => ({
|
||||||
<CustomSelect.Option key={mode.key} value={mode.key}>
|
key: mode.key,
|
||||||
|
isChecked: currentMode.key === mode.key,
|
||||||
|
onClick: () => setPeekMode(mode.key),
|
||||||
|
title: (
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-1.5 ${
|
className={`flex items-center gap-1.5 ${
|
||||||
currentMode.key === mode.key ? "text-secondary" : "text-placeholder hover:text-secondary"
|
currentMode.key === mode.key ? "text-secondary" : "text-placeholder hover:text-secondary"
|
||||||
|
|
@ -206,9 +207,9 @@ export const IssuePeekOverviewHeader = observer(function IssuePeekOverviewHeader
|
||||||
<mode.icon className="-my-1 h-4 w-4 flex-shrink-0" />
|
<mode.icon className="-my-1 h-4 w-4 flex-shrink-0" />
|
||||||
{t(mode.i18n_title)}
|
{t(mode.i18n_title)}
|
||||||
</div>
|
</div>
|
||||||
</CustomSelect.Option>
|
),
|
||||||
))}
|
}))}
|
||||||
</CustomSelect>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{metaSlot}
|
{metaSlot}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { useTranslation } from "@plane/i18n";
|
||||||
import type { TModuleStatus } from "@plane/propel/icons";
|
import type { TModuleStatus } from "@plane/propel/icons";
|
||||||
import { ModuleStatusIcon } from "@plane/propel/icons";
|
import { ModuleStatusIcon } from "@plane/propel/icons";
|
||||||
import type { IModule } from "@plane/types";
|
import type { IModule } from "@plane/types";
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
|
|
@ -27,8 +27,8 @@ export const ModuleStatusDropdown = observer(function ModuleStatusDropdown(props
|
||||||
if (!moduleStatus) return <></>;
|
if (!moduleStatus) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
customButton={
|
menuButton={
|
||||||
<span
|
<span
|
||||||
className={`flex h-6 w-20 items-center justify-center rounded-sm text-center text-11 ${
|
className={`flex h-6 w-20 items-center justify-center rounded-sm text-center text-11 ${
|
||||||
isDisabled ? "cursor-not-allowed" : "cursor-pointer"
|
isDisabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||||
|
|
@ -41,20 +41,19 @@ export const ModuleStatusDropdown = observer(function ModuleStatusDropdown(props
|
||||||
{(moduleStatus && t(moduleStatus?.i18n_label)) ?? t("project_modules.status.backlog")}
|
{(moduleStatus && t(moduleStatus?.i18n_label)) ?? t("project_modules.status.backlog")}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
value={moduleStatus?.value}
|
|
||||||
onChange={(val: TModuleStatus) => {
|
|
||||||
handleModuleDetailsChange({ status: val });
|
|
||||||
}}
|
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
>
|
menuButtonWrapperClassName="flex"
|
||||||
{MODULE_STATUS.map((status) => (
|
options={MODULE_STATUS.map((status) => ({
|
||||||
<CustomSelect.Option key={status.value} value={status.value}>
|
key: status.value,
|
||||||
|
isChecked: moduleStatus?.value === status.value,
|
||||||
|
onClick: () => handleModuleDetailsChange({ status: status.value as TModuleStatus }),
|
||||||
|
title: (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<ModuleStatusIcon status={status.value} />
|
<ModuleStatusIcon status={status.value} />
|
||||||
{t(status.i18n_label)}
|
{t(status.i18n_label)}
|
||||||
</div>
|
</div>
|
||||||
</CustomSelect.Option>
|
),
|
||||||
))}
|
}))}
|
||||||
</CustomSelect>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { useTranslation } from "@plane/i18n";
|
||||||
import { StatePropertyIcon, ModuleStatusIcon } from "@plane/propel/icons";
|
import { StatePropertyIcon, ModuleStatusIcon } from "@plane/propel/icons";
|
||||||
import type { IModule } from "@plane/types";
|
import type { IModule } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
// types
|
// types
|
||||||
// constants
|
// constants
|
||||||
|
|
||||||
|
|
@ -34,9 +34,8 @@ export function ModuleStatusSelect({ control, error, tabIndex }: Props) {
|
||||||
render={({ field: { value, onChange } }) => {
|
render={({ field: { value, onChange } }) => {
|
||||||
const selectedValue = MODULE_STATUS.find((s) => s.value === value);
|
const selectedValue = MODULE_STATUS.find((s) => s.value === value);
|
||||||
return (
|
return (
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
value={value}
|
menuButton={
|
||||||
label={
|
|
||||||
<div
|
<div
|
||||||
className={`flex items-center justify-center gap-2 py-0.5 text-11 ${error ? "text-danger-primary" : ""}`}
|
className={`flex items-center justify-center gap-2 py-0.5 text-11 ${error ? "text-danger-primary" : ""}`}
|
||||||
>
|
>
|
||||||
|
|
@ -50,19 +49,20 @@ export function ModuleStatusSelect({ control, error, tabIndex }: Props) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
onChange={onChange}
|
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
noChevron
|
menuButtonWrapperClassName="flex"
|
||||||
>
|
options={MODULE_STATUS.map((status) => ({
|
||||||
{MODULE_STATUS.map((status) => (
|
key: status.value,
|
||||||
<CustomSelect.Option key={status.value} value={status.value}>
|
isChecked: value === status.value,
|
||||||
|
onClick: () => onChange(status.value),
|
||||||
|
title: (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<ModuleStatusIcon status={status.value} />
|
<ModuleStatusIcon status={status.value} />
|
||||||
{t(status.i18n_label)}
|
{t(status.i18n_label)}
|
||||||
</div>
|
</div>
|
||||||
</CustomSelect.Option>
|
),
|
||||||
))}
|
}))}
|
||||||
</CustomSelect>
|
/>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { useTranslation } from "@plane/i18n";
|
||||||
import { StatePropertyIcon } from "@plane/propel/icons";
|
import { StatePropertyIcon } from "@plane/propel/icons";
|
||||||
import type { IModule } from "@plane/types";
|
import type { IModule } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
// types
|
// types
|
||||||
// common
|
// common
|
||||||
// constants
|
// constants
|
||||||
|
|
@ -38,8 +38,8 @@ export function SidebarStatusSelect({ control, submitChanges, watch }: Props) {
|
||||||
control={control}
|
control={control}
|
||||||
name="status"
|
name="status"
|
||||||
render={({ field: { value } }) => (
|
render={({ field: { value } }) => (
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
label={
|
menuButton={
|
||||||
<span className={`flex items-center gap-2 text-left capitalize ${value ? "" : "text-primary"}`}>
|
<span className={`flex items-center gap-2 text-left capitalize ${value ? "" : "text-primary"}`}>
|
||||||
<span
|
<span
|
||||||
className="h-2 w-2 flex-shrink-0 rounded-full"
|
className="h-2 w-2 flex-shrink-0 rounded-full"
|
||||||
|
|
@ -50,20 +50,19 @@ export function SidebarStatusSelect({ control, submitChanges, watch }: Props) {
|
||||||
{watch("status")}
|
{watch("status")}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
value={value}
|
menuButtonWrapperClassName="flex"
|
||||||
onChange={(value: any) => {
|
options={MODULE_STATUS.map((option) => ({
|
||||||
submitChanges({ status: value });
|
key: option.value,
|
||||||
}}
|
isChecked: value === option.value,
|
||||||
>
|
onClick: () => submitChanges({ status: option.value }),
|
||||||
{MODULE_STATUS.map((option) => (
|
title: (
|
||||||
<CustomSelect.Option key={option.value} value={option.value}>
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="h-2 w-2 flex-shrink-0 rounded-full" style={{ backgroundColor: option.color }} />
|
<span className="h-2 w-2 flex-shrink-0 rounded-full" style={{ backgroundColor: option.color }} />
|
||||||
{t(option.i18n_label)}
|
{t(option.i18n_label)}
|
||||||
</div>
|
</div>
|
||||||
</CustomSelect.Option>
|
),
|
||||||
))}
|
}))}
|
||||||
</CustomSelect>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { observer } from "mobx-react";
|
||||||
import { START_OF_THE_WEEK_OPTIONS } from "@plane/constants";
|
import { START_OF_THE_WEEK_OPTIONS } from "@plane/constants";
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { EStartOfTheWeek } from "@plane/types";
|
import type { EStartOfTheWeek } from "@plane/types";
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||||
// components
|
// components
|
||||||
import { SettingsControlItem } from "@/components/settings/control-item";
|
import { SettingsControlItem } from "@/components/settings/control-item";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -38,23 +38,17 @@ export const StartOfWeekPreference = observer(function StartOfWeekPreference(pro
|
||||||
title={props.option.title}
|
title={props.option.title}
|
||||||
description={props.option.description}
|
description={props.option.description}
|
||||||
control={
|
control={
|
||||||
<CustomSelect
|
<SelectionDropdown
|
||||||
value={userProfile.start_of_the_week}
|
|
||||||
label={getStartOfWeekLabel(userProfile.start_of_the_week)}
|
|
||||||
onChange={handleStartOfWeekChange}
|
|
||||||
buttonClassName="border border-subtle-1"
|
|
||||||
input
|
|
||||||
maxHeight="lg"
|
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
>
|
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"
|
||||||
{START_OF_THE_WEEK_OPTIONS.map((day) => (
|
options={START_OF_THE_WEEK_OPTIONS.map((day) => ({
|
||||||
<CustomSelect.Option key={day.value} value={day.value}>
|
key: `${day.value}`,
|
||||||
{day.label}
|
title: day.label,
|
||||||
</CustomSelect.Option>
|
isChecked: userProfile.start_of_the_week === day.value,
|
||||||
))}
|
onClick: () => handleStartOfWeekChange(day.value),
|
||||||
</>
|
}))}
|
||||||
</CustomSelect>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue