| @@ -16,6 +16,7 @@ import { ReactComponent as TimeIcon } from './assets/icons/time.svg'; | |||||
| import { IProfile } from "./structure/profile"; | import { IProfile } from "./structure/profile"; | ||||
| import { ALL_WORDS } from "./data/all-words"; | import { ALL_WORDS } from "./data/all-words"; | ||||
| import { WordDetails } from "./components/word-details/WordDetails"; | |||||
| export var userProfileData : IProfile = { | export var userProfileData : IProfile = { | ||||
| @@ -32,7 +33,14 @@ export var userProfileData : IProfile = { | |||||
| icon: <TimeIcon />, | icon: <TimeIcon />, | ||||
| shelves: [{ | shelves: [{ | ||||
| name: 'All Words', | name: 'All Words', | ||||
| words: [ALL_WORDS[0], ALL_WORDS[1], ALL_WORDS[2], ALL_WORDS[3]], | |||||
| words: [{ | |||||
| ...ALL_WORDS[0], | |||||
| tag: 'New Word', | |||||
| notes: ['Weird words but understandable for a newb'] | |||||
| }, { | |||||
| ...ALL_WORDS[1], | |||||
| tag: 'Learning' | |||||
| }, ALL_WORDS[2], ALL_WORDS[3]], | |||||
| revisedWords: [ALL_WORDS[1]], | revisedWords: [ALL_WORDS[1]], | ||||
| description: 'All Words that I use on a daily basis', | description: 'All Words that I use on a daily basis', | ||||
| viewPermission: 'PUBLIC' | viewPermission: 'PUBLIC' | ||||
| @@ -68,6 +76,7 @@ function App() { | |||||
| <Route path="/categories" component={Categories} /> | <Route path="/categories" component={Categories} /> | ||||
| <Route path="/category-details/" component={CategoryDetails} /> | <Route path="/category-details/" component={CategoryDetails} /> | ||||
| <Route path="/shelf-details/" component={ShelfDetails} /> | <Route path="/shelf-details/" component={ShelfDetails} /> | ||||
| <Route path="/word-details/" component={WordDetails} /> | |||||
| <Route path="/revise" component={Revise} /> | <Route path="/revise" component={Revise} /> | ||||
| <Redirect from="/" to="/home" /> | <Redirect from="/" to="/home" /> | ||||
| </Switch> | </Switch> | ||||
| @@ -254,6 +254,12 @@ | |||||
| color: var(--orange); | color: var(--orange); | ||||
| } | } | ||||
| } | } | ||||
| .tag { | |||||
| &::before { | |||||
| background-color: var(--orange); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| &:nth-child(4n - 3) { | &:nth-child(4n - 3) { | ||||
| @@ -278,6 +284,12 @@ | |||||
| color: var(--teal); | color: var(--teal); | ||||
| } | } | ||||
| } | } | ||||
| .tag { | |||||
| &::before { | |||||
| background-color: var(--teal); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| &:nth-child(4n - 2) { | &:nth-child(4n - 2) { | ||||
| @@ -302,6 +314,12 @@ | |||||
| color: var(--blue); | color: var(--blue); | ||||
| } | } | ||||
| } | } | ||||
| .tag { | |||||
| &::before { | |||||
| background-color: var(--blue); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -327,6 +345,12 @@ | |||||
| color: var(--red); | color: var(--red); | ||||
| } | } | ||||
| } | } | ||||
| .tag { | |||||
| &::before { | |||||
| background-color: var(--red); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| &::before { | &::before { | ||||
| @@ -344,6 +368,28 @@ | |||||
| position: relative; | position: relative; | ||||
| } | } | ||||
| .tag { | |||||
| background-color: transparent; | |||||
| padding: 0.2rem 0.5rem; | |||||
| color: var(--grey); | |||||
| font-size: 1.4rem; | |||||
| font-weight: 300; | |||||
| position: relative; | |||||
| overflow: hidden; | |||||
| &::before { | |||||
| content: ''; | |||||
| position: absolute; | |||||
| left: 0; | |||||
| top: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| background-color: var(--orange); | |||||
| opacity: 0.2; | |||||
| border-radius: 0.8rem; | |||||
| } | |||||
| } | |||||
| header { | header { | ||||
| display: flex; | display: flex; | ||||
| align-items: flex-start; | align-items: flex-start; | ||||
| @@ -1,7 +1,6 @@ | |||||
| import React, { useState } from "react"; | import React, { useState } from "react"; | ||||
| import styles from './ShelfDetails.module.scss'; | import styles from './ShelfDetails.module.scss'; | ||||
| import { ReactComponent as ChevronLeft } from '../../assets/icons/chevron-left.svg'; | |||||
| import { NavLink, useLocation } from "react-router-dom"; | |||||
| import { NavLink, useHistory, useLocation } from "react-router-dom"; | |||||
| import queryString from 'query-string'; | import queryString from 'query-string'; | ||||
| import { ICategory } from "../../structure/category"; | import { ICategory } from "../../structure/category"; | ||||
| import { IShelf } from "../../structure/shelf"; | import { IShelf } from "../../structure/shelf"; | ||||
| @@ -10,6 +9,7 @@ import { ReactComponent as BookIcon } from '../../assets/icons/book-sharp.svg'; | |||||
| import { ReactComponent as MoreIcon } from '../../assets/icons/more-alt.svg'; | import { ReactComponent as MoreIcon } from '../../assets/icons/more-alt.svg'; | ||||
| import { ReactComponent as SearchIcon } from '../../assets/icons/bx-search-alt.svg'; | import { ReactComponent as SearchIcon } from '../../assets/icons/bx-search-alt.svg'; | ||||
| import { ReactComponent as SpeakerIcon } from '../../assets/icons/speaker.svg'; | import { ReactComponent as SpeakerIcon } from '../../assets/icons/speaker.svg'; | ||||
| import { ReactComponent as ChevronLeft } from '../../assets/icons/chevron-left.svg'; | |||||
| import { IWord } from "../../structure/word"; | import { IWord } from "../../structure/word"; | ||||
| export const ShelfDetails: React.FC = () => { | export const ShelfDetails: React.FC = () => { | ||||
| @@ -20,6 +20,7 @@ export const ShelfDetails: React.FC = () => { | |||||
| const shelf: IShelf = category.shelves[shelf_id]; | const shelf: IShelf = category.shelves[shelf_id]; | ||||
| const [searchWordResult, setSearchResult] = useState<Array<IWord>>(shelf.words); | const [searchWordResult, setSearchResult] = useState<Array<IWord>>(shelf.words); | ||||
| const [selectedSegment, setSelectedSegment] = useState<'WORDS' | 'ABOUT'>('WORDS'); | const [selectedSegment, setSelectedSegment] = useState<'WORDS' | 'ABOUT'>('WORDS'); | ||||
| const history = useHistory(); | |||||
| const searchWords = (searchWord: string) => { | const searchWords = (searchWord: string) => { | ||||
| if (searchWord.length > 0) { | if (searchWord.length > 0) { | ||||
| @@ -86,8 +87,11 @@ export const ShelfDetails: React.FC = () => { | |||||
| { selectedSegment === 'WORDS' && | { selectedSegment === 'WORDS' && | ||||
| <ul className={styles.searchResult}> | <ul className={styles.searchResult}> | ||||
| { searchWordResult.map((word) => { | |||||
| return <li key={word.name}> | |||||
| { searchWordResult.map((word, index) => { | |||||
| return <li key={word.name} onClick={() => { | |||||
| history.push('/word-details/category_id=' + category_id + '&&shelf_id=' + shelf_id + '&&word_id=' + index); | |||||
| }}> | |||||
| { word.tag && <span className={styles.tag}> {word.tag} </span> } | |||||
| <header> | <header> | ||||
| <h4> { word.name } <span> { word.pronounciation } </span> </h4> | <h4> { word.name } <span> { word.pronounciation } </span> </h4> | ||||
| <button> | <button> | ||||
| @@ -0,0 +1,289 @@ | |||||
| .navHeader { | |||||
| background-color: transparent; | |||||
| text-align: center; | |||||
| position: relative; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| padding: 1rem 1.5rem; | |||||
| button { | |||||
| width: 4rem; | |||||
| text-align: center; | |||||
| background-color: transparent; | |||||
| border: 0; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: flex-start; | |||||
| svg { | |||||
| width: 1rem; | |||||
| color: var(--black); | |||||
| } | |||||
| } | |||||
| h5 { | |||||
| font-size: 1.5rem; | |||||
| } | |||||
| figure { | |||||
| display: block; | |||||
| img { | |||||
| display: block; | |||||
| width: 4rem; | |||||
| height: 4rem; | |||||
| border-radius: 50%; | |||||
| border: 1px solid var(--creamy-white); | |||||
| } | |||||
| } | |||||
| } | |||||
| .cardDetails { | |||||
| border-radius: 4rem; | |||||
| padding: 2rem; | |||||
| background-color: white; | |||||
| position: relative; | |||||
| overflow: hidden; | |||||
| &::before { | |||||
| content: ''; | |||||
| position: absolute; | |||||
| left: 0; | |||||
| top: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| background-color: var(--orange); | |||||
| opacity: 0.5; | |||||
| } | |||||
| & > * { | |||||
| position: relative; | |||||
| } | |||||
| .tag { | |||||
| font-size: 1.4rem; | |||||
| color: var(--orange); | |||||
| filter: brightness(60%); | |||||
| } | |||||
| .container { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: flex-start; | |||||
| padding: 0.2rem 0 2rem; | |||||
| .audioButton { | |||||
| background-color: transparent; | |||||
| border: none; | |||||
| background-color: var(--orange); | |||||
| width: 4rem; | |||||
| height: 4rem; | |||||
| border-radius: 50%; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| svg { | |||||
| width: 1.7rem; | |||||
| fill: white; | |||||
| } | |||||
| } | |||||
| .moreButton { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| align-self: flex-start; | |||||
| justify-content: center; | |||||
| width: 2rem; | |||||
| height: 4rem; | |||||
| background-color: transparent; | |||||
| border: none; | |||||
| margin-left: auto; | |||||
| svg { | |||||
| fill: var(--red); | |||||
| width: 1rem; | |||||
| height: 1.5rem; | |||||
| } | |||||
| } | |||||
| h2 { | |||||
| font-size: 2.2rem; | |||||
| font-weight: 600; | |||||
| color: var(--black); | |||||
| margin-left: 1rem; | |||||
| span { | |||||
| display: block; | |||||
| font-size: 1.4rem; | |||||
| color: var(--orange); | |||||
| filter: brightness(50%); | |||||
| font-weight: 500; | |||||
| } | |||||
| } | |||||
| } | |||||
| .multipleMeaningList { | |||||
| list-style: none; | |||||
| li { | |||||
| line-height: 2; | |||||
| margin-bottom: 1rem; | |||||
| h6 { | |||||
| color: var(--orange); | |||||
| font-size: 1.4rem; | |||||
| filter: brightness(40%); | |||||
| text-transform: lowercase; | |||||
| } | |||||
| p { | |||||
| color: var(--orange); | |||||
| font-size: 1.4rem; | |||||
| font-weight: 300; | |||||
| filter: brightness(40%); | |||||
| &.sentence { | |||||
| opacity: 0.7; | |||||
| } | |||||
| } | |||||
| .wordLinks { | |||||
| margin-top: 1rem; | |||||
| button { | |||||
| border: 1px solid var(--teal); | |||||
| border-radius: 2rem; | |||||
| height: 3rem; | |||||
| color: var(--teal); | |||||
| background-color: transparent; | |||||
| padding: 0 1.5rem; | |||||
| margin-right: 1rem; | |||||
| margin-bottom: 1rem; | |||||
| font-size: 1.4rem; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| .notes { | |||||
| header { | |||||
| padding: 2rem; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: space-between; | |||||
| h5 { | |||||
| color: var(--orange); | |||||
| font-size: 1.4rem; | |||||
| filter: brightness(40%); | |||||
| } | |||||
| button { | |||||
| background-color: var(--teal); | |||||
| color: white; | |||||
| padding: 0.8rem 2rem; | |||||
| border-radius: 3rem; | |||||
| border: none; | |||||
| } | |||||
| } | |||||
| ol { | |||||
| list-style: decimal; | |||||
| padding: 0 2rem 0 3.5rem; | |||||
| li { | |||||
| text-align: left; | |||||
| color: var(--orange); | |||||
| font-size: 1.4rem; | |||||
| font-weight: 300; | |||||
| filter: brightness(40%); | |||||
| margin-bottom: 1.5rem; | |||||
| } | |||||
| } | |||||
| } | |||||
| .popupHolder { | |||||
| position: fixed; | |||||
| z-index: 2; | |||||
| left: 0; | |||||
| top: 0; | |||||
| background-color: transparent; | |||||
| width: 100vw; | |||||
| height: 100vh; | |||||
| box-shadow: 0px -10rem 20rem 10rem inset var(--creamy-white); | |||||
| .background { | |||||
| height: 100%; | |||||
| width: 100%; | |||||
| } | |||||
| } | |||||
| .popup { | |||||
| position: absolute; | |||||
| left: 0; | |||||
| bottom: 0; | |||||
| width: 100%; | |||||
| border-top-right-radius: 3rem; | |||||
| border-top-left-radius: 3rem; | |||||
| overflow: hidden; | |||||
| box-shadow: 0px 0px 20px -3px var(--grey); | |||||
| header { | |||||
| background-color: var(--orange); | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: flex-start; | |||||
| padding: 2rem 2rem 6rem; | |||||
| svg { | |||||
| fill: white; | |||||
| width: 1rem; | |||||
| margin-right: 0.5rem; | |||||
| } | |||||
| h5 { | |||||
| font-size: 1.5rem; | |||||
| font-weight: 500; | |||||
| color: white; | |||||
| } | |||||
| } | |||||
| .form { | |||||
| background-color: var(--creamy-white); | |||||
| border-top-right-radius: 3rem; | |||||
| border-top-left-radius: 3rem; | |||||
| overflow: hidden; | |||||
| margin-top: -4rem; | |||||
| padding: 2rem; | |||||
| } | |||||
| textarea { | |||||
| border: none; | |||||
| width: 100%; | |||||
| height: 10rem; | |||||
| resize: none; | |||||
| display: block; | |||||
| border-radius: 2rem; | |||||
| box-shadow: 0px 2px 10px -5px inset var(--light-grey); | |||||
| padding: 1.5rem; | |||||
| font-size: 1.4rem; | |||||
| background-color: var(--form-input-bg); | |||||
| } | |||||
| .addButton { | |||||
| background-color: var(--teal); | |||||
| color: white; | |||||
| border-radius: 2rem; | |||||
| height: 3.5rem; | |||||
| width: 15rem; | |||||
| font-size: 1.4rem; | |||||
| font-weight: 600; | |||||
| border: none; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| margin: 2rem auto 0; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,102 @@ | |||||
| import React, { useState } from "react"; | |||||
| import styles from './WordDetails.module.scss'; | |||||
| import { userProfileData } from "../../App"; | |||||
| import queryString from 'query-string'; | |||||
| import { useLocation } from "react-router-dom"; | |||||
| import { IShelf } from "../../structure/shelf"; | |||||
| import { IWord } from "../../structure/word"; | |||||
| import { ReactComponent as SpeakerIcon } from '../../assets/icons/speaker.svg'; | |||||
| import { ReactComponent as MoreIcon } from '../../assets/icons/more-alt.svg'; | |||||
| import { ReactComponent as ChevronLeft } from '../../assets/icons/chevron-left.svg'; | |||||
| import { ReactComponent as PlusIcon } from '../../assets/icons/plus.svg'; | |||||
| export const WordDetails: React.FC = () => { | |||||
| const location = useLocation(); | |||||
| const category_id: any = queryString.parse(location.pathname)['/word-details/category_id']; | |||||
| const shelf_id: any = queryString.parse(location.pathname)['shelf_id']; | |||||
| const word_id: any = queryString.parse(location.pathname)['word_id']; | |||||
| const shelf: IShelf = userProfileData.categories[category_id].shelves[shelf_id]; | |||||
| const word: IWord = shelf.words[word_id]; | |||||
| const [showAddWord, setShowAddWord] = useState<Boolean>(false); | |||||
| const [newNote, setNewNote] = useState<string>(''); | |||||
| return <section className="modalPage"> | |||||
| <header className={styles.navHeader}> | |||||
| <button onClick={() => window.history.back()}> | |||||
| <ChevronLeft /> | |||||
| </button> | |||||
| <h5> { shelf.name } </h5> | |||||
| <figure> | |||||
| {/* eslint-disable-next-line */} | |||||
| <img src={ userProfileData.image } alt="profile-image" /> | |||||
| </figure> | |||||
| </header> | |||||
| <section className={styles.cardDetails}> | |||||
| { word.tag && <span className={styles.tag}> { word.tag } </span> } | |||||
| <div className={styles.container}> | |||||
| <button className={styles.audioButton}> | |||||
| <SpeakerIcon /> | |||||
| </button> | |||||
| <h2> { word.name } <span> { word.pronounciation } </span> </h2> | |||||
| <button className={styles.moreButton}> | |||||
| <MoreIcon /> | |||||
| </button> | |||||
| </div> | |||||
| <ul className={styles.multipleMeaningList}> | |||||
| { word.grammaticalDetails.map((detail, index) => { | |||||
| return <li key={index}> | |||||
| <h6> { detail.typeName } </h6> | |||||
| <p> { detail.description } </p> | |||||
| { detail.sentence && <p className={styles.sentence}> "{ detail.sentence }" </p> } | |||||
| </li> | |||||
| }) } | |||||
| { word.similarWords.length > 0 && <li> | |||||
| <h6> Similar: </h6> | |||||
| <div className={styles.wordLinks}> | |||||
| { word.similarWords.map((similarWord, index) => <button key={index}> { similarWord } </button>) } | |||||
| </div> | |||||
| </li> } | |||||
| </ul> | |||||
| </section> | |||||
| <section className={styles.notes}> | |||||
| <header> | |||||
| <h5> My Notes: </h5> | |||||
| <button onClick={() => setShowAddWord(true)}> Add Notes </button> | |||||
| </header> | |||||
| { word.notes && <ol> | |||||
| { word.notes.map((note, index) => <li> {note} </li>) } | |||||
| </ol> } | |||||
| </section> | |||||
| { showAddWord && <div className={styles.popupHolder}> | |||||
| <div className={styles.background} onClick={() => setShowAddWord(false)}></div> | |||||
| <section className={styles.popup}> | |||||
| <header> | |||||
| <PlusIcon /> | |||||
| <h5> Add Category </h5> | |||||
| </header> | |||||
| <section className={styles.form}> | |||||
| <textarea placeholder={'Enter your notes here'} onInput={(e) => { | |||||
| setNewNote(e.currentTarget.value); | |||||
| }}></textarea> | |||||
| <button className={styles.addButton} disabled={!newNote} | |||||
| onClick={() => { | |||||
| userProfileData.categories[category_id].shelves[shelf_id].words[word_id].notes?.push(newNote); | |||||
| setShowAddWord(false); | |||||
| }}> | |||||
| Add | |||||
| </button> | |||||
| </section> | |||||
| </section> | |||||
| </div> } | |||||
| </section> | |||||
| } | |||||
| @@ -9,7 +9,8 @@ export const ALL_WORDS: Array<IWord> = [{ | |||||
| description: 'a feeling of happy satisfaction and enjoyment.' | description: 'a feeling of happy satisfaction and enjoyment.' | ||||
| }, { | }, { | ||||
| typeName: 'ADJECTIVE', | typeName: 'ADJECTIVE', | ||||
| description: 'used or intended for entertainment rather than business.' | |||||
| description: 'used or intended for entertainment rather than business.', | |||||
| sentence: 'She smiled with pleasure at being praised' | |||||
| }], | }], | ||||
| similarWords: ['Happiness', 'Delight', 'Joy', 'Rapture', 'Glee'], | similarWords: ['Happiness', 'Delight', 'Joy', 'Rapture', 'Glee'], | ||||
| }, { | }, { | ||||
| @@ -47,5 +48,5 @@ export const ALL_WORDS: Array<IWord> = [{ | |||||
| typeName: 'VERB', | typeName: 'VERB', | ||||
| description: 'make or cause to make a short, high-pitched ringing sound.' | description: 'make or cause to make a short, high-pitched ringing sound.' | ||||
| }], | }], | ||||
| similarWords: [''], | |||||
| similarWords: [], | |||||
| }] | }] | ||||
| @@ -9,6 +9,11 @@ | |||||
| font-family: 'Poppins', sans-serif; | font-family: 'Poppins', sans-serif; | ||||
| } | } | ||||
| *:disabled { | |||||
| opacity: 0.5; | |||||
| pointer-events: none; | |||||
| } | |||||
| h1, h2, h3, h4, h5, h6 { | h1, h2, h3, h4, h5, h6 { | ||||
| font-weight: 600; | font-weight: 600; | ||||
| } | } | ||||
| @@ -2,9 +2,12 @@ export type IWord = { | |||||
| name: string, | name: string, | ||||
| pronounciation: string, | pronounciation: string, | ||||
| audioPronounciationURL: string, | audioPronounciationURL: string, | ||||
| tag? : string, | |||||
| notes? : Array<string>, | |||||
| grammaticalDetails: Array<{ | grammaticalDetails: Array<{ | ||||
| typeName: 'NOUN' | 'ADJECTIVE' | 'VERB' | 'ADVERB', | typeName: 'NOUN' | 'ADJECTIVE' | 'VERB' | 'ADVERB', | ||||
| description: string, | description: string, | ||||
| sentence?: string, | |||||
| }>, | }>, | ||||
| similarWords: Array<string> | similarWords: Array<string> | ||||
| } | } | ||||