[WEB-5804] refactor: decouple filter value types from filter configurations (#8441)
* [WEB-5804] refactor: decouple filter value types from filter configurations Remove value type constraints from filter configurations to support operator-specific value types. Different operators can accept different value types for the same filter property, so value types should be determined at the operator level rather than the filter level. - Remove generic value type parameter from TFilterConfig - Update TOperatorConfigMap to accept union of all value types - Simplify filter config factory signatures across all filter types - Add forceUpdate parameter to updateConditionValue method * refactor: remove filter value type constraints from filter configurations Eliminate the generic value type parameter from filter configurations to allow for operator-specific value types. This change enhances flexibility by enabling different operators to accept various value types for the same filter property. - Updated TFilterConfig and related interfaces to remove value type constraints - Adjusted filter configuration methods and types accordingly - Refactored date operator support to align with the new structure
This commit is contained in:
parent
5499e49b72
commit
f04be48f61
20 changed files with 126 additions and 133 deletions
|
|
@ -3,7 +3,7 @@ import { computedFn } from "mobx-utils";
|
|||
// plane imports
|
||||
import type { TConfigOptions } from "@plane/constants";
|
||||
import { DEFAULT_FILTER_CONFIG_OPTIONS } from "@plane/constants";
|
||||
import type { TExternalFilter, TFilterConfig, TFilterProperty, TFilterValue } from "@plane/types";
|
||||
import type { TExternalFilter, TFilterConfig, TFilterProperty } from "@plane/types";
|
||||
// local imports
|
||||
import type { IFilterConfig } from "./config";
|
||||
import { FilterConfig } from "./config";
|
||||
|
|
@ -24,17 +24,17 @@ import type { IFilterInstance } from "./filter";
|
|||
*/
|
||||
export interface IFilterConfigManager<P extends TFilterProperty> {
|
||||
// observables
|
||||
filterConfigs: Map<P, IFilterConfig<P, TFilterValue>>; // filter property -> config
|
||||
filterConfigs: Map<P, IFilterConfig<P>>; // filter property -> config
|
||||
configOptions: TConfigOptions;
|
||||
areConfigsReady: boolean;
|
||||
// computed
|
||||
allAvailableConfigs: IFilterConfig<P, TFilterValue>[];
|
||||
allAvailableConfigs: IFilterConfig<P>[];
|
||||
// computed functions
|
||||
getConfigByProperty: (property: P) => IFilterConfig<P, TFilterValue> | undefined;
|
||||
getConfigByProperty: (property: P) => IFilterConfig<P> | undefined;
|
||||
// helpers
|
||||
register: <C extends TFilterConfig<P, TFilterValue>>(config: C) => void;
|
||||
registerAll: (configs: TFilterConfig<P, TFilterValue>[]) => void;
|
||||
updateConfigByProperty: (property: P, configUpdates: Partial<TFilterConfig<P, TFilterValue>>) => void;
|
||||
register: <C extends TFilterConfig<P>>(config: C) => void;
|
||||
registerAll: (configs: TFilterConfig<P>[]) => void;
|
||||
updateConfigByProperty: (property: P, configUpdates: Partial<TFilterConfig<P>>) => void;
|
||||
setAreConfigsReady: (value: boolean) => void;
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ export class FilterConfigManager<
|
|||
* @returns The config for the property, or undefined if not found.
|
||||
*/
|
||||
getConfigByProperty: IFilterConfigManager<P>["getConfigByProperty"] = computedFn(
|
||||
(property) => this.filterConfigs.get(property) as IFilterConfig<P, TFilterValue>
|
||||
(property) => this.filterConfigs.get(property) as IFilterConfig<P>
|
||||
);
|
||||
|
||||
// ------------ helpers ------------
|
||||
|
|
@ -165,7 +165,7 @@ export class FilterConfigManager<
|
|||
|
||||
// ------------ private computed ------------
|
||||
|
||||
private get _allConfigs(): IFilterConfig<P, TFilterValue>[] {
|
||||
private get _allConfigs(): IFilterConfig<P>[] {
|
||||
return Array.from(this.filterConfigs.values());
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +173,7 @@ export class FilterConfigManager<
|
|||
* Returns all enabled filterConfigs.
|
||||
* @returns All enabled filterConfigs.
|
||||
*/
|
||||
private get _allEnabledConfigs(): IFilterConfig<P, TFilterValue>[] {
|
||||
private get _allEnabledConfigs(): IFilterConfig<P>[] {
|
||||
return this._allConfigs.filter((config) => config.isEnabled);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,41 +25,35 @@ type TOperatorOptionForDisplay = {
|
|||
label: string;
|
||||
};
|
||||
|
||||
export interface IFilterConfig<P extends TFilterProperty, V extends TFilterValue = TFilterValue> extends TFilterConfig<
|
||||
P,
|
||||
V
|
||||
> {
|
||||
export interface IFilterConfig<P extends TFilterProperty> extends TFilterConfig<P> {
|
||||
// computed
|
||||
allEnabledSupportedOperators: TSupportedOperators[];
|
||||
firstOperator: TSupportedOperators | undefined;
|
||||
// computed functions
|
||||
getOperatorConfig: (
|
||||
operator: TAllAvailableOperatorsForDisplay
|
||||
) => TOperatorSpecificConfigs<V>[keyof TOperatorSpecificConfigs<V>] | undefined;
|
||||
) => TOperatorSpecificConfigs[keyof TOperatorSpecificConfigs] | undefined;
|
||||
getLabelForOperator: (operator: TAllAvailableOperatorsForDisplay | undefined) => string;
|
||||
getDisplayOperatorByValue: <T extends TSupportedOperators>(operator: T, value: V) => T;
|
||||
getAllDisplayOperatorOptionsByValue: (value: V) => TOperatorOptionForDisplay[];
|
||||
getDisplayOperatorByValue: <T extends TSupportedOperators>(operator: T, value: TFilterValue) => T;
|
||||
getAllDisplayOperatorOptionsByValue: (value: TFilterValue) => TOperatorOptionForDisplay[];
|
||||
// actions
|
||||
mutate: (updates: Partial<TFilterConfig<P, V>>) => void;
|
||||
mutate: (updates: Partial<TFilterConfig<P>>) => void;
|
||||
}
|
||||
|
||||
export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TFilterValue> implements IFilterConfig<
|
||||
P,
|
||||
V
|
||||
> {
|
||||
export class FilterConfig<P extends TFilterProperty> implements IFilterConfig<P> {
|
||||
// observables
|
||||
id: IFilterConfig<P, V>["id"];
|
||||
label: IFilterConfig<P, V>["label"];
|
||||
icon?: IFilterConfig<P, V>["icon"];
|
||||
isEnabled: IFilterConfig<P, V>["isEnabled"];
|
||||
supportedOperatorConfigsMap: IFilterConfig<P, V>["supportedOperatorConfigsMap"];
|
||||
allowMultipleFilters: IFilterConfig<P, V>["allowMultipleFilters"];
|
||||
id: IFilterConfig<P>["id"];
|
||||
label: IFilterConfig<P>["label"];
|
||||
icon?: IFilterConfig<P>["icon"];
|
||||
isEnabled: IFilterConfig<P>["isEnabled"];
|
||||
supportedOperatorConfigsMap: IFilterConfig<P>["supportedOperatorConfigsMap"];
|
||||
allowMultipleFilters: IFilterConfig<P>["allowMultipleFilters"];
|
||||
|
||||
/**
|
||||
* Creates a new FilterConfig instance.
|
||||
* @param params - The parameters for the filter config.
|
||||
*/
|
||||
constructor(params: TFilterConfig<P, V>) {
|
||||
constructor(params: TFilterConfig<P>) {
|
||||
this.id = params.id;
|
||||
this.label = params.label;
|
||||
this.icon = params.icon;
|
||||
|
|
@ -88,7 +82,7 @@ export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TF
|
|||
* Returns all supported operators.
|
||||
* @returns All supported operators.
|
||||
*/
|
||||
get allEnabledSupportedOperators(): IFilterConfig<P, V>["allEnabledSupportedOperators"] {
|
||||
get allEnabledSupportedOperators(): IFilterConfig<P>["allEnabledSupportedOperators"] {
|
||||
return Array.from(this.supportedOperatorConfigsMap.entries())
|
||||
.filter(([, operatorConfig]) => operatorConfig.isOperatorEnabled)
|
||||
.map(([operator]) => operator);
|
||||
|
|
@ -98,7 +92,7 @@ export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TF
|
|||
* Returns the first operator.
|
||||
* @returns The first operator.
|
||||
*/
|
||||
get firstOperator(): IFilterConfig<P, V>["firstOperator"] {
|
||||
get firstOperator(): IFilterConfig<P>["firstOperator"] {
|
||||
return this.allEnabledSupportedOperators[0];
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +103,7 @@ export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TF
|
|||
* @param operator - The operator.
|
||||
* @returns The operator config.
|
||||
*/
|
||||
getOperatorConfig: IFilterConfig<P, V>["getOperatorConfig"] = computedFn((operator) =>
|
||||
getOperatorConfig: IFilterConfig<P>["getOperatorConfig"] = computedFn((operator) =>
|
||||
this.supportedOperatorConfigsMap.get(getOperatorForPayload(operator).operator)
|
||||
);
|
||||
|
||||
|
|
@ -118,7 +112,7 @@ export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TF
|
|||
* @param operator - The operator.
|
||||
* @returns The label for the operator.
|
||||
*/
|
||||
getLabelForOperator: IFilterConfig<P, V>["getLabelForOperator"] = computedFn((operator) => {
|
||||
getLabelForOperator: IFilterConfig<P>["getLabelForOperator"] = computedFn((operator) => {
|
||||
if (!operator) return EMPTY_OPERATOR_LABEL;
|
||||
|
||||
const operatorConfig = this.getOperatorConfig(operator);
|
||||
|
|
@ -139,7 +133,7 @@ export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TF
|
|||
* @param value - The value.
|
||||
* @returns The operator for the value.
|
||||
*/
|
||||
getDisplayOperatorByValue: IFilterConfig<P, V>["getDisplayOperatorByValue"] = computedFn((operator, value) => {
|
||||
getDisplayOperatorByValue: IFilterConfig<P>["getDisplayOperatorByValue"] = computedFn((operator, value) => {
|
||||
const operatorConfig = this.getOperatorConfig(operator);
|
||||
if (operatorConfig?.type === FILTER_FIELD_TYPE.MULTI_SELECT && (Array.isArray(value) ? value.length : 0) <= 1) {
|
||||
return operatorConfig.singleValueOperator as typeof operator;
|
||||
|
|
@ -155,28 +149,26 @@ export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TF
|
|||
* @param value - The current filter value used to determine the appropriate operator variant
|
||||
* @returns Array of operator options with their display labels and values
|
||||
*/
|
||||
getAllDisplayOperatorOptionsByValue: IFilterConfig<P, V>["getAllDisplayOperatorOptionsByValue"] = computedFn(
|
||||
(value) => {
|
||||
const operatorOptions: TOperatorOptionForDisplay[] = [];
|
||||
getAllDisplayOperatorOptionsByValue: IFilterConfig<P>["getAllDisplayOperatorOptionsByValue"] = computedFn((value) => {
|
||||
const operatorOptions: TOperatorOptionForDisplay[] = [];
|
||||
|
||||
// Process each supported operator to build display options
|
||||
for (const operator of this.allEnabledSupportedOperators) {
|
||||
const displayOperator = this.getDisplayOperatorByValue(operator, value);
|
||||
const displayOperatorLabel = this.getLabelForOperator(displayOperator);
|
||||
operatorOptions.push({
|
||||
value: operator,
|
||||
label: displayOperatorLabel,
|
||||
});
|
||||
// Process each supported operator to build display options
|
||||
for (const operator of this.allEnabledSupportedOperators) {
|
||||
const displayOperator = this.getDisplayOperatorByValue(operator, value);
|
||||
const displayOperatorLabel = this.getLabelForOperator(displayOperator);
|
||||
operatorOptions.push({
|
||||
value: operator,
|
||||
label: displayOperatorLabel,
|
||||
});
|
||||
|
||||
const additionalOperatorOption = this._getAdditionalOperatorOptions(operator, value);
|
||||
if (additionalOperatorOption) {
|
||||
operatorOptions.push(additionalOperatorOption);
|
||||
}
|
||||
const additionalOperatorOption = this._getAdditionalOperatorOptions(operator, value);
|
||||
if (additionalOperatorOption) {
|
||||
operatorOptions.push(additionalOperatorOption);
|
||||
}
|
||||
|
||||
return operatorOptions;
|
||||
}
|
||||
);
|
||||
|
||||
return operatorOptions;
|
||||
});
|
||||
|
||||
// ------------ actions ------------
|
||||
|
||||
|
|
@ -184,11 +176,11 @@ export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TF
|
|||
* Mutates the config.
|
||||
* @param updates - The updates to apply to the config.
|
||||
*/
|
||||
mutate: IFilterConfig<P, V>["mutate"] = action((updates) => {
|
||||
mutate: IFilterConfig<P>["mutate"] = action((updates) => {
|
||||
runInAction(() => {
|
||||
for (const key in updates) {
|
||||
if (updates.hasOwnProperty(key)) {
|
||||
const configKey = key as keyof TFilterConfig<P, V>;
|
||||
const configKey = key as keyof TFilterConfig<P>;
|
||||
set(this, configKey, updates[configKey]);
|
||||
}
|
||||
}
|
||||
|
|
@ -199,6 +191,6 @@ export class FilterConfig<P extends TFilterProperty, V extends TFilterValue = TF
|
|||
|
||||
private _getAdditionalOperatorOptions = (
|
||||
_operator: TSupportedOperators,
|
||||
_value: V
|
||||
_value: TFilterValue
|
||||
): TOperatorOptionForDisplay | undefined => undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,11 @@ export interface IFilterInstance<P extends TFilterProperty, E extends TExternalF
|
|||
isNegation: boolean
|
||||
) => void;
|
||||
updateConditionOperator: (conditionId: string, operator: TSupportedOperators, isNegation: boolean) => void;
|
||||
updateConditionValue: <V extends TFilterValue>(conditionId: string, value: SingleOrArray<V>) => void;
|
||||
updateConditionValue: <V extends TFilterValue>(
|
||||
conditionId: string,
|
||||
value: SingleOrArray<V>,
|
||||
forceUpdate?: boolean
|
||||
) => void;
|
||||
removeCondition: (conditionId: string) => void;
|
||||
// config actions
|
||||
clearFilters: () => Promise<void>;
|
||||
|
|
@ -439,9 +443,10 @@ export class FilterInstance<P extends TFilterProperty, E extends TExternalFilter
|
|||
* Updates the value of a condition in the filter expression with automatic optimization.
|
||||
* @param conditionId - The id of the condition to update.
|
||||
* @param value - The new value for the condition.
|
||||
* @param forceUpdate - Whether to force the update even if the value is the same as the condition before update.
|
||||
*/
|
||||
updateConditionValue: IFilterInstance<P, E>["updateConditionValue"] = action(
|
||||
<V extends TFilterValue>(conditionId: string, value: SingleOrArray<V>) => {
|
||||
<V extends TFilterValue>(conditionId: string, value: SingleOrArray<V>, forceUpdate: boolean = false) => {
|
||||
// If the expression is not valid, return
|
||||
if (!this.expression) return;
|
||||
|
||||
|
|
@ -458,7 +463,7 @@ export class FilterInstance<P extends TFilterProperty, E extends TExternalFilter
|
|||
}
|
||||
|
||||
// If the value is the same as the condition before update, return
|
||||
if (isEqual(conditionBeforeUpdate.value, value)) {
|
||||
if (!forceUpdate && isEqual(conditionBeforeUpdate.value, value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue