From 8bd620d8ab87346b64eb3be133ec5b61a36e40b4 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 17 Jun 2026 05:58:55 -0700 Subject: [PATCH] Enhancement: ignore diacritics, support multiple substring matching for UI filtering (#13021) --- src-ui/jest.config.js | 2 +- src-ui/package.json | 1 + src-ui/pnpm-lock.yaml | 11 +++++++++ .../custom-fields-dropdown.component.ts | 5 ++-- ...ustom-fields-query-dropdown.component.html | 3 +++ .../custom-fields-query-dropdown.component.ts | 9 ++++++++ .../common/input/select/select.component.html | 1 + .../input/select/select.component.spec.ts | 9 ++++++++ .../common/input/select/select.component.ts | 4 ++++ .../common/input/tags/tags.component.html | 1 + .../common/input/tags/tags.component.spec.ts | 9 ++++++++ .../common/input/tags/tags.component.ts | 9 ++++++++ src-ui/src/app/pipes/filter.pipe.ts | 5 ++-- src-ui/src/app/utils/text-search.spec.ts | 17 ++++++++++++++ src-ui/src/app/utils/text-search.ts | 23 +++++++++++++++++++ 15 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 src-ui/src/app/utils/text-search.spec.ts create mode 100644 src-ui/src/app/utils/text-search.ts diff --git a/src-ui/jest.config.js b/src-ui/jest.config.js index 7b06016dd..f3557702a 100644 --- a/src-ui/jest.config.js +++ b/src-ui/jest.config.js @@ -26,7 +26,7 @@ module.exports = { 'abstract-paperless-service', ], transformIgnorePatterns: [ - 'node_modules/(?!.*(\\.mjs$|tslib|lodash-es|@angular/common/locales/.*\\.js$))', + 'node_modules/(?!.*(\\.mjs$|tslib|lodash-es|normalize-diacritics|@angular/common/locales/.*\\.js$))', ], moduleNameMapper: { ...esmPreset.moduleNameMapper, diff --git a/src-ui/package.json b/src-ui/package.json index 5a18a52fc..e01e6e5b6 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -32,6 +32,7 @@ "ngx-cookie-service": "^21.3.1", "ngx-device-detector": "^11.0.0", "ngx-ui-tour-ng-bootstrap": "^18.0.0", + "normalize-diacritics": "^5.0.0", "pdfjs-dist": "^5.7.284", "rxjs": "^7.8.2", "tslib": "^2.8.1", diff --git a/src-ui/pnpm-lock.yaml b/src-ui/pnpm-lock.yaml index c9b58d44a..3aff9d6a9 100644 --- a/src-ui/pnpm-lock.yaml +++ b/src-ui/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: ngx-ui-tour-ng-bootstrap: specifier: ^18.0.0 version: 18.0.0(f910a33494d223bd6dd07ce1bf22a35e) + normalize-diacritics: + specifier: ^5.0.0 + version: 5.0.0 pdfjs-dist: specifier: ^5.7.284 version: 5.7.284 @@ -5516,6 +5519,10 @@ packages: engines: {node: ^20.17.0 || >=22.9.0} hasBin: true + normalize-diacritics@5.0.0: + resolution: {integrity: sha512-t6czCJOpbAtckN1wCC2qPWnO3GQvNANb9bcUNbiOLEqojVuP31+ELIs5KhEG8jyz0TH7iD9BWxWz8O3ic2/rMQ==} + engines: {node: '>= 14.x', npm: '>= 6.x'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -12931,6 +12938,10 @@ snapshots: dependencies: abbrev: 4.0.0 + normalize-diacritics@5.0.0: + dependencies: + tslib: 2.8.1 + normalize-path@3.0.0: {} npm-bundled@5.0.0: diff --git a/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.ts b/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.ts index b0fcd48b8..ac7b3a541 100644 --- a/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.ts +++ b/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.ts @@ -23,6 +23,7 @@ import { import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service' import { ToastService } from 'src/app/services/toast.service' import { pngxPopperOptions } from 'src/app/utils/popper-options' +import { matchesSearchText } from 'src/app/utils/text-search' import { LoadingComponentWithPermissions } from '../../loading-component/loading.component' import { CustomFieldEditDialogComponent } from '../edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component' @@ -69,9 +70,7 @@ export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissio public get filteredFields(): CustomField[] { return this.unusedFields.filter( - (f) => - !this.filterText || - f.name.toLowerCase().includes(this.filterText.toLowerCase()) + (f) => !this.filterText || matchesSearchText(f.name, this.filterText) ) } diff --git a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html index ae2e93998..1c0134f70 100644 --- a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html +++ b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html @@ -63,6 +63,7 @@ [(ngModel)]="atom.value" [disabled]="disabled" [virtualScroll]="getSelectOptionsForField(atom.field)?.length > 100" + [searchFn]="selectOptionSearchFn" (mousedown)="$event.stopImmediatePropagation()" > } @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.DocumentLink) { @@ -81,6 +82,7 @@ [disabled]="disabled" bindLabel="name" bindValue="id" + [searchFn]="customFieldSearchFn" (mousedown)="$event.stopImmediatePropagation()" >