Add "and" "ampersand" substitution to fuzzy match

This commit is contained in:
2026-07-21 10:34:40 -04:00
parent 50bef85367
commit a167f24cd3
4 changed files with 200 additions and 18 deletions

View File

@@ -1121,6 +1121,7 @@ func navigationSection() func() *VNode {
func searchSection() func() *VNode {
q := NewSignal("")
hit := NewSignal("")
andAmpQ := NewSignal("")
return func() *VNode {
options := []string{
@@ -1129,6 +1130,20 @@ func searchSection() func() *VNode {
"SegmentedButtons", "SignaturePad", "TabGroup", "ThemeToggle", "Toast", "ToggleSwitch", "Tooltip",
}
// Both spellings on purpose: with AndAmpersand on, typing either finds both,
// and the exact spelling ranks above the substituted one. "Standard" / "Brand"
// hold an "and" that is not the whole word, so it is left untouched.
andAmpNames := []string{
"First Bank & Trust",
"First Bank and Trust Company",
"Smith & Wesson Financial",
"Johnson and Johnson Federal CU",
"Highland Savings & Loan",
"Standard Chartered",
"Brand Mortgage Group",
"AT&T Employees CU",
}
return docSection("search", "Fuzzy search",
prose("Subsequence matching with a typo tolerance, scored so the best hit sorts first, and "+
"the matched characters highlighted in the result. \"atbl\" finds AutoTable; so does "+
@@ -1147,10 +1162,30 @@ func searchSection() func() *VNode {
}),
),
),
prose("Set AndAmpersand and the word \"and\" and the symbol \"&\" match each other, so "+
"\"First Bank and Trust\" also finds \"First Bank & Trust\". The exact spelling still wins — "+
"the substituted form is a penalized extra pass, not a free swap — and a stray \"and\" inside "+
"\"Standard\" or \"Brand\" is left alone."),
demo("AndAmpersand — type \"first bank and trust\", or \"smith & wesson\"",
row("max-w-md",
ui.FuzzyMatch(ui.FuzzyMatchProps{
Options: andAmpNames,
Query: andAmpQ.Get(),
AndAmpersand: true,
Placeholder: "Search bank names…",
MaxResults: 6,
ShowScores: true,
OnQueryChange: func(v string) { andAmpQ.Set(v) },
}),
),
),
apiTable(
apiRow{"RankFuzzyMatches", "The scorer, headless. Use it and render the results yourself."},
apiRow{"FuzzySegments", "Splits a result into matched / unmatched runs, for highlighting."},
apiRow{"FuzzyMatchTypoTolerant", "One transposition or substitution forgiven."},
apiRow{"FuzzyOptions{AndAmpersand}", "Treat the word \"and\" and \"&\" as interchangeable, exact spelling still first."},
),
)
}

View File

@@ -1332,6 +1332,20 @@ const KIT_NAMES = [
"Tooltip", "TutorialProvider", "SidebarNav", "Chart", "FuzzyMatch",
];
// Both "and" and "&" spellings appear here on purpose: with andAmpersand on, typing
// either finds both, and the exact spelling ranks above the substituted one.
// "Standard" / "Brand" hold an "and" that is NOT the whole word — left untouched.
const AND_AMP_NAMES = [
"First Bank & Trust",
"First Bank and Trust Company",
"Smith & Wesson Financial",
"Johnson and Johnson Federal CU",
"Highland Savings & Loan",
"Standard Chartered",
"Brand Mortgage Group",
"AT&T Employees CU",
];
function Search() {
const [hit, setHit] = createSignal("");
@@ -1359,6 +1373,25 @@ function Search() {
and you render them however you like.
</p>
</Panel>
<Prose>
Pass <code class="font-mono">andAmpersand</code> and the word "and" and the symbol "&amp;"
match each other, so "First Bank and Trust" also finds "First Bank &amp; Trust". The exact
spelling still wins the substituted form is a penalized extra pass, not a free swap and a
stray "and" inside "Standard" or "Brand" is left alone.
</Prose>
<Panel title="andAmpersand — type “first bank and trust”, or “smith & wesson”">
<div class="max-w-md">
<FuzzyMatch
options={AND_AMP_NAMES}
andAmpersand
placeholder="Search bank names…"
maxResults={6}
showScores
/>
</div>
</Panel>
</Section>
);
}