Accent-insensitive card search
A fold() (lowercase + strip accents, using the same table as nucleo's own normalization) is registered as a SQLite function and used by the name, oracle-text and location filters, so "khazad-dum" finds Troll of Khazad-dûm. The fuzzy name search folds the query too (accented queries used to match nothing) and its exact-name promotion; decklist/ManaBox import falls back to a folded name match; the deck quick filter and tag suggestions fold client-side. Closes #17
This commit is contained in:
@@ -632,6 +632,12 @@ export const CardSearchBox = forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
/** Lowercase and strip accents, so "khazad-dum" matches "Khazad-dûm". The
|
||||
* frontend twin of `mtgc_core::text::fold`, for matching done client-side. */
|
||||
export function fold(s: string): string {
|
||||
return s.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* A chip-based multi-value text filter with autocomplete: selected values
|
||||
* render as removable chips, and typing filters `suggestions` into a
|
||||
@@ -653,11 +659,11 @@ export function TagInput({
|
||||
const [text, setText] = useState("");
|
||||
const [sel, setSel] = useState(0);
|
||||
|
||||
const query = text.trim().toLowerCase();
|
||||
const query = fold(text.trim());
|
||||
const filtered = query
|
||||
? suggestions
|
||||
.filter((s) => !values.some((v) => v.toLowerCase() === s.toLowerCase()))
|
||||
.filter((s) => s.toLowerCase().includes(query))
|
||||
.filter((s) => fold(s).includes(query))
|
||||
.slice(0, 8)
|
||||
: [];
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
usePersistedState,
|
||||
useTileColumns,
|
||||
useViewMode,
|
||||
fold,
|
||||
} from "../components";
|
||||
import { SuggestionsModal } from "../SuggestionsModal";
|
||||
import { useBackButton } from "../backButton";
|
||||
@@ -878,9 +879,9 @@ export function DeckView({ refreshSignal }: { refreshSignal?: number }) {
|
||||
// filter only, distinct from the CardSearchBox boxes below (which search
|
||||
// the library/reference db to find NEW cards to add).
|
||||
function matchesFilter(e: DeckEntry): boolean {
|
||||
const q = filterQuery.trim().toLowerCase();
|
||||
const q = fold(filterQuery.trim());
|
||||
if (!q) return true;
|
||||
return e.name.toLowerCase().includes(q) || e.type_line.toLowerCase().includes(q);
|
||||
return fold(e.name).includes(q) || fold(e.type_line).includes(q);
|
||||
}
|
||||
|
||||
const owned = contents.filter((e) => !e.is_maybeboard && e.quantity_owned > 0 && matchesFilter(e));
|
||||
|
||||
Reference in New Issue
Block a user