Un thésaurus significatif
Mémorisez tous les mots que vous trouvez lorsque vous utilisez un thésaurus.
SynonymSearch vous aide à revoir ce que vous venez d'apprendre (et que vous pourriez autrement oublier) en triant une liste de toutes vos requêtes de recherche selon leurs définitions partagées au fur et à mesure que vous les trouvez.
SynonymSearch vous permet de tester la similitude de mots synonymes en les injectant dans une phrase dont le sens dépend de la définition qu'ils partagent. L'application vous aide à donner activement un sens à une définition, puis à la comparer à un référentiel de synonymes.
Ce projet est développé dans le cadre du module MS2 Interactive Front End du Code Institute. Il est écrit pour fonctionner avec le format de réponse JSON du Merriam-Webster's Collegiate® Thesaurus et est développé avec React via Next.js, démarré avec create-next-app
et déployé avec Vercel.
Veuillez visiter le projet ici : synonymes.vercel.app
Note pour référence : le schéma précédent était nommé « synonyme-chaser »
Les maquettes schématiques ont commencé début janvier 2021 pour établir des relations entre les composants disparates et attribuer les responsabilités appropriées.
Cette approche a permis de décomposer la portée du projet et, plus important encore, de fournir une référence de référence pour le développement et le dépannage.
voir Penser dans React (Docs)
{Results} => {Root} => {Sense} => {Option}
(Voir le répertoire des résultats){Saves} => {Selection}
(Voir le répertoire des sauvegardes){Results} =>{Root} => {Sense}
via {ReplaceSubStringNode}
(voir string.helper.js). Maintenant décomposé en {Saves} => {Selection}
(Voir Affichage) {Sense} => {Option}
dans l'arborescence des résultats (voir le répertoire des résultats) Une bordure supérieure et inférieure cohérente est appliquée à tous les nœuds dotés d'un contenu dynamique. Ces bordures discrètes sont conçues pour transmettre l'échelle matérielle d'un bloc-notes en tant que point de contact familier pour un utilisateur. Ces wordBoxes
ne varient qu'en termes d'espacement et sont chargées de transmettre visuellement la hiérarchie dans une liste de listes .
ThemeProvider
de Material-UI encapsule l'application afin de transmettre les styles dans l'arborescence des composants. Voir Thèmes Material-UI. La famille de polices est définie sur un ensemble de polices système afin de s'adapter à la configuration de l'appareil/de la plate-forme/de l'utilisateur et d'éviter de dépendre d'une police explicite. Un objet commonSettings
dans theme.context.js
contient une liste de ces remplacements, qui est fusionnée avec le fournisseur de thème de Material-UI. Voir 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 ( ',' ) ,
} ,
}
L'application propose deux ensembles de couleurs primaires et secondaires pour un thème clair et un thème sombre configuré via la configuration de la palette de Material UI. Les comportements et les états des couleurs reposent sur des propriétés définies dans l'objet Thème par défaut de Material-UI. Les couleurs secondaires sont définies sur un ton neutre, tandis que les couleurs primaires sont rendues comme une « couleur caractéristique » singulière. L'approche minimale des jeux de couleurs consiste à améliorer un bloc-notes
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 ,
} ) ;
Le site Web est modélisé comme une application Web progressive pour restituer les fonctionnalités d'une application d'une seule page sans rechargement de page complète et optimisé pour fonctionner comme une application native. Son framework garantit la flexibilité entre les navigateurs.
Le site Web profite de grands boutons d'action flottants (FAB) pour naviguer rapidement entre ses trois vues distantes : « Recherche », « Informations » et « Enregistrements ». Des métaphores visuelles sont utilisées pour représenter ces modes de vue de manière discrète et efficace.
Un bouton flottant de « numérotation rapide » persiste dans les vues pour accéder rapidement aux actions principales de l'application : interroger un terme, effacer l'historique enregistré et basculer entre le mode clair ou sombre.
L'application propose un écran de lancement personnalisé lors du chargement initial de la page pour décrire succinctement son objectif : "Une application de thésaurus interactive".
jsx
, jss
js
. (Amorcé avec Nestjs)next
à développer, next build
à préparer pour la production et next start
à servir.commit
dans Git et push
vers GitHub.push
simultanées via Git. Les styles sont écrits dans la syntaxe jss et suivent les spécifications de Material-UI pour fonctionner correctement avec le rendu côté serveur Nextjs, qui inclut cette logique de personnalisation pages/_document
pour injecter des styles rendus côté serveur dans le balisage juste avant son utilisation. Notez qu'il s'agit d'une recommandation non officielle et qu'elle devrait être refactorisée si/quand MUI publie un plugin officiel pour Nextjs similaire à celui pour MUI/Gatsby.
Tous les objets de style sont créés avec le hook makeStyles
de MUI et suivent les directives de la documentation de style MUI pour la thématique. Cela inclut les remplacements CSS et les requêtes multimédias. Les objets de style pour les composants principaux sont séparés par portée dans le répertoire des styles et importés dans les modules selon les besoins. Cependant, certains composants, à savoir Launcher
, ont tous les accessoires de style déclarés dans le fichier du composant lui-même. Notez que tous les styles doivent éventuellement être exportés à partir d'un seul répertoire pour des raisons de cohérence.
L'arborescence results
restitue les composants jsx
à chaque appel d'API sans initialiser les constantes. Les accessoires et les expressions conditionnelles contrôlent l'itération du schéma de réponse de l'API MW-Thesaurus. Notez que les valeurs et les vérifications Prop-Type sont spécifiquement écrites pour obtenir les bonnes données de ce schéma :
Chaque instance de mot enregistrée conserve les propriétés de la famille de résultats dont elle est "tirée" - telles que la définition particulière du mot, l'étiquette, le mot racine - pour incrémenter les valeurs des doublons et regrouper les mots par définitions de sens tout en garantissant que chaque instance, quelle que soit son identité. à la valeur du nom, est distinct si la définition et le sens sont différents
Les hooks React useReducer
, useContext
, createContext
sont assemblés ensemble dans context/words.context
(voir HistoryProvider) pour fournir un wrapper de contexte permettant aux composants de partager et de consommer les mêmes données. Notez que cette logique est directement modélisée à partir de l'exemple défini dans next.js/examples/with-context-api.
Suite à ce qui est réalisé par les hooks d'emballage de contexte exportés de HistoryProvider
useHistory
et useDispatchHstory
-- qui sont utilisés dans les composants d'ordre supérieur, les composants partagés et les fonctions d'assistance dans helpers/*
pour manipuler radicalement le DOM -- la même logique est réutilisée pour piloter plusieurs s'accroche à un wrapper personnalisé avec le ThemeProvider de MUI (voir ThemeProvider). Dans ce cas, la définition de la valeur d'affichage permet à n'importe quel composant de modifier la page de n'importe où, pour n'en nommer que quelques-uns. Notez la nature fourre-tout de ce fournisseur de mise en page contextuelle.
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 ) ;
L'utilisation de MW-Thesaurus est gratuite à des fins non commerciales et/ou éducatives. Les logos de marque présentés dans l'onglet Informations doivent suivre les directives de marque demandées, sans s'y limiter, par le centre de développement de Merriam-Webster.
La clé API du thésaurus enregistrée liée à ce projet est actuellement publique. Suite à cette discussion, les efforts visant à protéger les appels API côté client sont sans but car la clé privée sera toujours exposée. Une configuration supplémentaire pour avoir un point de terminaison interne dans /pages/api
doit être envisagée en suivant les routes de l'API Nextjs. Notez que la clé a été exposée depuis .env.local
pour les raisons ci-dessus et à des fins pédagogiques du projet.
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 ,
) ;
}
Remarque : Toutes les descriptions des méthodes atypiques peuvent être trouvées dans les blocs de commentaires qui suivent les normes jsDoc.
Un utilisateur s'attend à voir une description quelques secondes après son arrivée sur le site
Launcher
:useEffect
sans dépendances permet Launcher
de s'exécuter une fois lors du chargement initial de la pageuseContext
permet Launcher
de définir la value
d'affichage de la page actuelle sur « search
» après un délai d'attente.Synonym Search An Interactive Thesaurus App
» pendant deux secondes et demie en arrivant sur le 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 >
< / >
) ;
} ;
Un utilisateur souhaite naviguer rapidement entre les vues
Speed (Dial)
:onClick
.FixedBottom
(dérivé de FixeBottom) permet Speed Dial
inférieur de se déplacer de manière réactive au-dessus des barres inférieures sur mobile lors du défilement.useScrollTrigger
permet à Speed Dial
d'échanger les visibilités avec ScrollTop
, un bouton d'action flottant qui, lorsque vous cliquez dessus, fait défiler la fenêtre vers le haut de la page via une ancre avec id="back-to-top"
.Search
sont transmis à Speed Dial
pour activer onSearchTextChange
, une fonction qui envoie des requêtes à l'API Thesaurus.useState
permet useEffect
de toujours fermer SpeedDial
chaque fois que la value
de la page via useDispatch
change, sauf lorsque Search
est active
.ToggleTheme
, Search
, Clear
Saves
SpeedDial
SpeedDial
est ouvertToggleTheme
not
ce <mode>
SpeedDial
est ferméSaves
SpeedDial
SpeedDial
est ouvertSearch
SpeedDial
est toujours ouvertSearch
SpeedDial
disparaîtScrollTop
apparaîtScrollTop
SpeedDial
apparaîtx
et y
(haut/bas dans les vues, gauche/droite entre les vues) en cliquant sur les actions SpeedDial
pour les vues qu'elles représentent. De plus, la navigation par bouton par vue est accessible dans l’en-tête de la page. Les deux autres vues sont Saves
et Info
, qui appartiennent à la même famille de boutons d'action flottants que les actions de SpeedDial
. La cohérence et la singularité du thème, de la couleur et de l'interface utilisateur jouent sur les impulsions de l'utilisateur et sa familiarité avec le point de contact persistant transmis. 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 utilisateur souhaite voir des sources fiables
Brands
(Material-UI AvatarGroup
) :useEffect
permet useState
d'inverser les marges lorsque la valeur de la page est définie sur la view
actuelle => Info
, ce qui anime les avatars pour qu'ils se développent comme pour accueillir un utilisateur au bureau d'information, tandis qu'un court paragraphe est présenté pour décrire la " SynonymStory
" derrière " SynonymSearch
"Logo
, une pour chacune : React
, NextJS
, MaterialUI
, CodeInstitute
et Merriam-Webster
.Search
Info
Info
Brands
apparaîtMerriam-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 utilisateur souhaite rechercher des synonymes
Search
:loading
: si les résultats sont en cours de chargement,meta
: si la saisie d'un utilisateur produit un résultat valide,root
: indique si le premier élément d'un tableau de résultats correspond à ce que l'utilisateur a tapé.useState
permet à ces conditions de changer dynamiquement.useRef
est attaché au composant d'entrée et permet aux événements de touche et de souris de focus
de manière conditionnelle l'invite d'entrée en fonction des conditions ci-dessus.Field
, Input
.Search
Search
dans l'en-têteimmediately successful word
>immediately successful word
Search
dans l'en-têteincomplete word
>real word
>real word
Search
dans l'en-têtea 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 utilisateur souhaite voir l'historique des recherches
useHistory
est un hook Context pour accéder aux valeurs stockées dans HistoryProvider
.useReducer
permet HistoryProver
de regrouper les mots interrogés en fonction des définitions qu'ils partagent.Selection
Un utilisateur souhaite voir un mot utilisé dans une phrase
Display
:Sense
pour transmettre un prop sampleString
et optionWord
, qui active un Intersection Observer
et des événements de souris pour modifier dynamiquement le prop optionWord
.{it} {/it}
et {lquo} {rquo}
. Comme il s'agit de balises d'ouverture et de fermeture, les fonctions de remplacement d'expression régulière s'avèrent fiables pour nettoyer systématiquement la chaîne d'exemple lorsqu'une définition la contient. (Voir exemple de réponse de l'API Theaurus)Un utilisateur souhaite voir une balise à côté des résultats répétés (c'est-à-dire des mots déjà enregistrés)
useReducer
dans HistoryProvider
:HistoryProvider
sont enregistrés avec des propriétés qui permettent la vérification en comparant ces propriétés avec de nouveaux mots :Counters
:History
Saves
afin qu'un utilisateur puisse afficher les « répétitions », c'est-à-dire les mots enregistrés d'un utilisateur par les définitions de propriétés qu'ils partagent. 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 utilisateur souhaite supprimer l'historique de recherche enregistré
Clear
:DeleteForever
qui est une fonction dispatch
de useReducer
de HistoryProvider
qui supprime tout de Context.Delete
représentée par un composant 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 ) ;
Voir les derniers résultats
Lighthouse via Vercel est utilisé pour tester les performances, ce qui produit des résultats uniques à chaque git push
. lighthouse-badges est utilisé pour générer de nouveaux badges pour chaque déploiement en installant npm i -g lighthouse-badges
et en poussant la nouvelle URL hachée vers le tableau d'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
Les mesures de Lighthouse, à savoir l'accessibilité et les performances, génèrent des indicateurs spécifiques sur chaque audit. Des ajustements sont effectués à chaque poussée pour résoudre spécifiquement les problèmes.
role
clarté. Retour en haut
create-next-app
et déployé avec Vercel, ce que j'ai réalisé en suivant les étapes suivantes :commit
et push
le code de mon IDE local vers Github via Git et le terminal iTerm de mon MacBook Pro.select
, située en haut à gauche de l'invite immédiate."Votre projet a été déployé avec succès."
Retour en haut
git clone https://github.com/israelias/synonym-chaser
cd
au nom de ce repo : cd synonym-chaser
npm install
npm run dev
# or
yarn dev
ouvrez votre navigateur sur localhost:3000
info
provient du thésaurus collégial Merriam-Webster.Retour en haut