mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-01 13:37:47 +00:00
Add ability to receive string array, treat as one string for matching, but return same string array decorated
This commit is contained in:
parent
fdf1eae882
commit
7ddafdd45f
@ -510,13 +510,10 @@ export interface FuzzyScorer {
|
|||||||
): FuzzyScore | undefined;
|
): FuzzyScore | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createMatches(score: undefined | FuzzyScore): Match[] {
|
function _createMatches(score: FuzzyScore, wordPos: number) {
|
||||||
if (typeof score === "undefined") {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const res: Match[] = [];
|
const res: Match[] = [];
|
||||||
const wordPos = score[1];
|
|
||||||
for (let i = score.length - 1; i > 1; i--) {
|
for (let i = score.length - 1; i >= 0; i--) {
|
||||||
const pos = score[i] + wordPos;
|
const pos = score[i] + wordPos;
|
||||||
const last = res[res.length - 1];
|
const last = res[res.length - 1];
|
||||||
if (last && last.end === pos) {
|
if (last && last.end === pos) {
|
||||||
@ -525,9 +522,64 @@ export function createMatches(score: undefined | FuzzyScore): Match[] {
|
|||||||
res.push({ start: pos, end: pos + 1 });
|
res.push({ start: pos, end: pos + 1 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createMatches(score: undefined | FuzzyScore): Match[] {
|
||||||
|
if (typeof score === "undefined") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const wordPos = score[1];
|
||||||
|
const _score = score.splice(2);
|
||||||
|
|
||||||
|
return _createMatches(_score, wordPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first and second elements in score represent total score, and the offset at which
|
||||||
|
// matching started. For this method, we only care about match positions, not the score
|
||||||
|
// or offset.
|
||||||
|
const findFirstOutOfRangeElement = (number, score: FuzzyScore) =>
|
||||||
|
score.findIndex((num) => num < number);
|
||||||
|
|
||||||
|
export function createMatchesFragmented(
|
||||||
|
score: undefined | FuzzyScore,
|
||||||
|
strings: string[]
|
||||||
|
): Match[][] {
|
||||||
|
if (typeof score === "undefined") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const matches: Match[][] = [];
|
||||||
|
const wordPos = score[1];
|
||||||
|
let lengthCounter = 0;
|
||||||
|
const _score = score.splice(2);
|
||||||
|
const fragmentedScores: FuzzyScore[] = [];
|
||||||
|
|
||||||
|
for (const string of strings) {
|
||||||
|
const prevLengthCounter = lengthCounter;
|
||||||
|
lengthCounter += string.length;
|
||||||
|
const lastIndex = findFirstOutOfRangeElement(lengthCounter, _score);
|
||||||
|
|
||||||
|
if (lastIndex < 0) {
|
||||||
|
fragmentedScores.push([]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragmentedScores.push(
|
||||||
|
_score.splice(lastIndex).map((pos) => pos - prevLengthCounter)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const fragmentedScore of fragmentedScores) {
|
||||||
|
const res = _createMatches(fragmentedScore, wordPos);
|
||||||
|
matches.push(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fast function (therefore imprecise) to check if code points are emojis.
|
* A fast function (therefore imprecise) to check if code points are emojis.
|
||||||
* Generated using https://github.com/alexdima/unicode-utils/blob/master/generate-emoji-test.js
|
* Generated using https://github.com/alexdima/unicode-utils/blob/master/generate-emoji-test.js
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { createMatches, FuzzyScore, fuzzyScore } from "./filter";
|
import {
|
||||||
|
createMatches,
|
||||||
|
createMatchesFragmented,
|
||||||
|
FuzzyScore,
|
||||||
|
fuzzyScore,
|
||||||
|
} from "./filter";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether a sequence of letters exists in another string,
|
* Determine whether a sequence of letters exists in another string,
|
||||||
@ -23,8 +28,11 @@ export const fuzzySequentialMatch: FuzzySequentialMatcher = (
|
|||||||
) => {
|
) => {
|
||||||
let topScore = Number.NEGATIVE_INFINITY;
|
let topScore = Number.NEGATIVE_INFINITY;
|
||||||
const decoratedStrings: string[][] = [];
|
const decoratedStrings: string[][] = [];
|
||||||
|
const strings = item.treatArrayAsSingleString
|
||||||
|
? [item.strings.join("")]
|
||||||
|
: item.strings;
|
||||||
|
|
||||||
for (const word of item.strings) {
|
for (const word of strings) {
|
||||||
const scores = fuzzyScore(
|
const scores = fuzzyScore(
|
||||||
filter,
|
filter,
|
||||||
filter.toLowerCase(),
|
filter.toLowerCase(),
|
||||||
@ -36,7 +44,7 @@ export const fuzzySequentialMatch: FuzzySequentialMatcher = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (decorate) {
|
if (decorate) {
|
||||||
decoratedStrings.push(decorate(word, scores));
|
decoratedStrings.push(decorate(word, item, scores));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!scores) {
|
if (!scores) {
|
||||||
@ -81,6 +89,7 @@ export interface ScorableTextItem {
|
|||||||
score?: number;
|
score?: number;
|
||||||
strings: string[];
|
strings: string[];
|
||||||
decoratedStrings?: string[][];
|
decoratedStrings?: string[][];
|
||||||
|
treatArrayAsSingleString?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type FuzzyFilterSort = <T extends ScorableTextItem>(
|
type FuzzyFilterSort = <T extends ScorableTextItem>(
|
||||||
@ -109,43 +118,49 @@ export const fuzzyFilterSort: FuzzyFilterSort = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type MatchDecorator = (word: string, scores?: FuzzyScore) => string[];
|
type MatchDecorator = (
|
||||||
|
word: string,
|
||||||
|
item: ScorableTextItem,
|
||||||
|
scores?: FuzzyScore
|
||||||
|
) => string[];
|
||||||
export const createMatchDecorator: (
|
export const createMatchDecorator: (
|
||||||
left: string,
|
left: string,
|
||||||
right: string
|
right: string
|
||||||
) => MatchDecorator = (left, right) => (word, scores) =>
|
) => MatchDecorator = (left, right) => (word, item, scores) =>
|
||||||
_decorateMatch(word, [left, right], scores);
|
_decorateMatch(word, [left, right], item, scores);
|
||||||
|
|
||||||
const _decorateMatch: (
|
const _decorateMatch: (
|
||||||
word: string,
|
word: string,
|
||||||
surroundWith: [string, string],
|
surroundWith: [string, string],
|
||||||
|
item: ScorableTextItem,
|
||||||
scores?: FuzzyScore
|
scores?: FuzzyScore
|
||||||
) => string[] = (word, surroundWith, scores) => {
|
) => string[] = (word, surroundWith, item, scores) => {
|
||||||
if (!scores) {
|
if (!scores) {
|
||||||
return [word];
|
return [word];
|
||||||
}
|
}
|
||||||
|
|
||||||
const decoratedText: string[] = [];
|
const decoratedText: string[] = [];
|
||||||
const matches = createMatches(scores);
|
const matches = item.treatArrayAsSingleString
|
||||||
|
? createMatchesFragmented(scores, item.strings)
|
||||||
|
: [createMatches(scores)];
|
||||||
const [left, right] = surroundWith;
|
const [left, right] = surroundWith;
|
||||||
let pos = 0;
|
|
||||||
|
|
||||||
let actualWord = "";
|
for (let i = 0; i < matches.length; i++) {
|
||||||
for (const match of matches) {
|
const match = matches[i];
|
||||||
actualWord +=
|
const _word = item.treatArrayAsSingleString ? item.strings[i] : word;
|
||||||
word.substring(pos, match.start) +
|
let pos = 0;
|
||||||
left +
|
let actualWord = "";
|
||||||
word.substring(match.start, match.end) +
|
for (const fragmentedMatch of match) {
|
||||||
right;
|
actualWord +=
|
||||||
pos = match.end;
|
_word.substring(pos, fragmentedMatch.start) +
|
||||||
|
left +
|
||||||
|
_word.substring(fragmentedMatch.start, fragmentedMatch.end) +
|
||||||
|
right;
|
||||||
|
pos = fragmentedMatch.end;
|
||||||
|
}
|
||||||
|
actualWord += _word.substring(pos);
|
||||||
|
|
||||||
|
decoratedText.push(actualWord);
|
||||||
}
|
}
|
||||||
actualWord += word.substring(pos);
|
|
||||||
|
|
||||||
const fragments = actualWord.split("::");
|
|
||||||
|
|
||||||
for (const fragment of fragments) {
|
|
||||||
decoratedText.push(fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoratedText;
|
return decoratedText;
|
||||||
};
|
};
|
||||||
|
@ -476,7 +476,8 @@ export class QuickBar extends LitElement {
|
|||||||
return {
|
return {
|
||||||
...commandItem,
|
...commandItem,
|
||||||
categoryKey: "reload",
|
categoryKey: "reload",
|
||||||
strings: [`${commandItem.categoryText}::${commandItem.primaryText}`],
|
strings: [`${commandItem.categoryText} `, commandItem.primaryText],
|
||||||
|
treatArrayAsSingleString: true,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -510,7 +511,8 @@ export class QuickBar extends LitElement {
|
|||||||
return this._generateConfirmationCommand(
|
return this._generateConfirmationCommand(
|
||||||
{
|
{
|
||||||
...item,
|
...item,
|
||||||
strings: [`${item.categoryText}::${item.primaryText}`],
|
strings: [`${item.categoryText} `, item.primaryText],
|
||||||
|
treatArrayAsSingleString: true,
|
||||||
},
|
},
|
||||||
this.hass.localize("ui.dialogs.generic.ok")
|
this.hass.localize("ui.dialogs.generic.ok")
|
||||||
);
|
);
|
||||||
@ -611,7 +613,8 @@ export class QuickBar extends LitElement {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...navItem,
|
...navItem,
|
||||||
strings: [`${navItem.categoryText}::${navItem.primaryText}`],
|
strings: [`${navItem.categoryText} `, navItem.primaryText],
|
||||||
|
treatArrayAsSingleString: true,
|
||||||
categoryKey,
|
categoryKey,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user