Содержательный тезаурус
Запомните все слова, которые вы найдете, используя тезаурус.
SynonymSearch помогает вам просмотреть то, что вы только что узнали (и в противном случае могли бы забыть), сортируя список всех ваших поисковых запросов по их общим определениям по мере их нахождения.
SynonymSearch позволяет проверять сходство слов-синонимов, вводя их в предложение, значение которого зависит от общего определения. Приложение помогает вам активно разобраться в определении, а затем сверить его с хранилищем синонимов.
Этот проект разработан как часть модуля MS2 Interactive Front End в Code Institute. Он написан для работы с форматом ответов JSON Merriam-Webster Collegiate® Thesaurus, разработан с помощью React через Next.js, загружен с помощью create-next-app
и развернут с помощью Vercel.
Пожалуйста, посетите проект здесь: синонимы.vercel.app
Примечание для справки: ранее схема называлась «synonym-chaser».
В начале января 2021 года началось создание схематических макетов, чтобы установить связи между разрозненными компонентами и распределить соответствующие обязанности.
Такой подход помог разбить масштаб проекта и, что наиболее важно, предоставить ориентиры для разработки и устранения неполадок.
см. «Мышление в React» (Документация)
{Results} => {Root} => {Sense} => {Option}
(см. каталог результатов){Saves} => {Selection}
(см. каталог сохранений).{Results} =>{Root} => {Sense}
через {ReplaceSubStringNode}
(см. string.helper.js). Теперь разбито на {Saves} => {Selection}
(см. Дисплей) {Sense} => {Option}
в дереве результатов (см. каталог результатов). Ко всем узлам с динамическим содержимым применяется единая верхняя и нижняя граница. Эти дискретные границы предназначены для передачи пользователю материального масштаба блокнота как знакомой точки соприкосновения. Эти wordBoxes
различаются только по интервалу и отвечают за визуальное представление иерархии в списке списков .
ThemeProvider
Material-UI оборачивает приложение для передачи стилей по дереву компонентов. См. Темы Material-UI. Семейство шрифтов настроено как массив системных шрифтов, чтобы адаптироваться к конфигурации устройства/платформы/пользователя и избежать зависимости от явной гарнитуры. Объект commonSettings
в theme.context.js
содержит список этих переопределений, который объединен с поставщиком тем Material-UI. См. 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 ( ',' ) ,
} ,
}
Приложение имеет два набора основных и дополнительных цветов для светлой темы и темной темы, настроенной с помощью конфигурации палитры пользовательского интерфейса Material. Поведение и состояние цветов зависят от свойств, определенных в объекте темы по умолчанию Material-UI. Вторичным цветам присваиваются нейтральные тона, а основные цвета отображаются как единственный «функциональный цвет». Минимальный подход к наборам цветов — усовершенствовать блокнот.
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 ,
} ) ;
Веб-сайт смоделирован как прогрессивное веб-приложение, обеспечивающее функциональность одностраничного приложения без полной перезагрузки страницы и оптимизированное для работы как нативное приложение. Его структура обеспечивает гибкость в различных браузерах.
На веб-сайте используются большие плавающие кнопки действий (FAB) для быстрого перемещения между тремя удаленными представлениями: «Поиск», «Информация» и «Сохранения». Визуальные метафоры используются для дискретного и эффективного представления этих режимов просмотра.
Плавающая кнопка «быстрого набора» сохраняется во всех представлениях для быстрого доступа к основным действиям приложения: запросу термина, очистке сохраненной истории и переключению светлого или темного режима.
Приложение имеет настраиваемый экран запуска при начальной загрузке страницы, кратко описывающий его назначение: «Интерактивное приложение-тезаурус».
jsx
и jss
js
. (Загружено с помощью Nestjs)next
разработка, next build
для подготовки к производству и next start
обработка.commit
Git и push
на GitHub.push
через Git. Стили написаны с использованием синтаксиса jss и соответствуют спецификации Material-UI, чтобы хорошо сочетаться с рендерингом на стороне сервера Nextjs, который включает в себя логику настройки pages/_document
для внедрения стилей, отображаемых на стороне сервера, в разметку непосредственно перед ее использованием. Обратите внимание, что это неофициальная рекомендация, и ее следует переработать, если/когда MUI выпустит официальный плагин для Nextjs, аналогичный плагину для MUI/Gatsby.
Все объекты стилей создаются с помощью хука makeStyles
MUI и следуют рекомендациям в документации по стилям MUI по темам. Сюда входят переопределения CSS и медиа-запросы. Объекты стилей для основных компонентов разделяются по области действия в каталоге стилей и импортируются в модули по мере необходимости. Однако некоторые компоненты, а именно Launcher
, имеют все свойства стиля, объявленные в самом файле компонента. Обратите внимание, что все стили в конечном итоге должны быть экспортированы из одного каталога для обеспечения единообразия.
Дерево results
отображает компоненты jsx
при каждом вызове API без инициализации констант. Свойства и условные выражения управляют итерацией схемы ответа из API MW-Thesaurus. Обратите внимание, что значения и проверки Prop-Type специально написаны для получения правильных данных из этой схемы:
Каждый сохраненный экземпляр слова сохраняет свойства семейства результатов, из которого он «взят» — например, конкретное определение слова, метку, корневое слово — для увеличения значений дубликатов и группировки слов по смысловым определениям, обеспечивая при этом каждый экземпляр, независимо от того, является ли он идентичным. по значению имени отличается, если определение и смысл различны
Перехватчики React useReducer
, useContext
, createContext
собираются вместе в context/words.context
(см. HistoryProvider), чтобы предоставить контекстную оболочку для компонентов, которые могут совместно использовать и использовать одни и те же данные. Обратите внимание, что эта логика напрямую смоделирована из примера, установленного в файле next.js/examples/with-context-api.
Следуя тому, что достигается с помощью экспортированных обработчиков контекста HistoryProvider
useHistory
и useDispatchHstory
, которые используются в компонентах более высокого порядка, общих компонентах и вспомогательных функциях в helpers/*
для радикального манипулирования DOM, одна и та же логика перепрофилируется для использования нескольких перехватывает пользовательскую оболочку с ThemeProvider MUI (см. ThemeProvider). В этом случае установка значения представления позволяет любому компоненту изменять страницу из любого места, и это лишь некоторые из них. Обратите внимание на универсальный характер этого поставщика макетов контекста.
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 ) ;
Использование MW-Thesaurus бесплатно в некоммерческих и/или образовательных целях. Представленные логотипы брендов на вкладке «Информация» соответствуют рекомендациям по брендингу, запрошенным, помимо прочего, Центром разработки Merriam-Webster.
Зарегистрированный ключ API Тезауруса, связанный с этим проектом, в настоящее время является общедоступным. После этого обсуждения усилия по защите вызовов API на стороне клиента бесцельны, поскольку закрытый ключ всегда будет открыт. Дальнейшую настройку внутренней конечной точки в /pages/api
следует рассматривать в соответствии с маршрутами API Nextjs. Обратите внимание, что ключ был предоставлен из .env.local
по указанным выше причинам и в образовательных целях проекта.
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 ,
) ;
}
Примечание. Все описания нетипичных методов можно найти в блоках комментариев, соответствующих стандартам jsDoc.
Пользователь ожидает увидеть описание в течение нескольких секунд после перехода на сайт.
Launcher
:useEffect
без зависимостей позволяет запускать Launcher
один раз при начальной загрузке страницы.useContext
позволяет Launcher
установить для текущего значения просмотра страницы value
« search
» после таймаута.Synonym Search An Interactive Thesaurus App
» в течение двух с половиной секунд после перехода на сайт. 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 >
< / >
) ;
} ;
Пользователь хочет удобно перемещаться между представлениями
Speed (Dial)
:onClick
.FixedBottom
(развитая версия «FixedBottom») позволяет нижнему Speed Dial
быстро перемещаться над нижними полосами на мобильном устройстве при прокрутке.useScrollTrigger
позволяет Speed Dial
менять местами видимость с помощью ScrollTop
, плавающей кнопки действия, которая при нажатии прокручивает окно обратно в верхнюю часть страницы с помощью привязки с id="back-to-top"
.Search
передаются в Speed Dial
для активации onSearchTextChange
— функции, которая отправляет запросы в API тезауруса.useState
позволяет useEffect
всегда закрывать SpeedDial
при каждом изменении value
страницы через useDispatch
, за исключением случаев, когда active
Search
.ToggleTheme
, Search
, Clear
Saves
SpeedDial
SpeedDial
открытToggleTheme
not
этот <mode>
SpeedDial
закрыт.Saves
SpeedDial
SpeedDial
открытSearch
SpeedDial
все еще открытSearch
SpeedDial
исчезаетScrollTop
ScrollTop
SpeedDial
x
и y
(вверх/вниз в представлениях, влево/вправо между видами), нажимая действия SpeedDial
для представлений, которые они представляют. Кроме того, в заголовке страницы доступна кнопка навигации для каждого просмотра. Два других представления — Saves
и Info
— относятся к тому же семейству кнопок с плавающими действиями, что и действия в SpeedDial
. Последовательность и уникальность темы, цвета и пользовательского интерфейса влияют на импульсы пользователя и его знакомство с передаваемой постоянной точкой контакта. 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 ;
Пользователь хочет видеть надежные источники
Brands
(Material-UI AvatarGroup
):useEffect
позволяет useState
переворачивать поля, когда для значения страницы установлено текущее view
=> Info
, что анимирует расширение аватаров, как будто приветствуя пользователя на информационной стойке, в то время как короткий абзац содержит описание « SynonymStory
» позади " SynonymSearch
"Logo
, по одному на каждый: React
, NextJS
, MaterialUI
, CodeInstitute
и 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 >
) ;
} ;
Пользователь хочет найти синонимы
Search
:loading
: загружаются ли результаты,meta
: дает ли ввод пользователя действительный результат,root
: соответствует ли первый элемент массива результатов тому, что набрал пользователь.useState
позволяет этим условиям изменяться динамически.useRef
прикрепляется к компоненту ввода и позволяет событиям клавиатуры и мыши условно focus
приглашение ввода на основе вышеуказанных условий.Field
, Input
.Search
Search
в заголовкеimmediately successful word
>immediately successful word
Search
в заголовкеincomplete word
>real word
>real word
Search
в заголовкеa 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 >
) ;
} ;
Пользователь хочет видеть историю поиска
useHistory
— это контекстный хук для доступа к значениям, хранящимся в HistoryProvider
.useReducer
позволяет HistoryProver
группировать запрашиваемые слова по общим для них определениям.Selection
Пользователь хочет увидеть слово, используемое в предложении
Display
:Sense
для передачи свойств sampleString
и optionWord
, который активирует Intersection Observer
и события мыши для динамического изменения свойства optionWord
.{it} {/it}
и {lquo} {rquo}
. Поскольку функции замены регулярных выражений являются открывающими и закрывающими тегами, они надежно обеспечивают постоянную очистку строки примера, когда она есть в определении. (См. пример ответа от Theaurus API)Пользователь хочет видеть тег рядом с повторяющимися результатами (т. е. уже сохраненными словами).
useReducer
в HistoryProvider
:HistoryProvider
сохраняются со свойствами, которые позволяют проверять их путем сравнения этих свойств с новыми словами:Counters
:History
Saves
, поэтому пользователь может просматривать «повторы», то есть сохраненные пользователем слова, по общим определениям свойств. 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 ;
}
} , [ ] ) ;
Пользователь хочет удалить сохраненную историю поиска.
Clear
:DeleteForever
, которая является функцией dispatch
useReducer
из HistoryProvider
, которая удаляет все из контекста.Delete
, представленного компонентом 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 ) ;
Посмотреть последние результаты
Lighthouse через Vercel используется для тестирования производительности, которое дает уникальные результаты при каждом git push
. Lighthouse-badges используется для создания новых значков для каждого развертывания путем установки npm i -g lighthouse-badges
и отправки нового хешированного URL-адреса в массив 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
Метрики Lighthouse, а именно «Доступность» и «Производительность», генерируют определенные флаги при каждом аудите. При каждом нажатии вносятся корректировки, направленные конкретно на решение каких-либо проблем.
role
. Вернуться наверх
create-next-app
и развернут с помощью Vercel, чего я достиг с помощью следующих шагов:commit
и push
код из моей локальной IDE в Github через Git и терминал iTerm моего MacBook Pro.select
, расположенное в левом верхнем углу командной строки.«Ваш проект успешно развернут».
Вернуться наверх
git clone https://github.com/israelias/synonym-chaser
cd
к названию этого репозитория: cd synonym-chaser
npm install
npm run dev
# or
yarn dev
откройте браузер на localhost:3000
info
, взято из университетского тезауруса Merriam-Webster.Вернуться наверх