[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:

Github Search 1
Funcionalidade de pesquisa de arquivos no Github

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:

initial screen
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.

search
Funcionalidade de pesquisa inicial de trabalho

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 para true e atualize o filesList matriz de estado para incluir apenas arquivos e excluir as pastas.
  • Se o uso pressionar a tecla Escape, definiremos o isSearchView estado para false e atualize o filesList 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:

navigation
Pesquisar e navegar

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:

rendered html
HTML não renderizado corretamente

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:

highlight 1
Aplicação final de trabalho

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