Como casar essa lista de tarefas com a pesquisa instantânea no Svelte?
EDIT para a versão tldr, role para baixo até onde diz que estou extremamente próximo no código abaixo.
Problema
Eu tenho uma lista de tarefas básicas que, quando concluída, terá estas qualidades:
- Quando a página é carregada, o campo do formulário deve estar em foco.
- Quando o usuário clica nas setas para cima / para baixo, os itens da lista devem ser selecionados por meio de uma mudança de cor de fundo CSS.
- Quando o usuário digita um novo item no formulário, ele deve aparecer na lista para o usuário.
- Quando o usuário digita no campo do formulário, uma "pesquisa instantânea" é aplicada à lista e apenas os itens que correspondem à pesquisa aparecem. Todos os outros recursos ainda devem funcionar. Quando o usuário rola usando as setas para cima / para baixo, ele ainda deve funcionar, independentemente de quantos itens são exibidos na página. Se apenas dois itens forem exibidos, o usuário deve ser capaz de usar as setas para cima e para baixo para selecionar esses dois itens e o foco do campo do formulário.
Todos os itens, exceto o número 4, parecem funcionar bem. O código a seguir demonstra os itens 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)
}
-->
Quero casar o código acima com um recurso de pesquisa 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)
}
Tudo o que tentei até agora apenas bagunça, não retorna a lista e desfoca (onBlur) o campo de entrada.
O código abaixo de console.log é um array que me dá o que desejo enquanto o usuário pesquisa. A próxima etapa é aplicá-lo ao DOM. Tudo o que tentei até agora não funcionou.
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
Estou extremamente próximo no código abaixo.
Nesta versão, o único problema é que quando um usuário envia uma nova tarefa a lista não aparece no DOM. Todo o resto funciona nos bastidores. Para ver o que quero dizer, adicione um item ao campo de entrada, envie-o e, em seguida, digite a letra a no campo do formulário e pressione o botão de voltar. A lista reaparecerá.
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>
<!--
-->
Respostas
Deixa comigo. Eu defini uma cópia da matriz de clientes para uma matriz reativa. Espero que isso ajude alguém.
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 é uma versão um pouco mais sucinta do que você tem, com base no código encontrado aqui . No entanto, é efetivamente igual à sua solução (ou seja, criar e exibir uma 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>