diff --git a/adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md b/adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md
index 38fc21af8..325b7a323 100644
--- a/adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md
+++ b/adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md
@@ -491,4 +491,37 @@ export default {
],
```
-This way, when creating or editing a record you will be able to choose value for this field from a dropdown selector and on list and show pages this field will be displayed as a link to a foreign resource.
\ No newline at end of file
+This way, when creating or editing a record you will be able to choose value for this field from a dropdown selector and on list and show pages this field will be displayed as a link to a foreign resource.
+
+## Filtering
+
+### Filter Options
+
+You can specify the delay between filtering requests and filtering operator for a column using `filterOptions` field.
+
+```typescript title="./resources/adminuser.ts"
+export default {
+ name: 'adminuser',
+ columns: [
+ ...
+ {
+ name: "title",
+ required: true,
+ maxLength: 255,
+ minLength: 3,
+//diff-add
+ filterOptions: {
+//diff-add
+ debounceTimeMs: 500,
+//diff-add
+ substringSearch: false,
+//diff-add
+ },
+ },
+ ],
+ },
+ ...
+ ],
+```
+`debounceTimeMs` field dictates how long (in milliseconds) to wait between inputs to send updated data request. By increasing this value, you can reduce the amount of requests set to backend. Default value for this field is set to 10ms.
+`substringSearch` sets what comparison operator to use for text field. By default this field is set to `true`, which results in using case-insensitive `ILIKE` operator, that will look for records that have filter string anywhere inside field value. Setting this `substringSearch` to `false` will result in using more strict `EQ` operator, that will look for exact full-string matches.
diff --git a/adminforth/modules/configValidator.ts b/adminforth/modules/configValidator.ts
index e35609a49..ed15f6267 100644
--- a/adminforth/modules/configValidator.ts
+++ b/adminforth/modules/configValidator.ts
@@ -374,6 +374,30 @@ export default class ConfigValidator implements IConfigValidator {
//define default sortable
if (!Object.keys(col).includes('sortable')) { col.sortable = !col.virtual; }
+ // define default filter options
+ if (!Object.keys(col).includes('filterOptions')) {
+ col.filterOptions = {
+ debounceTimeMs: 10,
+ substringSearch: true,
+ };
+ } else {
+ if (col.filterOptions.debounceTimeMs !== undefined) {
+ if (typeof col.filterOptions.debounceTimeMs !== 'number') {
+ errors.push(`Resource "${res.resourceId}" column "${col.name}" filterOptions.debounceTimeMs must be a number`);
+ }
+ } else {
+ col.filterOptions.debounceTimeMs = 10;
+ }
+
+ if (col.filterOptions.substringSearch !== undefined) {
+ if (typeof col.filterOptions.substringSearch !== 'boolean') {
+ errors.push(`Resource "${res.resourceId}" column "${col.name}" filterOptions.substringSearch must be a boolean`);
+ }
+ } else {
+ col.filterOptions.substringSearch = true;
+ }
+ }
+
col.showIn = this.validateAndNormalizeShowIn(resInput, inCol, errors, warnings);
// check col.required is boolean or object
diff --git a/adminforth/spa/src/components/Filters.vue b/adminforth/spa/src/components/Filters.vue
index 6ca404d2e..338a3545b 100644
--- a/adminforth/spa/src/components/Filters.vue
+++ b/adminforth/spa/src/components/Filters.vue
@@ -27,7 +27,7 @@
multiple
class="w-full"
:options="columnOptions[c.name] || []"
- @update:modelValue="setFilterItem({ column: c, operator: 'in', value: $event.length ? $event : undefined })"
+ @update:modelValue="onFilterInput[c.name]({ column: c, operator: 'in', value: $event.length ? $event : undefined })"
:modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
/>
@@ -49,7 +49,7 @@
class="w-full"
v-else-if="c.enum"
:options="c.enum"
- @update:modelValue="setFilterItem({ column: c, operator: 'in', value: $event.length ? $event : undefined })"
+ @update:modelValue="onFilterInput[c.name]({ column: c, operator: 'in', value: $event.length ? $event : undefined })"
:modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
/>
@@ -58,17 +58,17 @@
type="text"
full-width
:placeholder="$t('Search')"
- @update:modelValue="setFilterItem({ column: c, operator: 'ilike', value: $event || undefined })"
- :modelValue="getFilterItem({ column: c, operator: 'ilike' })"
+ @update:modelValue="onFilterInput[c.name]({ column: c, operator: c.filterOptions?.substringSearch ? 'ilike' : 'eq', value: $event || undefined })"
+ :modelValue="getFilterItem({ column: c, operator: c.filterOptions?.substringSearch ? 'ilike' : 'eq' })"
/>