/// /// /// /** @jsx h */ /** @jsxFrag Fragment */ import { Fragment, h, render } from "../preact/mod.tsx"; import { useCallback, useMemo, useEffect, useState, } from "../preact/hooks.ts"; import { Asearch, MatchResult } from "../deno-asearch/mod.ts"; import { getMaxDistance } from "./distance.ts"; import { makeSource } from "./makeSource.ts"; export const mount = () => { const app = document.createElement("div"); const shadowRoot = app.attachShadow({ mode: "open" }); document.body.append(app); remove = () => app.remove(); render(, shadowRoot); }; let remove: () => void; const App = () => { const [pattern_, setPattern] = useState(""); const [data, setData] = useState<{ name: string, displayName: string }[]>([]); useEffect(() => { (async () => { for await (const candidates of makeSource()) { setData((prev) => [...prev, ...candidates]); } })(); }, []); const candidates = useMemo( () => { const pattern = pattern_.trim().replace(/^\//, "").replace(/\/$/, ""); if (pattern.length === 0) return []; const match = Asearch(`${pattern} `).match; const maxDistance = getMaxDistance[ Math.max(...pattern.split(/\s+/).map((word) => word.trim().length)) ]; return data.flatMap( ({ name, displayName }) => { const result1 = match(name, maxDistance); const result2 = match(displayName, maxDistance); if (!result1.found) { if (!result2.found) return []; return [{ candidate: displayName, distance: result2.distance }]; } if (!result2.found) return [{ candidate: name, distance: result1.distance }]; return result1.distance > result2.distance ? [{ candidate: displayName, distance: result2.distance }] : [{ candidate: name, distance: result1.distance }]; } ) // 1. 編集距離 2. 文字列超 3. 辞書順序 が小さい順に並び替える .sort((a, b) => { const diff = a.distance - b.distance; if (diff !== 0) return diff; const lenDiff = a.candidate.length - b.candidate.length; if (lenDiff !== 0) return lenDiff; return a.candidate.localeCompare(b.candidate); }); }, [pattern_, data], ); const handlePattern = useCallback( (e: h.JSX.TargetedEvent) => setPattern(e.currentTarget.value), [], ); return ( <>

{candidates.length > 0 ? `Matched ${candidates.length} words` : "No matched"}

    {candidates.map(({ candidate }) => (
  • {candidate}
  • ))}

); };