@@ -16,6 +16,7 @@ import { ReactComponent as TimeIcon } from './assets/icons/time.svg'; | |||
import { IProfile } from "./structure/profile"; | |||
import { ALL_WORDS } from "./data/all-words"; | |||
import { WordDetails } from "./components/word-details/WordDetails"; | |||
export var userProfileData : IProfile = { | |||
@@ -32,7 +33,14 @@ export var userProfileData : IProfile = { | |||
icon: <TimeIcon />, | |||
shelves: [{ | |||
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]], | |||
description: 'All Words that I use on a daily basis', | |||
viewPermission: 'PUBLIC' | |||
@@ -68,6 +76,7 @@ function App() { | |||
<Route path="/categories" component={Categories} /> | |||
<Route path="/category-details/" component={CategoryDetails} /> | |||
<Route path="/shelf-details/" component={ShelfDetails} /> | |||
<Route path="/word-details/" component={WordDetails} /> | |||
<Route path="/revise" component={Revise} /> | |||
<Redirect from="/" to="/home" /> | |||
</Switch> | |||
@@ -254,6 +254,12 @@ | |||
color: var(--orange); | |||
} | |||
} | |||
.tag { | |||
&::before { | |||
background-color: var(--orange); | |||
} | |||
} | |||
} | |||
&:nth-child(4n - 3) { | |||
@@ -278,6 +284,12 @@ | |||
color: var(--teal); | |||
} | |||
} | |||
.tag { | |||
&::before { | |||
background-color: var(--teal); | |||
} | |||
} | |||
} | |||
&:nth-child(4n - 2) { | |||
@@ -302,6 +314,12 @@ | |||
color: var(--blue); | |||
} | |||
} | |||
.tag { | |||
&::before { | |||
background-color: var(--blue); | |||
} | |||
} | |||
} | |||
@@ -327,6 +345,12 @@ | |||
color: var(--red); | |||
} | |||
} | |||
.tag { | |||
&::before { | |||
background-color: var(--red); | |||
} | |||
} | |||
} | |||
&::before { | |||
@@ -344,6 +368,28 @@ | |||
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 { | |||
display: flex; | |||
align-items: flex-start; | |||
@@ -1,7 +1,6 @@ | |||
import React, { useState } from "react"; | |||
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 { ICategory } from "../../structure/category"; | |||
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 SearchIcon } from '../../assets/icons/bx-search-alt.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"; | |||
export const ShelfDetails: React.FC = () => { | |||
@@ -20,6 +20,7 @@ export const ShelfDetails: React.FC = () => { | |||
const shelf: IShelf = category.shelves[shelf_id]; | |||
const [searchWordResult, setSearchResult] = useState<Array<IWord>>(shelf.words); | |||
const [selectedSegment, setSelectedSegment] = useState<'WORDS' | 'ABOUT'>('WORDS'); | |||
const history = useHistory(); | |||
const searchWords = (searchWord: string) => { | |||
if (searchWord.length > 0) { | |||
@@ -86,8 +87,11 @@ export const ShelfDetails: React.FC = () => { | |||
{ selectedSegment === 'WORDS' && | |||
<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> | |||
<h4> { word.name } <span> { word.pronounciation } </span> </h4> | |||
<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.' | |||
}, { | |||
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'], | |||
}, { | |||
@@ -47,5 +48,5 @@ export const ALL_WORDS: Array<IWord> = [{ | |||
typeName: 'VERB', | |||
description: 'make or cause to make a short, high-pitched ringing sound.' | |||
}], | |||
similarWords: [''], | |||
similarWords: [], | |||
}] |
@@ -9,6 +9,11 @@ | |||
font-family: 'Poppins', sans-serif; | |||
} | |||
*:disabled { | |||
opacity: 0.5; | |||
pointer-events: none; | |||
} | |||
h1, h2, h3, h4, h5, h6 { | |||
font-weight: 600; | |||
} | |||
@@ -2,9 +2,12 @@ export type IWord = { | |||
name: string, | |||
pronounciation: string, | |||
audioPronounciationURL: string, | |||
tag? : string, | |||
notes? : Array<string>, | |||
grammaticalDetails: Array<{ | |||
typeName: 'NOUN' | 'ADJECTIVE' | 'VERB' | 'ADVERB', | |||
description: string, | |||
sentence?: string, | |||
}>, | |||
similarWords: Array<string> | |||
} |