diff options
-rw-r--r-- | apps/Notes/Notes.module.scss | 231 | ||||
-rw-r--r-- | apps/Notes/components/Import.js | 88 | ||||
-rw-r--r-- | apps/Notes/components/List.js | 45 | ||||
-rw-r--r-- | apps/Notes/components/ListItem.js | 115 | ||||
-rw-r--r-- | apps/Notes/components/NoteEdit.js | 83 | ||||
-rw-r--r-- | apps/Notes/components/NoteView.js | 31 | ||||
-rw-r--r-- | apps/Notes/helpers/import.js | 41 | ||||
-rw-r--r-- | components/Form.js | 26 | ||||
-rw-r--r-- | components/Header.js | 68 | ||||
-rw-r--r-- | components/Layout.js | 38 | ||||
-rw-r--r-- | pages/_app.js | 2 | ||||
-rw-r--r-- | pages/index.js | 25 | ||||
-rw-r--r-- | pages/verify.js | 33 | ||||
-rw-r--r-- | styles/Main.module.scss | 125 | ||||
-rw-r--r-- | styles/_animations.scss | 4 | ||||
-rw-r--r-- | styles/global.scss | 39 | ||||
-rw-r--r-- | styles/styles.scss | 3 |
17 files changed, 503 insertions, 494 deletions
diff --git a/apps/Notes/Notes.module.scss b/apps/Notes/Notes.module.scss new file mode 100644 index 0000000..e91dab9 --- /dev/null +++ b/apps/Notes/Notes.module.scss @@ -0,0 +1,231 @@ +.notesList { + @keyframes fade-in { + from {opacity: 0;} + to {opacity: 1;} + } + + display: block; + overflow: auto; + width: 100%; + table-layout: fixed; + word-wrap: break-word; + height: 100%; + margin-top: -1em; + padding-top: 1em; + + & > tbody, + & > thead { + display: block; + } + + & tr { + display: flex; + padding: .5em; + } + + & th { + font-weight: 600; + text-align: left; + min-width: 10em; + white-space: nowrap; + padding-bottom: .5em; + cursor: pointer; + line-height: 1.1em; + + &:first-of-type { + width: 99%; + } + + svg { + animation: fade-in .3s; + } + } +} + +.listItem { + td { + min-width: 10em; + white-space: nowrap; + + &:first-of-type { + width: 99%; + display: flex; + padding-right: 1em; + + & > span:nth-child(1) { + text-overflow: ellipsis; + flex-grow: 1; + } + + & > span:nth-child(2), + & > span:nth-child(3) { + margin-left: .5em; + padding: .15em .5em; + line-height: 1em; + border-radius: 50%; + visibility: hidden; + opacity: 0; + font-size: 80%; + transition: .3s opacity linear .3s; + } + } + } + + &:hover { + background: #eee; + border-radius: .5em; + cursor: pointer; + + & > td:first-of-type > span:nth-child(2), + & > td:first-of-type > span:nth-child(3) { + color: #666; + visibility: visible; + opacity: 1; + } + } + + + & > td:first-of-type > span:nth-child(2):hover { + color: #333; + background-color: #deffde; + } + + & > td:first-of-type > span:nth-child(3):hover { + background-color: #ffdede; + color: #333; + } +} + +.noteView { + @keyframes fade-in { + from {opacity: 0;} + to {opacity: 1;} + } + + background: white; + padding: 1rem; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + animation: fade-in .3s; + + h2 { + font-size: 1.25em; + font-weight: 600; + padding: 1rem; + user-select: text; + } + + p { + padding: 0 1rem 1rem; + white-space: pre-line; + user-select: text; + } +} + +.noteEdit { + @keyframes fade-in { + from {opacity: 0;} + to {opacity: 1;} + } + + display: flex; + flex-direction: column; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: white; + padding: 1em 1em 2em; + animation: fade-in .3s; + + h2 { + font-size: 1.2em; + margin-bottom: .5em; + } + + form { + display: flex; + flex-direction: column; + width: 100%; + flex-grow: 1; + + & > div { + text-align: right; + } + } + + input[type=text] { + margin-bottom: .5rem; + height: 3rem; + border: none; + padding: 0.5rem; + font-size: 1rem; + // font-weight: 600; + border: 1px dashed #666; + + &:placeholder { + font: inherit; + } + } + + textarea { + font-size: 1rem; + flex-grow: 1; + resize: none; + height: 100%; + width: 100%; + border: none; + border: 1px dashed #666; + padding: 0.5rem; + + &:placeholder { + font: inherit; + } + } +} + +.import { + @keyframes fade-in { + from {opacity: 0;} + to {opacity: 1;} + } + + background: white; + padding: 1rem; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + animation: fade-in .3s; + + form { + padding: 1em; + } + + input[type=file] { + display: none; + } + + label { + display: inline-block; + cursor: pointer; + } + + li { + padding: .25em; + } + + p { + padding: 1em 0; + } + + .fa-check ~ span { + color: green; + } +} + diff --git a/apps/Notes/components/Import.js b/apps/Notes/components/Import.js index 71c08d6..2e2e5c7 100644 --- a/apps/Notes/components/Import.js +++ b/apps/Notes/components/Import.js @@ -1,58 +1,21 @@ +import styles from '../Notes.module.scss' import React, {useState} from 'react' import fetchJson from 'lib/fetchJson' import useNotes from '../hooks/useNotes' -import {faCheck, faTimes} from '@fortawesome/free-solid-svg-icons' -import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' +import {state, color, handleImport, handleChange} from '../helpers/import' const Import = ({action, setAction}) => { const [files, setFiles] = useState() const [done, setDone] = useState([]) const {mutateNotes} = useNotes() - const state = i => done[i] && <span><FontAwesomeIcon icon={done[i] === 1 ? faCheck : faTimes} /></span> - - const readFileAsText = (file) => new Promise((resolve,reject) => { - let fr = new FileReader() - - fr.onload = () => resolve(fr.result) - fr.onerror = () => reject(fr) - - fr.readAsText(file) - }) - - const handleImport = async e => { - e.preventDefault(); - - Array.from(files).forEach(async (file, i) => { - const title = file.name.replace(/\.[^/.]+$/, "") - const content = await readFileAsText(file); - - try { - const notes = await fetchJson('/api/notes', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({title, content}), - }) - if (i === files.length - 1) await mutateNotes(notes) - setDone((prev) => ({...prev, [i]: 1})) - } catch (e) { - setDone((prev) => ({...prev, [i]: 0})) - } - }) - } - - const handleChange = e => { - setFiles(e.currentTarget.files) - setDone([]) - } - return ( - <section> + <section className={styles.import}> <div className='window__submenu'> - <div onClick={() => { setAction('') }}>Back</div> + <div onClick={() => {setAction('')}}>Back</div> </div> <div className='window__scroll'> - <form onSubmit={handleImport}> + <form onSubmit={e => handleImport(e, files, mutateNotes, setDone)}> Import new notes: <div> <label className="window__button"> @@ -62,7 +25,7 @@ const Import = ({action, setAction}) => { type="file" multiple="multiple" accept="text/plain" - onChange={handleChange} + onChange={e => handleChange(e, setFiles, setDone)} /> </label> </div> @@ -70,7 +33,7 @@ const Import = ({action, setAction}) => { <> <p>Notes to import:</p> <ul> - {[...files].map((f, i) => <li key={f.name}>{f.name} {state(i)}</li>)} + {[...files].map((f, i) => <li style={color(done[i])} key={f.name}>{f.name} {state(done[i])}</li>)} </ul> { done.length === 0 @@ -81,43 +44,6 @@ const Import = ({action, setAction}) => { )} </form> </div> - <style jsx>{` - section { - background: white; - padding: 1rem; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - animation: fade-in .3s; - } - - form { - padding: 1em; - } - - input[type=file] { - display: none; - } - - label { - display: inline-block; - cursor: pointer; - } - - ul { - list-style: disc inside none; - } - - li { - padding: .25em; - } - - p { - padding: 1em 0; - } - `}</style> </section> ) } diff --git a/apps/Notes/components/List.js b/apps/Notes/components/List.js index ec19639..b9fde02 100644 --- a/apps/Notes/components/List.js +++ b/apps/Notes/components/List.js @@ -1,8 +1,8 @@ +import styles from '../Notes.module.scss' import React, {useState, useEffect, useRef} from 'react' import useUser from 'lib/useUser' import useNotes from '../hooks/useNotes' import useSort from '../hooks/useSort' -import fetchJson from 'lib/fetchJson' import {Layout} from 'components' import ListItem from './ListItem' import Actions from './Actions' @@ -33,12 +33,12 @@ const List = () => { <div onClick={() => setAction('import')}>Import</div> <div onClick={() => {}}>Export</div> </div> - <table className="list"> + <table className={styles.notesList}> <thead> <tr> - <th className='list__title' onClick={() => sortBy(1)}>Title {sortedBy(1)}</th> - <th className='list__date' onClick={() => sortBy(2)}>Created {sortedBy(2)}</th> - <th className='list__date' onClick={() => sortBy(3)}>Modified {sortedBy(3)}</th> + <th onClick={() => sortBy(1)}>Title {sortedBy(1)}</th> + <th onClick={() => sortBy(2)}>Created {sortedBy(2)}</th> + <th onClick={() => sortBy(3)}>Modified {sortedBy(3)}</th> </tr> </thead> <tbody> @@ -68,41 +68,6 @@ const List = () => { /> ) } - <style jsx>{` - table { - display: block; - overflow: auto; - width: 100%; - table-layout: fixed; - word-wrap: break-word; - height: 100%; - margin-top: -1em; - padding-top: 1em; - } - - tbody, thead { - display: block; - } - - tr { - display: flex; - padding: 0 .5em; - } - - th { - font-weight: 600; - text-align: left; - min-width: 10em; - white-space: nowrap; - padding-bottom: .5em; - cursor: pointer; - line-height: 1.1em; - } - - th:first-of-type { - width: 99%; - } - `}</style> </> ) } diff --git a/apps/Notes/components/ListItem.js b/apps/Notes/components/ListItem.js index 42d67c0..5d3b91f 100644 --- a/apps/Notes/components/ListItem.js +++ b/apps/Notes/components/ListItem.js @@ -1,3 +1,4 @@ +import styles from '../Notes.module.scss' import React, { useContext } from 'react' import fetchJson from 'lib/fetchJson' import {getNote, removeNote} from '../helpers/noteActions.js' @@ -22,97 +23,33 @@ const ListItem = ({note, setAction, setFetchedNote}) => { } return ( - <> - <tr key={note._id}> - <td - onClick={() => handleNoteAction('showNote', note)} + <tr className={styles.listItem} key={note._id}> + <td + onClick={() => handleNoteAction('showNote', note)} + > + <span>{`${note.title}`}</span> + <span + onClick={e => handleNoteAction('editNote', note, e)} > - <span>{`${note.title}`}</span> - <span - onClick={e => handleNoteAction('editNote', note, e)} - > - <FontAwesomeIcon icon={faEdit} /> - </span> - <span - onClick={(e) => removeNote(e, note._id, mutateNotes, setPopup, setAction)} - > - <FontAwesomeIcon icon={faTrash} /> - </span> - </td> - <td - onClick={() => handleNoteAction('showNote', note)} + <FontAwesomeIcon icon={faEdit} /> + </span> + <span + onClick={(e) => removeNote(e, note._id, mutateNotes, setPopup, setAction)} > - {datestring(note.created_at)} - </td> - <td - onClick={() => handleNoteAction('showNote', note)} - > - {datestring(note.updated_at)} - </td> - </tr> - <style jsx>{` - tr { - display: flex; - padding: .5em; - } - - td { - min-width: 10em; - white-space: nowrap; - } - - td:first-of-type { - width: 99%; - display: flex; - padding-right: 1em; - } - - td:first-of-type > span:nth-child(2), - td:first-of-type > span:nth-child(3) { - margin-left: .5em; - padding: .15em .5em; - line-height: 1em; - border-radius: 50%; - visibility: hidden; - opacity: 0; - font-size: 80%; - transition: .3s opacity linear .3s; - } - - td:first-of-type > span:nth-child(1) { - text-overflow: ellipsis; - flex-grow: 1; - } - - // td:first-of-type > span:nth-child(2) { - // margin-left: 1.25em; - // } - - tr:hover { - background: #eee; - border-radius: .5em; - cursor: pointer; - } - - tr:hover > td:first-of-type > span:nth-child(2), - tr:hover > td:first-of-type > span:nth-child(3) { - color: #666; - visibility: visible; - opacity: 1; - } - - tr > td:first-of-type > span:nth-child(2):hover { - color: #333; - background-color: #deffde; - } - - tr > td:first-of-type > span:nth-child(3):hover { - background-color: #ffdede; - color: #333; - } - - `}</style> - </> + <FontAwesomeIcon icon={faTrash} /> + </span> + </td> + <td + onClick={() => handleNoteAction('showNote', note)} + > + {datestring(note.created_at)} + </td> + <td + onClick={() => handleNoteAction('showNote', note)} + > + {datestring(note.updated_at)} + </td> + </tr> ) } diff --git a/apps/Notes/components/NoteEdit.js b/apps/Notes/components/NoteEdit.js index b568aa9..42eee46 100644 --- a/apps/Notes/components/NoteEdit.js +++ b/apps/Notes/components/NoteEdit.js @@ -1,3 +1,4 @@ +import styles from '../Notes.module.scss' import React, {useState, useContext} from 'react' import Context from 'context'; import fetchJson from 'lib/fetchJson' @@ -24,11 +25,10 @@ const NoteEdit = ({action, setAction, fetchedNote}) => { // } return ( - <div className='note'> + <div className={styles.noteEdit}> <h2>{fetchedNote ? 'Edit note:' : 'Add new note:'}</h2> - <form onSubmit={handleSubmit} className='note__form'> + <form onSubmit={handleSubmit}> <input - className='note__title' name='title' type='text' placeholder='Title' @@ -36,7 +36,6 @@ const NoteEdit = ({action, setAction, fetchedNote}) => { /> <textarea required - className='note__text' placeholder='Note' name='content' defaultValue={fetchedNote ? fetchedNote.content : ''} @@ -56,82 +55,6 @@ const NoteEdit = ({action, setAction, fetchedNote}) => { </div> </form> <style jsx>{` - .note { - display: flex; - flex-direction: column; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: white; - padding: 1em 1em 2em; - animation: fade-in .3s; - } - - @keyframes fade-in { - from {opacity: 0;} - to {opacity: 1;} - } - - h2 { - font-size: 1.2em; - margin-bottom: .5em; - } - - .note__form { - display: flex; - flex-direction: column; - width: 100%; - flex-grow: 1; - } - - .note__title { - margin-bottom: .5rem; - height: 3rem; - border: none; - padding: 0.5rem; - font-size: 1rem; - // font-weight: 600; - border: 1px dashed #666; - } - - .note__title:placeholder { - font: inherit; - } - - .note__text { - font-size: 1rem; - flex-grow: 1; - resize: none; - height: 100%; - width: 100%; - border: none; - border: 1px dashed #666; - padding: 0.5rem; - } - - .note__text:placeholder { - font: inherit; - } - - .note__close { - position: absolute; - top: 8px; - right: 15px; - transform: rotate(45deg); - font-size: 26px; - cursor: pointer; - transition: .3s transform; - } - - .note__close:hover { - transform: rotate(135deg); - } - - .note__buttons { - text-align: right; - } `}</style> </div> ) diff --git a/apps/Notes/components/NoteView.js b/apps/Notes/components/NoteView.js index 1735579..7d93b16 100644 --- a/apps/Notes/components/NoteView.js +++ b/apps/Notes/components/NoteView.js @@ -1,3 +1,4 @@ +import styles from '../Notes.module.scss' import React, {useContext} from 'react'; import Context from 'context'; import useNotes from '../hooks/useNotes' @@ -17,7 +18,7 @@ const NoteView = ({fetchedNote, setFetchedNote, setAction}) => { const {_id, content, title} = fetchedNote return ( - <section> + <section className={styles.noteView}> <div className='window__submenu'> <div onClick={() => { setFetchedNote(); setAction('') }}>Back</div> <div onClick={() => copyToClipboard(content, setPopup)}>Copy</div> @@ -30,34 +31,6 @@ const NoteView = ({fetchedNote, setFetchedNote, setAction}) => { <p>{content}</p> </div> <style jsx>{` - section { - background: white; - padding: 1rem; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - animation: fade-in .3s; - } - - @keyframes fade-in { - from {opacity: 0;} - to {opacity: 1;} - } - - h2 { - font-size: 1.25em; - font-weight: 600; - padding: 1rem; - user-select: text; - } - - p { - padding: 0 1rem 1rem; - white-space: pre-line; - user-select: text; - } `}</style> </section> ) diff --git a/apps/Notes/helpers/import.js b/apps/Notes/helpers/import.js new file mode 100644 index 0000000..b05a458 --- /dev/null +++ b/apps/Notes/helpers/import.js @@ -0,0 +1,41 @@ +import fetchJson from 'lib/fetchJson' +import {faCheck, faTimes} from '@fortawesome/free-solid-svg-icons' +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' + +export const state = s => s && <span><FontAwesomeIcon icon={s === 1 ? faCheck : faTimes} /></span> +export const color = s => s && {color: s === 1 ? 'green' : 'brown'} + +const readFileAsText = (file) => new Promise((resolve,reject) => { + let fr = new FileReader() + + fr.onload = () => resolve(fr.result) + fr.onerror = () => reject(fr) + + fr.readAsText(file) +}) + +export const handleImport = async (e, files, mutateNotes, setDone) => { + e.preventDefault(); + + Array.from(files).forEach(async (file, i) => { + const title = file.name.replace(/\.[^/.]+$/, "") + const content = await readFileAsText(file); + + try { + const notes = await fetchJson('/api/notes', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({title, content}), + }) + if (i === files.length - 1) await mutateNotes(notes) + setDone((prev) => ({...prev, [i]: 1})) + } catch (e) { + setDone((prev) => ({...prev, [i]: 0})) + } + }) +} + +export const handleChange = (e, setFiles, setDone) => { + setFiles(e.currentTarget.files) + setDone([]) +} diff --git a/components/Form.js b/components/Form.js index fc14a75..f41c958 100644 --- a/components/Form.js +++ b/components/Form.js @@ -1,8 +1,9 @@ +import styles from 'styles/Main.module.scss' import React from 'react' import PropTypes from 'prop-types' const Form = ({errorMessage, onSubmit, isLogin}) => ( - <form className='window window--popup' onSubmit={onSubmit}> + <form className={`window window--popup ${styles.userForm}`} onSubmit={onSubmit}> <div className="window__content--popup"> {isLogin ? 'Login to access your notes' : 'Register new user'} </div> @@ -12,29 +13,6 @@ const Form = ({errorMessage, onSubmit, isLogin}) => ( <input className='window__button' type="submit" value={isLogin ? 'Login' : 'Register'} /> {errorMessage && <p className="error">{errorMessage}</p>} - - <style jsx>{` - form, - label { - display: flex; - flex-flow: column; - } - label > span { - font-weight: 600; - } - input[type=email], - input[type=password] { - padding: .5em; - margin: .5em 0; - border: 1px solid #ccc; - border-radius: .25px; - } - .error { - text-align: center; - color: brown; - margin: 1rem 0 0; - } - `}</style> </form> ) diff --git a/components/Header.js b/components/Header.js index 4302af9..3d1f471 100644 --- a/components/Header.js +++ b/components/Header.js @@ -1,3 +1,4 @@ +import styles from 'styles/Main.module.scss' import React, {useState} from 'react' import Link from 'next/link' import useUser from 'lib/useUser' @@ -20,9 +21,9 @@ const Header = ({apps, setApps}) => { } return ( - <header> + <header className={styles.header}> <nav> - <ul className='header__apps'> + <ul> { apps && apps.map(app => ( <li @@ -53,12 +54,17 @@ const Header = ({apps, setApps}) => { {user?.isLoggedIn && ( <li> <p - className='user-menu' + className={styles.user} onClick={() => setUserMenu(!userMenu)} >{user?.email}</p> { userMenu && ( - <ul className='user-submenu'> + <ul className={styles.submenu}> + <li> + <span onClick={() => {}}> + Settings + </span> + </li> <li> <a href="/api/logout" onClick={handleLogout}> Logout @@ -71,60 +77,6 @@ const Header = ({apps, setApps}) => { )} </ul> </nav> - <style jsx>{` - header { - height: 2em; - padding: 0.5rem; - background-color: rgba(255, 255, 255, 0.4); - border-bottom: 1px solid rgba(255, 255, 255, 0.5) - } - - nav { - display: flex; - } - - .header__apps { - flex-grow: 1; - overflow: auto; - } - - li { - margin-left: 1em; - margin-right: 1em; - display: inline-block; - cursor: pointer; - } - - .header__separator { - margin-right: auto; - } - - a { - color: #333; - font-weight: 600; - text-decoration: none; - align-items: center; - } - - .user-menu { - font-weight: 600; - cursor: pointer; - position: relative; - } - - .user-submenu { - position: absolute; - right: 0; - top: 2.1em; - width: 10em; - padding: .5em; - background-color: rgba(255, 255, 255, .9); - border: 1px solid rgba(255, 255, 255, .5) - } - .user-submenu a { - text-align: right; - } - `}</style> </header> ) } diff --git a/components/Layout.js b/components/Layout.js index f77ea0c..8b6cf87 100644 --- a/components/Layout.js +++ b/components/Layout.js @@ -18,44 +18,6 @@ const Layout = ({ children, apps, setApps}) => { </main> <Header apps={apps} setApps={setApps} /> <Popup popup={popup} /> - <style jsx global>{` - main { - position: fixed; - top: 2em; - left: 0; - bottom: 0; - right: 0; - } - - body { - margin: 0; - color: #222; - height: 100vh; - overflow: hidden; - - background: #50a3a2; - background: -webkit-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); - background: -moz-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); - background: -o-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); - background: linear-gradient(to bottom right, #50a3a2 0%, #53e3a6 100%); - - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, - 'Helvetica Neue', Arial, Noto Sans, sans-serif, 'Apple Color Emoji', - 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; - } - - textarea, input { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, - 'Helvetica Neue', Arial, Noto Sans, sans-serif, 'Apple Color Emoji', - 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; - } - - .container { - margin: 1.5rem auto; - padding-left: 2rem; - padding-right: 2rem; - } - `}</style> </Context.Provider> ) } diff --git a/pages/_app.js b/pages/_app.js index 5196b22..4625291 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,6 +1,6 @@ import { SWRConfig } from 'swr' import fetchJson from 'lib/fetchJson' -import '/styles/styles.scss' +import '/styles/global.scss' function MyApp({Component, pageProps}) { return ( diff --git a/pages/index.js b/pages/index.js index 9df0acf..52421ea 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,3 +1,4 @@ +import styles from 'styles/Main.module.scss' import React, { useState ,useRef } from 'react' import useUser from 'lib/useUser' import {Layout, App} from 'components' @@ -21,7 +22,11 @@ const Home = () => { <> { Object.keys(appList).map(appName => ( - <div key={`${appName}_icon`} onClick={() => open(appName, apps, setApps)}> + <div + key={`${appName}_icon`} + className={styles.icon} + onClick={() => open(appName, apps, setApps)} + > <img src={`./${appName.toLowerCase()}.svg`} alt={`${appName} Icon`} /> <p>{appName}</p> </div> @@ -41,24 +46,6 @@ const Home = () => { </App> ); })} - <style jsx>{` - div { - text-decoration: none; - display: inline-block; - width: 4em; - cursor: pointer; - } - - img { - width: 4em; - } - - p { - padding-top: .5em; - text-align: center; - color: #333; - } - `}</style> </> </Layout> ) diff --git a/pages/verify.js b/pages/verify.js index 1d89e22..9d92390 100644 --- a/pages/verify.js +++ b/pages/verify.js @@ -1,3 +1,4 @@ +import styles from 'styles/Main.module.scss' import {useState, useEffect} from 'react' import {useRouter} from 'next/router' import useUser from 'lib/useUser' @@ -52,12 +53,12 @@ const Verify = () => { <p>Loading...</p> </div> ) : ( - <div className="window window--popup"> + <div className={`window window--popup ${styles.verify}`}> <p>One last step missing</p> <p>{`To start using Notes App type the verification code we sent to your email (${user.email}):`}</p> <form onSubmit={handleKey}> <input type="text" placeholder="Verification key" name="key" /> - <button className="window__button" type="submit">Send</button> + <button className="window__button" type="submit">Verify</button> </form> { sending @@ -69,39 +70,15 @@ const Verify = () => { <p>Mail was successfully sent again, check your mailbox!</p> ) : ( <p>If you didn't get verification email - <span className="verify__sendEmail" onClick={handleSendMail}>send it again</span>. + <span className={styles.email} onClick={handleSendMail}>send it again</span>. </p> ) ) } - {errorMsg && <p className="verify__errorMsg">{errorMsg}</p>} + {errorMsg && <p className={styles.error}>{errorMsg}</p>} </div> ) } - <style jsx>{` - form { - padding .5em 0; - } - - input { - padding: .75em; - border: 1px dashed #333; - border-radius: .5em; - } - - p { - line-height: 1.33; - padding: .5em 0; - } - - .verify__sendEmail { - color: blue; - cursor: pointer; - } - .verify__errorMsg { - color: brown; - } - `}</style> </Layout> ) } diff --git a/styles/Main.module.scss b/styles/Main.module.scss new file mode 100644 index 0000000..15be72a --- /dev/null +++ b/styles/Main.module.scss @@ -0,0 +1,125 @@ +.icon { + text-decoration: none; + display: inline-block; + width: 4em; + cursor: pointer; + + img { + width: 4em; + } + + p { + padding-top: .5em; + text-align: center; + color: #333; + } +} + +.userForm { + label, + & { + display: flex; + flex-flow: column; + } + + label > span { + font-weight: 600; + } + + input[type=email], + input[type=password] { + padding: .5em; + margin: .5em 0; + border: 1px solid #ccc; + border-radius: .25px; + } + .error { + text-align: center; + color: brown; + margin: 1rem 0 0; + } +} + +.header { + height: 2em; + padding: 0.5rem; + background-color: rgba(255, 255, 255, 0.4); + border-bottom: 1px solid rgba(255, 255, 255, 0.5); + + nav { + display: flex; + + & > ul:first-of-type { + flex-grow: 1; + overflow: auto; + } + } + + li { + margin-left: 1em; + margin-right: 1em; + display: inline-block; + cursor: pointer; + + & > span, + & > a { + color: #333; + font-weight: 600; + text-decoration: none; + align-items: center; + } + } +} + +.user { + font-weight: 600; + cursor: pointer; + position: relative; + +} + +.submenu { + position: absolute; + right: 4px; + top: 2.1em; + width: 10em; + padding: .5em; + background-color: rgba(255, 255, 255, .9); + border: 1px solid rgba(255, 255, 255, .5); + + & > li { + margin: 0; + padding:.5em; + width: 100%; + line-height: em; + + &:hover { + background-color: #ddd; + } + } +} + +.verify { + & > form { + padding: .5em 0; + + & > input { + padding: .75em; + border: 1px dashed #333; + border-radius: .5em; + } + } + + p { + line-height: 1.33; + padding: .5em 0; + } + + .email { + color: blue; + cursor: pointer; + } + .error { + color: brown; + } +} diff --git a/styles/_animations.scss b/styles/_animations.scss deleted file mode 100644 index b195c60..0000000 --- a/styles/_animations.scss +++ /dev/null @@ -1,4 +0,0 @@ -@keyframes fade-in { - from {opacity: 0;} - to {opacity: 1;} -} diff --git a/styles/global.scss b/styles/global.scss new file mode 100644 index 0000000..222bc44 --- /dev/null +++ b/styles/global.scss @@ -0,0 +1,39 @@ +@import "reset"; +@import "window"; + +main { + position: fixed; + top: 2em; + left: 0; + bottom: 0; + right: 0; +} + +body { + margin: 0; + color: #222; + height: 100vh; + overflow: hidden; + + background: #50a3a2; + background: -webkit-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); + background: -moz-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); + background: -o-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); + background: linear-gradient(to bottom right, #50a3a2 0%, #53e3a6 100%); + + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, Noto Sans, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} + +textarea, input { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, Noto Sans, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} + +.container { + margin: 1.5rem auto; + padding-left: 2rem; + padding-right: 2rem; +} diff --git a/styles/styles.scss b/styles/styles.scss deleted file mode 100644 index 73ef2ee..0000000 --- a/styles/styles.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import "reset"; -@import "animations"; -@import "window"; |