feat: add notebook navigation (incomplete)

This commit is contained in:
thecodrr
2019-12-06 17:25:28 +05:00
parent 9063d76107
commit a3dbddcc3f
10 changed files with 296 additions and 204 deletions

View File

@@ -4,3 +4,7 @@ import events from "events";
export const db = new Database(StorageInterface);
export const ev = new events.EventEmitter();
export function sendNewNoteEvent() {
ev.emit("onNewNote");
}

View File

@@ -0,0 +1,73 @@
import React from "react";
import { Flex, Box, Text } from "rebass";
import * as Icon from "react-feather";
import { ButtonPressedStyle } from "../../theme";
import Dropdown, { DropdownTrigger, DropdownContent } from "../dropdown";
import Menu from "../menu";
const ListItem = props => (
<Flex
onClick={e => {
if (props.onClick) {
props.onClick();
}
e.stopPropagation();
}}
alignItems="center"
justifyContent="space-between"
py={1}
sx={{
borderRadius: "default",
marginBottom: 2,
borderBottom: "1px solid",
borderBottomColor: "navbg",
cursor: "default",
...ButtonPressedStyle
}}
>
<Box>
<Flex flexDirection="row" justifyContent="space-between">
<Text fontFamily="body" fontSize="title" fontWeight="bold">
{props.title}
</Text>
</Flex>
<Text
display={props.body ? "flex" : "none"}
variant="body"
sx={{ marginBottom: 1 }}
>
{props.body}
</Text>
{props.subBody && props.subBody}
<Text
display={props.info ? "flex" : "none"}
variant="body"
fontSize={12}
color="accent"
>
{props.info}
</Text>
</Box>
<Dropdown
style={{ zIndex: 999 }}
ref={ref => (props.dropdownRefs[props.index] = ref)}
>
<DropdownTrigger>
<Icon.MoreVertical
size={24}
strokeWidth={1.5}
style={{ marginRight: -5 }}
/>
</DropdownTrigger>
<DropdownContent style={{ zIndex: 999, marginLeft: -70 }}>
<Menu
dropdownRef={props.dropdownRefs[props.index]}
menuItems={props.menuItems}
data={props.menuData}
/>
</DropdownContent>
</Dropdown>
</Flex>
);
export default ListItem;

View File

@@ -0,0 +1,49 @@
import React from "react";
import * as Icon from "react-feather";
import TimeAgo from "timeago-react";
import { db, ev } from "../../common";
import { showSnack } from "../snackbar";
import ListItem from "../list-item";
const dropdownRefs = [];
const menuItems = [
{ title: "Favorite", icon: Icon.Heart },
{ title: "Share", icon: Icon.Share2 },
{
title: "Delete",
icon: Icon.Trash,
color: "red",
onClick: note => {
ev.emit("onClearNote", note.dateCreated);
db.deleteNotes([note]).then(
//TODO implement undo
() => {
showSnack("Note deleted!", Icon.Check);
ev.emit("refreshNotes");
}
);
}
}
];
function sendOpenNoteEvent(note) {
ev.emit("onOpenNote", note);
}
const Note = ({ item, index }) => {
const note = item;
return (
<ListItem
title={note.title}
body={note.headline}
index={index}
onClick={sendOpenNoteEvent.bind(this, note)}
info={<TimeAgo datetime={note.dateCreated} />}
menuData={note}
menuItems={menuItems}
dropdownRefs={dropdownRefs}
/>
);
};
export default Note;

View File

@@ -0,0 +1,64 @@
import React from "react";
import { Flex, Text } from "rebass";
import * as Icon from "react-feather";
import ListItem from "../list-item";
const dropdownRefs = [];
const menuItems = [
{ title: "Favorite", icon: Icon.Heart },
{ title: "Share", icon: Icon.Share2 },
{
title: "Delete",
icon: Icon.Trash,
color: "red"
}
];
const Notebook = ({ item, index, onClick, onTopicClick }) => {
const notebook = item;
return (
<ListItem
onClick={onClick}
title={notebook.title}
body={notebook.description}
subBody={
<Flex sx={{ marginBottom: 1 }}>
{notebook.topics.slice(1, 4).map(topic => (
<Flex
onClick={e => {
onTopicClick(notebook, topic);
e.stopPropagation();
}}
key={topic.dateCreated + topic.title}
bg="accent"
px={1}
py={1}
sx={{
marginRight: 1,
borderRadius: "default",
color: "fontSecondary"
}}
>
<Text className="unselectable" variant="body" fontSize={11}>
{topic.title}
</Text>
</Flex>
))}
</Flex>
}
info={
<Text>
{new Date(notebook.dateCreated).toDateString().substring(4)}
<Text as="span" color="fontPrimary">
{" • " + notebook.totalNotes} Notes
</Text>
</Text>
}
dropdownRefs={dropdownRefs}
index={index}
menuData={notebook}
menuItems={menuItems}
/>
);
};
export default Notebook;

View File

@@ -2,7 +2,7 @@ import React from "react";
import { Flex, Box } from "rebass";
import { Input } from "@rebass/forms";
import * as Icon from "react-feather";
import theme, { SHADOW } from "../../theme";
import theme from "../../theme";
import "./search.css";
const Search = props => (
<Flex

View File

@@ -0,0 +1,38 @@
import React from "react";
import { Text } from "rebass";
import * as Icon from "react-feather";
import ListItem from "../list-item";
const dropdownRefs = [];
const menuItems = [
{ title: "Favorite", icon: Icon.Heart },
{ title: "Share", icon: Icon.Share2 },
{
title: "Delete",
icon: Icon.Trash,
color: "red"
}
];
const Topic = ({ item, index, onClick }) => {
const topic = item;
return (
<ListItem
onClick={onClick}
title={topic.title}
info={
<Text>
<Text as="span" color="fontPrimary">
{topic.totalNotes} Notes
</Text>
</Text>
}
index={index}
dropdownRefs={dropdownRefs}
menuData={topic}
menuItems={menuItems}
/>
);
};
export default Topic;

View File

@@ -1,4 +1,4 @@
import React from "react";
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Home from "../views/Home";
import Notebooks from "../views/Notebooks";
@@ -79,10 +79,13 @@ export function goBack(root) {
}
function ThemedComponent(props) {
const [title, setTitle] = useState(props.route.title);
return (
<ThemeProvider theme={theme}>
<Heading fontSize="heading">{props.route.title}</Heading>
{props.route.component && <props.route.component />}
<Heading fontSize="heading">{title}</Heading>
{props.route.component && (
<props.route.component changeTitle={setTitle} />
)}
</ThemeProvider>
);
}

View File

@@ -1,56 +1,22 @@
import React, { useEffect, useState } from "react";
import { Flex, Box, Text } from "rebass";
import { Flex, Text } from "rebass";
import * as Icon from "react-feather";
import { ButtonPressedStyle } from "../theme";
import Dropdown, {
DropdownTrigger,
DropdownContent
} from "../components/dropdown";
import TimeAgo from "timeago-react";
import { db, ev } from "../common";
import { db, ev, sendNewNoteEvent } from "../common";
import { Virtuoso as List } from "react-virtuoso";
import { showSnack } from "../components/snackbar";
import Menu from "../components/menu";
import Button from "../components/button";
import Search from "../components/search";
import Note from "../components/note";
const dropdownRefs = [];
const menuItems = [
{ title: "Favorite", icon: Icon.Heart },
{ title: "Share", icon: Icon.Share2 },
{
title: "Delete",
icon: Icon.Trash,
color: "red",
onClick: note => {
ev.emit("onClearNote", note.dateCreated);
db.deleteNotes([note]).then(
//TODO implement undo
() => {
showSnack("Note deleted!", Icon.Check);
//TODO very crude but works.
Home.onRefresh();
}
);
}
}
];
function sendNewNoteEvent() {
ev.emit("onNewNote");
}
function sendOpenNoteEvent(note) {
ev.emit("onOpenNote", note);
}
function Home() {
const [notes, setNotes] = useState([]);
useEffect(() => {
Home.onRefresh = () => {
function onRefreshNotes() {
setNotes(db.getNotes());
};
Home.onRefresh();
}
onRefreshNotes();
ev.addListener("refreshNotes", onRefreshNotes);
return () => {
Home.onRefresh = undefined;
ev.removeListener("refreshNotes", onRefreshNotes);
};
}, []);
return (
@@ -66,62 +32,7 @@ function Home() {
overflowX: "hidden"
}}
totalCount={notes.length}
item={index => {
const note = notes[index];
return (
<Box
onClick={e => {
sendOpenNoteEvent(note);
e.stopPropagation();
}}
py={3}
sx={{
borderRadius: "default",
marginBottom: 2,
borderBottom: "1px solid",
borderBottomColor: "navbg",
cursor: "default",
...ButtonPressedStyle
}}
>
<Flex flexDirection="row" justifyContent="space-between">
<Text fontFamily="body" fontSize="title" fontWeight="bold">
{note.title}
</Text>
<Dropdown
style={{ zIndex: 999 }}
ref={ref => (dropdownRefs[index] = ref)}
>
<DropdownTrigger>
<Icon.MoreVertical
size={20}
strokeWidth={1.5}
style={{ marginRight: -5 }}
/>
</DropdownTrigger>
<DropdownContent style={{ zIndex: 999, marginLeft: -70 }}>
<Menu
dropdownRef={dropdownRefs[index]}
menuItems={menuItems}
data={note}
/>
</DropdownContent>
</Dropdown>
</Flex>
<Text fontFamily="body" fontSize="body" sx={{ marginTop: 1 }}>
{note.headline}
</Text>
<Text
fontFamily="body"
fontWeight="body"
fontSize={12}
color="accent"
>
<TimeAgo datetime={note.dateCreated} />
</Text>
</Box>
);
}}
item={index => <Note index={index} item={notes[index]} />}
/>
<Button
Icon={Icon.Plus}

View File

@@ -1,48 +1,27 @@
import React, { useState, useEffect } from "react";
import { Flex, Box, Text, Button as RebassButton } from "rebass";
import { Input, Label } from "@rebass/forms";
import { Input } from "@rebass/forms";
import Button from "../components/button";
import * as Icon from "react-feather";
import theme, { ButtonPressedStyle, SHADOW, DIALOG_SHADOW } from "../theme";
import theme, { DIALOG_SHADOW } from "../theme";
import Search from "../components/search";
import Modal from "react-modal";
import { db, ev } from "../common";
import { db } from "../common";
import { showSnack } from "../components/snackbar";
import { Virtuoso as List } from "react-virtuoso";
import Dropdown, {
DropdownTrigger,
DropdownContent
} from "../components/dropdown";
import Menu from "../components/menu";
import Notebook from "../components/notebook";
import Topic from "../components/topic";
import Note from "../components/note";
const inputRefs = [];
const dropdownRefs = [];
const menuItems = [
{ title: "Favorite", icon: Icon.Heart },
{ title: "Share", icon: Icon.Share2 },
{
title: "Delete",
icon: Icon.Trash,
color: "red",
onClick: note => {
ev.emit("onClearNote", note.dateCreated);
db.deleteNotes([note]).then(
//TODO implement undo
async () => {
showSnack("Note deleted!", Icon.Check);
}
);
}
}
];
const Notebooks = props => {
const [open, setOpen] = useState(false);
const [notebooks, setNotebooks] = useState([]);
const [selected, setSelected] = useState({});
useEffect(() => {
Notebooks.onRefresh = () => {
setNotebooks(db.getNotebooks());
console.log(notebooks);
};
Notebooks.onRefresh();
return () => {
@@ -53,6 +32,11 @@ const Notebooks = props => {
<Flex flexDirection="column" flex="1 1 auto">
{notebooks.length > 0 ? (
<Flex flexDirection="column" flex="1 1 auto">
{selected.type === "topic" && (
<Text variant="title" color="accent">
{selected.title}
</Text>
)}
<Search placeholder="Search" />
<List
style={{
@@ -61,85 +45,50 @@ const Notebooks = props => {
height: "auto",
overflowX: "hidden"
}}
totalCount={notebooks.length}
totalCount={
selected.type === "notebook"
? selected.topics.length
: selected.type === "topic"
? selected.notes.length
: notebooks.length
}
item={index => {
const notebook = notebooks[index];
return (
<Box
onClick={e => {
e.stopPropagation();
return selected.type === "notebook" ? (
<Topic
index={index}
item={selected.topics[index]}
onClick={() => {
setSelected(selected.topics[index]);
}}
py={1}
sx={{
borderRadius: "default",
marginBottom: 2,
borderBottom: "1px solid",
borderBottomColor: "navbg",
cursor: "default",
...ButtonPressedStyle
/>
) : selected.type === "topic" ? (
<Note index={index} item={selected.notes[index]} />
) : (
<Notebook
index={index}
item={notebooks[index]}
onClick={() => {
props.changeTitle(notebooks[index].title);
setSelected(notebooks[index]);
}}
>
<Flex flexDirection="row" justifyContent="space-between">
<Text fontFamily="body" fontSize="title" fontWeight="bold">
{notebook.title}
</Text>
<Dropdown
style={{ zIndex: 999 }}
ref={ref => (dropdownRefs[index] = ref)}
>
<DropdownTrigger>
<Icon.MoreVertical
size={20}
strokeWidth={1.5}
style={{ marginRight: -5 }}
/>
</DropdownTrigger>
<DropdownContent style={{ zIndex: 999, marginLeft: -70 }}>
<Menu
dropdownRef={dropdownRefs[index]}
menuItems={menuItems}
data={notebook}
/>
</DropdownContent>
</Dropdown>
</Flex>
<Text variant="body" sx={{ marginBottom: 1 }}>
{notebook.description}
</Text>
<Flex sx={{ marginBottom: 1 }}>
{Object.keys(notebook.topics)
.slice(0, 3)
.map(topic => (
<Flex
bg="accent"
px={1}
py={1}
sx={{
marginRight: 1,
borderRadius: "default",
color: "fontSecondary"
}}
>
<Text variant="body" fontSize={11}>
{topic}
</Text>
</Flex>
))}
</Flex>
<Text variant="body" fontSize={12} color="accent">
{new Date(notebook.dateCreated).toDateString().substring(4)}
<Text as="span" color="fontPrimary">
{" • " + notebook.totalNotes} Notes
</Text>
</Text>
</Box>
onTopicClick={(notebook, topic) => {
props.changeTitle(notebook.title);
setSelected(topic);
}}
/>
);
}}
/>
<Button
Icon={Icon.Plus}
onClick={() => setOpen(true)}
content="Create a notebook"
content={
selected.type === "notebook"
? "Add more topics"
: selected.type === "topic"
? "Make a new note"
: "Create a notebook"
}
/>
</Flex>
) : (
@@ -244,9 +193,9 @@ const CreateNotebookDialog = props => {
Topics (optional):
</Text>
<Box sx={{ maxHeight: 44 * 5, overflowY: "auto", marginBottom: 1 }}>
{topics.map((item, index) => (
{topics.map((value, index) => (
<Flex
key={item + index.toString()}
key={index.toString()}
flexDirection="row"
sx={{ marginBottom: 1 }}
>

View File

@@ -6994,8 +6994,9 @@ normalize-url@^3.0.0, normalize-url@^3.0.1:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
notes-core@../notes-core:
version "0.1.0"
"notes-core@https://github.com/thecodrr/notes-core.git":
version "0.2.0"
resolved "https://github.com/thecodrr/notes-core.git#281414d69b6bdc73b70e5347745ce8b797b55991"
dependencies:
fast-filter "^1.0.4"
fuzzysearch "^1.0.3"