diff options
author | 2021-08-16 21:22:32 +0200 | |
---|---|---|
committer | 2021-08-16 21:24:42 +0200 | |
commit | eb28244fc8f98e28728c7b3f951e102b9cc56590 (patch) | |
tree | 76237e414671730371f87c8e263a18c2d2e8c17f /apps/Notes | |
parent | b43d34c0136552cd2d83258fb5523bb873177908 (diff) | |
download | my_apps-eb28244fc8f98e28728c7b3f951e102b9cc56590.tar.gz my_apps-eb28244fc8f98e28728c7b3f951e102b9cc56590.tar.bz2 my_apps-eb28244fc8f98e28728c7b3f951e102b9cc56590.zip |
styles moved to scss
Diffstat (limited to 'apps/Notes')
-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 |
7 files changed, 315 insertions, 319 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([]) +} |