diff --git a/judo-ui-react/src/main/resources/actor/src/components/widgets/CardsFilter.tsx.hbs b/judo-ui-react/src/main/resources/actor/src/components/widgets/CardsFilter.tsx.hbs index e7b5ff3a..907800d5 100644 --- a/judo-ui-react/src/main/resources/actor/src/components/widgets/CardsFilter.tsx.hbs +++ b/judo-ui-react/src/main/resources/actor/src/components/widgets/CardsFilter.tsx.hbs @@ -1,22 +1,29 @@ {{> fragment.header.hbs }} import { type FC, useState, useCallback } from 'react'; +import Box from '@mui/material/Box'; import Grid from '@mui/material/Grid'; import Typography from '@mui/material/Typography'; import Button from '@mui/material/Button'; import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; +import TextField from '@mui/material/TextField'; +import { debounce } from '@mui/material/utils'; import { FilterType } from '~/components-api'; import { useL10N } from '~/l10n/l10n-context'; import { useTranslation } from 'react-i18next'; +import { debounceInputs } from '~/config'; export interface CardsFilterDefinition { type: FilterType; operator?: any; field: keyof T; label: string; - values: { value: any, label: string }[]; + inputType: 'options' | 'text', + searchLabel?: string; + allowSearch?: boolean; + values?: { value: any, label: string }[]; } export const CardsFilter: FC<{ filterDefinitions: CardsFilterDefinition[], onFiltersChanged?: (values: Record) => void }> = ({ filterDefinitions, onFiltersChanged }) => { @@ -24,6 +31,8 @@ export const CardsFilter: FC<{ filterDefinitions: CardsFilterDefinition[], const { t } = useTranslation(); const [values, setValues] = useState>({}); + const [searchValues, setSearchValues] = useState>({}); + const [visibleValues, setVisibleValues] = useState>({}); const updateValue = useCallback((field: string, value: any) => { const newValues = { @@ -34,6 +43,11 @@ export const CardsFilter: FC<{ filterDefinitions: CardsFilterDefinition[], onFiltersChanged?.(newValues); }, [values, onFiltersChanged]); + const updateValueDebounced = useCallback( + debounce(updateValue, debounceInputs), + [values, onFiltersChanged, updateValue], + ); + const clearFilters = useCallback(() => { // We need to explicitly null out values because our filter may refer to Transfer fields which are not in the list // of columns. Other framework features rely on column info for e.g. queryCustomizer cleanup. @@ -57,22 +71,49 @@ export const CardsFilter: FC<{ filterDefinitions: CardsFilterDefinition[], {filterDefinitions.map(d => ( - - {d.label}: - - - {d.values.map(v => ( - updateValue(d.field as string, v.value)} - />} - label={v.label} - /> - ))} - + {d.label}: + {d.allowSearch ? ( + { + setSearchValues((prev) => { + return { + ...prev, + [d.field as string]: evt.target.value, + }; + }); + if (d.inputType === 'text') { + updateValueDebounced(d.field as string, evt.target.value); + } + } } + /> + ) : null} + {d.values ? + + {d.values + .filter((v) => + searchValues[d.field as string] + ? v.label.toLowerCase().includes(searchValues[d.field as string].toLowerCase()) + : true, + ) + .map((v) => ( + updateValue(d.field as string, v.value)} + /> + } + label={v.label} + /> + ))} + + : null} ))}