Un tesauro significativo
Recuerde todas las palabras que encuentre cuando utilice un diccionario de sinónimos.
SynonymSearch le ayuda a revisar lo que acaba de aprender (y que de otro modo podría olvidar) ordenando una lista de todas sus consultas de búsqueda por sus definiciones compartidas a medida que las va encontrando.
SynonymSearch le permite probar la similitud de palabras sinónimas inyectándolas en una oración cuyo significado depende de la definición que comparten. La aplicación le ayuda a dar sentido activamente a una definición y luego compararla con un repositorio de sinónimos.
Este proyecto se desarrolla como parte del módulo Front End interactivo de MS2 en Code Institute. Está escrito para funcionar con el formato de respuesta JSON del Merriam-Webster's Collegiate® Thesaurus y se desarrolla con React a través de Next.js, se inicia con create-next-app
y se implementa con Vercel.
Visite el proyecto aquí: sinónimos.vercel.app
Nota como referencia: el esquema anterior se denominaba 'synonym-chaser'
Las maquetas esquemáticas comenzaron a principios de enero de 2021 para establecer relaciones entre componentes dispares y asignar responsabilidades adecuadas.
Este enfoque ayudó a desglosar el alcance del proyecto y, lo más importante, a proporcionar un punto de referencia para las referencias para el desarrollo y la resolución de problemas.
ver Pensando en React (Documentos)
{Results} => {Root} => {Sense} => {Option}
(Ver directorio de resultados){Saves} => {Selection}
(Ver directorio de guardados){Results} =>{Root} => {Sense}
a través de {ReplaceSubStringNode}
(consulte string.helper.js). Ahora desglosado en {Saves} => {Selection}
(Ver visualización) La visualización ahora es un componente ficticio ya que reaccionar-intersección-observador se maneja completamente entre {Sense} => {Option}
en el árbol de resultados (ver directorio de resultados) Se aplica un borde superior e inferior consistente a todos los nodos con contenido dinámico. Estos bordes discretos están diseñados para transmitir la escala material de un bloc de notas como un punto de contacto familiar para el usuario. Estos wordBoxes
solo varían en el espaciado y son responsables de transmitir visualmente la jerarquía en una lista de listas .
ThemeProvider
de Material-UI envuelve la aplicación para pasar estilos al árbol de componentes. Consulte Temas de Material-UI. La familia de fuentes está configurada para una variedad de fuentes del sistema para adaptarse a la configuración del dispositivo/plataforma/usuario y evitar depender de un tipo de letra explícito. Un objeto commonSettings
en theme.context.js
contiene una lista de estas anulaciones, que se fusiona con el proveedor de temas de Material-UI. Ver 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 ( ',' ) ,
} ,
}
La aplicación presenta dos conjuntos de colores primarios y secundarios para un tema claro y un tema oscuro configurado a través de la configuración de paleta de Material UI. Los comportamientos y estados de los colores dependen de las propiedades definidas en el objeto Tema predeterminado de Material-UI. Los colores secundarios se establecen en tonos neutros, mientras que los primarios se representan como el "color característico" singular. El enfoque mínimo para los conjuntos de colores es mejorar un bloc 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 ,
} ) ;
El sitio web está modelado como una aplicación web progresiva para ofrecer la funcionalidad de una aplicación de una sola página sin recargas de página completa y optimizado para funcionar como una aplicación nativa. Su marco garantiza flexibilidad en todos los navegadores.
El sitio web aprovecha los grandes botones de acción flotante (FAB) para navegar rápidamente entre sus tres vistas remotas: "Buscar", "Información" y "Guardar". Se utilizan metáforas visuales para representar estos modos de visión de manera discreta y eficiente.
Un botón flotante de "marcación rápida" persiste en todas las vistas para acceder rápidamente a las acciones principales de la aplicación: consultar un término, borrar el historial guardado y alternar el modo claro u oscuro.
La aplicación presenta una pantalla de inicio personalizada al cargar la página inicial para describir sucintamente su propósito: "Una aplicación de sinónimos interactiva".
jsx
, jss
js
. (Arrancado con Nestjs)next
para desarrollar, next build
para preparar la producción y next start
para servir.commit
con Git y push
a GitHub.push
simultáneos a través de Git. Los estilos están escritos en sintaxis jss y siguen las especificaciones de Material-UI para funcionar bien con la renderización del lado del servidor Nextjs, que incluye esta lógica de personalizar pages/_document
para inyectar estilos renderizados del lado del servidor en el marcado justo antes de usarlo. Tenga en cuenta que esta es una recomendación no oficial y debe refactorizarse si MUI lanza un complemento oficial para Nextjs similar al de MUI/Gatsby.
Todos los objetos de estilo se crean con el gancho makeStyles
de MUI y siguen las pautas en los documentos de estilo de MUI para la temática. Esto incluye anulaciones de CSS y consultas de medios. Los objetos de estilo para los componentes principales se separan por alcance en el directorio de estilos y se importan en módulos según sea necesario. Sin embargo, algunos componentes, concretamente Launcher
, tienen todos los accesorios de estilo declarados en el propio archivo del componente. Tenga en cuenta que, en última instancia, todos los estilos deberían exportarse desde un directorio para mantener la coherencia.
El árbol results
representa componentes jsx
en cada llamada a la API sin inicializar constantes. Los accesorios y las expresiones condicionales controlan la iteración del esquema de respuesta de la API de MW-Thesaurus. Tenga en cuenta que los valores y las verificaciones de tipo de propiedad se escriben específicamente para obtener los datos correctos de este esquema:
Cada instancia de palabra guardada conserva las propiedades de la familia de Resultados de la que se "toma", como la definición particular de la palabra, la etiqueta, la palabra raíz, para incrementar los valores de los duplicados y agrupar palabras por definiciones de sentido, al mismo tiempo que se garantiza que cada instancia, sin importar si es idéntica. a valor nominal, es distinta si la definición y el sentido son diferentes
Los ganchos de reacción useReducer
, useContext
y createContext
se ensamblan en context/words.context
(consulte HistoryProvider) para proporcionar un contenedor de contexto para que los componentes compartan y consuman los mismos datos. Tenga en cuenta que esta lógica se modela directamente a partir del ejemplo establecido en next.js/examples/with-context-api.
Siguiendo lo logrado por los ganchos contenedores de contexto exportados de HistoryProvider
useHistory
y useDispatchHstory
, que se utilizan en componentes de orden superior, componentes compartidos y funciones auxiliares en helpers/*
para manipular radicalmente el DOM, la misma lógica se reutiliza para montar múltiples se engancha a lo largo de un contenedor personalizado con ThemeProvider de MUI (consulte ThemeProvider). En este caso, establecer el valor de vista permite que cualquier componente cambie la página desde cualquier lugar, por nombrar algunos. Tenga en cuenta la naturaleza general de este proveedor de diseño 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 ) ;
El uso de MW-Thesaurus es gratuito para fines no comerciales y/o educativos. Los logotipos de marcas destacados en la pestaña de información deben seguir las pautas de marca solicitadas por, entre otros, el centro de desarrollo de Merriam-Webster.
La clave API del diccionario de sinónimos registrada vinculada a este proyecto es pública actualmente. Después de esta discusión, el esfuerzo por proteger las llamadas API en el lado del cliente no tiene sentido ya que la clave privada siempre estará expuesta. Se debe considerar una configuración adicional para tener un punto final interno en /pages/api
siguiendo las rutas API de Nextjs. Tenga en cuenta que la clave se ha expuesto desde .env.local
por los motivos anteriores y con fines educativos del proyecto.
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 las descripciones de los métodos atípicos se pueden encontrar en los bloques de comentarios que siguen los estándares jsDoc.
Un usuario espera ver una descripción a los pocos segundos de llegar al sitio.
Launcher
:useEffect
sin dependencias permite que Launcher
se ejecute una vez al cargar la página inicialuseContext
permite Launcher
establecer el value
de vista de la página actual en ' search
' después de un tiempo de espera.Synonym Search An Interactive Thesaurus App
" durante dos segundos y medio al llegar al sitio. 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 >
< / >
) ;
} ;
Un usuario quiere navegar cómodamente entre vistas.
Speed (Dial)
:onClick
.FixedBottom
(bifurcado de FixedBottom) permite que Speed Dial
inferior se mueva de manera receptiva por encima de las barras inferiores en el dispositivo móvil cuando se desplaza.useScrollTrigger
permite que Speed Dial
intercambie visibilidades con ScrollTop
, un botón de acción flotante que, cuando se hace clic, desplaza la ventana hacia la parte superior de la página mediante un ancla con id="back-to-top"
.Search
se pasan a Speed Dial
para activar onSearchTextChange
, una función que envía consultas a la API del diccionario de sinónimos.useState
permite que useEffect
cierre siempre SpeedDial
cada vez que cambia el value
de la página a través de useDispatch
, excepto cuando Search
está active
.ToggleTheme
, Search
, Clear
Saves
SpeedDial
SpeedDial
está abiertoToggleTheme
not
este <mode>
SpeedDial
está cerradoSaves
SpeedDial
SpeedDial
está abiertoSearch
SpeedDial
todavía está abiertoSearch
.SpeedDial
desapareceScrollTop
ScrollTop
SpeedDial
x
e y
(arriba/abajo en las vistas, izquierda/derecha entre las vistas) haciendo clic en las acciones SpeedDial
para las vistas que representan. Además, se puede acceder a la navegación con botones por vista en el encabezado de la página. Las otras dos vistas son Saves
e Info
, que pertenecen a la misma familia de botones de acción flotante que las acciones dentro de SpeedDial
. La coherencia y singularidad del tema, el color y la interfaz de usuario juegan con los impulsos del usuario y su familiaridad con el punto de contacto 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 ;
Un usuario quiere ver fuentes confiables
Brands
(Material-UI AvatarGroup
):useEffect
permite useState
invertir los márgenes cuando el valor de la página se establece en la view
actual => Info
, lo que anima a los avatares a expandirse como para dar la bienvenida a un usuario al escritorio de información, mientras que se presenta un breve párrafo para describir la " SynonymStory
" detrás " SynonymSearch
"Logo
, una para cada: React
, NextJS
, MaterialUI
, CodeInstitute
y Merriam-Webster
.Search
Info
Info
Brands
Merriam-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 >
) ;
} ;
Un usuario quiere buscar sinónimos.
Search
:loading
: si los resultados se están cargando,meta
: si la entrada de un usuario produce un resultado válido,root
: si el primer elemento de una matriz de resultados coincide con lo que ha escrito el usuario.useState
permite que estas condiciones cambien dinámicamente.useRef
está adjunto al componente de entrada y permite que los eventos de tecla y mouse focus
condicionalmente el mensaje de entrada según las condiciones anteriores.Field
, Input
.Search
Search
en el encabezadoimmediately successful word
>immediately successful word
Search
en el encabezadoincomplete word
>real word
>real word
Search
en el encabezadoa 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 >
) ;
} ;
Un usuario quiere ver el historial de búsquedas.
useHistory
es un enlace de contexto para acceder a los valores almacenados en HistoryProvider
.useReducer
permite HistoryProver
agrupar palabras consultadas según las definiciones que comparten.Selection
Un usuario quiere ver una palabra usada en una oración.
Display
:Sense
para pasar un accesorio sampleString
y optionWord
, que activa un Intersection Observer
y eventos del mouse para cambiar dinámicamente el accesorio optionWord
.{it} {/it}
y {lquo} {rquo}
. Como son etiquetas de apertura y cierre, las funciones de reemplazo de expresiones regulares resultan confiables para limpiar consistentemente la cadena de ejemplo cuando una definición la tiene. (Ver ejemplo de respuesta de la API de Theaurus)Un usuario quiere ver una etiqueta junto a los resultados repetidos (también conocidas como palabras ya guardadas)
useReducer
en HistoryProvider
:HistoryProvider
se guardan con propiedades que permiten la verificación comparando estas propiedades con nuevas palabras:Counters
:History
Saves
para que un usuario pueda ver las 'repeticiones', también conocidas como palabras guardadas de un usuario, según las definiciones de propiedades que comparten. 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 ;
}
} , [ ] ) ;
Un usuario quiere eliminar el historial de búsqueda guardado
Clear
:DeleteForever
que es una función dispatch
de useReducer
de HistoryProvider
que elimina todo de Context.Delete
representado por un 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 los últimos resultados
Lighthouse vía Vercel se utiliza para probar el rendimiento, lo que produce resultados únicos en cada git push
. lighthouse-badges se utiliza para generar nuevas insignias para cada implementación instalando npm i -g lighthouse-badges
y enviando la nueva URL con hash al conjunto de URL:
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
Las métricas de Lighthouse, concretamente Accesibilidad y Rendimiento, generan indicadores específicos en cada auditoría. Se realizan ajustes en cada impulso que abordan específicamente cualquier problema.
role
. Volver arriba
create-next-app
y se implementa con Vercel, lo cual logré con los siguientes pasos:commit
y push
el código desde mi IDE local a Github a través de Git y el terminal iTerm de mi MacBook Pro.select
, ubicada en la parte superior izquierda del mensaje inmediato."Su proyecto se ha implementado con éxito".
Volver arriba
git clone https://github.com/israelias/synonym-chaser
cd
al nombre de este repositorio: cd synonym-chaser
npm install
npm run dev
# or
yarn dev
abra su navegador en localhost:3000
info
se obtiene a través del Tesauro colegiado de Merriam-Webster.Volver arriba