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);
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();
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/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/Editor.js b/frontend/components/Editor/Editor.js
index 46f55213a..7e814dca4 100644
--- a/frontend/components/Editor/Editor.js
+++ b/frontend/components/Editor/Editor.js
@@ -4,7 +4,7 @@ import { observable } from 'mobx';
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';
@@ -50,7 +50,7 @@ type KeyData = {
onImageUploadStop: props.onImageUploadStop,
});
- if (props.text) {
+ if (props.text.trim().length) {
this.editorState = Markdown.deserialize(props.text);
} else {
this.editorState = Plain.deserialize('');
@@ -73,12 +73,12 @@ type KeyData = {
}
}
- onChange = (editorState: State) => {
- this.editorState = editorState;
- };
+ onChange = (state: State) => {
+ if (this.editorState !== state) {
+ this.props.onChange(Markdown.serialize(state));
+ }
- onDocumentChange = (document: Document, editorState: State) => {
- this.props.onChange(Markdown.serialize(editorState));
+ this.editorState = editorState;
};
handleDrop = async (ev: SyntheticEvent) => {
@@ -205,7 +205,6 @@ type KeyData = {
state={this.editorState}
onKeyDown={this.onKeyDown}
onChange={this.onChange}
- onDocumentChange={this.onDocumentChange}
onSave={onSave}
readOnly={readOnly}
/>
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..281d40cbc 100644
--- a/frontend/components/Editor/components/Toolbar/Toolbar.js
+++ b/frontend/components/Editor/components/Toolbar/Toolbar.js
@@ -144,15 +144,16 @@ 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;
+ 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/DocumentResult.js b/frontend/components/Editor/components/Toolbar/components/DocumentResult.js
index e7ac8c2e4..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 Icon from 'components/Icon';
+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/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/Editor/components/Toolbar/components/LinkToolbar.js b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js
index e34d04b4b..c7b38bb24 100644
--- a/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js
+++ b/frontend/components/Editor/components/Toolbar/components/LinkToolbar.js
@@ -11,7 +11,9 @@ import DocumentResult from './DocumentResult';
import type { State } from '../../../types';
import DocumentsStore from 'stores/DocumentsStore';
import keydown from 'react-keydown';
-import Icon from 'components/Icon';
+import CloseIcon from 'components/Icon/CloseIcon';
+import OpenIcon from 'components/Icon/OpenIcon';
+import TrashIcon from 'components/Icon/TrashIcon';
import Flex from 'components/Flex';
@keydown
@@ -109,16 +111,16 @@ 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 });
+ transform.setInline({ type: 'link', data: { href } });
+ } else {
+ transform.unwrapInline('link');
}
- const state = transform.apply();
- this.props.onChange(state);
+ this.props.onChange(transform.apply());
this.props.onBlur();
};
@@ -144,12 +146,10 @@ class LinkToolbar extends Component {
/>
{this.isEditing &&
-
+
}
- {this.isEditing
- ?
- : }
+ {this.isEditing ? : }
{hasResults &&
@@ -180,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/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;
diff --git a/frontend/components/Editor/plugins/MarkdownShortcuts.js b/frontend/components/Editor/plugins/MarkdownShortcuts.js
index de530b006..10fad8247 100644
--- a/frontend/components/Editor/plugins/MarkdownShortcuts.js
+++ b/frontend/components/Editor/plugins/MarkdownShortcuts.js
@@ -73,8 +73,22 @@ 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;
+ 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 &&
+ (beginningOfBlock || endOfBlock || surroundedByWhitespaces)
+ )
inlineTags.push(i);
}
diff --git a/frontend/components/Icon/BackIcon.js b/frontend/components/Icon/BackIcon.js
new file mode 100644
index 000000000..4586fd1a5
--- /dev/null
+++ b/frontend/components/Icon/BackIcon.js
@@ -0,0 +1,15 @@
+// @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..fa5dd4e62 100644
--- a/frontend/components/Icon/BoldIcon.js
+++ b/frontend/components/Icon/BoldIcon.js
@@ -6,16 +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 b87afa195..89fa741b9 100644
--- a/frontend/components/Icon/BulletedListIcon.js
+++ b/frontend/components/Icon/BulletedListIcon.js
@@ -6,16 +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
new file mode 100644
index 000000000..5540808c8
--- /dev/null
+++ b/frontend/components/Icon/CheckboxIcon.js
@@ -0,0 +1,20 @@
+// @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 88453bbce..000000000
--- a/frontend/components/Icon/ChevronIcon.js
+++ /dev/null
@@ -1,21 +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..d21adb3d8
--- /dev/null
+++ b/frontend/components/Icon/CloseIcon.js
@@ -0,0 +1,15 @@
+// @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 1116cb68b..ea08486b0 100644
--- a/frontend/components/Icon/CodeIcon.js
+++ b/frontend/components/Icon/CodeIcon.js
@@ -6,16 +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
new file mode 100644
index 000000000..8e2a24557
--- /dev/null
+++ b/frontend/components/Icon/CollapsedIcon.js
@@ -0,0 +1,12 @@
+// @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..411d0920f
--- /dev/null
+++ b/frontend/components/Icon/CollectionIcon.js
@@ -0,0 +1,17 @@
+// @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..95df6e521
--- /dev/null
+++ b/frontend/components/Icon/DocumentIcon.js
@@ -0,0 +1,15 @@
+// @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..cfe26540b
--- /dev/null
+++ b/frontend/components/Icon/EditIcon.js
@@ -0,0 +1,12 @@
+// @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/GoToIcon.js b/frontend/components/Icon/GoToIcon.js
new file mode 100644
index 000000000..04dbb6269
--- /dev/null
+++ b/frontend/components/Icon/GoToIcon.js
@@ -0,0 +1,15 @@
+// @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/Heading1Icon.js b/frontend/components/Icon/Heading1Icon.js
index 1dc440549..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 46b115906..567fa4181 100644
--- a/frontend/components/Icon/Heading2Icon.js
+++ b/frontend/components/Icon/Heading2Icon.js
@@ -6,16 +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
new file mode 100644
index 000000000..47d51f01a
--- /dev/null
+++ b/frontend/components/Icon/HomeIcon.js
@@ -0,0 +1,12 @@
+// @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..a0a663c28
--- /dev/null
+++ b/frontend/components/Icon/HorizontalRuleIcon.js
@@ -0,0 +1,15 @@
+// @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/Icon.js b/frontend/components/Icon/Icon.js
index 5921db7eb..46bf1acbc 100644
--- a/frontend/components/Icon/Icon.js
+++ b/frontend/components/Icon/Icon.js
@@ -1,53 +1,46 @@
// @flow
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,
+ size?: number,
+ onClick?: Function,
};
type BaseProps = {
- children?: React$Element,
+ children?: React$Element<*>,
};
export default function Icon({
children,
- light,
- type,
+ className,
+ onClick,
...rest
}: Props & BaseProps) {
- if (type) {
- children = React.createElement(Icons[type], {
- size: '1em',
- color: light ? color.white : undefined,
- ...rest,
- });
+ const size = rest.size ? rest.size + 'px' : '24px';
- return (
-
- {children}
-
- );
- }
+ 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 (
-
+
+
);
}
-
-const FeatherWrapper = styled.span`
- position: relative;
- top: .1em;
-`;
-
-const Wrapper = styled.span`
- svg {
- fill: ${props => (props.light ? color.white : color.black)}
- }
-`;
diff --git a/frontend/components/Icon/ImageIcon.js b/frontend/components/Icon/ImageIcon.js
new file mode 100644
index 000000000..3901f9e03
--- /dev/null
+++ b/frontend/components/Icon/ImageIcon.js
@@ -0,0 +1,12 @@
+// @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..0987bdda6 100644
--- a/frontend/components/Icon/ItalicIcon.js
+++ b/frontend/components/Icon/ItalicIcon.js
@@ -6,16 +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 5f4c80e2f..73d9257d7 100644
--- a/frontend/components/Icon/LinkIcon.js
+++ b/frontend/components/Icon/LinkIcon.js
@@ -6,16 +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
new file mode 100644
index 000000000..cffcbe0fc
--- /dev/null
+++ b/frontend/components/Icon/MoreIcon.js
@@ -0,0 +1,15 @@
+// @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..65140ce87
--- /dev/null
+++ b/frontend/components/Icon/NewDocumentIcon.js
@@ -0,0 +1,15 @@
+// @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/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 (
+
+
+
+ );
+}
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 89e51f225..855794bea 100644
--- a/frontend/components/Icon/OrderedListIcon.js
+++ b/frontend/components/Icon/OrderedListIcon.js
@@ -6,16 +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
new file mode 100644
index 000000000..9301d2a80
--- /dev/null
+++ b/frontend/components/Icon/PlusIcon.js
@@ -0,0 +1,12 @@
+// @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/SearchIcon.js b/frontend/components/Icon/SearchIcon.js
new file mode 100644
index 000000000..d111fef9e
--- /dev/null
+++ b/frontend/components/Icon/SearchIcon.js
@@ -0,0 +1,12 @@
+// @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..8c6da2003
--- /dev/null
+++ b/frontend/components/Icon/StarredIcon.js
@@ -0,0 +1,12 @@
+// @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..f00c91ec6 100644
--- a/frontend/components/Icon/StrikethroughIcon.js
+++ b/frontend/components/Icon/StrikethroughIcon.js
@@ -6,16 +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
new file mode 100644
index 000000000..fb5e527f2
--- /dev/null
+++ b/frontend/components/Icon/TableIcon.js
@@ -0,0 +1,15 @@
+// @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
new file mode 100644
index 000000000..7cd7c1984
--- /dev/null
+++ b/frontend/components/Icon/TodoListIcon.js
@@ -0,0 +1,12 @@
+// @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/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/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/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..7cb3e992a 100644
--- a/frontend/components/Layout/components/SidebarCollections.js
+++ b/frontend/components/Layout/components/SidebarCollections.js
@@ -8,17 +8,20 @@ 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';
import UiStore from 'stores/UiStore';
import Document from 'models/Document';
+import DocumentsStore from 'stores/DocumentsStore';
import { type NavigationNode } from 'types';
type Props = {
history: Object,
collections: CollectionsStore,
+ documents: DocumentsStore,
activeDocument: ?Document,
onCreateCollection: () => void,
activeDocumentRef: HTMLElement => void,
@@ -35,6 +38,7 @@ type Props = {
activeDocument,
ui,
activeDocumentRef,
+ documents,
} = this.props;
return (
@@ -47,13 +51,17 @@ type Props = {
collection={collection}
activeDocument={activeDocument}
activeDocumentRef={activeDocumentRef}
+ prefetchDocument={documents.prefetchDocument}
ui={ui}
/>
))}
{collections.isLoaded &&
-
- Add new collection
+ }
+ >
+ New collection…
}
);
@@ -76,7 +84,9 @@ type Props = {
activeDocument,
ui,
activeDocumentRef,
+ prefetchDocument,
} = this.props;
+ const expanded = collection.id === ui.activeCollectionId;
return (
(this.dropzoneRef = ref)}
>
-
+ }
+ >
{collection.name}
@@ -103,7 +117,7 @@ type Props = {
- {collection.id === ui.activeCollectionId &&
+ {expanded &&
{collection.documents.map(document => (
))}
@@ -127,6 +142,7 @@ type DocumentLinkProps = {
history: Object,
activeDocument: ?Document,
activeDocumentRef: HTMLElement => void,
+ prefetchDocument: (documentId: string) => void,
depth: number,
};
@@ -135,22 +151,29 @@ const DocumentLink = observer(
document,
activeDocument,
activeDocumentRef,
+ prefetchDocument,
depth,
}: DocumentLinkProps) => {
const isActiveDocument =
activeDocument && activeDocument.id === document.id;
- const showChildren =
- activeDocument &&
+ const showChildren = !!(activeDocument &&
(activeDocument.pathToDocument
.map(entry => entry.id)
.includes(document.id) ||
- isActiveDocument);
+ isActiveDocument));
+
+ const handleMouseEnter = (event: SyntheticEvent) => {
+ event.stopPropagation();
+ event.preventDefault();
+ prefetchDocument(document.id);
+ };
return (
0}
- expanded={showChildren}
+ expand={showChildren}
+ expandedContent={
+ document.children.length
+ ?
+ {document.children.map(childDocument => (
+
+ ))}
+
+ : undefined
+ }
>
{document.title}
-
- {showChildren &&
-
- {document.children &&
- document.children.map(childDocument => (
-
- ))}
- }
);
}
);
const CollectionAction = styled.a`
+ position: absolute;
+ right: 0;
color: ${color.slate};
svg { opacity: .75; }
@@ -206,15 +232,16 @@ const StyledDropToImport = styled(DropToImport)`
`;
const Header = styled(Flex)`
- font-size: 11px;
+ font-size: 12px;
font-weight: ${fontWeight.semiBold};
text-transform: uppercase;
color: ${color.slate};
letter-spacing: 0.04em;
+ margin-bottom: 4px;
`;
const Children = styled(Flex)`
margin-left: 20px;
`;
-export default inject('collections', 'ui')(SidebarCollections);
+export default inject('collections', 'ui', 'documents')(SidebarCollections);
diff --git a/frontend/components/Layout/components/SidebarLink.js b/frontend/components/Layout/components/SidebarLink.js
index b10bf94be..5a349fd47 100644
--- a/frontend/components/Layout/components/SidebarLink.js
+++ b/frontend/components/Layout/components/SidebarLink.js
@@ -1,11 +1,12 @@
// @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';
-
import Flex from 'components/Flex';
-import ChevronIcon from 'components/Icon/ChevronIcon';
+import CollapsedIcon from 'components/Icon/CollapsedIcon';
const activeStyle = {
color: color.black,
@@ -15,12 +16,25 @@ const activeStyle = {
// This is a hack for `styleComponent()` as NavLink fails to render without `to` prop
const StyleableDiv = props => ;
+const StyledGoTo = styled(CollapsedIcon)`
+ margin-bottom: -4px;
+ margin-right: 0;
+ ${({ 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 +44,68 @@ 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<*>,
+ expand?: boolean,
+ expandedContent?: React$Element<*>,
+};
- return (
-
-
- {props.hasChildren && }
- {props.children}
-
-
- );
+@observer class SidebarLink extends Component {
+ props: Props;
+
+ 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 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/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/BlockMenu.js b/frontend/menus/BlockMenu.js
index 5cd3e6117..08192ce35 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
);
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..3e74bd8b2 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
@@ -61,11 +61,13 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
>
New child
- Move
- Export
+
+ Download
+
+ Move…
{allowDelete &&
- Delete
+ Delete…
}
);
diff --git a/frontend/scenes/Document/Document.js b/frontend/scenes/Document/Document.js
index 3db9f3b07..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';
@@ -31,6 +32,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 = `
@@ -44,6 +46,7 @@ type Props = {
location: Object,
keydown: Object,
documents: DocumentsStore,
+ collections: CollectionsStore,
newDocument?: boolean,
ui: UiStore,
};
@@ -57,7 +60,6 @@ type Props = {
@observable isDragging = false;
@observable isLoading = false;
@observable isSaving = false;
- @observable showAsSaved = false;
@observable notFound = false;
@observable moveModalOpen: boolean = false;
@@ -169,17 +171,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;
};
@@ -193,7 +187,7 @@ type Props = {
this.document.updateData({ text }, true);
};
- onCancel = () => {
+ onDiscard = () => {
let url;
if (this.document && this.document.url) {
url = this.document.url;
@@ -221,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();
@@ -264,7 +260,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 &&
@@ -303,7 +299,7 @@ type Props = {
{!this.isEditing &&
- New
+
}
@@ -325,19 +321,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,
- svg {
+ a {
color: ${color.text};
- opacity: .8;
- transition: opacity 100ms ease-in-out;
-
- &:hover {
- opacity: 1;
- }
+ height: 24px;
}
`;
@@ -378,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/scenes/Document/components/DocumentMove/components/PathToDocument.js b/frontend/scenes/Document/components/DocumentMove/components/PathToDocument.js
index 2e5bab449..82058efd4 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,8 @@ const ResultWrapperLink = ResultWrapper.withComponent('a').extend`
outline: none;
cursor: pointer;
- ${StyledChevronIcon} svg {
- fill: ${color.smokeLight};
+ ${StyledGoToIcon} {
+ fill: ${color.white};
}
}
`;
@@ -82,14 +79,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/scenes/Search/components/SearchField/SearchField.js b/frontend/scenes/Search/components/SearchField/SearchField.js
index dd9642d07..8c0645767 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: 4px;
`;
export default SearchField;
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<*> => {
diff --git a/frontend/stores/DocumentsStore.js b/frontend/stores/DocumentsStore.js
index 9ddf8925f..6314d1b05 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 });
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/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(),
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"