diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2026-09-02 18:26:45 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2026-09-02 18:26:45 +0800 |
| commit | f7a43077fe4e139dd7e3f46f448e0951740ec148 (patch) | |
| tree | f6a68248fe42bd899e99049bab7fdb7e5d26ea60 /components/Searchable.qml | |
| parent | 2f7576faf493b4667b02dcb60ddbf3aa0a9f654b (diff) | |
fix: qmlfmt
IListView: animation
Diffstat (limited to 'components/Searchable.qml')
| -rw-r--r-- | components/Searchable.qml | 510 |
1 files changed, 255 insertions, 255 deletions
diff --git a/components/Searchable.qml b/components/Searchable.qml index a3f1034..666e2d9 100644 --- a/components/Searchable.qml +++ b/components/Searchable.qml @@ -6,259 +6,259 @@ import "../utils/fuzzysort.js" as Fuzzy import "../utils/levendist.js" as Levendist Scope { - required property var list - property string key: "name" - - enum SearchAlgorithm { - Levendist, - Fzf, - Fuzzysort, - Include - } - - property int algorithm: SearchAlgorithm.Levendist - property int scoreThreshold: 50 - property var extraOpts: ({}) - property list<string> keys: [key] - property list<real> weights: [1] - property var queryCache: ({}) - - readonly property var fzf: algorithm === Searchable.SearchAlgorithm.Fzf ? new Fzf.Finder(list, Object.assign({ - selector - }, extraOpts)) : null - - readonly property list<var> fzfFinders: algorithm === Searchable.SearchAlgorithm.Fzf ? keys.map(k => { - return new Fzf.Finder(list, Object.assign({ - selector: item => fieldString(item, k) - }, extraOpts)); - }) : [] - - readonly property list<var> fuzzyPrepped: algorithm === Searchable.SearchAlgorithm.Fuzzysort ? list.map(e => { - const obj = { - _item: e - }; - for (const k of keys) - obj[k] = Fuzzy.prepare(String(e[k] ?? "")); - return obj; - }) : [] - - function transformSearch(search: string): string { - return search; - } - - function fieldString(item: var, k: string): string { - const v = item[k]; - return v == null ? "" : String(v); - } - - function selector(item: var): string { - return fieldString(item, keys[0]); - } - - function getCachedSource(search) { - if (queryCache[search]) - return { - hit: true, - data: queryCache[search] - }; - let source = list; - let previousLists = Object.keys(queryCache).filter(key => key && search.startsWith(key)); - if (previousLists.length > 0) { - var longest = previousLists.reduce((a, b) => a.length >= b.length ? a : b); - source = queryCache[longest]; - } - return { - hit: false, - data: source - }; - } - - function levendistQuery(search: string): list<var> { - const prefixGroups = keys.map(() => []); - const fuzzyGroups = keys.map(() => []); - - for (const s of list) { - let best = null; - for (let i = 0; i < keys.length; i++) { - const t = fieldString(s, keys[i]).toLowerCase(); - if (!t) - continue; - - if (t.startsWith(search)) { - if (!best || i < best.index || best.type !== "prefix") { - best = { - type: "prefix", - index: i, - score: 0 - }; - if (best.index === 0) - break; - } - continue; - } - - let pos = 0; - for (const c of search) { - pos = t.indexOf(c, pos); - if (pos < 0) { - pos = 0; - break; - } - pos++; - } - if (pos) { - const d = Levendist.distance(search, t); - if (d <= scoreThreshold) { - if (!best || i < best.index || (best.type === "fuzzy" && i === best.index && d < best.score) || best.type === "prefix") { - if (!best || i < best.index || d < best.score) { - best = { - type: "fuzzy", - index: i, - score: d - }; - } - } - } - } - } - - if (best) { - if (best.type === "prefix") - prefixGroups[best.index].push(s); - else - fuzzyGroups[best.index].push({ - s, - score: best.score - }); - } - } - - const results = []; - const seen = new Set(); - for (let i = 0; i < keys.length; i++) { - for (const item of prefixGroups[i]) { - if (!seen.has(item)) { - results.push(item); - seen.add(item); - } - } - fuzzyGroups[i].sort((a, b) => a.score - b.score).forEach(({ - s - }) => { - if (!seen.has(s)) { - results.push(s); - seen.add(s); - } - }); - } - - return results; - } - - function fuzzysortQuery(search: string): list<var> { - const res = Fuzzy.go(search, fuzzyPrepped, Object.assign({ - all: true, - keys, - scoreFn: r => weights.reduce((a, w, i) => a + r[i].score * w, 0) - }, extraOpts)); - - const groups = keys.map(() => []); - const noMatch = []; - for (const r of res) { - let idx = -1; - for (let i = 0; i < keys.length; i++) { - const kr = r[i]; - if (kr && kr.score !== -Infinity) { - idx = i; - break; - } - } - if (idx === -1) - noMatch.push(r); - else - groups[idx].push(r); - } - - const ordered = []; - for (let i = 0; i < groups.length; i++) { - for (const r of groups[i]) - ordered.push(r.obj._item); - } - for (const r of noMatch) - ordered.push(r.obj._item); - - return ordered; - } - - function fzfQuery(search: string): list<var> { - let results; - if (keys.length <= 1) { - results = fzf.find(search).sort((a, b) => { - if (a.score === b.score) - return (selector(a.item).trim().length - selector(b.item).trim().length); - return b.score - a.score; - }).map(r => r.item); - } else { - const groups = keys.map((_, i) => { - const arr = fzfFinders[i].find(search); - arr.sort((a, b) => { - if (a.score === b.score) { - const la = fieldString(a.item, keys[i]).trim().length; - const lb = fieldString(b.item, keys[i]).trim().length; - return la - lb; - } - return b.score - a.score; - }); - return arr; - }); - - const seen = new Set(); - const out = []; - for (let i = 0; i < groups.length; i++) { - for (const r of groups[i]) { - const it = r.item; - if (!seen.has(it)) { - out.push(it); - seen.add(it); - } - } - } - results = out; - } - - return results; - } - - function includeQuery(search: string): list<var> { - return list.filter(item => keys.some(k => transformSearch(fieldString(item, k)).includes(search))); - } - - function query(search: string): list<var> { - search = transformSearch(search); - if (!search) - return list; - - const cacheCheck = getCachedSource(search); - if (cacheCheck.hit) - return cacheCheck.data; - - let result; - - if (algorithm === Searchable.SearchAlgorithm.Levendist) - result = levendistQuery(search.toLowerCase()); - - if (algorithm === Searchable.SearchAlgorithm.Fuzzysort) - result = fuzzysortQuery(search); - - if (algorithm === Searchable.SearchAlgorithm.Fzf) - result = fzfQuery(search); - - if (algorithm === Searchable.SearchAlgorithm.Include) - result = includeQuery(search); - - queryCache[search] = result; - - return result; - return []; - } + required property var list + property string key: "name" + + enum SearchAlgorithm { + Levendist, + Fzf, + Fuzzysort, + Include + } + + property int algorithm: SearchAlgorithm.Levendist + property int scoreThreshold: 50 + property var extraOpts: ({}) + property list<string> keys: [key] + property list<real> weights: [1] + property var queryCache: ({}) + + readonly property var fzf: algorithm === Searchable.SearchAlgorithm.Fzf ? new Fzf.Finder(list, Object.assign({ + selector + }, extraOpts)) : null + + readonly property list<var> fzfFinders: algorithm === Searchable.SearchAlgorithm.Fzf ? keys.map(k => { + return new Fzf.Finder(list, Object.assign({ + selector: item => fieldString(item, k) + }, extraOpts)); + }) : [] + + readonly property list<var> fuzzyPrepped: algorithm === Searchable.SearchAlgorithm.Fuzzysort ? list.map(e => { + const obj = { + _item: e + }; + for (const k of keys) + obj[k] = Fuzzy.prepare(String(e[k] ?? "")); + return obj; + }) : [] + + function transformSearch(search: string): string { + return search; + } + + function fieldString(item: var, k: string): string { + const v = item[k]; + return v == null ? "" : String(v); + } + + function selector(item: var): string { + return fieldString(item, keys[0]); + } + + function getCachedSource(search) { + if (queryCache[search]) + return { + hit: true, + data: queryCache[search] + }; + let source = list; + let previousLists = Object.keys(queryCache).filter(key => key && search.startsWith(key)); + if (previousLists.length > 0) { + var longest = previousLists.reduce((a, b) => a.length >= b.length ? a : b); + source = queryCache[longest]; + } + return { + hit: false, + data: source + }; + } + + function levendistQuery(search: string): list<var> { + const prefixGroups = keys.map(() => []); + const fuzzyGroups = keys.map(() => []); + + for (const s of list) { + let best = null; + for (let i = 0; i < keys.length; i++) { + const t = fieldString(s, keys[i]).toLowerCase(); + if (!t) + continue; + + if (t.startsWith(search)) { + if (!best || i < best.index || best.type !== "prefix") { + best = { + type: "prefix", + index: i, + score: 0 + }; + if (best.index === 0) + break; + } + continue; + } + + let pos = 0; + for (const c of search) { + pos = t.indexOf(c, pos); + if (pos < 0) { + pos = 0; + break; + } + pos++; + } + if (pos) { + const d = Levendist.distance(search, t); + if (d <= scoreThreshold) { + if (!best || i < best.index || (best.type === "fuzzy" && i === best.index && d < best.score) || best.type === "prefix") { + if (!best || i < best.index || d < best.score) { + best = { + type: "fuzzy", + index: i, + score: d + }; + } + } + } + } + } + + if (best) { + if (best.type === "prefix") + prefixGroups[best.index].push(s); + else + fuzzyGroups[best.index].push({ + s, + score: best.score + }); + } + } + + const results = []; + const seen = new Set(); + for (let i = 0; i < keys.length; i++) { + for (const item of prefixGroups[i]) { + if (!seen.has(item)) { + results.push(item); + seen.add(item); + } + } + fuzzyGroups[i].sort((a, b) => a.score - b.score).forEach(({ + s + }) => { + if (!seen.has(s)) { + results.push(s); + seen.add(s); + } + }); + } + + return results; + } + + function fuzzysortQuery(search: string): list<var> { + const res = Fuzzy.go(search, fuzzyPrepped, Object.assign({ + all: true, + keys, + scoreFn: r => weights.reduce((a, w, i) => a + r[i].score * w, 0) + }, extraOpts)); + + const groups = keys.map(() => []); + const noMatch = []; + for (const r of res) { + let idx = -1; + for (let i = 0; i < keys.length; i++) { + const kr = r[i]; + if (kr && kr.score !== -Infinity) { + idx = i; + break; + } + } + if (idx === -1) + noMatch.push(r); + else + groups[idx].push(r); + } + + const ordered = []; + for (let i = 0; i < groups.length; i++) { + for (const r of groups[i]) + ordered.push(r.obj._item); + } + for (const r of noMatch) + ordered.push(r.obj._item); + + return ordered; + } + + function fzfQuery(search: string): list<var> { + let results; + if (keys.length <= 1) { + results = fzf.find(search).sort((a, b) => { + if (a.score === b.score) + return (selector(a.item).trim().length - selector(b.item).trim().length); + return b.score - a.score; + }).map(r => r.item); + } else { + const groups = keys.map((_, i) => { + const arr = fzfFinders[i].find(search); + arr.sort((a, b) => { + if (a.score === b.score) { + const la = fieldString(a.item, keys[i]).trim().length; + const lb = fieldString(b.item, keys[i]).trim().length; + return la - lb; + } + return b.score - a.score; + }); + return arr; + }); + + const seen = new Set(); + const out = []; + for (let i = 0; i < groups.length; i++) { + for (const r of groups[i]) { + const it = r.item; + if (!seen.has(it)) { + out.push(it); + seen.add(it); + } + } + } + results = out; + } + + return results; + } + + function includeQuery(search: string): list<var> { + return list.filter(item => keys.some(k => transformSearch(fieldString(item, k)).includes(search))); + } + + function query(search: string): list<var> { + search = transformSearch(search); + if (!search) + return list; + + const cacheCheck = getCachedSource(search); + if (cacheCheck.hit) + return cacheCheck.data; + + let result; + + if (algorithm === Searchable.SearchAlgorithm.Levendist) + result = levendistQuery(search.toLowerCase()); + + if (algorithm === Searchable.SearchAlgorithm.Fuzzysort) + result = fuzzysortQuery(search); + + if (algorithm === Searchable.SearchAlgorithm.Fzf) + result = fzfQuery(search); + + if (algorithm === Searchable.SearchAlgorithm.Include) + result = includeQuery(search); + + queryCache[search] = result; + + return result; + return []; + } } |
