From 3d67c32500cb6415250960d12affad297d6572dc Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Fri, 20 Oct 2017 00:27:01 -0700 Subject: [PATCH 01/20] Prefetch documents on hover --- .../Layout/components/SidebarCollections.js | 109 ++++++++++-------- frontend/stores/DocumentsStore.js | 8 +- 2 files changed, 67 insertions(+), 50 deletions(-) diff --git a/frontend/components/Layout/components/SidebarCollections.js b/frontend/components/Layout/components/SidebarCollections.js index a1cf5aa2d..3d71e41ad 100644 --- a/frontend/components/Layout/components/SidebarCollections.js +++ b/frontend/components/Layout/components/SidebarCollections.js @@ -14,6 +14,7 @@ import CollectionMenu from 'menus/CollectionMenu'; import CollectionsStore from 'stores/CollectionsStore'; import UiStore from 'stores/UiStore'; import Document from 'models/Document'; +import DocumentsStore from 'stores/DocumentsStore'; import { type NavigationNode } from 'types'; type Props = { @@ -123,6 +124,7 @@ type Props = { } type DocumentLinkProps = { + documents: DocumentsStore, document: NavigationNode, history: Object, activeDocument: ?Document, @@ -130,58 +132,69 @@ type DocumentLinkProps = { depth: number, }; -const DocumentLink = observer( - ({ - document, - activeDocument, - activeDocumentRef, - depth, - }: DocumentLinkProps) => { - const isActiveDocument = - activeDocument && activeDocument.id === document.id; - const showChildren = - activeDocument && - (activeDocument.pathToDocument - .map(entry => entry.id) - .includes(document.id) || - isActiveDocument); +const DocumentLink = inject('documents')( + observer( + ({ + documents, + document, + activeDocument, + activeDocumentRef, + depth, + }: DocumentLinkProps) => { + const isActiveDocument = + activeDocument && activeDocument.id === document.id; + const showChildren = + activeDocument && + (activeDocument.pathToDocument + .map(entry => entry.id) + .includes(document.id) || + isActiveDocument); - return ( - - { + event.stopPropagation(); + event.preventDefault(); + documents.prefetchDocument(document.id); + }; + + return ( + - 0} - expanded={showChildren} + - {document.title} - - + 0} + expanded={showChildren} + > + {document.title} + + - {showChildren && - - {document.children && - document.children.map(childDocument => ( - - ))} - } - - ); - } + {showChildren && + + {document.children && + document.children.map(childDocument => ( + + ))} + } + + ); + } + ) ); const CollectionAction = styled.a` diff --git a/frontend/stores/DocumentsStore.js b/frontend/stores/DocumentsStore.js index 9ddf8925f..9d30926a9 100644 --- a/frontend/stores/DocumentsStore.js +++ b/frontend/stores/DocumentsStore.js @@ -111,8 +111,12 @@ class DocumentsStore extends BaseStore { return data.map(documentData => documentData.id); }; - @action fetch = async (id: string): Promise<*> => { - this.isFetching = true; + @action prefetchDocument = async (id: string) => { + if (!this.getById(id)) this.fetch(id, true); + }; + + @action fetch = async (id: string, prefetch: boolean): Promise<*> => { + if (!prefetch) this.isFetching = true; try { const res = await client.post('/documents.info', { id }); From b92bc9cf78998bfd4a85f0ad6ed7435dc476b78e Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Fri, 20 Oct 2017 21:59:29 -0700 Subject: [PATCH 02/20] Fixes: Link paste behavior --- .../components/Toolbar/components/LinkToolbar.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js index e34d04b4b..8fcd8ddb2 100644 --- a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js +++ b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js @@ -109,16 +109,15 @@ class LinkToolbar extends Component { save = (href: string) => { href = href.trim(); - const transform = this.props.state.transform(); - transform.unwrapInline('link'); + const { state } = this.props; + const transform = state.transform(); - if (href) { - const data = { href }; - transform.wrapInline({ type: 'link', data }); + if (state.selection.isExpanded) { + transform.unwrapInline('link'); + if (href) transform.wrapInline({ type: 'link', data: { href } }); } - const state = transform.apply(); - this.props.onChange(state); + this.props.onChange(transform.apply()); this.props.onBlur(); }; From 56d27400ac421bd9410db781cfb72db56e77f5d2 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 21 Oct 2017 12:08:33 -0700 Subject: [PATCH 03/20] New Icons --- frontend/components/Icon/BackIcon.js | 23 +++++++++++++++++ frontend/components/Icon/BoldIcon.js | 3 +-- frontend/components/Icon/BulletedListIcon.js | 3 +-- frontend/components/Icon/ChevronIcon.js | 3 +-- frontend/components/Icon/CodeIcon.js | 4 +-- frontend/components/Icon/CollapsedIcon.js | 20 +++++++++++++++ frontend/components/Icon/CollectionIcon.js | 25 +++++++++++++++++++ frontend/components/Icon/DocumentIcon.js | 23 +++++++++++++++++ frontend/components/Icon/EditIcon.js | 20 +++++++++++++++ frontend/components/Icon/Heading1Icon.js | 4 +-- frontend/components/Icon/Heading2Icon.js | 3 +-- frontend/components/Icon/HomeIcon.js | 20 +++++++++++++++ .../components/Icon/HorizontalRuleIcon.js | 20 +++++++++++++++ frontend/components/Icon/ImageIcon.js | 20 +++++++++++++++ frontend/components/Icon/ItalicIcon.js | 3 +-- frontend/components/Icon/LinkIcon.js | 3 +-- frontend/components/Icon/OrderedListIcon.js | 3 +-- frontend/components/Icon/SearchIcon.js | 20 +++++++++++++++ frontend/components/Icon/StarredIcon.js | 20 +++++++++++++++ frontend/components/Icon/StrikethroughIcon.js | 3 +-- frontend/components/Icon/TodoListIcon.js | 20 +++++++++++++++ frontend/components/Icon/UnderlinedIcon.js | 21 ---------------- frontend/menus/BlockMenu.js | 13 ++++++---- 23 files changed, 251 insertions(+), 46 deletions(-) create mode 100644 frontend/components/Icon/BackIcon.js create mode 100644 frontend/components/Icon/CollapsedIcon.js create mode 100644 frontend/components/Icon/CollectionIcon.js create mode 100644 frontend/components/Icon/DocumentIcon.js create mode 100644 frontend/components/Icon/EditIcon.js create mode 100644 frontend/components/Icon/HomeIcon.js create mode 100644 frontend/components/Icon/HorizontalRuleIcon.js create mode 100644 frontend/components/Icon/ImageIcon.js create mode 100644 frontend/components/Icon/SearchIcon.js create mode 100644 frontend/components/Icon/StarredIcon.js create mode 100644 frontend/components/Icon/TodoListIcon.js delete mode 100644 frontend/components/Icon/UnderlinedIcon.js diff --git a/frontend/components/Icon/BackIcon.js b/frontend/components/Icon/BackIcon.js new file mode 100644 index 000000000..bc082e7fd --- /dev/null +++ b/frontend/components/Icon/BackIcon.js @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function BackIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/BoldIcon.js b/frontend/components/Icon/BoldIcon.js index 011686bda..284d8daa8 100644 --- a/frontend/components/Icon/BoldIcon.js +++ b/frontend/components/Icon/BoldIcon.js @@ -13,8 +13,7 @@ export default function BoldIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + ); diff --git a/frontend/components/Icon/BulletedListIcon.js b/frontend/components/Icon/BulletedListIcon.js index b87afa195..01be8d1f8 100644 --- a/frontend/components/Icon/BulletedListIcon.js +++ b/frontend/components/Icon/BulletedListIcon.js @@ -13,8 +13,7 @@ export default function BulletedListIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + ); diff --git a/frontend/components/Icon/ChevronIcon.js b/frontend/components/Icon/ChevronIcon.js index 88453bbce..65e59b550 100644 --- a/frontend/components/Icon/ChevronIcon.js +++ b/frontend/components/Icon/ChevronIcon.js @@ -13,8 +13,7 @@ export default function NextIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + ); diff --git a/frontend/components/Icon/CodeIcon.js b/frontend/components/Icon/CodeIcon.js index 1116cb68b..246492684 100644 --- a/frontend/components/Icon/CodeIcon.js +++ b/frontend/components/Icon/CodeIcon.js @@ -13,8 +13,8 @@ export default function CodeIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + + {' '} ); diff --git a/frontend/components/Icon/CollapsedIcon.js b/frontend/components/Icon/CollapsedIcon.js new file mode 100644 index 000000000..76ecdcfeb --- /dev/null +++ b/frontend/components/Icon/CollapsedIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function CollapsedIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/CollectionIcon.js b/frontend/components/Icon/CollectionIcon.js new file mode 100644 index 000000000..eddb11a2d --- /dev/null +++ b/frontend/components/Icon/CollectionIcon.js @@ -0,0 +1,25 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function CollectionIcon({ + expanded, + ...rest +}: Props & { expanded: boolean }) { + return ( + + + {expanded + ? + : } + + + ); +} diff --git a/frontend/components/Icon/DocumentIcon.js b/frontend/components/Icon/DocumentIcon.js new file mode 100644 index 000000000..8a7dc304c --- /dev/null +++ b/frontend/components/Icon/DocumentIcon.js @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function DocumentIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/EditIcon.js b/frontend/components/Icon/EditIcon.js new file mode 100644 index 000000000..e24b9304e --- /dev/null +++ b/frontend/components/Icon/EditIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function EditIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/Heading1Icon.js b/frontend/components/Icon/Heading1Icon.js index 1dc440549..ea54065ee 100644 --- a/frontend/components/Icon/Heading1Icon.js +++ b/frontend/components/Icon/Heading1Icon.js @@ -13,8 +13,8 @@ export default function Heading1Icon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + + {' '} ); diff --git a/frontend/components/Icon/Heading2Icon.js b/frontend/components/Icon/Heading2Icon.js index 46b115906..1ac85828a 100644 --- a/frontend/components/Icon/Heading2Icon.js +++ b/frontend/components/Icon/Heading2Icon.js @@ -13,8 +13,7 @@ export default function Heading2Icon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + ); diff --git a/frontend/components/Icon/HomeIcon.js b/frontend/components/Icon/HomeIcon.js new file mode 100644 index 000000000..ce807c01a --- /dev/null +++ b/frontend/components/Icon/HomeIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function HomeIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/HorizontalRuleIcon.js b/frontend/components/Icon/HorizontalRuleIcon.js new file mode 100644 index 000000000..42382a65d --- /dev/null +++ b/frontend/components/Icon/HorizontalRuleIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function HorizontalRuleIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/ImageIcon.js b/frontend/components/Icon/ImageIcon.js new file mode 100644 index 000000000..9ba5fd5f8 --- /dev/null +++ b/frontend/components/Icon/ImageIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function ImageIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/ItalicIcon.js b/frontend/components/Icon/ItalicIcon.js index 9b194b6b0..4a550843c 100644 --- a/frontend/components/Icon/ItalicIcon.js +++ b/frontend/components/Icon/ItalicIcon.js @@ -13,8 +13,7 @@ export default function ItalicIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + ); diff --git a/frontend/components/Icon/LinkIcon.js b/frontend/components/Icon/LinkIcon.js index 5f4c80e2f..5a8ca7d78 100644 --- a/frontend/components/Icon/LinkIcon.js +++ b/frontend/components/Icon/LinkIcon.js @@ -13,8 +13,7 @@ export default function LinkIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + ); diff --git a/frontend/components/Icon/OrderedListIcon.js b/frontend/components/Icon/OrderedListIcon.js index 89e51f225..8bb2836c4 100644 --- a/frontend/components/Icon/OrderedListIcon.js +++ b/frontend/components/Icon/OrderedListIcon.js @@ -13,8 +13,7 @@ export default function OrderedListIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + ); diff --git a/frontend/components/Icon/SearchIcon.js b/frontend/components/Icon/SearchIcon.js new file mode 100644 index 000000000..76d523f71 --- /dev/null +++ b/frontend/components/Icon/SearchIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function SearchIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/StarredIcon.js b/frontend/components/Icon/StarredIcon.js new file mode 100644 index 000000000..fdf6b2e6a --- /dev/null +++ b/frontend/components/Icon/StarredIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function StarredIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/StrikethroughIcon.js b/frontend/components/Icon/StrikethroughIcon.js index 27c8d1b2c..94bc9448b 100644 --- a/frontend/components/Icon/StrikethroughIcon.js +++ b/frontend/components/Icon/StrikethroughIcon.js @@ -13,8 +13,7 @@ export default function StrikethroughIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - - + ); diff --git a/frontend/components/Icon/TodoListIcon.js b/frontend/components/Icon/TodoListIcon.js new file mode 100644 index 000000000..f6ba15c31 --- /dev/null +++ b/frontend/components/Icon/TodoListIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function TodoListIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/UnderlinedIcon.js b/frontend/components/Icon/UnderlinedIcon.js deleted file mode 100644 index 14379e98c..000000000 --- a/frontend/components/Icon/UnderlinedIcon.js +++ /dev/null @@ -1,21 +0,0 @@ -// @flow -import React from 'react'; -import Icon from './Icon'; -import type { Props } from './Icon'; - -export default function UnderlinedIcon(props: Props) { - return ( - - - - - - - ); -} diff --git a/frontend/menus/BlockMenu.js b/frontend/menus/BlockMenu.js index 5cd3e6117..ebdd24143 100644 --- a/frontend/menus/BlockMenu.js +++ b/frontend/menus/BlockMenu.js @@ -1,6 +1,9 @@ // @flow import React, { Component } from 'react'; -import Icon from 'components/Icon'; +import ImageIcon from 'components/Icon/ImageIcon'; +import BulletedListIcon from 'components/Icon/BulletedListIcon'; +import HorizontalRuleIcon from 'components/Icon/HorizontalRuleIcon'; +import TodoListIcon from 'components/Icon/TodoListIcon'; import { observer } from 'mobx-react'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; @@ -30,16 +33,16 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; {...rest} > - Add images + Add images - Start list + Start list - Start checklist + Start checklist - Add break + Add break ); From 38228d03150abdd214377ce0efb40a869b11ee00 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 21 Oct 2017 14:05:20 -0700 Subject: [PATCH 04/20] WIP --- .../DropdownMenu/DropdownMenuItem.js | 8 ++- .../Editor/components/BlockInsert.js | 9 ++- .../Editor/components/Toolbar/Toolbar.js | 2 +- .../Toolbar/components/DocumentResult.js | 4 +- .../Toolbar/components/FormattingToolbar.js | 18 +++++- frontend/components/Icon/CheckboxIcon.js | 28 +++++++++ frontend/components/Icon/ChevronIcon.js | 20 ------ frontend/components/Icon/CloseIcon.js | 23 +++++++ frontend/components/Icon/CodeIcon.js | 1 - frontend/components/Icon/GoToIcon.js | 23 +++++++ .../components/Icon/HorizontalRuleIcon.js | 5 +- frontend/components/Icon/Icon.js | 36 +++++------ frontend/components/Icon/MoreIcon.js | 23 +++++++ frontend/components/Icon/NewDocumentIcon.js | 23 +++++++ frontend/components/Icon/PlusIcon.js | 20 ++++++ frontend/components/Icon/TableIcon.js | 23 +++++++ frontend/components/Icon/TodoListIcon.js | 5 +- frontend/components/Layout/Layout.js | 16 ++--- .../Layout/components/SidebarCollections.js | 29 ++++++--- .../Layout/components/SidebarLink.js | 63 ++++++++++++------- frontend/components/Modal/Modal.js | 4 +- frontend/menus/CollectionMenu.js | 15 ++--- frontend/menus/DocumentMenu.js | 4 +- frontend/scenes/Document/Document.js | 12 +--- .../DocumentMove/components/PathToDocument.js | 19 +++--- frontend/styles/base.css | 2 +- package.json | 1 - yarn.lock | 4 -- 28 files changed, 304 insertions(+), 136 deletions(-) create mode 100644 frontend/components/Icon/CheckboxIcon.js delete mode 100644 frontend/components/Icon/ChevronIcon.js create mode 100644 frontend/components/Icon/CloseIcon.js create mode 100644 frontend/components/Icon/GoToIcon.js create mode 100644 frontend/components/Icon/MoreIcon.js create mode 100644 frontend/components/Icon/NewDocumentIcon.js create mode 100644 frontend/components/Icon/PlusIcon.js create mode 100644 frontend/components/Icon/TableIcon.js diff --git a/frontend/components/DropdownMenu/DropdownMenuItem.js b/frontend/components/DropdownMenu/DropdownMenuItem.js index 18d824ce3..7e6738f45 100644 --- a/frontend/components/DropdownMenu/DropdownMenuItem.js +++ b/frontend/components/DropdownMenu/DropdownMenuItem.js @@ -1,6 +1,7 @@ // @flow import React from 'react'; import styled from 'styled-components'; +import Flex from 'components/Flex'; import { color } from 'styles/constants'; const DropdownMenuItem = ({ @@ -17,13 +18,12 @@ const DropdownMenuItem = ({ ); }; -const MenuItem = styled.div` +const MenuItem = styled(Flex)` margin: 0; padding: 5px 10px; height: 32px; color: ${color.slateDark}; - display: flex; justify-content: left; align-items: center; cursor: pointer; @@ -41,6 +41,10 @@ const MenuItem = styled.div` &:hover { color: ${color.white}; background: ${color.primary}; + + svg { + fill: ${color.white}; + } } `; diff --git a/frontend/components/Editor/components/BlockInsert.js b/frontend/components/Editor/components/BlockInsert.js index 43df62813..441d8880a 100644 --- a/frontend/components/Editor/components/BlockInsert.js +++ b/frontend/components/Editor/components/BlockInsert.js @@ -7,7 +7,7 @@ import { observable } from 'mobx'; import { observer } from 'mobx-react'; import styled from 'styled-components'; import { color } from 'styles/constants'; -import Icon from 'components/Icon'; +import PlusIcon from 'components/Icon/PlusIcon'; import BlockMenu from 'menus/BlockMenu'; import type { State } from '../types'; @@ -151,7 +151,7 @@ export default class BlockInsert extends Component { accept="image/*" /> } + label={} onPickImage={this.onPickImage} onInsertList={ev => this.insertBlock(ev, { @@ -183,11 +183,10 @@ const Trigger = styled.div` z-index: 1; opacity: 0; background-color: ${color.white}; - border-radius: 4px; transition: opacity 250ms ease-in-out, transform 250ms ease-in-out; line-height: 0; - height: 16px; - width: 16px; + margin-top: -2px; + margin-left: -4px; transform: scale(.9); ${({ active }) => active && ` diff --git a/frontend/components/Editor/components/Toolbar/Toolbar.js b/frontend/components/Editor/components/Toolbar/Toolbar.js index 106c347fb..777765faa 100644 --- a/frontend/components/Editor/components/Toolbar/Toolbar.js +++ b/frontend/components/Editor/components/Toolbar/Toolbar.js @@ -144,7 +144,7 @@ const Menu = styled.div` top: -10000px; left: -10000px; opacity: 0; - background-color: #222; + background-color: #2F3336; border-radius: 4px; transition: opacity 250ms ease-in-out, transform 250ms ease-in-out; line-height: 0; diff --git a/frontend/components/Editor/components/Toolbar/components/DocumentResult.js b/frontend/components/Editor/components/Toolbar/components/DocumentResult.js index e7ac8c2e4..8351681d6 100644 --- a/frontend/components/Editor/components/Toolbar/components/DocumentResult.js +++ b/frontend/components/Editor/components/Toolbar/components/DocumentResult.js @@ -3,7 +3,7 @@ import React from 'react'; import styled from 'styled-components'; import { fontWeight, color } from 'styles/constants'; import Document from 'models/Document'; -import Icon from 'components/Icon'; +import GoToIcon from 'components/Icon/GoToIcon'; type Props = { innerRef?: Function, @@ -14,7 +14,7 @@ type Props = { function DocumentResult({ document, ...rest }: Props) { return ( - + {document.title} ); diff --git a/frontend/components/Editor/components/Toolbar/components/FormattingToolbar.js b/frontend/components/Editor/components/Toolbar/components/FormattingToolbar.js index c56ef9259..effd427ff 100644 --- a/frontend/components/Editor/components/Toolbar/components/FormattingToolbar.js +++ b/frontend/components/Editor/components/Toolbar/components/FormattingToolbar.js @@ -1,5 +1,6 @@ // @flow import React, { Component } from 'react'; +import styled from 'styled-components'; import type { State } from '../../../types'; import ToolbarButton from './ToolbarButton'; import BoldIcon from 'components/Icon/BoldIcon'; @@ -10,7 +11,7 @@ import ItalicIcon from 'components/Icon/ItalicIcon'; import LinkIcon from 'components/Icon/LinkIcon'; import StrikethroughIcon from 'components/Icon/StrikethroughIcon'; -export default class FormattingToolbar extends Component { +class FormattingToolbar extends Component { props: { state: State, onChange: Function, @@ -92,9 +93,11 @@ export default class FormattingToolbar extends Component { {this.renderMarkButton('bold', BoldIcon)} {this.renderMarkButton('italic', ItalicIcon)} {this.renderMarkButton('deleted', StrikethroughIcon)} + {this.renderMarkButton('code', CodeIcon)} + {this.renderBlockButton('heading1', Heading1Icon)} {this.renderBlockButton('heading2', Heading2Icon)} - {this.renderMarkButton('code', CodeIcon)} + @@ -102,3 +105,14 @@ export default class FormattingToolbar extends Component { ); } } + +const Separator = styled.div` + height: 100%; + width: 1px; + background: #FFF; + opacity: .2; + display: inline-block; + margin-left: 10px; +`; + +export default FormattingToolbar; diff --git a/frontend/components/Icon/CheckboxIcon.js b/frontend/components/Icon/CheckboxIcon.js new file mode 100644 index 000000000..b4206c259 --- /dev/null +++ b/frontend/components/Icon/CheckboxIcon.js @@ -0,0 +1,28 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function CheckboxIcon({ + checked, + ...rest +}: Props & { checked: boolean }) { + return ( + + + {checked + ? + : } + + + ); +} diff --git a/frontend/components/Icon/ChevronIcon.js b/frontend/components/Icon/ChevronIcon.js deleted file mode 100644 index 65e59b550..000000000 --- a/frontend/components/Icon/ChevronIcon.js +++ /dev/null @@ -1,20 +0,0 @@ -// @flow -import React from 'react'; -import Icon from './Icon'; -import type { Props } from './Icon'; - -export default function NextIcon(props: Props) { - return ( - - - - - - ); -} diff --git a/frontend/components/Icon/CloseIcon.js b/frontend/components/Icon/CloseIcon.js new file mode 100644 index 000000000..36e0f0dec --- /dev/null +++ b/frontend/components/Icon/CloseIcon.js @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function CloseIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/CodeIcon.js b/frontend/components/Icon/CodeIcon.js index 246492684..3b1026c6f 100644 --- a/frontend/components/Icon/CodeIcon.js +++ b/frontend/components/Icon/CodeIcon.js @@ -14,7 +14,6 @@ export default function CodeIcon(props: Props) { xmlns="http://www.w3.org/2000/svg" > - {' '} ); diff --git a/frontend/components/Icon/GoToIcon.js b/frontend/components/Icon/GoToIcon.js new file mode 100644 index 000000000..cc1d1e10d --- /dev/null +++ b/frontend/components/Icon/GoToIcon.js @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function GoToIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/HorizontalRuleIcon.js b/frontend/components/Icon/HorizontalRuleIcon.js index 42382a65d..fcafd88b1 100644 --- a/frontend/components/Icon/HorizontalRuleIcon.js +++ b/frontend/components/Icon/HorizontalRuleIcon.js @@ -13,7 +13,10 @@ export default function HorizontalRuleIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - + ); diff --git a/frontend/components/Icon/Icon.js b/frontend/components/Icon/Icon.js index 5921db7eb..2d4a1354f 100644 --- a/frontend/components/Icon/Icon.js +++ b/frontend/components/Icon/Icon.js @@ -2,12 +2,14 @@ import React from 'react'; import styled from 'styled-components'; import { color } from 'styles/constants'; -import * as Icons from 'react-feather'; export type Props = { className?: string, type?: string, light?: boolean, + black?: boolean, + primary?: boolean, + color?: string, }; type BaseProps = { @@ -17,37 +19,27 @@ type BaseProps = { export default function Icon({ children, light, + black, + primary, + color, type, ...rest }: Props & BaseProps) { - if (type) { - children = React.createElement(Icons[type], { - size: '1em', - color: light ? color.white : undefined, - ...rest, - }); - - return ( - - {children} - - ); - } - return ( - + {children} ); } -const FeatherWrapper = styled.span` - position: relative; - top: .1em; -`; - const Wrapper = styled.span` svg { - fill: ${props => (props.light ? color.white : color.black)} + fill: ${props => { + if (props.color) return props.color; + if (props.light) return color.white; + if (props.black) return color.black; + if (props.primary) return color.primary; + return color.slateDark; + }}; } `; diff --git a/frontend/components/Icon/MoreIcon.js b/frontend/components/Icon/MoreIcon.js new file mode 100644 index 000000000..a997ea0d1 --- /dev/null +++ b/frontend/components/Icon/MoreIcon.js @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function MoreIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/NewDocumentIcon.js b/frontend/components/Icon/NewDocumentIcon.js new file mode 100644 index 000000000..826e5967d --- /dev/null +++ b/frontend/components/Icon/NewDocumentIcon.js @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function NewDocumentIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/PlusIcon.js b/frontend/components/Icon/PlusIcon.js new file mode 100644 index 000000000..5e5524388 --- /dev/null +++ b/frontend/components/Icon/PlusIcon.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function PlusIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/TableIcon.js b/frontend/components/Icon/TableIcon.js new file mode 100644 index 000000000..c7d5283be --- /dev/null +++ b/frontend/components/Icon/TableIcon.js @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function TableIcon(props: Props) { + return ( + + + + + + ); +} diff --git a/frontend/components/Icon/TodoListIcon.js b/frontend/components/Icon/TodoListIcon.js index f6ba15c31..836eaa89b 100644 --- a/frontend/components/Icon/TodoListIcon.js +++ b/frontend/components/Icon/TodoListIcon.js @@ -13,7 +13,10 @@ export default function TodoListIcon(props: Props) { width="24" xmlns="http://www.w3.org/2000/svg" > - + ); diff --git a/frontend/components/Layout/Layout.js b/frontend/components/Layout/Layout.js index e87ab1364..22dc66b93 100644 --- a/frontend/components/Layout/Layout.js +++ b/frontend/components/Layout/Layout.js @@ -12,7 +12,9 @@ import { documentEditUrl, homeUrl, searchUrl } from 'utils/routeHelpers'; import Avatar from 'components/Avatar'; import { LoadingIndicatorBar } from 'components/LoadingIndicator'; import Scrollable from 'components/Scrollable'; -import Icon from 'components/Icon'; +import HomeIcon from 'components/Icon/HomeIcon'; +import SearchIcon from 'components/Icon/SearchIcon'; +import StarredIcon from 'components/Icon/StarredIcon'; import Toasts from 'components/Toasts'; import AccountMenu from 'menus/AccountMenu'; @@ -127,14 +129,14 @@ type Props = { - - Home + }> + Home - - Search + }> + Search - - Starred + }> + Starred diff --git a/frontend/components/Layout/components/SidebarCollections.js b/frontend/components/Layout/components/SidebarCollections.js index a1cf5aa2d..d730dc9d8 100644 --- a/frontend/components/Layout/components/SidebarCollections.js +++ b/frontend/components/Layout/components/SidebarCollections.js @@ -8,7 +8,8 @@ import { color, fontWeight } from 'styles/constants'; import SidebarLink from './SidebarLink'; import DropToImport from 'components/DropToImport'; -import Icon from 'components/Icon'; +import PlusIcon from 'components/Icon/PlusIcon'; +import CollectionIcon from 'components/Icon/CollectionIcon'; import CollectionMenu from 'menus/CollectionMenu'; import CollectionsStore from 'stores/CollectionsStore'; @@ -52,8 +53,11 @@ type Props = { ))} {collections.isLoaded && - - Add new collection + } + > + New collection… } ); @@ -77,6 +81,7 @@ type Props = { ui, activeDocumentRef, } = this.props; + const expanded = collection.id === ui.activeCollectionId; return ( (this.dropzoneRef = ref)} > - + } + > {collection.name} @@ -103,7 +112,7 @@ type Props = { - {collection.id === ui.activeCollectionId && + {expanded && {collection.documents.map(document => ( { const isActiveDocument = activeDocument && activeDocument.id === document.id; - const showChildren = - activeDocument && + const showChildren = !!(activeDocument && (activeDocument.pathToDocument .map(entry => entry.id) .includes(document.id) || - isActiveDocument); + isActiveDocument)); return (
; +const StyledGoTo = styled(CollapsedIcon)` + margin-right: -10px; + + svg { + margin-bottom: -4px; + margin-right: 6px; + + ${({ expanded }) => !expanded && 'transform: rotate(-90deg);'} + } +`; + +const IconWrapper = styled.span` + margin-left: -4px; + margin-right: 4px; + height: 24px; +`; + const styleComponent = component => styled(component)` display: flex; width: 100%; + position: relative; overflow: hidden; text-overflow: ellipsis; - padding: 5px 0; + padding: 4px 0; margin-left: ${({ hasChildren }) => (hasChildren ? '-20px;' : '0')}; color: ${color.slateDark}; font-size: 15px; @@ -30,38 +47,36 @@ const styleComponent = component => styled(component)` color: ${color.text}; } - &.active ${StyledChevron} svg { - fill: ${activeStyle.color}; + &.active { + svg { + fill: ${activeStyle.color} + } } `; -function SidebarLink(props: Object) { - const Component = styleComponent(props.to ? NavLink : StyleableDiv); +type Props = { + to?: string, + onClick?: SyntheticEvent => *, + children?: React$Element<*>, + icon?: React$Element<*>, + hasChildren?: boolean, + expanded?: boolean, +}; + +function SidebarLink({ icon, children, expanded, ...rest }: Props) { + const Component = styleComponent(rest.to ? NavLink : StyleableDiv); return ( - - {props.hasChildren && } - {props.children} + + {icon && {icon}} + {rest.hasChildren && } + {children} ); } -const StyledChevron = styled(ChevronIcon)` - margin-right: -10px; - - svg { - height: 18px; - margin-bottom: -4px; - margin-right: 6px; - - fill: ${color.slateDark}; - - ${({ expanded }) => expanded && 'transform: rotate(90deg);'} - } -`; - const Content = styled.div` width: 100%; `; diff --git a/frontend/components/Modal/Modal.js b/frontend/components/Modal/Modal.js index 92dc4db66..de4f8841b 100644 --- a/frontend/components/Modal/Modal.js +++ b/frontend/components/Modal/Modal.js @@ -5,7 +5,7 @@ import styled from 'styled-components'; import ReactModal from 'react-modal'; import { color } from 'styles/constants'; import { fadeAndScaleIn } from 'styles/animations'; -import Icon from 'components/Icon'; +import CloseIcon from 'components/Icon/CloseIcon'; import Flex from 'components/Flex'; type Props = { @@ -33,7 +33,7 @@ const Modal = ({ > {title &&

{title}

} - + {children}
diff --git a/frontend/menus/CollectionMenu.js b/frontend/menus/CollectionMenu.js index fc29530b8..76c778be0 100644 --- a/frontend/menus/CollectionMenu.js +++ b/frontend/menus/CollectionMenu.js @@ -1,17 +1,16 @@ // @flow import React, { Component } from 'react'; import { inject, observer } from 'mobx-react'; -import styled from 'styled-components'; import Collection from 'models/Collection'; import UiStore from 'stores/UiStore'; -import Icon from 'components/Icon'; +import MoreIcon from 'components/Icon/MoreIcon'; import Flex from 'components/Flex'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; @observer class CollectionMenu extends Component { props: { - label?: React$Element, + label?: React$Element<*>, onOpen?: () => void, onClose?: () => void, onImport?: () => void, @@ -41,7 +40,7 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; return ( } + label={label || } onOpen={onOpen} onClose={onClose} > @@ -53,17 +52,13 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; Import document - Edit + Edit… } {allowDelete && - Delete} + Delete…} ); } } -const MoreIcon = styled(Icon)` - width: 22px; -`; - export default inject('ui')(CollectionMenu); diff --git a/frontend/menus/DocumentMenu.js b/frontend/menus/DocumentMenu.js index e60933f84..8cb78b7d2 100644 --- a/frontend/menus/DocumentMenu.js +++ b/frontend/menus/DocumentMenu.js @@ -4,7 +4,7 @@ import { withRouter } from 'react-router-dom'; import { inject, observer } from 'mobx-react'; import Document from 'models/Document'; import UiStore from 'stores/UiStore'; -import Icon from 'components/Icon'; +import MoreIcon from 'components/Icon/MoreIcon'; import { documentMoveUrl } from 'utils/routeHelpers'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; @@ -49,7 +49,7 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; const { allowDelete } = document; return ( - }> + }> {document.starred ? Unstar diff --git a/frontend/scenes/Document/Document.js b/frontend/scenes/Document/Document.js index 3db9f3b07..f89127fd3 100644 --- a/frontend/scenes/Document/Document.js +++ b/frontend/scenes/Document/Document.js @@ -31,6 +31,7 @@ import LoadingIndicator from 'components/LoadingIndicator'; import Collaborators from 'components/Collaborators'; import CenteredContent from 'components/CenteredContent'; import PageTitle from 'components/PageTitle'; +import NewDocumentIcon from 'components/Icon/NewDocumentIcon'; import Search from 'scenes/Search'; const DISCARD_CHANGES = ` @@ -303,7 +304,7 @@ type Props = { {!this.isEditing && - New + } @@ -329,15 +330,8 @@ const HeaderAction = styled(Flex)` color: ${color.text}; padding: 0 0 0 14px; - a, - svg { + a { color: ${color.text}; - opacity: .8; - transition: opacity 100ms ease-in-out; - - &:hover { - opacity: 1; - } } `; diff --git a/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js b/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js index 2e5bab449..b8ed4f376 100644 --- a/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js +++ b/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js @@ -7,7 +7,7 @@ import styled from 'styled-components'; import { color } from 'styles/constants'; import Flex from 'components/Flex'; -import ChevronIcon from 'components/Icon/ChevronIcon'; +import GoToIcon from 'components/Icon/GoToIcon'; import Document from 'models/Document'; @@ -19,10 +19,7 @@ const ResultWrapper = styled.div` cursor: default; `; -const StyledChevronIcon = styled(ChevronIcon)` - padding-top: 2px; - width: 24px; - height: 24px; +const StyledGoToIcon = styled(GoToIcon)` `; const ResultWrapperLink = ResultWrapper.withComponent('a').extend` @@ -40,8 +37,10 @@ const ResultWrapperLink = ResultWrapper.withComponent('a').extend` outline: none; cursor: pointer; - ${StyledChevronIcon} svg { - fill: ${color.smokeLight}; + ${StyledGoToIcon} { + svg { + fill: ${color.white}; + } } } `; @@ -82,14 +81,14 @@ type Props = { if (!result) return
; return ( - + {result.path .map(doc => {doc.title}) - .reduce((prev, curr) => [prev, , curr])} + .reduce((prev, curr) => [prev, , curr])} {document && {' '} - + {' '}{document.title} } diff --git a/frontend/styles/base.css b/frontend/styles/base.css index d7d50a1f2..711104aa5 100644 --- a/frontend/styles/base.css +++ b/frontend/styles/base.css @@ -50,7 +50,7 @@ svg { max-height: 100%; } a { - color: #005aa6; + color: #16B3FF; text-decoration: none; cursor: pointer; } diff --git a/package.json b/package.json index 04d7b820b..da53a3aac 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,6 @@ "react-addons-css-transition-group": "15.3.2", "react-dom": "^15.6.1", "react-dropzone": "3.6.0", - "react-feather": "^1.0.7", "react-helmet": "3.1.0", "react-keydown": "^1.7.3", "react-modal": "^2.2.1", diff --git a/yarn.lock b/yarn.lock index 16b429b5b..80829ae84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7310,10 +7310,6 @@ react-dropzone@3.6.0: dependencies: attr-accept "^1.0.3" -react-feather@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-1.0.7.tgz#f2118f1d2402b0c1e6f23c732f9e7f9fd4ca61e2" - react-helmet@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-3.1.0.tgz#63486194682f33004826f3687dc49a138b557050" From a756769060b061f350d24593fd253a9112c0dc61 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 21 Oct 2017 15:23:23 -0700 Subject: [PATCH 05/20] Almost all icons updated --- .../Toolbar/components/LinkToolbar.js | 3 ++- frontend/components/Icon/Icon.js | 19 +++++++------------ .../components/Toasts/components/Toast.js | 4 ---- frontend/menus/DocumentMenu.js | 8 +++++--- .../components/SearchField/SearchField.js | 9 +++++---- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js index e34d04b4b..3d868241f 100644 --- a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js +++ b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js @@ -11,6 +11,7 @@ import DocumentResult from './DocumentResult'; import type { State } from '../../../types'; import DocumentsStore from 'stores/DocumentsStore'; import keydown from 'react-keydown'; +import CloseIcon from 'components/Icon/CloseIcon'; import Icon from 'components/Icon'; import Flex from 'components/Flex'; @@ -149,7 +150,7 @@ class LinkToolbar extends Component { {this.isEditing ? - : } + : } {hasResults && diff --git a/frontend/components/Icon/Icon.js b/frontend/components/Icon/Icon.js index 2d4a1354f..f7bf1b9ac 100644 --- a/frontend/components/Icon/Icon.js +++ b/frontend/components/Icon/Icon.js @@ -5,28 +5,20 @@ import { color } from 'styles/constants'; export type Props = { className?: string, - type?: string, light?: boolean, black?: boolean, primary?: boolean, color?: string, + size?: number, }; type BaseProps = { - children?: React$Element, + children?: React$Element<*>, }; -export default function Icon({ - children, - light, - black, - primary, - color, - type, - ...rest -}: Props & BaseProps) { +export default function Icon({ children, ...rest }: Props & BaseProps) { return ( - + {children} ); @@ -34,6 +26,9 @@ export default function Icon({ const Wrapper = styled.span` svg { + width: ${props => (props.size ? props.size + 'px' : 'auto')}; + height: ${props => (props.size ? props.size + 'px' : 'auto')}; + fill: ${props => { if (props.color) return props.color; if (props.light) return color.white; diff --git a/frontend/components/Toasts/components/Toast.js b/frontend/components/Toasts/components/Toast.js index d4a96f5f4..da25a022b 100644 --- a/frontend/components/Toasts/components/Toast.js +++ b/frontend/components/Toasts/components/Toast.js @@ -4,7 +4,6 @@ import styled from 'styled-components'; import { darken } from 'polished'; import { color } from 'styles/constants'; import { fadeAndScaleIn } from 'styles/animations'; -import Icon from 'components/Icon'; type Props = { onRequestClose: () => void, @@ -38,9 +37,6 @@ class Toast extends Component { return ( - {type === 'info' - ? - : } {message} ); diff --git a/frontend/menus/DocumentMenu.js b/frontend/menus/DocumentMenu.js index 8cb78b7d2..3e74bd8b2 100644 --- a/frontend/menus/DocumentMenu.js +++ b/frontend/menus/DocumentMenu.js @@ -61,11 +61,13 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; > New child - Move - Export + + Download + + Move… {allowDelete && - Delete + Delete… } ); diff --git a/frontend/scenes/Search/components/SearchField/SearchField.js b/frontend/scenes/Search/components/SearchField/SearchField.js index dd9642d07..48432320b 100644 --- a/frontend/scenes/Search/components/SearchField/SearchField.js +++ b/frontend/scenes/Search/components/SearchField/SearchField.js @@ -1,6 +1,6 @@ // @flow import React, { Component } from 'react'; -import Icon from 'components/Icon'; +import SearchIcon from 'components/Icon/SearchIcon'; import Flex from 'components/Flex'; import { color } from 'styles/constants'; import styled from 'styled-components'; @@ -37,7 +37,7 @@ class SearchField extends Component { innerRef={this.setRef} onChange={this.handleChange} spellCheck="false" - placeholder="Search…" + placeholder="search…" autoFocus /> @@ -59,8 +59,9 @@ const StyledInput = styled.input` :-ms-input-placeholder { color: ${color.slateLight}; } `; -const StyledIcon = styled(Icon)` - top: 3px; +const StyledIcon = styled(SearchIcon)` + position: relative; + top: 6px; `; export default SearchField; From bafc4919013ce93c60df71ee6f6e092bbeeb75d7 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 21 Oct 2017 15:34:00 -0700 Subject: [PATCH 06/20] Document stars --- .../DocumentPreview/DocumentPreview.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/frontend/components/DocumentPreview/DocumentPreview.js b/frontend/components/DocumentPreview/DocumentPreview.js index 14ec4e2bd..d9da9bd8e 100644 --- a/frontend/components/DocumentPreview/DocumentPreview.js +++ b/frontend/components/DocumentPreview/DocumentPreview.js @@ -5,7 +5,7 @@ import { Link } from 'react-router-dom'; import Document from 'models/Document'; import styled from 'styled-components'; import { color } from 'styles/constants'; -import Icon from 'components/Icon'; +import StarredIcon from 'components/Icon/StarredIcon'; import PublishingInfo from './components/PublishingInfo'; type Props = { @@ -15,18 +15,13 @@ type Props = { innerRef?: Function, }; -const StyledStar = styled(({ solid, ...props }) => ).attrs({ - type: 'Star', - color: color.text, -})` - width: 16px; - height: 16px; - top: 1px; - margin-left: 4px; +const StyledStar = styled(({ solid, ...props }) => ( + +))` + position: absolute; opacity: ${props => (props.solid ? '1 !important' : 0)}; transition: all 100ms ease-in-out; - - ${props => props.solid && 'polygon { fill: #000};'} + margin-left: 2px; &:hover { transform: scale(1.1); From c4d8c07859e6c211ef88d436ad80e4da3be6e3f5 Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sat, 21 Oct 2017 16:11:08 -0700 Subject: [PATCH 07/20] addressed comments and added time limit --- .../Layout/components/SidebarCollections.js | 124 +++++++++--------- frontend/models/Document.js | 6 + frontend/stores/DocumentsStore.js | 5 + 3 files changed, 75 insertions(+), 60 deletions(-) diff --git a/frontend/components/Layout/components/SidebarCollections.js b/frontend/components/Layout/components/SidebarCollections.js index 3d71e41ad..a40a2d0ea 100644 --- a/frontend/components/Layout/components/SidebarCollections.js +++ b/frontend/components/Layout/components/SidebarCollections.js @@ -20,6 +20,7 @@ import { type NavigationNode } from 'types'; type Props = { history: Object, collections: CollectionsStore, + documents: DocumentsStore, activeDocument: ?Document, onCreateCollection: () => void, activeDocumentRef: HTMLElement => void, @@ -36,6 +37,7 @@ type Props = { activeDocument, ui, activeDocumentRef, + documents, } = this.props; return ( @@ -48,6 +50,7 @@ type Props = { collection={collection} activeDocument={activeDocument} activeDocumentRef={activeDocumentRef} + prefetchDocument={documents.prefetchDocument} ui={ui} /> ))} @@ -77,6 +80,7 @@ type Props = { activeDocument, ui, activeDocumentRef, + prefetchDocument, } = this.props; return ( @@ -113,6 +117,7 @@ type Props = { history={history} document={document} activeDocument={activeDocument} + prefetchDocument={prefetchDocument} depth={0} /> ))} @@ -124,77 +129,76 @@ type Props = { } type DocumentLinkProps = { - documents: DocumentsStore, document: NavigationNode, history: Object, activeDocument: ?Document, activeDocumentRef: HTMLElement => void, + prefetchDocument: string => void, depth: number, }; -const DocumentLink = inject('documents')( - observer( - ({ - documents, - document, - activeDocument, - activeDocumentRef, - depth, - }: DocumentLinkProps) => { - const isActiveDocument = - activeDocument && activeDocument.id === document.id; - const showChildren = - activeDocument && - (activeDocument.pathToDocument - .map(entry => entry.id) - .includes(document.id) || - isActiveDocument); +const DocumentLink = observer( + ({ + documents, + document, + activeDocument, + activeDocumentRef, + prefetchDocument, + depth, + }: DocumentLinkProps) => { + const isActiveDocument = + activeDocument && activeDocument.id === document.id; + const showChildren = + activeDocument && + (activeDocument.pathToDocument + .map(entry => entry.id) + .includes(document.id) || + isActiveDocument); - const handleMouseEnter = (event: SyntheticEvent) => { - event.stopPropagation(); - event.preventDefault(); - documents.prefetchDocument(document.id); - }; + const handleMouseEnter = (event: SyntheticEvent) => { + event.stopPropagation(); + event.preventDefault(); + prefetchDocument(document.id); + }; - return ( - + - 0} + expanded={showChildren} > - 0} - expanded={showChildren} - > - {document.title} - - + {document.title} + + - {showChildren && - - {document.children && - document.children.map(childDocument => ( - - ))} - } - - ); - } - ) + {showChildren && + + {document.children && + document.children.map(childDocument => ( + + ))} + } + + ); + } ); const CollectionAction = styled.a` @@ -230,4 +234,4 @@ const Children = styled(Flex)` margin-left: 20px; `; -export default inject('collections', 'ui')(SidebarCollections); +export default inject('collections', 'ui', 'documents')(SidebarCollections); diff --git a/frontend/models/Document.js b/frontend/models/Document.js index 4f4c760ef..b6a3455d5 100644 --- a/frontend/models/Document.js +++ b/frontend/models/Document.js @@ -40,6 +40,7 @@ class Document extends BaseModel { views: number; data: Object; + fetchedAt: Date; /* Computed */ @@ -101,6 +102,10 @@ class Document extends BaseModel { : null; } + get timeSinceFetch(): number { + return (new Date() - this.fetchedAt) / 1000; + } + /* Actions */ @action star = async () => { @@ -252,6 +257,7 @@ class Document extends BaseModel { this.updateData(data); this.errors = stores.errors; + this.fetchedAt = new Date(); } } diff --git a/frontend/stores/DocumentsStore.js b/frontend/stores/DocumentsStore.js index 9d30926a9..692ce14bb 100644 --- a/frontend/stores/DocumentsStore.js +++ b/frontend/stores/DocumentsStore.js @@ -116,6 +116,11 @@ class DocumentsStore extends BaseStore { }; @action fetch = async (id: string, prefetch: boolean): Promise<*> => { + /** If document has been fetched under 5s ago, return it */ + const existingDocument = this.getById(id); + if (existingDocument && existingDocument.timeSinceFetch < 5) + return existingDocument; + if (!prefetch) this.isFetching = true; try { From a909bf86b4a86b4b7ba5df601f35a77fd7856fc1 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 21 Oct 2017 16:21:43 -0700 Subject: [PATCH 08/20] Refactor. Add trash and open icons --- .../components/DropdownMenu/DropdownMenu.js | 2 +- .../Editor/components/Toolbar/Toolbar.js | 5 ++- .../Toolbar/components/LinkToolbar.js | 9 ++-- frontend/components/Icon/BackIcon.js | 16 ++------ frontend/components/Icon/BoldIcon.js | 10 +---- frontend/components/Icon/BulletedListIcon.js | 10 +---- frontend/components/Icon/CheckboxIcon.js | 20 +++------ frontend/components/Icon/CloseIcon.js | 16 ++------ frontend/components/Icon/CodeIcon.js | 10 +---- frontend/components/Icon/CollapsedIcon.js | 10 +---- frontend/components/Icon/CollectionIcon.js | 14 ++----- frontend/components/Icon/DocumentIcon.js | 16 ++------ frontend/components/Icon/EditIcon.js | 10 +---- frontend/components/Icon/GoToIcon.js | 16 ++------ frontend/components/Icon/Heading1Icon.js | 11 +---- frontend/components/Icon/Heading2Icon.js | 10 +---- frontend/components/Icon/HomeIcon.js | 10 +---- .../components/Icon/HorizontalRuleIcon.js | 16 ++------ frontend/components/Icon/Icon.js | 41 ++++++++++--------- frontend/components/Icon/ImageIcon.js | 10 +---- frontend/components/Icon/ItalicIcon.js | 10 +---- frontend/components/Icon/LinkIcon.js | 10 +---- frontend/components/Icon/MoreIcon.js | 16 ++------ frontend/components/Icon/NewDocumentIcon.js | 16 ++------ frontend/components/Icon/OpenIcon.js | 12 ++++++ frontend/components/Icon/OrderedListIcon.js | 10 +---- frontend/components/Icon/PlusIcon.js | 10 +---- frontend/components/Icon/SearchIcon.js | 10 +---- frontend/components/Icon/StarredIcon.js | 10 +---- frontend/components/Icon/StrikethroughIcon.js | 10 +---- frontend/components/Icon/TableIcon.js | 16 ++------ frontend/components/Icon/TodoListIcon.js | 13 +----- frontend/components/Icon/TrashIcon.js | 12 ++++++ .../Layout/components/SidebarLink.js | 11 ++--- frontend/menus/BlockMenu.js | 2 +- frontend/scenes/Document/Document.js | 20 +++------ .../components/SearchField/SearchField.js | 2 +- 37 files changed, 122 insertions(+), 330 deletions(-) create mode 100644 frontend/components/Icon/OpenIcon.js create mode 100644 frontend/components/Icon/TrashIcon.js diff --git a/frontend/components/DropdownMenu/DropdownMenu.js b/frontend/components/DropdownMenu/DropdownMenu.js index 949ca3081..2ce37c8c4 100644 --- a/frontend/components/DropdownMenu/DropdownMenu.js +++ b/frontend/components/DropdownMenu/DropdownMenu.js @@ -85,7 +85,7 @@ const Label = styled(Flex).attrs({ `; const Menu = styled.div` - animation: ${fadeAndScaleIn} 250ms ease; + animation: ${fadeAndScaleIn} 200ms ease; transform-origin: 75% 0; position: absolute; diff --git a/frontend/components/Editor/components/Toolbar/Toolbar.js b/frontend/components/Editor/components/Toolbar/Toolbar.js index 777765faa..281d40cbc 100644 --- a/frontend/components/Editor/components/Toolbar/Toolbar.js +++ b/frontend/components/Editor/components/Toolbar/Toolbar.js @@ -146,13 +146,14 @@ const Menu = styled.div` opacity: 0; background-color: #2F3336; border-radius: 4px; - transition: opacity 250ms ease-in-out, transform 250ms ease-in-out; + transform: scale(.95); + transition: opacity 150ms cubic-bezier(0.175, 0.885, 0.32, 1.275), transform 150ms cubic-bezier(0.175, 0.885, 0.32, 1.275); line-height: 0; height: 40px; min-width: 260px; ${({ active }) => active && ` - transform: translateY(-6px); + transform: translateY(-6px) scale(1); opacity: 1; `} `; diff --git a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js index 3d868241f..3d8f5a552 100644 --- a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js +++ b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js @@ -12,7 +12,8 @@ import type { State } from '../../../types'; import DocumentsStore from 'stores/DocumentsStore'; import keydown from 'react-keydown'; import CloseIcon from 'components/Icon/CloseIcon'; -import Icon from 'components/Icon'; +import OpenIcon from 'components/Icon/OpenIcon'; +import TrashIcon from 'components/Icon/TrashIcon'; import Flex from 'components/Flex'; @keydown @@ -145,12 +146,10 @@ class LinkToolbar extends Component { /> {this.isEditing && - + } - {this.isEditing - ? - : } + {this.isEditing ? : } {hasResults && diff --git a/frontend/components/Icon/BackIcon.js b/frontend/components/Icon/BackIcon.js index bc082e7fd..4586fd1a5 100644 --- a/frontend/components/Icon/BackIcon.js +++ b/frontend/components/Icon/BackIcon.js @@ -6,18 +6,10 @@ import type { Props } from './Icon'; export default function BackIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/BoldIcon.js b/frontend/components/Icon/BoldIcon.js index 284d8daa8..fa5dd4e62 100644 --- a/frontend/components/Icon/BoldIcon.js +++ b/frontend/components/Icon/BoldIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function BoldIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/BulletedListIcon.js b/frontend/components/Icon/BulletedListIcon.js index 01be8d1f8..89fa741b9 100644 --- a/frontend/components/Icon/BulletedListIcon.js +++ b/frontend/components/Icon/BulletedListIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function BulletedListIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/CheckboxIcon.js b/frontend/components/Icon/CheckboxIcon.js index b4206c259..5540808c8 100644 --- a/frontend/components/Icon/CheckboxIcon.js +++ b/frontend/components/Icon/CheckboxIcon.js @@ -9,20 +9,12 @@ export default function CheckboxIcon({ }: Props & { checked: boolean }) { return ( - - {checked - ? - : } - + {checked + ? + : } ); } diff --git a/frontend/components/Icon/CloseIcon.js b/frontend/components/Icon/CloseIcon.js index 36e0f0dec..d21adb3d8 100644 --- a/frontend/components/Icon/CloseIcon.js +++ b/frontend/components/Icon/CloseIcon.js @@ -6,18 +6,10 @@ import type { Props } from './Icon'; export default function CloseIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/CodeIcon.js b/frontend/components/Icon/CodeIcon.js index 3b1026c6f..ea08486b0 100644 --- a/frontend/components/Icon/CodeIcon.js +++ b/frontend/components/Icon/CodeIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function CodeIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/CollapsedIcon.js b/frontend/components/Icon/CollapsedIcon.js index 76ecdcfeb..8e2a24557 100644 --- a/frontend/components/Icon/CollapsedIcon.js +++ b/frontend/components/Icon/CollapsedIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function CollapsedIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/CollectionIcon.js b/frontend/components/Icon/CollectionIcon.js index eddb11a2d..411d0920f 100644 --- a/frontend/components/Icon/CollectionIcon.js +++ b/frontend/components/Icon/CollectionIcon.js @@ -9,17 +9,9 @@ export default function CollectionIcon({ }: Props & { expanded: boolean }) { return ( - - {expanded - ? - : } - + {expanded + ? + : } ); } diff --git a/frontend/components/Icon/DocumentIcon.js b/frontend/components/Icon/DocumentIcon.js index 8a7dc304c..95df6e521 100644 --- a/frontend/components/Icon/DocumentIcon.js +++ b/frontend/components/Icon/DocumentIcon.js @@ -6,18 +6,10 @@ import type { Props } from './Icon'; export default function DocumentIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/EditIcon.js b/frontend/components/Icon/EditIcon.js index e24b9304e..cfe26540b 100644 --- a/frontend/components/Icon/EditIcon.js +++ b/frontend/components/Icon/EditIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function EditIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/GoToIcon.js b/frontend/components/Icon/GoToIcon.js index cc1d1e10d..04dbb6269 100644 --- a/frontend/components/Icon/GoToIcon.js +++ b/frontend/components/Icon/GoToIcon.js @@ -6,18 +6,10 @@ import type { Props } from './Icon'; export default function GoToIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/Heading1Icon.js b/frontend/components/Icon/Heading1Icon.js index ea54065ee..41fd8552f 100644 --- a/frontend/components/Icon/Heading1Icon.js +++ b/frontend/components/Icon/Heading1Icon.js @@ -6,16 +6,7 @@ import type { Props } from './Icon'; export default function Heading1Icon(props: Props) { return ( - - - {' '} - + ); } diff --git a/frontend/components/Icon/Heading2Icon.js b/frontend/components/Icon/Heading2Icon.js index 1ac85828a..567fa4181 100644 --- a/frontend/components/Icon/Heading2Icon.js +++ b/frontend/components/Icon/Heading2Icon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function Heading2Icon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/HomeIcon.js b/frontend/components/Icon/HomeIcon.js index ce807c01a..47d51f01a 100644 --- a/frontend/components/Icon/HomeIcon.js +++ b/frontend/components/Icon/HomeIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function HomeIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/HorizontalRuleIcon.js b/frontend/components/Icon/HorizontalRuleIcon.js index fcafd88b1..a0a663c28 100644 --- a/frontend/components/Icon/HorizontalRuleIcon.js +++ b/frontend/components/Icon/HorizontalRuleIcon.js @@ -6,18 +6,10 @@ import type { Props } from './Icon'; export default function HorizontalRuleIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/Icon.js b/frontend/components/Icon/Icon.js index f7bf1b9ac..23619a08f 100644 --- a/frontend/components/Icon/Icon.js +++ b/frontend/components/Icon/Icon.js @@ -1,6 +1,5 @@ // @flow import React from 'react'; -import styled from 'styled-components'; import { color } from 'styles/constants'; export type Props = { @@ -16,25 +15,29 @@ type BaseProps = { children?: React$Element<*>, }; -export default function Icon({ children, ...rest }: Props & BaseProps) { +export default function Icon({ + children, + className, + ...rest +}: Props & BaseProps) { + const size = rest.size ? rest.size + 'px' : '24px'; + + let fill = color.slateDark; + if (rest.color) fill = rest.color; + if (rest.light) fill = color.white; + if (rest.black) fill = color.black; + if (rest.primary) fill = color.primary; + return ( - + {children} - + ); } - -const Wrapper = styled.span` - svg { - width: ${props => (props.size ? props.size + 'px' : 'auto')}; - height: ${props => (props.size ? props.size + 'px' : 'auto')}; - - fill: ${props => { - if (props.color) return props.color; - if (props.light) return color.white; - if (props.black) return color.black; - if (props.primary) return color.primary; - return color.slateDark; - }}; - } -`; diff --git a/frontend/components/Icon/ImageIcon.js b/frontend/components/Icon/ImageIcon.js index 9ba5fd5f8..3901f9e03 100644 --- a/frontend/components/Icon/ImageIcon.js +++ b/frontend/components/Icon/ImageIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function ImageIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/ItalicIcon.js b/frontend/components/Icon/ItalicIcon.js index 4a550843c..0987bdda6 100644 --- a/frontend/components/Icon/ItalicIcon.js +++ b/frontend/components/Icon/ItalicIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function ItalicIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/LinkIcon.js b/frontend/components/Icon/LinkIcon.js index 5a8ca7d78..73d9257d7 100644 --- a/frontend/components/Icon/LinkIcon.js +++ b/frontend/components/Icon/LinkIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function LinkIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/MoreIcon.js b/frontend/components/Icon/MoreIcon.js index a997ea0d1..cffcbe0fc 100644 --- a/frontend/components/Icon/MoreIcon.js +++ b/frontend/components/Icon/MoreIcon.js @@ -6,18 +6,10 @@ import type { Props } from './Icon'; export default function MoreIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/NewDocumentIcon.js b/frontend/components/Icon/NewDocumentIcon.js index 826e5967d..65140ce87 100644 --- a/frontend/components/Icon/NewDocumentIcon.js +++ b/frontend/components/Icon/NewDocumentIcon.js @@ -6,18 +6,10 @@ import type { Props } from './Icon'; export default function NewDocumentIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/OpenIcon.js b/frontend/components/Icon/OpenIcon.js new file mode 100644 index 000000000..4324f7610 --- /dev/null +++ b/frontend/components/Icon/OpenIcon.js @@ -0,0 +1,12 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function OpenIcon(props: Props) { + return ( + + + + ); +} diff --git a/frontend/components/Icon/OrderedListIcon.js b/frontend/components/Icon/OrderedListIcon.js index 8bb2836c4..855794bea 100644 --- a/frontend/components/Icon/OrderedListIcon.js +++ b/frontend/components/Icon/OrderedListIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function OrderedListIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/PlusIcon.js b/frontend/components/Icon/PlusIcon.js index 5e5524388..9301d2a80 100644 --- a/frontend/components/Icon/PlusIcon.js +++ b/frontend/components/Icon/PlusIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function PlusIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/SearchIcon.js b/frontend/components/Icon/SearchIcon.js index 76d523f71..d111fef9e 100644 --- a/frontend/components/Icon/SearchIcon.js +++ b/frontend/components/Icon/SearchIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function SearchIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/StarredIcon.js b/frontend/components/Icon/StarredIcon.js index fdf6b2e6a..8c6da2003 100644 --- a/frontend/components/Icon/StarredIcon.js +++ b/frontend/components/Icon/StarredIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function StarredIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/StrikethroughIcon.js b/frontend/components/Icon/StrikethroughIcon.js index 94bc9448b..f00c91ec6 100644 --- a/frontend/components/Icon/StrikethroughIcon.js +++ b/frontend/components/Icon/StrikethroughIcon.js @@ -6,15 +6,7 @@ import type { Props } from './Icon'; export default function StrikethroughIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/TableIcon.js b/frontend/components/Icon/TableIcon.js index c7d5283be..fb5e527f2 100644 --- a/frontend/components/Icon/TableIcon.js +++ b/frontend/components/Icon/TableIcon.js @@ -6,18 +6,10 @@ import type { Props } from './Icon'; export default function TableIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/TodoListIcon.js b/frontend/components/Icon/TodoListIcon.js index 836eaa89b..7cd7c1984 100644 --- a/frontend/components/Icon/TodoListIcon.js +++ b/frontend/components/Icon/TodoListIcon.js @@ -6,18 +6,7 @@ import type { Props } from './Icon'; export default function TodoListIcon(props: Props) { return ( - - - + ); } diff --git a/frontend/components/Icon/TrashIcon.js b/frontend/components/Icon/TrashIcon.js new file mode 100644 index 000000000..d76379fe1 --- /dev/null +++ b/frontend/components/Icon/TrashIcon.js @@ -0,0 +1,12 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function TrashIcon(props: Props) { + return ( + + + + ); +} diff --git a/frontend/components/Layout/components/SidebarLink.js b/frontend/components/Layout/components/SidebarLink.js index 7bb24290d..b3c5456a4 100644 --- a/frontend/components/Layout/components/SidebarLink.js +++ b/frontend/components/Layout/components/SidebarLink.js @@ -15,14 +15,9 @@ const activeStyle = { const StyleableDiv = props =>
; const StyledGoTo = styled(CollapsedIcon)` - margin-right: -10px; - - svg { - margin-bottom: -4px; - margin-right: 6px; - - ${({ expanded }) => !expanded && 'transform: rotate(-90deg);'} - } + margin-bottom: -4px; + margin-right: 0; + ${({ expanded }) => !expanded && 'transform: rotate(-90deg);'} `; const IconWrapper = styled.span` diff --git a/frontend/menus/BlockMenu.js b/frontend/menus/BlockMenu.js index ebdd24143..08192ce35 100644 --- a/frontend/menus/BlockMenu.js +++ b/frontend/menus/BlockMenu.js @@ -33,7 +33,7 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; {...rest} > - Add images + Add images Start list diff --git a/frontend/scenes/Document/Document.js b/frontend/scenes/Document/Document.js index f89127fd3..371765cd9 100644 --- a/frontend/scenes/Document/Document.js +++ b/frontend/scenes/Document/Document.js @@ -58,7 +58,6 @@ type Props = { @observable isDragging = false; @observable isLoading = false; @observable isSaving = false; - @observable showAsSaved = false; @observable notFound = false; @observable moveModalOpen: boolean = false; @@ -170,17 +169,9 @@ type Props = { if (redirect || this.props.newDocument) { this.props.history.push(document.url); - } else { - this.toggleShowAsSaved(); } }; - toggleShowAsSaved() { - this.showAsSaved = true; - this.isSaving = false; - this.savedTimeout = setTimeout(() => (this.showAsSaved = false), 2000); - } - onImageUploadStart = () => { this.isLoading = true; }; @@ -194,7 +185,7 @@ type Props = { this.document.updateData({ text }, true); }; - onCancel = () => { + onDiscard = () => { let url; if (this.document && this.document.url) { url = this.document.url; @@ -265,7 +256,7 @@ type Props = { onImageUploadStop={this.onImageUploadStop} onChange={this.onChange} onSave={this.onSave} - onCancel={this.onCancel} + onCancel={this.onDiscard} readOnly={!this.isEditing} /> {this.isEditing && - Cancel + Discard } {!this.isEditing && @@ -326,12 +317,11 @@ const Separator = styled.div` const HeaderAction = styled(Flex)` justify-content: center; align-items: center; - min-height: 43px; - color: ${color.text}; - padding: 0 0 0 14px; + padding: 0 0 0 10px; a { color: ${color.text}; + height: 24px; } `; diff --git a/frontend/scenes/Search/components/SearchField/SearchField.js b/frontend/scenes/Search/components/SearchField/SearchField.js index 48432320b..8c0645767 100644 --- a/frontend/scenes/Search/components/SearchField/SearchField.js +++ b/frontend/scenes/Search/components/SearchField/SearchField.js @@ -61,7 +61,7 @@ const StyledInput = styled.input` const StyledIcon = styled(SearchIcon)` position: relative; - top: 6px; + top: 4px; `; export default SearchField; From bd1cdb4a9f18549e3e57d509f2795199922f5d3c Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sat, 21 Oct 2017 16:36:55 -0700 Subject: [PATCH 09/20] fixed lint --- frontend/stores/DocumentsStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/stores/DocumentsStore.js b/frontend/stores/DocumentsStore.js index 692ce14bb..817b11456 100644 --- a/frontend/stores/DocumentsStore.js +++ b/frontend/stores/DocumentsStore.js @@ -115,7 +115,7 @@ class DocumentsStore extends BaseStore { if (!this.getById(id)) this.fetch(id, true); }; - @action fetch = async (id: string, prefetch: boolean): Promise<*> => { + @action fetch = async (id: string, prefetch?: boolean): Promise<*> => { /** If document has been fetched under 5s ago, return it */ const existingDocument = this.getById(id); if (existingDocument && existingDocument.timeSinceFetch < 5) From 55bacb8094ac8b0cd35f3746a8fc3595baff56e8 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 21 Oct 2017 16:39:12 -0700 Subject: [PATCH 10/20] Fix: active/hover state on document path --- .../components/DocumentMove/components/PathToDocument.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js b/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js index b8ed4f376..82058efd4 100644 --- a/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js +++ b/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js @@ -38,9 +38,7 @@ const ResultWrapperLink = ResultWrapper.withComponent('a').extend` cursor: pointer; ${StyledGoToIcon} { - svg { - fill: ${color.white}; - } + fill: ${color.white}; } } `; From 3652b19e99a3fdd114dd26c94a9552c47ad3f8e0 Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sat, 21 Oct 2017 17:16:07 -0700 Subject: [PATCH 11/20] Only use inline tags if they have space around them --- .../components/Editor/plugins/MarkdownShortcuts.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/components/Editor/plugins/MarkdownShortcuts.js b/frontend/components/Editor/plugins/MarkdownShortcuts.js index de530b006..2431b973e 100644 --- a/frontend/components/Editor/plugins/MarkdownShortcuts.js +++ b/frontend/components/Editor/plugins/MarkdownShortcuts.js @@ -73,8 +73,19 @@ export default function MarkdownShortcuts() { let { mark, shortcut } = key; let inlineTags = []; + // only add tags if they have spaces around them or the tag is beginning or the end of the block for (let i = 0; i < startBlock.text.length; i++) { - if (startBlock.text.slice(i, i + shortcut.length) === shortcut) + const { text } = startBlock; + const start = i; + const end = i + shortcut.length; + if ( + text.slice(start, end) === shortcut && + (start === 0 || + end === text.length || + [text.slice(start - 1, start), text.slice(end, end + 1)].includes( + ' ' + )) + ) inlineTags.push(i); } From 000e25be5e7d025f37ec4b216c7cdea327275ab5 Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sat, 21 Oct 2017 19:20:18 -0700 Subject: [PATCH 12/20] Fixed importing with parent document --- frontend/components/DropToImport/DropToImport.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/components/DropToImport/DropToImport.js b/frontend/components/DropToImport/DropToImport.js index 39924d48b..8c1341f70 100644 --- a/frontend/components/DropToImport/DropToImport.js +++ b/frontend/components/DropToImport/DropToImport.js @@ -40,11 +40,7 @@ class DropToImport extends Component { text, }; - if (documentId) { - data.parentDocument = { - id: documentId, - }; - } + if (documentId) data.parentDocument = documentId; let document = new Document(data); document = await document.save(); From 30bb7bcde5351b56cef0697e75deb0c4e7f1771b Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sat, 21 Oct 2017 19:29:14 -0700 Subject: [PATCH 13/20] Fixes --- frontend/components/Layout/components/SidebarCollections.js | 3 +-- frontend/models/Document.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/components/Layout/components/SidebarCollections.js b/frontend/components/Layout/components/SidebarCollections.js index a40a2d0ea..d49c7cc72 100644 --- a/frontend/components/Layout/components/SidebarCollections.js +++ b/frontend/components/Layout/components/SidebarCollections.js @@ -133,13 +133,12 @@ type DocumentLinkProps = { history: Object, activeDocument: ?Document, activeDocumentRef: HTMLElement => void, - prefetchDocument: string => void, + prefetchDocument: (documentId: string) => void, depth: number, }; const DocumentLink = observer( ({ - documents, document, activeDocument, activeDocumentRef, diff --git a/frontend/models/Document.js b/frontend/models/Document.js index b6a3455d5..dbde53e3c 100644 --- a/frontend/models/Document.js +++ b/frontend/models/Document.js @@ -250,6 +250,7 @@ class Document extends BaseModel { if (dirty) this.hasPendingChanges = true; this.data = data; extendObservable(this, data); + this.fetchedAt = new Date(); } constructor(data?: Object = {}) { @@ -257,7 +258,6 @@ class Document extends BaseModel { this.updateData(data); this.errors = stores.errors; - this.fetchedAt = new Date(); } } From 0514090253fa29bf68b7aa8f0567108f90f8f6bb Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sat, 21 Oct 2017 19:39:13 -0700 Subject: [PATCH 14/20] fixes --- frontend/models/Document.js | 6 ------ frontend/stores/DocumentsStore.js | 5 ----- 2 files changed, 11 deletions(-) diff --git a/frontend/models/Document.js b/frontend/models/Document.js index dbde53e3c..4f4c760ef 100644 --- a/frontend/models/Document.js +++ b/frontend/models/Document.js @@ -40,7 +40,6 @@ class Document extends BaseModel { views: number; data: Object; - fetchedAt: Date; /* Computed */ @@ -102,10 +101,6 @@ class Document extends BaseModel { : null; } - get timeSinceFetch(): number { - return (new Date() - this.fetchedAt) / 1000; - } - /* Actions */ @action star = async () => { @@ -250,7 +245,6 @@ class Document extends BaseModel { if (dirty) this.hasPendingChanges = true; this.data = data; extendObservable(this, data); - this.fetchedAt = new Date(); } constructor(data?: Object = {}) { diff --git a/frontend/stores/DocumentsStore.js b/frontend/stores/DocumentsStore.js index 817b11456..6314d1b05 100644 --- a/frontend/stores/DocumentsStore.js +++ b/frontend/stores/DocumentsStore.js @@ -116,11 +116,6 @@ class DocumentsStore extends BaseStore { }; @action fetch = async (id: string, prefetch?: boolean): Promise<*> => { - /** If document has been fetched under 5s ago, return it */ - const existingDocument = this.getById(id); - if (existingDocument && existingDocument.timeSinceFetch < 5) - return existingDocument; - if (!prefetch) this.isFetching = true; try { From ca46ca2f06cc86ae3d22cce8815b92a53218445f Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sun, 22 Oct 2017 11:02:05 -0700 Subject: [PATCH 15/20] Added predictive document title --- frontend/scenes/Document/Document.js | 10 ++++++++-- frontend/stores/CollectionsStore.js | 14 ++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/frontend/scenes/Document/Document.js b/frontend/scenes/Document/Document.js index 371765cd9..d290ad791 100644 --- a/frontend/scenes/Document/Document.js +++ b/frontend/scenes/Document/Document.js @@ -22,6 +22,7 @@ import Document from 'models/Document'; import DocumentMove from './components/DocumentMove'; import UiStore from 'stores/UiStore'; import DocumentsStore from 'stores/DocumentsStore'; +import CollectionsStore from 'stores/CollectionsStore'; import DocumentMenu from 'menus/DocumentMenu'; import SaveAction from './components/SaveAction'; import LoadingPlaceholder from 'components/LoadingPlaceholder'; @@ -45,6 +46,7 @@ type Props = { location: Object, keydown: Object, documents: DocumentsStore, + collections: CollectionsStore, newDocument?: boolean, ui: UiStore, }; @@ -213,7 +215,9 @@ type Props = { const isMoving = this.props.match.path === matchDocumentMove; const document = this.document; const isFetching = !document; - const titleText = get(document, 'title', ''); + const titleText = + get(document, 'title', '') || + this.props.collections.titleForDocument(this.props.location.pathname); if (this.notFound) { return this.renderNotFound(); @@ -362,4 +366,6 @@ const StyledDropToImport = styled(DropToImport)` flex: 1; `; -export default withRouter(inject('ui', 'user', 'documents')(DocumentScene)); +export default withRouter( + inject('ui', 'user', 'documents', 'collections')(DocumentScene) +); diff --git a/frontend/stores/CollectionsStore.js b/frontend/stores/CollectionsStore.js index 6b5d2d997..891206c19 100644 --- a/frontend/stores/CollectionsStore.js +++ b/frontend/stores/CollectionsStore.js @@ -25,6 +25,7 @@ type Options = { type DocumentPathItem = { id: string, title: string, + url: string, type: 'document' | 'collection', }; @@ -59,16 +60,16 @@ class CollectionsStore { let results = []; const travelDocuments = (documentList, path) => documentList.forEach(document => { - const { id, title } = document; - const node = { id, title, type: 'document' }; + const { id, title, url } = document; + const node = { id, title, url, type: 'document' }; results.push(_.concat(path, node)); travelDocuments(document.children, _.concat(path, [node])); }); if (this.isLoaded) { this.data.forEach(collection => { - const { id, name } = collection; - const node = { id, title: name, type: 'collection' }; + const { id, name, url } = collection; + const node = { id, title: name, url, type: 'collection' }; results.push([node]); travelDocuments(collection.documents, [node]); }); @@ -87,6 +88,11 @@ class CollectionsStore { return this.pathsToDocuments.find(path => path.id === documentId); } + titleForDocument(documentUrl: string): ?string { + const path = this.pathsToDocuments.find(path => path.url === documentUrl); + if (path) return path.title; + } + /* Actions */ @action fetchAll = async (): Promise<*> => { From 6959cec1bfcd0e15ef216103ed7338d36447c4a9 Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sun, 22 Oct 2017 11:22:25 -0700 Subject: [PATCH 16/20] improved code readability --- .../components/Editor/plugins/MarkdownShortcuts.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/components/Editor/plugins/MarkdownShortcuts.js b/frontend/components/Editor/plugins/MarkdownShortcuts.js index 2431b973e..10fad8247 100644 --- a/frontend/components/Editor/plugins/MarkdownShortcuts.js +++ b/frontend/components/Editor/plugins/MarkdownShortcuts.js @@ -78,13 +78,16 @@ export default function MarkdownShortcuts() { const { text } = startBlock; const start = i; const end = i + shortcut.length; + const beginningOfBlock = start === 0; + const endOfBlock = end === text.length; + const surroundedByWhitespaces = [ + text.slice(start - 1, start), + text.slice(end, end + 1), + ].includes(' '); + if ( text.slice(start, end) === shortcut && - (start === 0 || - end === text.length || - [text.slice(start - 1, start), text.slice(end, end + 1)].includes( - ' ' - )) + (beginningOfBlock || endOfBlock || surroundedByWhitespaces) ) inlineTags.push(i); } From 28fa5f2e3526c60c4115926864cf9164fc4c1b18 Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sun, 22 Oct 2017 20:17:54 -0700 Subject: [PATCH 17/20] Allow toggling sidebar links --- frontend/components/Icon/Icon.js | 3 + .../Layout/components/SidebarCollections.js | 34 +++++----- .../Layout/components/SidebarLink.js | 62 ++++++++++++++----- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/frontend/components/Icon/Icon.js b/frontend/components/Icon/Icon.js index 23619a08f..46bf1acbc 100644 --- a/frontend/components/Icon/Icon.js +++ b/frontend/components/Icon/Icon.js @@ -9,6 +9,7 @@ export type Props = { primary?: boolean, color?: string, size?: number, + onClick?: Function, }; type BaseProps = { @@ -18,6 +19,7 @@ type BaseProps = { export default function Icon({ children, className, + onClick, ...rest }: Props & BaseProps) { const size = rest.size ? rest.size + 'px' : '24px'; @@ -36,6 +38,7 @@ export default function Icon({ viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" className={className} + onClick={onClick} > {children} diff --git a/frontend/components/Layout/components/SidebarCollections.js b/frontend/components/Layout/components/SidebarCollections.js index 0f2242508..7cb3e992a 100644 --- a/frontend/components/Layout/components/SidebarCollections.js +++ b/frontend/components/Layout/components/SidebarCollections.js @@ -182,27 +182,27 @@ const DocumentLink = observer( > 0} - expanded={showChildren} + expand={showChildren} + expandedContent={ + document.children.length + ? + {document.children.map(childDocument => ( + + ))} + + : undefined + } > {document.title} - - {showChildren && - - {document.children && - document.children.map(childDocument => ( - - ))} - } ); } diff --git a/frontend/components/Layout/components/SidebarLink.js b/frontend/components/Layout/components/SidebarLink.js index b3c5456a4..5a349fd47 100644 --- a/frontend/components/Layout/components/SidebarLink.js +++ b/frontend/components/Layout/components/SidebarLink.js @@ -1,5 +1,7 @@ // @flow -import React from 'react'; +import React, { Component } from 'react'; +import { observable, action } from 'mobx'; +import { observer } from 'mobx-react'; import { NavLink } from 'react-router-dom'; import { color, fontWeight } from 'styles/constants'; import styled from 'styled-components'; @@ -54,22 +56,54 @@ type Props = { onClick?: SyntheticEvent => *, children?: React$Element<*>, icon?: React$Element<*>, - hasChildren?: boolean, - expanded?: boolean, + expand?: boolean, + expandedContent?: React$Element<*>, }; -function SidebarLink({ icon, children, expanded, ...rest }: Props) { - const Component = styleComponent(rest.to ? NavLink : StyleableDiv); +@observer class SidebarLink extends Component { + props: Props; - return ( - - - {icon && {icon}} - {rest.hasChildren && } - {children} - - - ); + componentDidMount() { + if (this.props.expand) this.handleExpand(); + } + + componentDidReceiveProps(nextProps: Props) { + if (nextProps.expand) this.handleExpand(); + } + + @observable expanded: boolean = false; + + @action handleClick = (event: SyntheticEvent) => { + event.preventDefault(); + event.stopPropagation(); + this.expanded = !this.expanded; + }; + + @action handleExpand = () => { + this.expanded = true; + }; + + render() { + const { icon, children, expandedContent, ...rest } = this.props; + const Component = styleComponent(rest.to ? NavLink : StyleableDiv); + + return ( + + + {icon && {icon}} + {expandedContent && + } + {children} + + {this.expanded && expandedContent} + + ); + } } const Content = styled.div` From c6537279ba85fb2f3a356b8841e6657aa1be8862 Mon Sep 17 00:00:00 2001 From: Jori Lallo Date: Sun, 22 Oct 2017 22:20:53 -0700 Subject: [PATCH 18/20] Fixes for empty document --- frontend/components/Editor/Editor.js | 2 +- server/presenters/document.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/components/Editor/Editor.js b/frontend/components/Editor/Editor.js index 6a99e730f..659cb4b20 100644 --- a/frontend/components/Editor/Editor.js +++ b/frontend/components/Editor/Editor.js @@ -51,7 +51,7 @@ type KeyData = { onImageUploadStop: props.onImageUploadStop, }); - if (props.text) { + if (props.text.trim().length) { this.state = { state: Markdown.deserialize(props.text) }; } else { this.state = { state: Plain.deserialize('') }; diff --git a/server/presenters/document.js b/server/presenters/document.js index bf483cd1d..575d18f3b 100644 --- a/server/presenters/document.js +++ b/server/presenters/document.js @@ -14,6 +14,11 @@ async function present(ctx: Object, document: Document, options: ?Options) { ...options, }; ctx.cache.set(document.id, document); + + // For empty document content, return the title + if (document.text.trim().length === 0) + document.text = `# ${document.title || 'Untitled document'}`; + const data = { id: document.id, url: document.getUrl(), From e79897e5cb86db49caff84b7c30f013485d30ed6 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 23 Oct 2017 20:13:22 -0700 Subject: [PATCH 19/20] Fixes link editing --- frontend/components/Editor/Editor.js | 9 ++++----- .../Toolbar/components/DocumentResult.js | 4 ++-- .../components/Toolbar/components/LinkToolbar.js | 7 ++++--- frontend/components/Icon/NextIcon.js | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 frontend/components/Icon/NextIcon.js diff --git a/frontend/components/Editor/Editor.js b/frontend/components/Editor/Editor.js index 659cb4b20..b73d77388 100644 --- a/frontend/components/Editor/Editor.js +++ b/frontend/components/Editor/Editor.js @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import { observer } from 'mobx-react'; import { Editor, Plain } from 'slate'; import keydown from 'react-keydown'; -import type { Document, State, Editor as EditorType } from './types'; +import type { State, Editor as EditorType } from './types'; import getDataTransferFiles from 'utils/getDataTransferFiles'; import Flex from 'components/Flex'; import ClickablePadding from './components/ClickablePadding'; @@ -76,10 +76,10 @@ type KeyData = { onChange = (state: State) => { this.setState({ state }); - }; - onDocumentChange = (document: Document, state: State) => { - this.props.onChange(Markdown.serialize(state)); + if (this.state.state !== state) { + this.props.onChange(Markdown.serialize(state)); + } }; handleDrop = async (ev: SyntheticEvent) => { @@ -205,7 +205,6 @@ type KeyData = { state={this.state.state} onKeyDown={this.onKeyDown} onChange={this.onChange} - onDocumentChange={this.onDocumentChange} onSave={onSave} readOnly={readOnly} /> diff --git a/frontend/components/Editor/components/Toolbar/components/DocumentResult.js b/frontend/components/Editor/components/Toolbar/components/DocumentResult.js index 8351681d6..7d4b157b0 100644 --- a/frontend/components/Editor/components/Toolbar/components/DocumentResult.js +++ b/frontend/components/Editor/components/Toolbar/components/DocumentResult.js @@ -3,7 +3,7 @@ import React from 'react'; import styled from 'styled-components'; import { fontWeight, color } from 'styles/constants'; import Document from 'models/Document'; -import GoToIcon from 'components/Icon/GoToIcon'; +import NextIcon from 'components/Icon/NextIcon'; type Props = { innerRef?: Function, @@ -14,7 +14,7 @@ type Props = { function DocumentResult({ document, ...rest }: Props) { return ( - + {document.title} ); diff --git a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js index 010048344..c7b38bb24 100644 --- a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js +++ b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js @@ -114,9 +114,10 @@ class LinkToolbar extends Component { const { state } = this.props; const transform = state.transform(); - if (state.selection.isExpanded) { + if (href) { + transform.setInline({ type: 'link', data: { href } }); + } else { transform.unwrapInline('link'); - if (href) transform.wrapInline({ type: 'link', data: { href } }); } this.props.onChange(transform.apply()); @@ -179,7 +180,7 @@ class LinkToolbar extends Component { } const SearchResults = styled.div` - background: rgba(34, 34, 34, .95); + background: #2F3336; position: absolute; top: 100%; width: 100%; diff --git a/frontend/components/Icon/NextIcon.js b/frontend/components/Icon/NextIcon.js new file mode 100644 index 000000000..ab9144dc6 --- /dev/null +++ b/frontend/components/Icon/NextIcon.js @@ -0,0 +1,15 @@ +// @flow +import React from 'react'; +import Icon from './Icon'; +import type { Props } from './Icon'; + +export default function NextIcon(props: Props) { + return ( + + + + ); +} From 87b3bcba614d0fbb2dd5f38b7e5ec43db4b1cbc6 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 24 Oct 2017 00:08:14 -0700 Subject: [PATCH 20/20] Quick fix for #367 --- frontend/components/Editor/insertImage.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/components/Editor/insertImage.js b/frontend/components/Editor/insertImage.js index 7f59ab5ce..d3e4c6bd7 100644 --- a/frontend/components/Editor/insertImage.js +++ b/frontend/components/Editor/insertImage.js @@ -15,6 +15,7 @@ export default async function insertImageFile( try { // load the file as a data URL const id = uuid.v4(); + const alt = ''; const reader = new FileReader(); reader.addEventListener('load', () => { const src = reader.result; @@ -24,7 +25,7 @@ export default async function insertImageFile( .insertBlock({ type: 'image', isVoid: true, - data: { src, id, loading: true }, + data: { src, id, alt, loading: true }, }) .apply(); editor.onChange(state); @@ -45,7 +46,7 @@ export default async function insertImageFile( ); return finalTransform.setNodeByKey(placeholder.key, { - data: { src, loading: false }, + data: { src, alt, loading: false }, }); } catch (err) { throw err;