diff --git a/.changeset/hip-pumas-build.md b/.changeset/hip-pumas-build.md new file mode 100644 index 0000000..87df123 --- /dev/null +++ b/.changeset/hip-pumas-build.md @@ -0,0 +1,5 @@ +--- +"@fn-sphere/filter": patch +--- + +Add `tryRetainArgs` to allow retaining `args` when filter is changed diff --git a/packages/filter/src/hooks/use-filter-select.ts b/packages/filter/src/hooks/use-filter-select.ts index 253e95d..78e05cf 100644 --- a/packages/filter/src/hooks/use-filter-select.ts +++ b/packages/filter/src/hooks/use-filter-select.ts @@ -1,5 +1,6 @@ import { isEqualPath, + isSameType, type FilterField, type SingleFilter, type StandardFnSchema, @@ -7,26 +8,29 @@ import { import { useFilterRule } from "./use-filter-rule.js"; import { useFilterSchemaContext } from "./use-filter-schema-context.js"; -export type UpdateFieldOptions = { +export interface UpdateFilterOptions { /** - * Try to continue using the current filter when the field is changed. + * Try to continue using the current args when the field is changed. * * @default true */ - tryRetainFilter?: boolean; + tryRetainArgs?: boolean; +} + +export interface UpdateFieldOptions extends UpdateFilterOptions { /** - * Automatically select the first filter when the field is changed and the filter is not retained. + * Try to continue using the current filter when the field is changed. * * @default true */ - autoSelectFirstFilter?: boolean; + tryRetainFilter?: boolean; /** - * Try to continue using the current args when the field is changed. + * Automatically select the first filter when the field is changed and the filter is not retained. * * @default true */ - tryRetainArgs?: boolean; -}; + autoSelectFirstFilter?: boolean; +} export const useFilterSelect = (rule: SingleFilter) => { const { @@ -67,6 +71,28 @@ export const useFilterSelect = (rule: SingleFilter) => { value: filter, })); + /** + * Checks if the new filter schema has the same arguments as the current filter schema. + */ + const canRetainArgs = (newFilterSchema: StandardFnSchema) => { + if (!selectedField) { + // This should not happen since the field should be selected before the filter + console.error("Field not found", rule); + return false; + } + const currentFilterSchema = selectedField.filterFnList.find( + (filter) => filter.name === rule.name, + ); + if (!currentFilterSchema) { + // Select filter first time + return false; + } + return isSameType( + newFilterSchema.define.parameters(), + currentFilterSchema.define.parameters(), + ); + }; + const updateField = ( newField: FilterField, { @@ -79,7 +105,7 @@ export const useFilterSelect = (rule: SingleFilter) => { console.error("Field has no filter", newField); throw new Error("Field has no filter"); } - // Retain filter when possible + // If new field has the same filter, it can be retained const canRetainFilter = newField.filterFnList.some( (filter) => filter.name === rule.name, ); @@ -88,13 +114,16 @@ export const useFilterSelect = (rule: SingleFilter) => { ? newField.filterFnList[0].name : undefined; - // TODO check if the arguments are matched new filter when autoSelectFirstFilter enabled - const needRetainArgs = tryRetainArgs && needRetainFilter; + const newFilterSchema = needRetainFilter ? rule.name : fallbackFilter; + + const needRetainArgs = + tryRetainArgs && + (needRetainFilter || canRetainArgs(newField.filterFnList[0])); updateRule({ ...rule, path: newField.path, - name: needRetainFilter ? rule.name : fallbackFilter, + name: newFilterSchema, // If the filter is retained, keep the arguments args: needRetainArgs ? rule.args @@ -103,11 +132,15 @@ export const useFilterSelect = (rule: SingleFilter) => { }); }; - const updateFilter = (filterSchema: StandardFnSchema) => { + const updateFilter = ( + filterSchema: StandardFnSchema, + { tryRetainArgs = true }: UpdateFilterOptions = {}, + ) => { + const needRetainArgs = tryRetainArgs && canRetainArgs(filterSchema); updateRule({ ...rule, name: filterSchema.name, - args: [], + args: needRetainArgs ? rule.args : [], }); }; diff --git a/packages/filter/src/views/filter-select.tsx b/packages/filter/src/views/filter-select.tsx index dc7b92f..2a2fa15 100644 --- a/packages/filter/src/views/filter-select.tsx +++ b/packages/filter/src/views/filter-select.tsx @@ -1,12 +1,18 @@ import type { SingleFilter } from "@fn-sphere/core"; -import { useFilterSelect } from "../hooks/use-filter-select.js"; +import { + useFilterSelect, + type UpdateFilterOptions, +} from "../hooks/use-filter-select.js"; import { useView } from "../theme/index.js"; export type FilterSelectProps = { rule: SingleFilter; -}; +} & UpdateFilterOptions; -export const FilterSelect = ({ rule }: FilterSelectProps) => { +export const FilterSelect = ({ + rule, + ...updateFilterOptions +}: FilterSelectProps) => { const { Select: SelectView } = useView("components"); const { selectedField, selectedFilter, filterOptions, updateFilter } = useFilterSelect(rule); @@ -16,7 +22,7 @@ export const FilterSelect = ({ rule }: FilterSelectProps) => { value={selectedFilter} disabled={!selectedField} options={filterOptions} - onChange={updateFilter} + onChange={(val) => updateFilter(val, updateFilterOptions)} /> ); };