[ad_1]
Neste artigo, criaremos um projeto que imita a funcionalidade de pesquisa de arquivos menos conhecida, mas impressionante, fornecida pelo GitHub.
Para ver como funciona, acesse qualquer repositório GitHub e pressione a letra t que direcionará você para a visualização de pesquisa. Em seguida, você pode pesquisar e rolar simultaneamente pela lista, como mostrado no gif abaixo:
Ao criar este aplicativo, você aprenderá o seguinte:
- Como criar uma interface do usuário semelhante a um repositório do GitHub
- Como trabalhar com eventos do teclado no React
- Como trabalhar com a navegação usando as setas do teclado
- Como destacar o texto correspondente durante a pesquisa
- Como adicionar ícones no React
- Como renderizar conteúdo HTML em uma expressão JSX
E muito mais.
Você pode ver a demonstração ao vivo do aplicativo aqui.
Vamos começar
Crie um novo projeto usando create-react-app
:
create-react-app github-file-search-react
Após a criação do projeto, exclua todos os arquivos do diretório src
pasta e criar index.js
, App.js
e styles.scss
arquivos dentro do src
pasta. Crie também components
e utils
pastas dentro do src
pasta.
Instale as dependências necessárias:
yarn add [email protected] [email protected] [email protected] [email protected]
Abrir styles.scss
e adicione o conteúdo de aqui dentro dele.
Crie um novo arquivo Header.js
dentro de components
pasta com o seguinte conteúdo:
import React from 'react';const Header = () => GitHub File Search
;export default Header;
Crie um novo arquivo api.js
dentro de utils
pasta e adicione o conteúdo de aqui dentro dele.
Neste arquivo, criamos dados estáticos para serem exibidos na interface do usuário para manter o aplicativo simples e fácil de entender.
Crie um novo arquivo ListItem.js
dentro de components
pasta com o seguinte conteúdo:
import React from 'react';import moment from 'moment';import { AiFillFolder, AiOutlineFile } from 'react-icons/ai';const ListItem = ({ type, name, comment, modified_time }) => { return ( {type === 'folder' ? ( ) : ( )} {name} {comment} {moment(modified_time).fromNow()} );};export default ListItem;
Nesse arquivo, coletamos os dados de cada arquivo que queremos exibir e exibimos o ícone da pasta / arquivo, o nome do arquivo, os comentários e a última vez que o arquivo foi modificado.
Para exibir os ícones, usaremos o react-icons
biblioteca npm. Ele tem um site muito bom que permite pesquisar e usar com facilidade os ícones que você precisa. Confira aqui.
O componente de ícones aceita o color
e size
adereços para personalizar o ícone que usamos no código acima.
Crie um novo arquivo chamado FilesList.js
dentro de components
pasta com o seguinte conteúdo:
import React from 'react';import ListItem from './ListItem';const FilesList = ({ files }) => { return ( {files.length > 0 ? ( files.map((file, index) => { return ; }) ) : ( No matching files found
)} );};export default FilesList;
Neste arquivo, lemos os dados estáticos do api.js
e, em seguida, exiba cada elemento da matriz de arquivos usando o método de mapa de matriz.
Agora abra o src/App.js
e adicione o seguinte código dentro dele:
import React from 'react';import Header from './components/Header';import FilesList from './components/FilesList';import files from './utils/api';export default class App extends React.Component { state = { filesList: files }; render() { const { counter, filesList } = this.state; return ( ); }}
Nesse arquivo, adicionamos um estado para armazenar os dados dos arquivos estáticos, que podemos modificar sempre que necessário. Então nós passamos para o FilesList
componente a ser exibido na interface do usuário.
Agora, abra o index.js
e adicione o seguinte código dentro dele:
import React from 'react';import ReactDOM from 'react-dom';import App from './App';import './styles.scss';ReactDOM.render( , document.getElementById('root'));
Agora, inicie seu aplicativo executando o yarn start
comando no terminal ou prompt de comando e você verá a seguinte tela inicial:
Você pode encontrar o código até este ponto em este ramo.
Adicionar funcionalidade básica de pesquisa
Agora, vamos adicionar a funcionalidade que altera a interface do usuário e nos permite pesquisar arquivos quando pressionamos a letra t no nosso teclado.
Dentro de utils
pasta crie um novo arquivo chamado keyCodes.js
com o seguinte conteúdo:
export const ESCAPE_CODE = 27;export const HOTKEY_CODE = 84; // key code of letter texport const UP_ARROW_CODE = 38;export const DOWN_ARROW_CODE = 40;
Crie um novo arquivo chamado SearchView.js
dentro de components
pasta com o seguinte conteúdo:
import React, { useState, useEffect, useRef } from 'react';const SearchView = ({ onSearch }) => { const [input, setInput] = useState(''); const inputRef = useRef(); useEffect(() => { inputRef.current.focus(); }, []); const onInputChange = (event) => { const input = event.target.value; setInput(input); onSearch(input); }; return ( My Repository / );};export default SearchView;
Estamos usando o React Hooks aqui para nossos métodos de estado e ciclo de vida. Se você é novo no React Hooks, confira Este artigo para uma introdução.
Neste arquivo, declaramos primeiro um estado para armazenar a entrada digitada pelo usuário. Então nós adicionamos um ref
usando o useRef
Gancho para que possamos focar no campo de entrada quando o componente estiver montado.
const inputRef = useRef();useEffect(() => { inputRef.current.focus();}, []);...
Nesse código, passando a matriz Vazia []
como o segundo argumento para o useEffect
gancho, o código dentro do useEffect
O gancho será executado apenas uma vez quando o componente estiver montado. Isso atua como o componentDidMount
método de ciclo de vida nos componentes da classe.
Então atribuímos o ref
para o campo de entrada como ref={inputRef}
. Na alteração do campo de entrada dentro do onInputChange
manipulador, estamos chamando o onSearch
método passado como suporte ao componente do App.js
Arquivo.
Agora abra App.js
e substitua seu conteúdo pelo seguinte código:
import React from 'react';import Header from './components/Header';import FilesList from './components/FilesList';import SearchView from './components/SearchView';import { ESCAPE_CODE, HOTKEY_CODE } from './utils/keyCodes';import files from './utils/api';export default class App extends React.Component { state = { isSearchView: false, filesList: files }; componentDidMount() { window.addEventListener('keydown', this.handleEvent); } componentWillUnmount() { window.removeEventListener('keydown', this.handleEvent); } handleEvent = (event) => { const keyCode = event.keyCode || event.which; switch (keyCode) { case HOTKEY_CODE: this.setState((prevState) => ({ isSearchView: true, filesList: prevState.filesList.filter((file) => file.type === 'file') })); break; case ESCAPE_CODE: this.setState({ isSearchView: false, filesList: files }); break; default: break; } }; handleSearch = (searchTerm) => { let list; if (searchTerm) { list = files.filter( (file) => file.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1 && file.type === 'file' ); } else { list = files.filter((file) => file.type === 'file'); } this.setState({ filesList: list }); }; render() { const { isSearchView, filesList } = this.state; return ( {isSearchView ? ( ) : ( )} ); }}
Agora, reinicie o aplicativo executando o yarn start
comando novamente e verifique sua funcionalidade.
Como você pode ver, inicialmente todas as pastas e arquivos são exibidos. Então, quando pressionamos a letra t
no teclado, a visualização muda para permitir a pesquisa nos arquivos exibidos.
Agora, vamos entender o código do App.js
Arquivo.
Neste arquivo, declaramos primeiro isSearchView
como uma variável de estado. Então dentro do componentDidMount
e componentWillUnmount
métodos de ciclo de vida que estamos adicionando e removendo o keydown
manipulador de eventos, respectivamente.
Então dentro do handleEvent
, estamos verificando qual tecla é pressionada pelo usuário.
- Se o usuário pressionar a tecla t, definiremos o
isSearchView
estado paratrue
e atualize ofilesList
matriz de estado para incluir apenas arquivos e excluir as pastas. - Se o uso pressionar a tecla Escape, definiremos o
isSearchView
estado parafalse
e atualize ofilesList
matriz de estados para incluir todos os arquivos e pastas.
A razão pela qual declaramos HOTKEY_CODE
e ESCAPE_CODE
em arquivos separados (keyCodes.js
em vez de usar diretamente o código-chave como 84
) é que mais tarde, se quisermos mudar a tecla de atalho de t
para s
, basta alterar o código da chave nesse arquivo. Ele refletirá a alteração em todos os arquivos em que é usada sem precisar alterá-la em todos os arquivos.
Agora, vamos entender o handleSearch
função. Nesta função, verificamos se o usuário inseriu algo na caixa de pesquisa de entrada e depois filtramos o (s) nome (s) do arquivo correspondente (s) que inclui esse termo de pesquisa. Em seguida, atualizamos o estado com os resultados filtrados.
Em seguida, dentro do método render, com base no isSearchView
valor, exibimos a exibição da lista de arquivos ou a pesquisa do usuário.
Você pode encontrar código até este ponto em este ramo.
Adicione funcionalidade para navegar entre arquivos
Agora, vamos adicionar a funcionalidade para exibir uma seta na frente do arquivo atualmente selecionado enquanto navega na lista de arquivos.
Crie um novo arquivo chamado InfoMessage.js
dentro de components
pasta com o seguinte conteúdo:
import React from 'react';const InfoMessage = () => { return ( You've activated the file finder. Start typing to filter the file list. Use ↑ and{' '} ↓ to navigate,{' '} esc to exit. );};export default InfoMessage;
Agora, abra o App.js
arquivo e importe o InfoMessage
componente para usá-lo:
import InfoMessage from './components/InfoMessage';
Adicione uma nova variável de estado chamada counter
com o valor inicial de 0
. Isso é para acompanhar o índice da seta.
Dentro de handleEvent
manipulador, obtenha o filesList
e counter
valores do estado:
const { filesList, counter } = this.state;
Adicione dois novos casos de switch:
case UP_ARROW_CODE: if (counter > 0) { this.setState({ counter: counter - 1 }); } break;case DOWN_ARROW_CODE: if (counter < filesList.length - 1) { this.setState({ counter: counter + 1 }); } break;
Aqui, diminuímos o counter
valor do estado quando pressionamos a seta para cima no teclado e incrementamos quando pressionamos a seta para baixo.
Importe também as constantes da matriz para cima e para baixo na parte superior do arquivo:
import { ESCAPE_CODE, HOTKEY_CODE, UP_ARROW_CODE, DOWN_ARROW_CODE} from './utils/keyCodes';
Dentro de handleSearch
função, redefina o counter
estado para 0
no final da função, para que a seta sempre seja exibida para o primeiro arquivo da lista enquanto estiver filtrando a lista de arquivos.
this.setState({ filesList: list, counter: 0});
Altere o método de renderização para exibir o InfoMessage
componente e passe counter
e isSearchView
como adereços para o FilesList
componente:
render() { const { isSearchView, counter, filesList } = this.state; return ( {isSearchView ? ( ) : ( )} );}
Agora, abra o FilesList.js
arquivo e aceite o isSearchView
e counter
adereços e passá-los para o ListItem
componente.
Seu FilesList.js
O arquivo ficará assim agora:
import React from 'react';import ListItem from './ListItem';const FilesList = ({ files, isSearchView, counter }) => { return ( {files.length > 0 ? ( files.map((file, index) => { return ( ); }) ) : ( No matching files found
)} );};export default FilesList;
Agora abra ListItem.js
e substitua seu conteúdo pelo seguinte conteúdo:
import React from 'react';import moment from 'moment';import { AiFillFolder, AiOutlineFile, AiOutlineRight } from 'react-icons/ai';const ListItem = ({ index, type, name, comment, modified_time, isSearchView, counter}) => { const isSelected = counter === index; return ( {isSearchView && ( )} {type === 'folder' ? ( ) : ( )} {name} {!isSearchView && ( {comment} {moment(modified_time).fromNow()} )} );};export default ListItem;
Nesse arquivo, primeiro aceitamos o isSearchView
e counter
suporte. Em seguida, verificamos se o índice do arquivo atualmente exibido na lista corresponde ao counter
valor.
Com base nisso, exibimos a seta na frente apenas para esse arquivo. Então, quando usamos a seta para baixo ou para cima para navegar pela lista, aumentamos ou diminuímos o valor do contador, respectivamente, no App.js
Arquivo.
Com base no isSearchView
valor que exibimos ou ocultamos a coluna de comentário e hora na exibição de pesquisa na interface do usuário.
Agora, reinicie o aplicativo executando o yarn start
comando novamente e verifique sua funcionalidade:
Você pode encontrar o código até este ponto em este ramo.
Adicione funcionalidade para destacar o texto correspondente
Agora, vamos adicionar a funcionalidade para destacar o texto correspondente do nome do arquivo quando filtramos o arquivo.
Abrir App.js
e mude o handleSearch
para o seguinte código:
handleSearch = (searchTerm) => { let list; if (searchTerm) { const pattern = new RegExp(searchTerm, 'gi'); list = files .filter( (file) => file.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1 && file.type === 'file' ) .map((file) => { return { ...file, name: file.name.replace(pattern, (match) => { return `${match}`; }) }; }); } else { list = files.filter((file) => file.type === 'file'); } this.setState({ filesList: list, counter: 0 });};
Nesse código, primeiro usamos o RegExp
construtor para criar uma expressão regular dinâmica para pesquisa global e sem distinção entre maiúsculas e minúsculas:
const pattern = new RegExp(searchTerm, 'gi');
Em seguida, filtramos os arquivos que correspondem aos critérios de pesquisa:
files.filter( (file) => file.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1 && file.type === 'file');
Em seguida, chamamos o método de mapa de matriz no resultado obtido da funcionalidade de filtro acima.
No método map, usamos a string replace
método.
o replace
O método aceita dois parâmetros:
- padrão para procurar
- função a ser executada para cada padrão correspondente
Nós usamos o replace
método para encontrar todas as correspondências para o pattern
e substitua-o pela corda ${match}
. Aqui match
conterá o texto correspondente do nome do arquivo.
Se você verificar a estrutura JSON no diretório utils/api.js
arquivo, a estrutura de cada arquivo fica assim:
{ id: 12, type: 'file', name: 'Search.js', comment: 'changes using react context', modified_time: '2020-06-30T07:55:33Z'}
Como queremos substituir o texto apenas do campo de nome, espalhamos as propriedades do objeto de arquivo e apenas alteramos o nome, mantendo outros valores como estão.
{ ...file, name: file.name.replace(pattern, (match) => { return `${match}`; })}
Agora, reinicie o aplicativo executando o yarn start
comando novamente e verifique sua funcionalidade.
Você verá que o HTML é exibido como na interface do usuário ao pesquisar:
Isso ocorre porque estamos exibindo o nome do arquivo no diretório ListItem.js
arquivo da seguinte maneira:
{name}
E para prevenir Cross-site scripting (XSS)
ataques, o React escapa todo o conteúdo exibido usando a Expressão JSX (que está entre colchetes).
Portanto, se realmente queremos exibir o HTML correto, precisamos usar um suporte especial conhecido como dangerouslySetInnerHTML
. Passa o __html
nome com o HTML para exibir o valor como este:
Agora, reinicie o aplicativo executando o yarn start
comando novamente e verifique sua funcionalidade:
Como você pode ver, o termo de pesquisa está sendo destacado corretamente no nome do arquivo.
É isso aí!
Você pode encontrar o código até este ponto em este ramo.
Código fonte completo do GitHub: aqui
Demonstração ao vivo: aqui
Confira meus outros artigos sobre React, Node.js e Javascript em Médio, dev.to e assine para receber atualizações semanais diretamente na sua caixa de entrada aqui.
[ad_2]
Fonte