| @@ -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 > | |||