This website is a MVP clone of evernote.com, with Obsidian-inspired styling & layout. Live website is here.
- Notes
- users can create new Notes (note icon on top left)
- users can rename Notes (input field in the top line)
- users can delete Notes (trash can icon on top right)
- users can assign notes to folders (top line dropdown)
- Folders
- users can create new folders (folder icon on top left)
- users can delete folders (trash can icon next to folder name)
- users can rename folders (double click on folder name)
- rename is saved when you unfocus the field (onblur)
- Markdown editor
- users can switch between READ and EDIT mode (toggle in top line)
- in EDIT mode, the text is entered in Markdown format
- in READ mode, the markdown text is rendered
- Autosave of note title and note content
- as the user types the content is automatically patched (with 1 sec debounce)
Feature not implemented (maybe in the future):
- d3 visualizion of node connections (a graph!)
- tokenized (ts-vector) search of note content
Can be viewed in the Project Wiki.
- Requires a
Postgresinstallations - Run the following commands (these instructions are incomplete)
git clone https://github.com/ily123/grafnote cd grafnote npm install npm start - Make sure to create & populate the
.envfile following the example given in.env.example.
- Express backend
- React front-end with Redux state management
- Vanilla CSS
- Example below is of two react functional components: a
FileLinkand aFolderLinknavigation items for theSideBarcomponent.
function FileLink ({ type, payload }) { const dispatch = useDispatch(); return ( <div className={type + '-link'} key={type + payload.id} onClick={() => dispatch(setActiveNoteId(payload.id))} >{payload.title}</div> ); } function FolderLink ({ type, payload }) { const dispatch = useDispatch(); const [title, setTitle] = useState(payload.title); const [readOnly, setReadOnly] = useState(true); if (!payload || !type) return null; const sendNewTitle = () => { setReadOnly(true); dispatch(editFolderTitle(payload.id, title)); }; return ( <div className={type + '-link'} key={type + payload.id} > <i className="fas fa-folder" style={{ paddingRight: '5px' }}></i> <input type="text" value={title} readOnly={readOnly} className={readOnly ? 'inactive' : 'active'} onDoubleClick={() => setReadOnly(false)} onBlur={(e) => sendNewTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)} ></input> <i className="far fa-trash-alt" onClick={() => dispatch(deleteFolder(payload.id))} ></i> </div> ); };
-
Main note display (read mode) read note display
-
Main note display (edit mode) Main note display (edit mode)
-
Note navigation sidebar Note navigation sidebar
- Ilya Novikov
Unlicense (https://en.wikipedia.org/wiki/Unlicense).