@@ -3,12 +3,16 @@ import { Categories } from './components/categories/Categories'; | |||
import { Revise } from './components/revise/Revise'; | |||
import { Tabs } from "./components/tabs/Tabs"; | |||
import { BrowserRouter, Route, Redirect, Switch } from "react-router-dom"; | |||
import { AddWord } from "./components/add-word/AddWord"; | |||
import { AddShelf } from "./components/add-shelf/AddShelf"; | |||
function App() { | |||
return ( | |||
<BrowserRouter> | |||
<Switch> | |||
<Route path="/home" component={Home} /> | |||
<Route path="/add-word" component={AddWord} /> | |||
<Route path="/add-shelf" component={AddShelf} /> | |||
<Route path="/categories" component={Categories} /> | |||
<Route path="/revise" component={Revise} /> | |||
<Redirect from="/" to="/home" /> | |||
@@ -1,6 +1,6 @@ | |||
<svg xmlns="http://www.w3.org/2000/svg" width="20.36" height="20.014" viewBox="0 0 20.36 20.014"> | |||
<g id="Group_11" data-name="Group 11" transform="translate(-17.641 -24)"> | |||
<path id="back-light" d="M18.365,19.53l-8.81-8.666L18.365,2.2,17,.857,6.857,10.869,17,20.871Z" transform="translate(19.635 23.143)" fill="#fff" fill-rule="evenodd"/> | |||
<path id="back-light-2" data-name="back-light" d="M11.508,18.673,2.7,10.007l8.81-8.666L10.146,0,0,10.012l10.146,10Z" transform="translate(29.148 44.014) rotate(180)" fill="#fff" fill-rule="evenodd"/> | |||
<path id="back-light" d="M18.365,19.53l-8.81-8.666L18.365,2.2,17,.857,6.857,10.869,17,20.871Z" transform="translate(19.635 23.143)" fill-rule="evenodd"/> | |||
<path id="back-light-2" data-name="back-light" d="M11.508,18.673,2.7,10.007l8.81-8.666L10.146,0,0,10.012l10.146,10Z" transform="translate(29.148 44.014) rotate(180)" fill-rule="evenodd"/> | |||
</g> | |||
</svg> |
@@ -0,0 +1,247 @@ | |||
.createShelfModal { | |||
background-color: var(--creamy-white); | |||
z-index: 2; | |||
width: 100vw; | |||
height: 100vh; | |||
overflow: auto; | |||
opacity: 0; | |||
position: relative; | |||
animation: fadeIn 0.3s forwards; | |||
@keyframes fadeIn { | |||
0% { | |||
opacity: 0; | |||
transform: translateY(10vh); | |||
} 100% { | |||
opacity: 1; | |||
transform: translateY(0vh); | |||
} | |||
} | |||
} | |||
.modalHeader { | |||
background-color: var(--orange); | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
padding-bottom: 4.5rem; | |||
padding-top: 0.5rem; | |||
h4 { | |||
flex-grow: 1; | |||
padding-left: 4rem; | |||
color: white; | |||
font-size: 1.5rem; | |||
text-align: center; | |||
font-weight: 500; | |||
} | |||
button { | |||
margin-left: auto; | |||
margin-right: 1rem; | |||
width: 4rem; | |||
height: 4rem; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
background-color: transparent; | |||
border: none; | |||
svg { | |||
width: 1.5rem; | |||
fill: white; | |||
} | |||
} | |||
} | |||
.form { | |||
border-radius: 3rem; | |||
background-color: var(--creamy-white); | |||
overflow: hidden; | |||
margin-top: -4rem; | |||
padding: 2rem; | |||
input { | |||
border: none; | |||
height: 4rem; | |||
width: 100%; | |||
display: block; | |||
border-radius: 3rem; | |||
box-shadow: 0px 2px 10px -5px inset var(--light-grey); | |||
padding: 0 1.5rem; | |||
font-size: 1.2rem; | |||
margin-bottom: 1.5rem; | |||
background-color: #efe6d6; | |||
} | |||
textarea { | |||
background-color: #efe6d6; | |||
font-size: 1.2rem; | |||
padding: 1rem 1.5rem; | |||
box-shadow: 0px 2px 10px -5px inset var(--light-grey); | |||
border: none; | |||
resize: none; | |||
display: block; | |||
width: 100%; | |||
height: 10rem; | |||
border-radius: 1.5rem; | |||
margin-bottom: 1.5rem; | |||
} | |||
} | |||
.blockHeader { | |||
display: flex; | |||
align-items: center; | |||
justify-content: flex-start; | |||
h5 { | |||
font-size: 1.2rem; | |||
font-weight: 600; | |||
color: var(--black); | |||
flex-grow: 1; | |||
svg { | |||
width: 2rem; | |||
height: 2rem; | |||
fill: var(--black); | |||
vertical-align: middle; | |||
margin-right: 1rem; | |||
} | |||
} | |||
button, a { | |||
background-color: var(--teal); | |||
width: 3rem; | |||
height: 3rem; | |||
border: none; | |||
border-radius: 50%; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
margin-left: 1rem; | |||
&.expandButton { | |||
background-color: var(--red); | |||
} | |||
svg { | |||
fill: white; | |||
width: 1.2rem; | |||
} | |||
} | |||
} | |||
.Grid { | |||
padding: 0 0.5rem; | |||
ul { | |||
list-style: none; | |||
display: grid; | |||
grid-template-columns: 1fr 1fr; | |||
} | |||
li { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
margin: 1rem 0; | |||
&:nth-child(1) .icon { | |||
background-color: var(--orange); | |||
} | |||
&:nth-child(2) .icon { | |||
background-color: var(--blue); | |||
} | |||
&:nth-child(3) .icon { | |||
background-color: var(--red); | |||
} | |||
&:nth-child(4) .icon { | |||
background-color: var(--teal); | |||
} | |||
} | |||
.icon { | |||
width: 4.5rem; | |||
height: 4.5rem; | |||
background-color: var(--grey); | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
border-radius: 1rem; | |||
svg { | |||
fill: white; | |||
width: 2rem; | |||
} | |||
} | |||
.info { | |||
width: calc(100% - 5.5rem); | |||
label, span { | |||
display: block; | |||
} | |||
label { | |||
font-size: 1.2rem; | |||
color: var(--black); | |||
font-weight: 700; | |||
} | |||
span { | |||
font-size: 1rem; | |||
color: var(--grey); | |||
} | |||
} | |||
} | |||
.toggleHolder { | |||
width: 100%; | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
padding: 2rem 0.5rem; | |||
label { | |||
font-size: 1.2rem; | |||
color: var(--black); | |||
font-weight: 600; | |||
} | |||
.toggle { | |||
background-color: #efe6d6; | |||
width: 4rem; | |||
height: 2rem; | |||
border-radius: 3rem; | |||
box-shadow: 0px 0px 10px -5px inset var(--light-grey); | |||
span { | |||
width: 2rem; | |||
height: 2rem; | |||
background-color: var(--red); | |||
border-radius: 50%; | |||
display: block; | |||
transform: scale(0.8); | |||
} | |||
} | |||
} | |||
.publishButton { | |||
width: 9rem; | |||
height: 3.5rem; | |||
background-color: var(--teal); | |||
font-size: 1.2rem; | |||
font-weight: 500; | |||
color: white; | |||
display: block; | |||
margin: 2rem auto; | |||
border: none; | |||
border-radius: 3rem; | |||
position: fixed; | |||
left: calc(50% - 4.5rem); | |||
bottom: 1rem; | |||
z-index: 1; | |||
} |
@@ -0,0 +1,55 @@ | |||
import React from "react"; | |||
import styles from './AddShelf.module.scss'; | |||
import { ReactComponent as CloseIcon } from '../../assets/icons/close.svg'; | |||
import { ReactComponent as PlusIcon } from '../../assets/icons/plus.svg'; | |||
import { userProfileData } from "../home/Home"; | |||
export const AddShelf: React.FC = () => { | |||
return <section className={styles.createShelfModal}> | |||
<header className={styles.modalHeader}> | |||
<h4> Create New Shelf </h4> | |||
<button onClick={() => { window.history.back() }}> | |||
<CloseIcon /> | |||
</button> | |||
</header> | |||
<section className={styles.form}> | |||
<input type="text" placeholder={'Shelf Name'} /> | |||
<textarea placeholder={'Add description'}></textarea> | |||
<section className={styles.Grid}> | |||
<header className={styles.blockHeader}> | |||
<h5> | |||
Choose a category | |||
</h5> | |||
<button> | |||
<PlusIcon /> | |||
</button> | |||
</header> | |||
<ul> | |||
{ userProfileData.categories.map(category => { | |||
return <li key={category.name}> | |||
<div className={styles.icon}> | |||
{ category.icon } | |||
</div> | |||
<div className={styles.info}> | |||
<label> { category.name } </label> | |||
<span> { category.shelves.length } Shelves </span> | |||
</div> | |||
</li> | |||
})} | |||
</ul> | |||
</section> | |||
<section className={styles.toggleHolder}> | |||
<label> Public </label> | |||
<div className={styles.toggle}> | |||
<span></span> | |||
</div> | |||
</section> | |||
<button className={styles.publishButton}> Publish </button> | |||
</section> | |||
</section> | |||
} |
@@ -1,13 +1,11 @@ | |||
.modalPage { | |||
background-color: var(--creamy-white); | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
z-index: 2; | |||
width: 100vw; | |||
height: 100vh; | |||
overflow: auto; | |||
opacity: 0; | |||
position: relative; | |||
animation: fadeIn 0.3s forwards; | |||
@keyframes fadeIn { | |||
@@ -234,13 +232,18 @@ | |||
.shelfList { | |||
list-style: none; | |||
padding: 1rem 2rem; | |||
background-color: var(--creamy-white); | |||
border-top-right-radius: 3rem; | |||
border-top-left-radius: 3rem; | |||
margin: -4rem 0; | |||
min-height: 5rem; | |||
padding: 2rem; | |||
header { | |||
margin: 0 auto 1rem; | |||
h5 { | |||
font-weight: 300; | |||
font-size: 1.2rem; | |||
font-size: 1.3rem; | |||
color: var(--black); | |||
} | |||
} | |||
@@ -313,6 +316,7 @@ | |||
box-shadow: 0px 0px 5px var(--creamy-white); | |||
font-weight: 600; | |||
color: var(--black); | |||
font-size: 1.3rem; | |||
span { | |||
width: 3rem; | |||
@@ -8,14 +8,10 @@ import { ReactComponent as AddIcon } from '../../assets/icons/plus.svg'; | |||
import { IWord } from "../../structure/word"; | |||
import { ALL_WORDS } from "../../data/all-words"; | |||
import { userProfileData } from "../home/Home"; | |||
import { NavLink } from "react-router-dom"; | |||
type OwnProps = { | |||
hideModal: () => void | |||
}; | |||
export const AddWord: React.FC<OwnProps> = (props: OwnProps) => { | |||
const [searchResult, setSearchResult] = useState<Array<IWord>>([]); | |||
export const AddWord: React.FC = () => { | |||
const [searchWordResult, setSearchResult] = useState<Array<IWord>>([]); | |||
const [selectedWord, setSelectedWord] = useState<IWord>(); | |||
const searchWords = (searchWord: string) => { | |||
@@ -32,63 +28,83 @@ export const AddWord: React.FC<OwnProps> = (props: OwnProps) => { | |||
return <section className={styles.modalPage}> | |||
<header className={styles.navHeader}> | |||
<button onClick={props.hideModal}> | |||
<button onClick={() => { | |||
if (selectedWord) { | |||
setSelectedWord(undefined); | |||
} else { | |||
window.history.back() | |||
} | |||
}}> | |||
<ChevronLeft /> | |||
</button> | |||
<h5> Add a Word </h5> | |||
{ !selectedWord && <h5> Add a Word </h5> } | |||
{ selectedWord && <h5> Add to Shelf </h5> } | |||
<figure> | |||
<figure style={{ opacity: selectedWord ? 0 : 1 }}> | |||
{/* eslint-disable-next-line */} | |||
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSERA5Pm3aRBV7AaI8tvpZfzpD24ZgrU1_8NA&usqp=CAU" alt="profile-image" /> | |||
<img src={ userProfileData.image } alt="profile-image" /> | |||
</figure> | |||
</header> | |||
<div className={styles.searchBarHolder}> | |||
<div className={styles.searchBar}> | |||
<input type="text" autoFocus={true} placeholder={'Search and add a word'} onChange={(event) => searchWords(event.currentTarget.value)} /> | |||
<SearchIcon /> | |||
{ !selectedWord && <section> | |||
<div className={styles.searchBarHolder}> | |||
<div className={styles.searchBar}> | |||
<input type="text" autoFocus={true} placeholder={'Search and add a word'} onChange={(event) => searchWords(event.currentTarget.value)} /> | |||
<SearchIcon /> | |||
</div> | |||
</div> | |||
</div> | |||
<ul className={styles.searchResult}> | |||
{ !selectedWord && searchResult.map((word) => { | |||
return <li key={word.name}> | |||
<header> | |||
<h4> { word.name } <span> { word.pronounciation } </span> </h4> | |||
<button> | |||
<SpeakerIcon /> | |||
</button> | |||
</header> | |||
<div className={styles.description}> | |||
<span> { word.grammaticalDetails[0].typeName } </span> | |||
<p> { word.grammaticalDetails[0].description } </p> | |||
</div> | |||
<button className={styles.addButton} onClick={() => setSelectedWord(word)}> <AddIcon /> Add </button> | |||
</li> | |||
}) } | |||
</ul> | |||
{ selectedWord && <ul className={styles.shelfList}> | |||
<header> | |||
<h5> All Shelves </h5> | |||
</header> | |||
{ userProfileData.categories.map((category, categoryIndex) => { | |||
return category.shelves.map((shelf, shelfIndex) => { | |||
return <li key={shelf.name} onClick={() => { | |||
userProfileData.categories[categoryIndex].shelves[shelfIndex].words.push(selectedWord); | |||
}}> | |||
<div className={styles.icon}> | |||
{ category.icon } | |||
</div> | |||
<div className={styles.info}> | |||
<label> { shelf.name } </label> | |||
<span> { category.name } </span> | |||
<ul className={styles.searchResult}> | |||
{ searchWordResult.map((word) => { | |||
return <li key={word.name}> | |||
<header> | |||
<h4> { word.name } <span> { word.pronounciation } </span> </h4> | |||
<button> | |||
<SpeakerIcon /> | |||
</button> | |||
</header> | |||
<div className={styles.description}> | |||
<span> { word.grammaticalDetails[0].typeName } </span> | |||
<p> { word.grammaticalDetails[0].description } </p> | |||
</div> | |||
<button className={styles.addButton} onClick={() => setSelectedWord(word)}> <AddIcon /> Add </button> | |||
</li> | |||
}) | |||
}) } | |||
}) } | |||
</ul> | |||
</section> } | |||
{ selectedWord && <section> | |||
<div className={styles.searchBarHolder}> | |||
<div className={styles.searchBar}> | |||
<input type="text" placeholder={'Search shelves'}/> | |||
<SearchIcon /> | |||
</div> | |||
</div> | |||
<ul className={styles.shelfList}> | |||
<header> | |||
<h5> All Shelves </h5> | |||
</header> | |||
{ userProfileData.categories.map((category, categoryIndex) => { | |||
return category.shelves.map((shelf, shelfIndex) => { | |||
return <li key={shelf.name} onClick={() => { | |||
userProfileData.categories[categoryIndex].shelves[shelfIndex].words.push(selectedWord); | |||
}}> | |||
<div className={styles.icon}> | |||
{ category.icon } | |||
</div> | |||
<div className={styles.info}> | |||
<label> { shelf.name } </label> | |||
<span> { category.name } </span> | |||
</div> | |||
</li> | |||
}) | |||
}) } | |||
<button className={styles.AddShelfButton}> <span> <AddIcon /> </span> Create New Shelf </button> | |||
</ul> } | |||
<NavLink to={'/add-shelf'} className={styles.AddShelfButton}> <span> <AddIcon /> </span> Create New Shelf </NavLink> | |||
</ul> | |||
</section> } | |||
</section> | |||
} |
@@ -4,6 +4,10 @@ | |||
padding: 1.5rem 0; | |||
position: relative; | |||
svg { | |||
transform: scale(0.9); | |||
} | |||
figure { | |||
display: block; | |||
position: absolute; | |||
@@ -2,6 +2,10 @@ | |||
background-color: var(--orange); | |||
text-align: center; | |||
padding: 1rem 0 0.5rem; | |||
svg { | |||
transform: scale(0.9); | |||
} | |||
} | |||
.upfold { | |||
@@ -119,7 +123,7 @@ | |||
} | |||
} | |||
button { | |||
button, a { | |||
background-color: var(--teal); | |||
width: 3rem; | |||
height: 3rem; | |||
@@ -16,8 +16,8 @@ import { ReactComponent as PersonSpeakerIcon } from '../../assets/icons/user-spe | |||
import { ReactComponent as BrainIcon } from '../../assets/icons/bx-brain.svg'; | |||
import { ReactComponent as InternBadge } from '../../assets/icons/intern-badge.svg'; | |||
import { CircularProgressbar } from 'react-circular-progressbar'; | |||
import { AddWord } from "../add-word/AddWord"; | |||
import { IProfile } from "../../structure/profile"; | |||
import { NavLink } from "react-router-dom"; | |||
export var userProfileData : IProfile = { | |||
@@ -61,8 +61,6 @@ export var userProfileData : IProfile = { | |||
}; | |||
export const Home: React.FC = () => { | |||
const [isAddWordModalOpen, setAddWordModalState] = useState<boolean>(false); | |||
return <section className="page"> | |||
<header className={styles.pageHeader}> | |||
@@ -99,10 +97,10 @@ export const Home: React.FC = () => { | |||
</div> | |||
</section> | |||
<div className={styles.searchBar}> | |||
<input type="text" placeholder={'Search and add a word'} onClick={() => setAddWordModalState(true)} /> | |||
<NavLink to={'/add-word'} className={styles.searchBar}> | |||
<input type="text" placeholder={'Search and add a word'} /> | |||
<SearchIcon /> | |||
</div> | |||
</NavLink> | |||
<section className={styles.List}> | |||
<header className={styles.blockHeader}> | |||
@@ -154,9 +152,9 @@ export const Home: React.FC = () => { | |||
<button className={styles.expandButton}> | |||
<ExpandIcon /> | |||
</button> | |||
<button> | |||
<NavLink to={'/add-shelf'}> | |||
<PlusIcon /> | |||
</button> | |||
</NavLink> | |||
</header> | |||
<ul> | |||
{ userProfileData.categories.map(category => { | |||
@@ -210,12 +208,8 @@ export const Home: React.FC = () => { | |||
<span> { category.shelves.length } Shelves </span> | |||
</div> | |||
</li> | |||
})} | |||
})} | |||
</ul> | |||
</section> | |||
{ isAddWordModalOpen && <AddWord hideModal={() => { setAddWordModalState(false); }} /> } | |||
</section> | |||
} |
@@ -12,7 +12,7 @@ export const Tabs: React.FC = () => { | |||
return <section className={styles.tabs}> | |||
<NavLink to={'/home'} activeClassName={styles.active}> | |||
<HomeIcon /> | |||
</NavLink > | |||
</NavLink> | |||
<NavLink to={'/categories'} activeClassName={styles.active}> | |||
<GridIcon /> | |||
</NavLink > | |||