¿Cómo combinar esta lista de tareas pendientes con la búsqueda instantánea en Svelte?
EDITAR para la versión tldr, desplácese hacia abajo hasta donde dice que estoy extremadamente cerca en el código a continuación.
Problema
Tengo una lista básica de tareas que cuando se complete tendrá estas cualidades:
- Cuando se carga la página, el campo del formulario debe estar enfocado.
- Cuando el usuario hace clic en las flechas arriba / abajo, los elementos de la lista deben seleccionarse mediante un cambio de color de fondo CSS.
- Cuando el usuario escribe un nuevo elemento en el formulario, debería aparecer en la lista para el usuario.
- Cuando el usuario escribe en el campo del formulario, se aplica una "búsqueda instantánea" a la lista y solo aparecen los elementos que coinciden con la búsqueda. Todas las demás funciones deberían seguir funcionando. Cuando el usuario se desplaza usando las flechas arriba / abajo, debería funcionar sin importar cuántos elementos se muestren en la página. Si solo se muestran dos elementos, el usuario debería poder usar las flechas hacia arriba y hacia abajo para seleccionar esos dos elementos y el foco del campo del formulario.
Todos los elementos excepto el número 4 parecen funcionar bien. El siguiente código muestra los elementos 1-3
https://svelte.dev/repl/214b9c033d1c489e991484d387c267c2?version=3.30.0
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
onMount(function() {
clientInputTextField.focus();
});
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clients.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clients.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clients.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each clients as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>
<!--
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
console.log(filteredCharacters)
}
-->
Quiero unir el código anterior con una función de búsqueda instantánea.
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
console.log(filteredCharacters)
}
Todo lo que he probado hasta ahora es un desastre, no devuelve la lista y desenfoca (onBlur) el campo de entrada.
El código debajo de console.log es una matriz que me da lo que quiero mientras el usuario busca. El siguiente paso es aplicarlo al DOM. Todo lo que he probado hasta ahora no ha funcionado.
https://svelte.dev/repl/ceca685a3a4c49b4b5ccd780a101fc63?version=3.30.0
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
onMount(function() {
clientInputTextField.focus();
});
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
console.log(filteredCharacters)
}
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clients.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clients.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clients.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} on:input={search} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each clients as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>
<!--
-->
EDITAR
Estoy extremadamente cerca en el código a continuación.
En esta versión, el único problema es que cuando un usuario envía un nuevo todo, la lista no aparece en el DOM. Todo lo demás detrás de escena funciona. Para ver lo que quiero decir, agregue un elemento al campo de entrada, envíelo y luego escriba la letra a en el campo del formulario y luego presione el botón de retroceso. La lista volverá a aparecer.
https://svelte.dev/repl/f8fb733401eb4e27b9b8e54c688d355a?version=3.30.0
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
let clientsCopy = [...clients];
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
onMount(function() {
clientInputTextField.focus();
});
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
clientsCopy = [...filteredCharacters]
}
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clientsCopy.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clientsCopy.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clientsCopy.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} on:input={search} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each clientsCopy as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>
<!--
-->
Respuestas
Lo tengo. Configuré una copia de la matriz de clientes en una matriz reactiva. Espero que esto ayude a alguien.
https://svelte.dev/repl/ad1c185bf97a47b98506c3603f510de6?version=3.30.0
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
$: clientsCopy = clients ;
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
onMount(function() {
clientInputTextField.focus();
});
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
clientsCopy = [...filteredCharacters]
}
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clientsCopy.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clientsCopy.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clientsCopy.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} on:input={search} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each clientsCopy as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>
<!--
-->
Esta es una versión un poco más sucinta de lo que tiene, basada en el código que se encuentra en un resumen aquí . Sin embargo, es efectivamente lo mismo que su solución (es decir, crear y mostrar una lista "filtrada" separada).
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
const startsWith = (item, query) => item.toLowerCase().indexOf(query.toLowerCase()) != -1;
$: filteredClients = clientVal && clientVal.length ? clients.filter(item => startsWith(item, clientVal)) : clients;
onMount(function() {
clientInputTextField.focus();
});
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clients.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clients.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clients.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function onChange(e) {
console.log("what")
const searchTerm = e.target.value.toLowerCase();
console.log(searchTerm)
clients = clients.filter(client => client.toLowerCase().includes(searchTerm))
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<!-- <input type="text" name="input" on:change={onChange} bind:this={clientInputTextField} bind:value={clientVal} autocomplete="off"> -->
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each filteredClients as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>