Um dicionário de sinônimos significativo
Lembre-se de todas as palavras que encontrar ao usar um dicionário de sinônimos.
SynonymSearch ajuda você a revisar o que você acabou de aprender (e pode esquecer), classificando uma lista de todas as suas consultas de pesquisa por suas definições compartilhadas à medida que você as encontra.
SynonymSearch permite testar a semelhança de palavras sinônimas, injetando-as em uma frase cujo significado depende da definição que compartilham. O aplicativo ajuda você a entender ativamente uma definição e, em seguida, revisá-la em um repositório de sinônimos.
Este projeto é desenvolvido como parte do módulo MS2 Interactive Front End no Code Institute. Ele foi escrito para funcionar com o formato de resposta JSON do Merriam-Webster's Collegiate® Thesaurus e é desenvolvido com React via Next.js, inicializado com create-next-app
e implantado com Vercel.
Visite o projeto aqui: sinônimos.vercel.app
Nota para referência: o esquema anterior era denominado 'caçador de sinônimos'
Os modelos esquemáticos começaram no início de janeiro de 2021 para estabelecer relações entre componentes díspares e atribuir responsabilidades apropriadas.
Essa abordagem ajudou a detalhar o escopo do projeto e, o mais importante, a fornecer uma referência para desenvolvimento e solução de problemas.
veja Pensando em React (Documentos)
{Results} => {Root} => {Sense} => {Option}
(Ver diretório de resultados){Saves} => {Selection}
(Veja o diretório de salvamentos){Results} =>{Root} => {Sense}
via {ReplaceSubStringNode}
(consulte string.helper.js). Agora dividido em {Saves} => {Selection}
(Veja Display) {Sense} => {Option}
na árvore de resultados (ver diretório de resultados) Uma borda superior e inferior consistente é aplicada a todos os nós com conteúdo dinâmico. Essas bordas discretas são projetadas para transmitir a escala do material de um bloco de notas como um ponto de contato familiar para o usuário. Essas wordBoxes
variam apenas no espaçamento e são responsáveis por transmitir visualmente a hierarquia na lista de listas .
ThemeProvider
do Material-UI envolve o aplicativo para passar estilos pela árvore de componentes. Consulte Temas de Material-UI. A família de fontes é definida como uma variedade de fontes do sistema para se adaptar à configuração do dispositivo/plataforma/usuário e evitar depender de um tipo de letra explícito. Um objeto commonSettings
em theme.context.js
contém uma lista dessas substituições, que é mesclada com o provedor de tema do Material-UI. Consulte Material-UI Global-css
export const commonSettings = {
typography : {
fontFamily : [
'-apple-system' ,
'BlinkMacSystemFont' ,
'"Segoe UI"' ,
'Roboto' ,
'"Helvetica Neue"' ,
'Arial' ,
'sans-serif' ,
'"Apple Color Emoji"' ,
'"Segoe UI Emoji"' ,
'"Segoe UI Symbol"' ,
] . join ( ',' ) ,
} ,
}
O aplicativo apresenta dois conjuntos de cores primárias e secundárias para um tema claro e um tema escuro configurado por meio da configuração da paleta do Material UI. Os comportamentos e estados das cores dependem de propriedades definidas no objeto Tema padrão do Material-UI. As cores secundárias são definidas como tons neutros, enquanto as primárias são renderizadas como a "cor característica" singular. A abordagem mínima para conjuntos de cores é aprimorar um bloco de notas
const lightTheme = createMuiTheme ( {
palette : {
type : 'light' ,
primary : {
main : '#ff3200' ,
} ,
secondary : {
main : grey [ 900 ] ,
} ,
} ,
... commonSettings ,
} ) ;
const darkTheme = createMuiTheme ( {
palette : {
type : 'dark' ,
primary : {
main : lightblue [ 500 ] ,
} ,
secondary : {
main : '#fafafa' ,
} ,
background : {
default : '#151515' ,
} ,
} ,
... commonSettings ,
} ) ;
O site é modelado como um aplicativo da web progressivo para renderizar a funcionalidade de um aplicativo de página única sem recarregar a página inteira e otimizado para funcionar como um aplicativo nativo. Sua estrutura garante flexibilidade entre navegadores.
O site aproveita grandes botões de ação flutuantes (FAB) para navegar rapidamente entre suas três visualizações remotas: “Pesquisar”, “Informações” e “Salvar”. Metáforas visuais são utilizadas para representar esses modos de visualização de forma discreta e eficiente.
Um botão flutuante de “discagem rápida” persiste nas visualizações para acessar rapidamente as principais ações do aplicativo: consultar um termo, limpar o histórico salvo e alternar entre o modo claro ou escuro.
O aplicativo apresenta uma tela de inicialização personalizada no carregamento inicial da página para descrever sucintamente sua finalidade: "Um aplicativo de dicionário de sinônimos interativo".
jsx
, jss
js
. (inicializado com Nestjs)next
a desenvolver, next build
para preparar para produção e next start
a servir.commit
com Git e push
para GitHub.push
simultâneos via Git. Os estilos são escritos na sintaxe jss e seguem as especificações do Material-UI para funcionar bem com a renderização do lado do servidor Nextjs, que inclui essa lógica de personalização pages/_document
para injetar estilos renderizados do lado do servidor na marcação antes de ser usado. Observe que esta é uma recomendação não oficial e deve ser refatorada se/quando o MUI lançar um plugin oficial para Nextjs semelhante ao do MUI/Gatsby.
Todos os objetos de estilo são criados com o gancho makeStyles
do MUI e seguem as diretrizes dos documentos de estilo MUI para temas. Isso inclui substituições de CSS e consultas de mídia. Os objetos de estilo dos componentes principais são separados por escopo no diretório de estilos e importados em módulos conforme necessário. No entanto, alguns componentes, nomeadamente Launcher
, têm todos os adereços de estilo declarados no próprio arquivo do componente. Observe que todos os estilos devem eventualmente ser exportados de um diretório para consistência.
A árvore results
renderiza componentes jsx
em cada chamada de API sem inicializar constantes. Props e expressões condicionais controlam a iteração do esquema de resposta da API MW-Thesaurus. Observe que os valores e as verificações do Prop-Type são escritos especificamente para obter os dados corretos deste esquema:
Cada instância de palavra salva retém propriedades da família Results da qual é "retirada" - como a definição específica da palavra, rótulo, palavra raiz - para incrementar valores de duplicatas e agrupar palavras por definições de sentido, garantindo que cada instância, não importa se é idêntica no valor do nome, é distinto se a definição e o sentido forem diferentes
Os ganchos React useReducer
, useContext
, createContext
são montados juntos em context/words.context
(consulte HistoryProvider) para fornecer um wrapper de contexto para os componentes compartilharem e consumirem os mesmos dados. Observe que essa lógica é modelada diretamente a partir do exemplo definido em next.js/examples/with-context-api.
Seguindo o que é alcançado pelos ganchos wrapper de contexto exportados de HistoryProvider
useHistory
e useDispatchHstory
- que são utilizados em componentes de ordem superior, componentes compartilhados e funções auxiliares em helpers/*
para manipular radicalmente o DOM - a mesma lógica é reaproveitada para montar múltiplos conecta-se a um wrapper personalizado com o ThemeProvider do MUI (consulte ThemeProvider). Nesse caso, definir o valor da visualização permite que qualquer componente altere a página de qualquer lugar, para citar alguns. Observe a natureza abrangente desse provedor de layout de contexto.
export const ThemeContextProvider = ( { children } ) => {
// Sets the theme for MUI ThemeProvider to use
const [ darkMode , setDarkMode ] = useState ( false ) ;
// Sets the current view
const [ value , setValue ] = useState ( 'launch' ) ;
// Hooks to set warning colors for searchText input
const [ meta , setMeta ] = useState ( true ) ;
const [ root , setRoot ] = useState ( '' ) ;
// Rides an MUI hook to set a memoized theme
const prefersDarkMode = useMediaQuery ( '(prefers-color-scheme: dark)' ) ;
useMemo (
( ) => ( prefersDarkMode
? setDarkMode ( true )
: setDarkMode ( false ) ) ,
[ prefersDarkMode ] ,
) ;
// Provides all values and the hooks that control them
return (
< ThemeDispatchContext . Provider
value = { {
darkMode ,
setDarkMode ,
value ,
setValue ,
meta ,
setMeta ,
root ,
setRoot ,
} }
>
< ThemeProvider
theme = {
darkMode
? darkTheme
: lightTheme
}
>
{ children }
< / ThemeProvider >
< / ThemeDispatchContext . Provider >
) ;
} ;
// All state/hooks are accessible by importing
// a catch-all export below
// Example:
// // anyFile.js
// import { useDispatchTheme } from './../theme.context'
//
// const dispatch = useDispatchTheme()
//
// Change any/all states from any component
// dispatch.setDarkMode(false)
// dispatch.setValue('search')
//
export const useDispatchTheme = ( ) => useContext ( ThemeDispatchContext ) ;
O uso do MW-Thesaurus é gratuito para fins não comerciais e/ou educacionais. Os logotipos de marcas apresentados na guia de informações seguem as diretrizes de marca solicitadas, mas não limitadas ao centro de desenvolvimento Merriam-Webster.
A chave registrada da API do Thesaurus vinculada a este projeto é atualmente pública. Após esta discussão, o esforço para proteger as chamadas de API no lado do cliente é inútil, pois a chave privada estará sempre exposta. Configuração adicional para ter um endpoint interno em /pages/api
deve ser considerada seguindo as rotas da API Nextjs. Observe que a chave foi exposta em .env.local
pelos motivos acima e para fins educacionais do projeto.
const axiosConfig = { baseURL : 'https://dictionaryapi.com/api/v3/references/' , } ;
function searchThesaurus ( searchText , selection ) {
const query = selection || searchText ;
// Key is processed from ignored env.local
// use this method if API endpoint is set up in /pages/api/*
const key = process . env . MW_THESAURUS_KEY ;
// @note Key is explicitly declared otherwise
// for Production/submission
return axiosGetCancellable (
`/thesaurus/json/ ${ query } ?key= ${ key } ` ,
axiosConfig ,
) ;
}
Nota: Todas as descrições de métodos atípicos podem ser encontradas em blocos de comentários que seguem os padrões jsDoc.
Um usuário espera ver uma descrição alguns segundos depois de chegar ao site
Launcher
:useEffect
sem dependências permite que Launcher
seja executado uma vez no carregamento inicial da páginauseContext
permite que Launcher
defina o value
atual da visualização da página como ' search
' após um tempo limite.Synonym Search An Interactive Thesaurus App
" por dois segundos e meio ao chegar ao site. import React , { useEffect , useState } from 'react' ;
import { useDispatchTheme } from '../../context/theme.context' ;
const Launcher = ( ) => {
const classes = useStyles ( ) ;
const viewDispatch = useDispatchTheme ( ) ;
const [ open , setOpen ] = useState ( true ) ;
const [ showOpen , setShowOpen ] = useState ( true ) ;
useEffect ( ( ) => {
setTimeout ( ( ) => {
setShowOpen ( false ) ;
setTimeout ( ( ) => {
setOpen ( false ) ;
viewDispatch . setValue ( 'search' ) ;
} , 350 ) ;
} , 2500 ) ;
} , [ ] ) ;
return (
< >
< Backdrop
className = { classes . backdrop }
open = { open }
/ >
< Grow
in = { showOpen }
unmountOnExit
>
< Box className = { classes . launch } >
{ ... excerpt . . . }
Synonym
/Search
An Interactive Thesaurus App
{ ... excerpt . . . }
< / Box >
< / Grow >
< / >
) ;
} ;
Um usuário deseja navegar de maneira conveniente entre as visualizações
Speed (Dial)
:onClick
.FixedBottom
(bifurcado de FixedBottom) permite que Speed Dial
inferior se mova responsivamente acima das barras inferiores no celular durante a rolagem.useScrollTrigger
permite que Speed Dial
troque visibilidades com ScrollTop
, um botão de ação flutuante que, quando clicado, rola a janela de volta ao topo da página por meio de uma âncora com id="back-to-top"
.Search
são passados para Speed Dial
para ativar onSearchTextChange
, uma função que envia consultas para a API Thesaurus.useState
permite que useEffect
sempre feche SpeedDial
sempre que o value
da página via useDispatch
for alterado, exceto quando Search
estiver active
.ToggleTheme
, Search
, Clear
Saves
SpeedDial
SpeedDial
está abertoToggleTheme
not
este <mode>
SpeedDial
está fechadoSaves
SpeedDial
SpeedDial
está abertoSearch
SpeedDial
ainda está abertoSearch
SpeedDial
desapareceScrollTop
apareceScrollTop
SpeedDial
aparecex
e y
(para cima/para baixo nas visualizações, esquerda/direita entre as visualizações) rapidamente clicando nas ações SpeedDial
para as visualizações que elas representam. Além disso, a navegação de botões por visualização pode ser acessada no cabeçalho da página. As outras duas visualizações são Saves
e Info
, que são da mesma família de botões de ação flutuantes que as ações em SpeedDial
. A consistência e a singularidade do tema, da cor e da interface do usuário atendem aos impulsos e à familiaridade do usuário com o ponto de contato persistente transmitido. import React , { useEffect , useState } from 'react' ;
import { useDispatchTheme } from '../../context/theme.context' ;
const Speed = ( {
children ,
value ,
index ,
searchText ,
loading ,
onSearchTextChange ,
... other
} ) => {
const trigger = useScrollTrigger ( ) ;
const classes = useStyles ( ) ;
const [ open , setOpen ] = useState ( false ) ;
const [ direction , setDirection ] = useState ( 'up' ) ;
const matches = useMediaQuery ( '(min-width:600px)' ) ;
const viewDispatch = useDispatchTheme ( ) ;
const view = viewDispatch . value ? viewDispatch . value : null ;
const handleClick = ( event ) => setOpen ( ! open ) ;
open && trigger ? setOpen ( false ) : null ;
useEffect ( ( ) => {
setTimeout ( ( ) => {
if ( viewDispatch . value !== 'search' ) {
setOpen ( false ) ;
}
} , 10 ) ;
} , [ view ] ) ;
return (
< Slide appear direction = "up" in = { ! trigger } >
< FixedBottom offset = { matches ? 16 : 48 } >
< SpeedDial
ariaLabel = "actions"
className = { classes . speedDialGroup }
FabProps = { {
className : clsx ( classes . speedDial , classes . bottom ) ,
size : matches ? 'medium' : 'small' ,
style : { padding : matches ? '12px' : '8px' } ,
} }
onClick = { handleClick }
open = { open }
direction = { direction }
>
{ ... Search action }
{ ... Toggle theme action }
{ ... Clear cache action }
/ >
< / SpeedDial >
< / FixedBottom >
< / Slide >
) ;
} ;
export default Speed ;
Um usuário deseja ver fontes confiáveis
Brands
(Material-UI AvatarGroup
):useEffect
permite que useState
inverta as margens quando o valor da página é definido para a view
atual => Info
, o que anima os avatares para se expandirem como se para dar as boas-vindas a um usuário no balcão de informações, enquanto um pequeno parágrafo é apresentado para descrever o " SynonymStory
" por trás " SynonymSearch
"Logo
, uma para cada: React
, NextJS
, MaterialUI
, CodeInstitute
e Merriam-Webster
.Search
Info
Info
Brands
apareceMerriam-Webster
Merriam-Webster Developer Center website
Info
import React , { useEffect , useState } from 'react' ;
import { useDispatchTheme } from '../../context/theme.context' ;
const Brands = ( { children } ) => {
const classes = useStyles ( ) ;
const viewDispatch = useDispatchTheme ( ) ;
const [ active , setActive ] = useState ( false ) ;
const { value } = viewDispatch ;
useEffect ( ( ) => {
if ( value === 'info' ) {
setTimeout ( ( ) => {
setActive ( true ) ;
} , 750 ) ;
}
} , [ ] ) ;
return (
< AvatarGroup
className = {
clsx (
classes . avatarGroup , active
? classes . active
: classes . inactive ,
)
}
>
< Logo
name = "React"
url = "https://react.org/"
path = "/images/reactLogo.png"
/ >
< Logo
name = "Next JS"
url = "https://nextjs.org/"
path = "/images/nextJSLogo.svg"
/ >
< Logo
name = "Material UI"
url = "https://material-ui.com/"
path = "/images/materialUILogoLight.png"
darkImage = "/images/materialUILogoDark.png"
/ >
< Logo
name = "Code Institute"
url = "https://codeinstitute.net/"
path = "/images/codeInstituteLogo.png"
/ >
< Logo
name = "Merriam-Webster"
url = "https://dictionaryapi.com/"
path = "/images/merriamWebsterLogoLight.png"
/ >
< / AvatarGroup >
) ;
} ;
Um usuário deseja pesquisar sinônimos
Search
:loading
: se os resultados estão carregando,meta
: se a entrada de um usuário produz um resultado válido,root
: se o primeiro item em uma matriz de resultados corresponde ao que o usuário digitou.useState
permite que essas condições mudem dinamicamente.useRef
é anexado ao componente de entrada e permite que eventos de tecla e mouse focus
condicionalmente o prompt de entrada com base nas condições acima.Field
, Input
.Search
Search
no cabeçalhoimmediately successful word
>immediately successful word
Search
no cabeçalhoincomplete word
>real word
>real word
Search
no cabeçalhoa word with no matches
>good word
>new word
const Field = ( {
label ,
onChange ,
placeHolder ,
helperText ,
loading ,
} ) => {
const theme = useTheme ( ) ;
const trigger = useScrollTrigger ( ) ;
const [ active , setActive ] = useState ( false ) ;
const textInput = useRef ( null ) ;
const metaDispatch = useDispatchTheme ( ) ;
const { meta , root } = metaDispatch ;
const handleSearchButton = ( ) => {
setActive ( true ) ;
setTimeout ( ( ) => {
textInput . current && textInput . current . focus ( ) ;
} , 100 ) ;
} ;
const handleClickAway = ( ) => setActive ( false ) ;
const handleBackDrop = ( ) => setActive ( false ) ;
const onKeyPress = ( ) => setActive ( false ) ;
const match
= textInput . current
? textInput . current . value === root
: false ;
active && trigger ? setActive ( false ) : null ;
useEffect ( ( ) => {
if ( active && match ) {
setTimeout ( ( ) => {
setActive ( false ) ;
} , 2000 ) ;
}
} , [ ] ) ;
return (
< ClickAwayListener
onClickAway = { handleClickAway }
>
< >
< Backdrop
open = { active }
onClick = { handleBackDrop }
/ >
< Fab
size = "small"
color = "primary"
aria-label = "search"
onClick = { handleSearchButton }
variant = { active ? 'extended' : 'round' }
style = { active ? {
backgroundColor :
loading
? theme . palette . warning . main
: ! meta
? theme . palette . error . main
: meta && match
? theme . palette . success . main
: theme . palette . primary . main ,
} : null }
>
< Input
label = { label }
placeHolder = { placeHolder }
helperText = { helperText }
active = { active }
match = { match }
meta = { meta }
loading = { loading }
textInput = { textInput }
onKeyPress = { onKeyPress }
onChange = { onChange }
/ >
< / Fab >
< / >
< / ClickAwayListener >
) ;
} ;
Um usuário deseja ver o histórico pesquisado
useHistory
é um gancho de contexto para acessar os valores armazenados em HistoryProvider
.useReducer
permite que HistoryProver
agrupe palavras consultadas pelas definições que elas compartilham.Selection
Um usuário deseja ver uma palavra usada em uma frase
Display
:Sense
para passar uma propriedade sampleString
e optionWord
, que ativa um Intersection Observer
e eventos de mouse para alterar dinamicamente a propriedade optionWord
.{it} {/it}
e {lquo} {rquo}
. Como são tags de abertura e fechamento, as funções de substituição de regex são confiáveis na limpeza consistente da string de exemplo quando uma definição a possui. (Veja exemplo de resposta da API Theaurus)Um usuário deseja ver uma tag ao lado dos resultados repetidos (também conhecidos como palavras já salvas)
useReducer
em HistoryProvider
:HistoryProvider
são salvos com propriedades que permitem a verificação comparando essas propriedades com novas palavras:Counters
:History
Saves
para que um usuário possa visualizar as 'repetições', também conhecidas como palavras salvas de um usuário, pelas definições de propriedade que eles compartilham. const [ savedWords , dispatch ] = useReducer ( ( state , action ) => {
switch ( action . type ) {
case 'add' :
const wordIndex = state . findIndex ( ( word ) => word . uuid === action . uuid
|| word . name === action . name
&& word . sense === action . sense ) ;
if ( wordIndex !== - 1 ) {
return state . map ( ( word , i ) => ( {
... word ,
value : word . value + ( wordIndex === i ? 1 : 0 ) ,
} ) ) ;
}
return [
... state ,
{
id : state . length ,
name : action . name ,
value : 1 ,
root : action . root ,
label : action . label ,
uuid : action . uuid ,
sense : action . sense ,
} ,
] ;
case 'remove' :
return state . filter ( ( word ) => word . id !== action . id ) ;
case 'clear' :
return [ ] ;
default :
return state ;
}
} , [ ] ) ;
Um usuário deseja excluir o histórico de pesquisa salvo
Clear
:DeleteForever
que é uma função de dispatch
de useReducer
do HistoryProvider
que exclui tudo do Context.Delete
representado por um componente Material-UI Chip
. // clear.button.js
// excerpt
const wordsDispatch = useDispatchHistory ( ) ;
const handleClick = ( event ) => {
wordsDispatch ( {
type : 'clear' ,
} ) ;
} ;
// words.context.js
// excerpt from reducer
// the above handler calls case 'remove'
case 'remove' :
return state . filter ( ( word ) => word . id !== action . id ) ;
Ver resultados mais recentes
O Lighthouse via Vercel é usado para testar o desempenho, que produz resultados únicos em cada git push
. lighthouse-badges é usado para gerar novos emblemas para cada implantação, instalando npm i -g lighthouse-badges
e enviando o novo URL com hash para a matriz de URLs:
lighthouse-badges
-o docs/badges -r
-u https://synonyms.vercel.app/ [... all other urls]
# Output to docs/badges
# Badges will contain the respective
average score(s) of all the urls
supplied, combined
As métricas do Lighthouse, nomeadamente Acessibilidade e Desempenho, geram sinalizadores específicos em cada auditoria. Ajustes são feitos em cada push que abordam especificamente quaisquer problemas.
role
. Voltar ao topo
create-next-app
e implantado com Vercel, o que consegui com os seguintes passos:commit
e push
o código do meu IDE local para o Github via Git e o terminal iTerm do meu MacBook Pro.select
, localizada no canto superior esquerdo do prompt imediato."Seu projeto foi implantado com sucesso."
Voltar ao topo
git clone https://github.com/israelias/synonym-chaser
cd
para o nome deste repositório: cd synonym-chaser
npm install
npm run dev
# or
yarn dev
abra seu navegador em localhost:3000
info
é fornecido pelo Merriam-Webster's Collegiate Thesaurus.Voltar ao topo