Ignore diacritics (accents) in search (#21150)

* Ignore diacritics in search

* Rename variable
This commit is contained in:
Paul Bottein 2024-06-26 10:07:31 +02:00 committed by GitHub
parent 1acada625f
commit f42a9ac070
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 35 additions and 20 deletions

View File

@ -1,5 +1,6 @@
import Fuse from "fuse.js";
import type { IFuseOptions } from "fuse.js";
import Fuse from "fuse.js";
import { stripDiacritics } from "../../../src/common/string/strip-diacritics";
import { StoreAddon } from "../../../src/data/supervisor/store";
export function filterAndSort(addons: StoreAddon[], filter: string) {
@ -8,7 +9,8 @@ export function filterAndSort(addons: StoreAddon[], filter: string) {
isCaseSensitive: false,
minMatchCharLength: Math.min(filter.length, 2),
threshold: 0.2,
getFn: (obj, path) => stripDiacritics(Fuse.config.getFn(obj, path)),
};
const fuse = new Fuse(addons, options);
return fuse.search(filter).map((result) => result.item);
return fuse.search(stripDiacritics(filter)).map((result) => result.item);
}

View File

@ -1,3 +1,4 @@
import { stripDiacritics } from "../strip-diacritics";
import { fuzzyScore } from "./filter";
/**
@ -19,10 +20,10 @@ export const fuzzySequentialMatch = (
for (const word of item.strings) {
const scores = fuzzyScore(
filter,
filter.toLowerCase(),
stripDiacritics(filter.toLowerCase()),
0,
word,
word.toLowerCase(),
stripDiacritics(word.toLowerCase()),
0,
true
);

View File

@ -0,0 +1,2 @@
export const stripDiacritics = (str) =>
str.normalize("NFD").replace(/[\u0300-\u036F]/g, "");

View File

@ -1,5 +1,6 @@
import { expose } from "comlink";
import { stringCompare } from "../../common/string/compare";
import { stripDiacritics } from "../../common/string/strip-diacritics";
import type {
ClonedDataTableColumnData,
DataTableRowData,
@ -12,20 +13,18 @@ const filterData = (
columns: SortableColumnContainer,
filter: string
) => {
filter = filter.toUpperCase();
filter = stripDiacritics(filter.toLowerCase());
return data.filter((row) =>
Object.entries(columns).some((columnEntry) => {
const [key, column] = columnEntry;
if (column.filterable) {
if (
String(
column.filterKey
? row[column.valueColumn || key][column.filterKey]
: row[column.valueColumn || key]
)
.toUpperCase()
.includes(filter)
) {
const value = String(
column.filterKey
? row[column.valueColumn || key][column.filterKey]
: row[column.valueColumn || key]
);
if (stripDiacritics(value).toLowerCase().includes(filter)) {
return true;
}
}

View File

@ -54,6 +54,7 @@ import {
AddAutomationElementDialogParams,
PASTE_VALUE,
} from "./show-add-automation-element-dialog";
import { stripDiacritics } from "../../../common/string/strip-diacritics";
const TYPES = {
trigger: { groups: TRIGGER_GROUPS, icons: TRIGGER_ICONS },
@ -208,9 +209,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
isCaseSensitive: false,
minMatchCharLength: Math.min(filter.length, 2),
threshold: 0.2,
getFn: (obj, path) => stripDiacritics(Fuse.config.getFn(obj, path)),
};
const fuse = new Fuse(items, options);
return fuse.search(filter).map((result) => result.item);
return fuse.search(stripDiacritics(filter)).map((result) => result.item);
}
);

View File

@ -55,6 +55,7 @@ import {
showYamlIntegrationDialog,
} from "./show-add-integration-dialog";
import { getConfigEntries } from "../../../data/config_entries";
import { stripDiacritics } from "../../../common/string/strip-diacritics";
export interface IntegrationListItem {
name: string;
@ -255,6 +256,7 @@ class AddIntegrationDialog extends LitElement {
isCaseSensitive: false,
minMatchCharLength: Math.min(filter.length, 2),
threshold: 0.2,
getFn: (obj, path) => stripDiacritics(Fuse.config.getFn(obj, path)),
};
const helpers = Object.entries(h).map(([domain, integration]) => ({
domain,
@ -264,15 +266,16 @@ class AddIntegrationDialog extends LitElement {
is_built_in: integration.is_built_in !== false,
cloud: integration.iot_class?.startsWith("cloud_"),
}));
const normalizedFilter = stripDiacritics(filter);
return [
...new Fuse(integrations, options)
.search(filter)
.search(normalizedFilter)
.map((result) => result.item),
...new Fuse(yamlIntegrations, options)
.search(filter)
.search(normalizedFilter)
.map((result) => result.item),
...new Fuse(helpers, options)
.search(filter)
.search(normalizedFilter)
.map((result) => result.item),
];
}

View File

@ -71,6 +71,7 @@ import { showAddIntegrationDialog } from "./show-add-integration-dialog";
import "./ha-disabled-config-entry-card";
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
import "../../../components/search-input-outlined";
import { stripDiacritics } from "../../../common/string/strip-diacritics";
export interface ConfigEntryExtended extends ConfigEntry {
localized_domain_name?: string;
@ -208,9 +209,12 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
isCaseSensitive: false,
minMatchCharLength: Math.min(filter.length, 2),
threshold: 0.2,
getFn: (obj, path) => stripDiacritics(Fuse.config.getFn(obj, path)),
};
const fuse = new Fuse(configEntriesInProgress, options);
filteredEntries = fuse.search(filter).map((result) => result.item);
filteredEntries = fuse
.search(stripDiacritics(filter))
.map((result) => result.item);
} else {
filteredEntries = configEntriesInProgress;
}

View File

@ -16,6 +16,7 @@ import memoizeOne from "memoize-one";
import { storage } from "../../../../common/decorators/storage";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stringCompare } from "../../../../common/string/compare";
import { stripDiacritics } from "../../../../common/string/strip-diacritics";
import "../../../../components/ha-circular-progress";
import "../../../../components/search-input";
import { isUnavailableState } from "../../../../data/entity";
@ -86,9 +87,10 @@ export class HuiCardPicker extends LitElement {
isCaseSensitive: false,
minMatchCharLength: Math.min(filter.length, 2),
threshold: 0.2,
getFn: (obj, path) => stripDiacritics(Fuse.config.getFn(obj, path)),
};
const fuse = new Fuse(cards, options);
cards = fuse.search(filter).map((result) => result.item);
cards = fuse.search(stripDiacritics(filter)).map((result) => result.item);
return cardElements.filter((cardElement: CardElement) =>
cards.includes(cardElement.card)
);