fix: can't search ccx chords

fixes #98

feat: improve search page responsiveness
This commit is contained in:
2024-04-06 16:42:10 +02:00
parent ecef11ac2d
commit 1a6c85a361
3 changed files with 50 additions and 41 deletions

View File

@@ -115,7 +115,6 @@ const de = {
DUPLICATE: "Akkord existiert bereits", DUPLICATE: "Akkord existiert bereits",
search: { search: {
PLACEHOLDER: "{0} Akkord{{|e}} durchsuchen", PLACEHOLDER: "{0} Akkord{{|e}} durchsuchen",
INDEXING: "{0} Akkord{{|e}} werden indiziert...",
NO_RESULTS: "Keine Ergebnisse", NO_RESULTS: "Keine Ergebnisse",
}, },
conflict: { conflict: {

View File

@@ -114,7 +114,6 @@ const en = {
DUPLICATE: "Chord already exists", DUPLICATE: "Chord already exists",
search: { search: {
PLACEHOLDER: "Search {0} chord{{|s}}", PLACEHOLDER: "Search {0} chord{{|s}}",
INDEXING: "Indexing {0} chord{{|s}}...",
NO_RESULTS: "No results", NO_RESULTS: "No results",
}, },
conflict: { conflict: {

View File

@@ -3,7 +3,7 @@
import FlexSearch from "flexsearch"; import FlexSearch from "flexsearch";
import LL from "../../../i18n/i18n-svelte"; import LL from "../../../i18n/i18n-svelte";
import { action } from "$lib/title"; import { action } from "$lib/title";
import { onDestroy, onMount, setContext } from "svelte"; import { onDestroy, onMount, setContext, tick } from "svelte";
import { changes, ChangeType, chords } from "$lib/undo-redo"; import { changes, ChangeType, chords } from "$lib/undo-redo";
import type { ChordInfo } from "$lib/undo-redo"; import type { ChordInfo } from "$lib/undo-redo";
import { derived, writable } from "svelte/store"; import { derived, writable } from "svelte/store";
@@ -12,12 +12,16 @@
import ChordActionEdit from "./ChordActionEdit.svelte"; import ChordActionEdit from "./ChordActionEdit.svelte";
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { expoOut } from "svelte/easing"; import { expoOut } from "svelte/easing";
import { osLayout } from "$lib/os-layout";
const resultSize = 38; const resultSize = 38;
let results: HTMLElement; let results: HTMLElement;
const pageSize = writable(0); const pageSize = writable(0);
let resizeObserver: ResizeObserver; let resizeObserver: ResizeObserver;
let abortIndexing: (() => void) | undefined;
let progress = 0;
onMount(() => { onMount(() => {
resizeObserver = new ResizeObserver(() => { resizeObserver = new ResizeObserver(() => {
pageSize.set(Math.floor(results.clientHeight / resultSize)); pageSize.set(Math.floor(results.clientHeight / resultSize));
@@ -30,18 +34,42 @@
resizeObserver?.disconnect(); resizeObserver?.disconnect();
}); });
let searchIndex = derived(chords, (chords) => buildIndex(chords)); let index = new FlexSearch.Index({ tokenize: "full" });
let searchIndex = writable<FlexSearch.Index | undefined>(undefined);
$: {
abortIndexing?.();
progress = 0;
buildIndex($chords, $osLayout).then(searchIndex.set);
}
async function buildIndex(chords: ChordInfo[]): Promise<FlexSearch.Index> { async function buildIndex(
const index = new FlexSearch.Index({ tokenize: "full" }); chords: ChordInfo[],
osLayout: Map<string, string>,
): Promise<FlexSearch.Index> {
if (chords.length === 0 || !browser) return index; if (chords.length === 0 || !browser) return index;
index = new FlexSearch.Index({ tokenize: "full" });
let abort = false;
abortIndexing = () => {
abort = true;
};
for (let i = 0; i < chords.length; i++) { for (let i = 0; i < chords.length; i++) {
if (abort) return index;
const chord = chords[i]!; const chord = chords[i]!;
progress = i;
if ("phrase" in chord) { if ("phrase" in chord) {
await index.addAsync( await index.addAsync(
i, i,
chord.phrase chord.phrase
.map((it) => KEYMAP_CODES.get(it)?.id) .map((it) => {
const info = KEYMAP_CODES.get(it);
if (!info) return "";
return (
(info.keyCode && osLayout.get(info.keyCode)) || info.id || ""
);
})
.filter((it) => !!it) .filter((it) => !!it)
.join(""), .join(""),
); );
@@ -101,19 +129,12 @@
</svelte:head> </svelte:head>
<div class="search-container"> <div class="search-container">
{#await $searchIndex} <input
<input type="search"
type="search" placeholder={$LL.configure.chords.search.PLACEHOLDER(progress + 1)}
placeholder={$LL.configure.chords.search.INDEXING($chords.length)} on:input={(event) => search($searchIndex, event)}
disabled={true} class:loading={progress !== $chords.length - 1}
/> />
{:then index}
<input
type="search"
placeholder={$LL.configure.chords.search.PLACEHOLDER($chords.length)}
on:input={(event) => search(index, event)}
/>
{/await}
<div class="paginator"> <div class="paginator">
{#if $lastPage !== -1} {#if $lastPage !== -1}
{page + 1} / {$lastPage + 1} {page + 1} / {$lastPage + 1}
@@ -135,7 +156,8 @@
<section bind:this={results}> <section bind:this={results}>
<div class="results"> <div class="results">
{#await $searchIndex then} <!-- fixes some unresponsiveness -->
{#await tick() then}
<table transition:fly={{ y: 48, easing: expoOut }}> <table transition:fly={{ y: 48, easing: expoOut }}>
{#if $lastPage !== -1} {#if $lastPage !== -1}
{#if page === 0} {#if page === 0}
@@ -184,20 +206,13 @@
transition: outline-color 250ms ease; transition: outline-color 250ms ease;
background: none; background: none;
color: inherit; color: inherit;
outline: 1px dashed var(--md-sys-color-surface-variant); border: 1px dashed var(--md-sys-color-outline);
outline: 2px solid transparent;
outline-offset: -1px;
margin: 2px; margin: 2px;
border: none;
padding: 8px; padding: 8px;
border-radius: 4px; border-radius: 4px;
@media (prefers-contrast: more) {
outline-color: var(--md-sys-color-outline);
&:focus {
outline-width: 2px;
}
}
&:focus { &:focus {
outline-color: var(--md-sys-color-primary); outline-color: var(--md-sys-color-primary);
} }
@@ -205,13 +220,13 @@
@keyframes pulse { @keyframes pulse {
0% { 0% {
opacity: 0.1; opacity: 0.4;
} }
50% { 50% {
opacity: 0.8; opacity: 1;
} }
100% { 100% {
opacity: 0.1; opacity: 0.4;
} }
} }
@@ -237,11 +252,7 @@
&::placeholder { &::placeholder {
color: var(--md-sys-color-on-surface-variant); color: var(--md-sys-color-on-surface-variant);
opacity: 0.2; opacity: 0.8;
@media (prefers-contrast: more) {
opacity: 0.8;
}
} }
&:focus { &:focus {
@@ -250,8 +261,8 @@
outline: none; outline: none;
} }
&:disabled { &.loading {
animation: pulse 1s infinite; opacity: 0.4;
} }
} }