mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-07 10:32:49 +00:00
fix: can't search ccx chords
fixes #98 feat: improve search page responsiveness
This commit is contained in:
@@ -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: {
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user