Upgrade prettier
This commit is contained in:
@ -10,7 +10,8 @@ type Props = {
|
||||
type?: 'info' | 'success' | 'warning' | 'danger' | 'offline',
|
||||
};
|
||||
|
||||
@observer class Alert extends React.Component {
|
||||
@observer
|
||||
class Alert extends React.Component {
|
||||
props: Props;
|
||||
defaultProps = {
|
||||
type: 'info',
|
||||
|
@ -29,35 +29,35 @@ const RealButton = styled.button`
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
top: .05em;
|
||||
top: 0.05em;
|
||||
}
|
||||
|
||||
${props => props.light && `
|
||||
${props =>
|
||||
props.light &&
|
||||
`
|
||||
color: ${color.text};
|
||||
background: ${lighten(0.08, color.slateLight)};
|
||||
|
||||
&:hover {
|
||||
background: ${color.slateLight};
|
||||
}
|
||||
`}
|
||||
|
||||
${props => props.neutral && `
|
||||
`} ${props =>
|
||||
props.neutral &&
|
||||
`
|
||||
background: ${color.slate};
|
||||
|
||||
&:hover {
|
||||
background: ${darken(0.05, color.slate)};
|
||||
}
|
||||
`}
|
||||
|
||||
${props => props.danger && `
|
||||
`} ${props =>
|
||||
props.danger &&
|
||||
`
|
||||
background: ${color.danger};
|
||||
|
||||
&:hover {
|
||||
background: ${darken(0.05, color.danger)};
|
||||
}
|
||||
`}
|
||||
|
||||
&:disabled {
|
||||
`} &:disabled {
|
||||
background: ${color.slateLight};
|
||||
cursor: default;
|
||||
}
|
||||
@ -68,7 +68,7 @@ const Label = styled.span`
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
${props => props.hasIcon && 'padding-left: 2px;'}
|
||||
${props => props.hasIcon && 'padding-left: 2px;'};
|
||||
`;
|
||||
|
||||
const Inner = styled.span`
|
||||
@ -78,7 +78,9 @@ const Inner = styled.span`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
${props => props.hasIcon && (props.small ? 'padding-left: 6px;' : 'padding-left: 10px;')}
|
||||
${props =>
|
||||
props.hasIcon &&
|
||||
(props.small ? 'padding-left: 6px;' : 'padding-left: 10px;')};
|
||||
`;
|
||||
|
||||
export type Props = {
|
||||
|
@ -19,9 +19,7 @@ const Content = styled.div`
|
||||
const CenteredContent = ({ children, ...rest }: Props) => {
|
||||
return (
|
||||
<Container {...rest}>
|
||||
<Content>
|
||||
{children}
|
||||
</Content>
|
||||
<Content>{children}</Content>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
@ -25,7 +25,8 @@ type Props = {
|
||||
value?: string,
|
||||
};
|
||||
|
||||
@observer class ColorPicker extends React.Component {
|
||||
@observer
|
||||
class ColorPicker extends React.Component {
|
||||
props: Props;
|
||||
|
||||
@observable selectedColor: string = colors[0];
|
||||
@ -52,26 +53,30 @@ type Props = {
|
||||
);
|
||||
};
|
||||
|
||||
@computed get customColor(): string {
|
||||
@computed
|
||||
get customColor(): string {
|
||||
return this.customColorValue &&
|
||||
validateColorHex(`#${this.customColorValue}`)
|
||||
? `#${this.customColorValue}`
|
||||
: colors[0];
|
||||
}
|
||||
|
||||
@action setColor = (color: string) => {
|
||||
@action
|
||||
setColor = (color: string) => {
|
||||
this.selectedColor = color;
|
||||
this.customColorSelected = false;
|
||||
this.fireCallback();
|
||||
};
|
||||
|
||||
@action focusOnCustomColor = (event: SyntheticEvent) => {
|
||||
@action
|
||||
focusOnCustomColor = (event: SyntheticEvent) => {
|
||||
this.selectedColor = '';
|
||||
this.customColorSelected = true;
|
||||
this.fireCallback();
|
||||
};
|
||||
|
||||
@action setCustomColor = (event: SyntheticEvent) => {
|
||||
@action
|
||||
setCustomColor = (event: SyntheticEvent) => {
|
||||
let target = event.target;
|
||||
if (target instanceof HTMLInputElement) {
|
||||
const color = target.value;
|
||||
@ -137,9 +142,7 @@ const SwatchOutset = styled(Flex)`
|
||||
border: 2px solid ${({ active, color }) => (active ? color : 'transparent')};
|
||||
border-radius: 2px;
|
||||
background: ${({ color }) => color};
|
||||
${({ onClick }) => onClick && `cursor: pointer;`}
|
||||
|
||||
&:last-child {
|
||||
${({ onClick }) => onClick && `cursor: pointer;`} &:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
`;
|
||||
|
@ -4,7 +4,11 @@ import styled from 'styled-components';
|
||||
import Flex from 'shared/components/Flex';
|
||||
|
||||
const Divider = () => {
|
||||
return <Flex auto justify="center"><Content /></Flex>;
|
||||
return (
|
||||
<Flex auto justify="center">
|
||||
<Content />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const Content = styled.span`
|
||||
|
@ -49,7 +49,7 @@ const DocumentLink = styled(Link)`
|
||||
outline: none;
|
||||
|
||||
${StyledStar} {
|
||||
opacity: .5;
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
@ -63,11 +63,12 @@ const DocumentLink = styled(Link)`
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: .25em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
`;
|
||||
|
||||
@observer class DocumentPreview extends Component {
|
||||
@observer
|
||||
class DocumentPreview extends Component {
|
||||
props: Props;
|
||||
|
||||
star = (ev: SyntheticEvent) => {
|
||||
@ -89,13 +90,15 @@ const DocumentLink = styled(Link)`
|
||||
<DocumentLink to={document.url} innerRef={innerRef} {...rest}>
|
||||
<h3>
|
||||
{document.title}
|
||||
{document.starred
|
||||
? <a onClick={this.unstar}>
|
||||
<StyledStar solid />
|
||||
</a>
|
||||
: <a onClick={this.star}>
|
||||
<StyledStar />
|
||||
</a>}
|
||||
{document.starred ? (
|
||||
<a onClick={this.unstar}>
|
||||
<StyledStar solid />
|
||||
</a>
|
||||
) : (
|
||||
<a onClick={this.star}>
|
||||
<StyledStar />
|
||||
</a>
|
||||
)}
|
||||
</h3>
|
||||
<PublishingInfo
|
||||
document={document}
|
||||
|
@ -36,24 +36,24 @@ class PublishingInfo extends Component {
|
||||
|
||||
return (
|
||||
<Container align="center">
|
||||
{createdAt === updatedAt
|
||||
? <span>
|
||||
{createdBy.name}
|
||||
{createdAt === updatedAt ? (
|
||||
<span>
|
||||
{createdBy.name} published {moment(createdAt).fromNow()}
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
{updatedBy.name}
|
||||
<Modified highlight={modifiedSinceViewed}>
|
||||
{' '}
|
||||
published
|
||||
{' '}
|
||||
{moment(createdAt).fromNow()}
|
||||
</span>
|
||||
: <span>
|
||||
{updatedBy.name}
|
||||
<Modified highlight={modifiedSinceViewed}>
|
||||
{' '}
|
||||
modified
|
||||
{' '}
|
||||
{moment(updatedAt).fromNow()}
|
||||
</Modified>
|
||||
</span>}
|
||||
{collection && <span> in <strong>{collection.name}</strong></span>}
|
||||
modified {moment(updatedAt).fromNow()}
|
||||
</Modified>
|
||||
</span>
|
||||
)}
|
||||
{collection && (
|
||||
<span>
|
||||
in <strong>{collection.name}</strong>
|
||||
</span>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ class DocumentViewersStore {
|
||||
@observable viewers: Array<View>;
|
||||
@observable isFetching: boolean;
|
||||
|
||||
@action fetchViewers = async () => {
|
||||
@action
|
||||
fetchViewers = async () => {
|
||||
this.isFetching = true;
|
||||
|
||||
try {
|
||||
|
@ -25,7 +25,8 @@ type Props = {
|
||||
count: number,
|
||||
};
|
||||
|
||||
@observer class DocumentViews extends Component {
|
||||
@observer
|
||||
class DocumentViews extends Component {
|
||||
anchor: HTMLElement;
|
||||
store: DocumentViewersStore;
|
||||
props: Props;
|
||||
@ -55,19 +56,16 @@ type Props = {
|
||||
return (
|
||||
<Container align="center">
|
||||
<a ref={this.setRef} onClick={this.openPopover}>
|
||||
Viewed
|
||||
{' '}
|
||||
{this.props.count}
|
||||
{' '}
|
||||
{this.props.count === 1 ? 'time' : 'times'}
|
||||
Viewed {this.props.count} {this.props.count === 1 ? 'time' : 'times'}
|
||||
</a>
|
||||
{this.state.opened &&
|
||||
{this.state.opened && (
|
||||
<Popover anchor={this.anchor} onClose={this.closePopover}>
|
||||
<DocumentViewers
|
||||
onMount={this.store.fetchViewers}
|
||||
viewers={this.store.viewers}
|
||||
/>
|
||||
</Popover>}
|
||||
</Popover>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -40,8 +40,7 @@ class DocumentViewers extends Component {
|
||||
{map(this.props.viewers, view => (
|
||||
<li key={view.user.id}>
|
||||
<Flex align="center">
|
||||
<Avatar src={view.user.avatarUrl} />
|
||||
{' '}
|
||||
<Avatar src={view.user.avatarUrl} />{' '}
|
||||
<UserName>{view.user.name}</UserName>
|
||||
</Flex>
|
||||
</li>
|
||||
|
@ -17,7 +17,8 @@ type Props = {
|
||||
style?: Object,
|
||||
};
|
||||
|
||||
@observer class DropdownMenu extends Component {
|
||||
@observer
|
||||
class DropdownMenu extends Component {
|
||||
props: Props;
|
||||
@observable top: number;
|
||||
@observable left: number;
|
||||
@ -93,7 +94,8 @@ const Menu = styled.div`
|
||||
border-radius: 2px;
|
||||
min-width: 160px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 0 1px rgba(0,0,0,.05), 0 4px 8px rgba(0,0,0,.08), 0 2px 4px rgba(0,0,0,.08);
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 8px rgba(0, 0, 0, 0.08),
|
||||
0 2px 4px rgba(0, 0, 0, 0.08);
|
||||
`;
|
||||
|
||||
export default DropdownMenu;
|
||||
|
@ -11,11 +11,7 @@ const DropdownMenuItem = ({
|
||||
onClick?: SyntheticEvent => void,
|
||||
children?: React.Element<any>,
|
||||
}) => {
|
||||
return (
|
||||
<MenuItem onClick={onClick}>
|
||||
{children}
|
||||
</MenuItem>
|
||||
);
|
||||
return <MenuItem onClick={onClick}>{children}</MenuItem>;
|
||||
};
|
||||
|
||||
const MenuItem = styled(Flex)`
|
||||
|
@ -34,7 +34,8 @@ type KeyData = {
|
||||
key: string,
|
||||
};
|
||||
|
||||
@observer class MarkdownEditor extends Component {
|
||||
@observer
|
||||
class MarkdownEditor extends Component {
|
||||
props: Props;
|
||||
editor: EditorType;
|
||||
schema: Object;
|
||||
@ -193,14 +194,16 @@ type KeyData = {
|
||||
<MaxWidth column auto>
|
||||
<Header onClick={this.focusAtStart} readOnly={readOnly} />
|
||||
<Contents state={this.editorState} />
|
||||
{!readOnly &&
|
||||
<Toolbar state={this.editorState} onChange={this.onChange} />}
|
||||
{!readOnly &&
|
||||
{!readOnly && (
|
||||
<Toolbar state={this.editorState} onChange={this.onChange} />
|
||||
)}
|
||||
{!readOnly && (
|
||||
<BlockInsert
|
||||
state={this.editorState}
|
||||
onChange={this.onChange}
|
||||
onInsertImage={this.insertImageFile}
|
||||
/>}
|
||||
/>
|
||||
)}
|
||||
<StyledEditor
|
||||
innerRef={ref => (this.editor = ref)}
|
||||
placeholder="Start with a title…"
|
||||
@ -234,7 +237,7 @@ const Header = styled(Flex)`
|
||||
height: 60px;
|
||||
flex-shrink: 0;
|
||||
align-items: flex-end;
|
||||
${({ readOnly }) => !readOnly && 'cursor: text;'}
|
||||
${({ readOnly }) => !readOnly && 'cursor: text;'};
|
||||
`;
|
||||
|
||||
const StyledEditor = styled(Editor)`
|
||||
@ -326,7 +329,8 @@ const StyledEditor = styled(Editor)`
|
||||
padding: 5px 20px 5px 0;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
b,
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
`;
|
||||
|
@ -126,20 +126,23 @@ const Trigger = styled.div`
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
background-color: ${color.white};
|
||||
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);
|
||||
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;
|
||||
margin-left: -10px;
|
||||
box-shadow: inset 0 0 0 2px ${color.slate};
|
||||
border-radius: 100%;
|
||||
transform: scale(.9);
|
||||
transform: scale(0.9);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: ${color.smokeDark};
|
||||
}
|
||||
|
||||
${({ active }) => active && `
|
||||
${({ active }) =>
|
||||
active &&
|
||||
`
|
||||
transform: scale(1);
|
||||
opacity: .9;
|
||||
`}
|
||||
`};
|
||||
`;
|
||||
|
@ -16,7 +16,7 @@ const Container = styled.div`
|
||||
padding-top: 50px;
|
||||
cursor: ${({ onClick }) => (onClick ? 'text' : 'default')};
|
||||
|
||||
${({ grow }) => grow && `flex-grow: 1;`}
|
||||
${({ grow }) => grow && `flex-grow: 1;`};
|
||||
`;
|
||||
|
||||
export default ClickablePadding;
|
||||
|
@ -12,16 +12,14 @@ export default function Code({ children, node, readOnly, attributes }: Props) {
|
||||
<Container {...attributes}>
|
||||
{readOnly && <CopyButton text={node.text} />}
|
||||
<Pre className={`language-${language}`}>
|
||||
<code className={`language-${language}`}>
|
||||
{children}
|
||||
</code>
|
||||
<code className={`language-${language}`}>{children}</code>
|
||||
</Pre>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const Pre = styled.pre`
|
||||
padding: .5em 1em;
|
||||
padding: 0.5em 1em;
|
||||
background: ${color.smokeLight};
|
||||
border-radius: 4px;
|
||||
border: 1px solid ${color.smokeDark};
|
||||
|
@ -12,7 +12,8 @@ type Props = {
|
||||
state: State,
|
||||
};
|
||||
|
||||
@observer class Contents extends Component {
|
||||
@observer
|
||||
class Contents extends Component {
|
||||
props: Props;
|
||||
@observable activeHeading: ?string;
|
||||
|
||||
|
@ -6,7 +6,8 @@ import { color } from 'shared/styles/constants';
|
||||
import styled from 'styled-components';
|
||||
import CopyToClipboard from 'components/CopyToClipboard';
|
||||
|
||||
@observer class CopyButton extends Component {
|
||||
@observer
|
||||
class CopyButton extends Component {
|
||||
@observable copied: boolean = false;
|
||||
copiedTimeout: ?number;
|
||||
|
||||
|
@ -41,26 +41,29 @@ function Heading(props: Props) {
|
||||
|
||||
return (
|
||||
<Component {...attributes} id={slugish}>
|
||||
<Wrapper hasEmoji={startsWithEmojiAndSpace}>
|
||||
{children}
|
||||
</Wrapper>
|
||||
{showPlaceholder &&
|
||||
<Wrapper hasEmoji={startsWithEmojiAndSpace}>{children}</Wrapper>
|
||||
{showPlaceholder && (
|
||||
<Placeholder contentEditable={false}>
|
||||
{editor.props.placeholder}
|
||||
</Placeholder>}
|
||||
{showHash && <Anchor name={slugish} href={`#${slugish}`}>#</Anchor>}
|
||||
</Placeholder>
|
||||
)}
|
||||
{showHash && (
|
||||
<Anchor name={slugish} href={`#${slugish}`}>
|
||||
#
|
||||
</Anchor>
|
||||
)}
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: inline;
|
||||
margin-left: ${(props: Props) => (props.hasEmoji ? '-1.2em' : 0)}
|
||||
margin-left: ${(props: Props) => (props.hasEmoji ? '-1.2em' : 0)};
|
||||
`;
|
||||
|
||||
const Anchor = styled.a`
|
||||
visibility: hidden;
|
||||
padding-left: .25em;
|
||||
padding-left: 0.25em;
|
||||
color: #dedede;
|
||||
|
||||
&:hover {
|
||||
|
@ -11,10 +11,11 @@ function HorizontalRule(props: Props) {
|
||||
}
|
||||
|
||||
const StyledHr = styled.hr`
|
||||
padding-top: .75em;
|
||||
padding-top: 0.75em;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid ${props => (props.active ? color.slate : color.slateLight)};
|
||||
border-bottom: 1px solid
|
||||
${props => (props.active ? color.slate : color.slateLight)};
|
||||
`;
|
||||
|
||||
export default HorizontalRule;
|
||||
|
@ -35,26 +35,28 @@ class Image extends Component {
|
||||
|
||||
return (
|
||||
<CenteredImage>
|
||||
{!readOnly
|
||||
? <StyledImg
|
||||
{...attributes}
|
||||
src={src}
|
||||
alt={caption}
|
||||
active={active}
|
||||
loading={loading}
|
||||
/>
|
||||
: <ImageZoom
|
||||
image={{
|
||||
src,
|
||||
alt: caption,
|
||||
style: {
|
||||
maxWidth: '100%',
|
||||
},
|
||||
...attributes,
|
||||
}}
|
||||
shouldRespectMaxDimension
|
||||
/>}
|
||||
{showCaption &&
|
||||
{!readOnly ? (
|
||||
<StyledImg
|
||||
{...attributes}
|
||||
src={src}
|
||||
alt={caption}
|
||||
active={active}
|
||||
loading={loading}
|
||||
/>
|
||||
) : (
|
||||
<ImageZoom
|
||||
image={{
|
||||
src,
|
||||
alt: caption,
|
||||
style: {
|
||||
maxWidth: '100%',
|
||||
},
|
||||
...attributes,
|
||||
}}
|
||||
shouldRespectMaxDimension
|
||||
/>
|
||||
)}
|
||||
{showCaption && (
|
||||
<Caption
|
||||
type="text"
|
||||
placeholder="Write a caption"
|
||||
@ -64,7 +66,8 @@ class Image extends Component {
|
||||
contentEditable={false}
|
||||
disabled={readOnly}
|
||||
tabIndex={-1}
|
||||
/>}
|
||||
/>
|
||||
)}
|
||||
</CenteredImage>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import styled from 'styled-components';
|
||||
import { color } from 'shared/styles/constants';
|
||||
|
||||
const InlineCode = styled.code`
|
||||
padding: .25em;
|
||||
padding: 0.25em;
|
||||
background: ${color.smoke};
|
||||
border-radius: 4px;
|
||||
border: 1px solid ${color.smokeDark};
|
||||
|
@ -31,8 +31,16 @@ export default function Link({ attributes, node, children, readOnly }: Props) {
|
||||
const path = getPathFromUrl(href);
|
||||
|
||||
if (isOutlineUrl(href) && readOnly) {
|
||||
return <InternalLink {...attributes} to={path}>{children}</InternalLink>;
|
||||
return (
|
||||
<InternalLink {...attributes} to={path}>
|
||||
{children}
|
||||
</InternalLink>
|
||||
);
|
||||
} else {
|
||||
return <a {...attributes} href={href} target="_blank">{children}</a>;
|
||||
return (
|
||||
<a {...attributes} href={href} target="_blank">
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,10 +25,11 @@ export default function Link({
|
||||
return (
|
||||
<p {...attributes}>
|
||||
{children}
|
||||
{showPlaceholder &&
|
||||
{showPlaceholder && (
|
||||
<Placeholder contentEditable={false}>
|
||||
{editor.props.bodyPlaceholder}
|
||||
</Placeholder>}
|
||||
</Placeholder>
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ export default styled.span`
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
color: #B1BECC;
|
||||
color: #b1becc;
|
||||
`;
|
||||
|
@ -170,7 +170,7 @@ const Bar = styled(Flex)`
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
|
@ -99,7 +99,9 @@ export default class Toolbar extends Component {
|
||||
|
||||
const left =
|
||||
rect.left + window.scrollX - this.menu.offsetWidth / 2 + rect.width / 2;
|
||||
data.top = `${Math.round(rect.top + window.scrollY - this.menu.offsetHeight)}px`;
|
||||
data.top = `${Math.round(
|
||||
rect.top + window.scrollY - this.menu.offsetHeight
|
||||
)}px`;
|
||||
data.left = `${Math.round(Math.max(padding, left))}px`;
|
||||
this.setState(data);
|
||||
}
|
||||
@ -120,17 +122,15 @@ export default class Toolbar extends Component {
|
||||
return (
|
||||
<Portal>
|
||||
<Menu active={this.state.active} innerRef={this.setRef} style={style}>
|
||||
{link &&
|
||||
<LinkToolbar
|
||||
{...this.props}
|
||||
link={link}
|
||||
onBlur={this.handleBlur}
|
||||
/>}
|
||||
{!link &&
|
||||
{link && (
|
||||
<LinkToolbar {...this.props} link={link} onBlur={this.handleBlur} />
|
||||
)}
|
||||
{!link && (
|
||||
<FormattingToolbar
|
||||
onCreateLink={this.handleFocus}
|
||||
{...this.props}
|
||||
/>}
|
||||
/>
|
||||
)}
|
||||
</Menu>
|
||||
</Portal>
|
||||
);
|
||||
@ -144,16 +144,19 @@ const Menu = styled.div`
|
||||
top: -10000px;
|
||||
left: -10000px;
|
||||
opacity: 0;
|
||||
background-color: #2F3336;
|
||||
background-color: #2f3336;
|
||||
border-radius: 4px;
|
||||
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);
|
||||
transform: scale(0.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 && `
|
||||
${({ active }) =>
|
||||
active &&
|
||||
`
|
||||
transform: translateY(-6px) scale(1);
|
||||
opacity: 1;
|
||||
`}
|
||||
`};
|
||||
`;
|
||||
|
@ -14,7 +14,9 @@ type Props = {
|
||||
function DocumentResult({ document, ...rest }: Props) {
|
||||
return (
|
||||
<ListItem {...rest} href="">
|
||||
<i><NextIcon light /></i>
|
||||
<i>
|
||||
<NextIcon light />
|
||||
</i>
|
||||
{document.title}
|
||||
</ListItem>
|
||||
);
|
||||
|
@ -42,7 +42,10 @@ class FormattingToolbar extends Component {
|
||||
ev.preventDefault();
|
||||
let { state } = this.props;
|
||||
|
||||
state = state.transform().toggleMark(type).apply();
|
||||
state = state
|
||||
.transform()
|
||||
.toggleMark(type)
|
||||
.apply();
|
||||
this.props.onChange(state);
|
||||
};
|
||||
|
||||
@ -50,7 +53,10 @@ class FormattingToolbar extends Component {
|
||||
ev.preventDefault();
|
||||
let { state } = this.props;
|
||||
|
||||
state = state.transform().setBlock(type).apply();
|
||||
state = state
|
||||
.transform()
|
||||
.setBlock(type)
|
||||
.apply();
|
||||
this.props.onChange(state);
|
||||
};
|
||||
|
||||
@ -59,7 +65,10 @@ class FormattingToolbar extends Component {
|
||||
ev.stopPropagation();
|
||||
let { state } = this.props;
|
||||
const data = { href: '' };
|
||||
state = state.transform().wrapInline({ type: 'link', data }).apply();
|
||||
state = state
|
||||
.transform()
|
||||
.wrapInline({ type: 'link', data })
|
||||
.apply();
|
||||
this.props.onChange(state);
|
||||
this.props.onCreateLink();
|
||||
};
|
||||
@ -109,8 +118,8 @@ class FormattingToolbar extends Component {
|
||||
const Separator = styled.div`
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
background: #FFF;
|
||||
opacity: .2;
|
||||
background: #fff;
|
||||
opacity: 0.2;
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
@ -39,7 +39,8 @@ class LinkToolbar extends Component {
|
||||
this.isEditing = !!this.props.link.data.get('href');
|
||||
}
|
||||
|
||||
@action search = async () => {
|
||||
@action
|
||||
search = async () => {
|
||||
this.isFetching = true;
|
||||
|
||||
if (this.searchTerm) {
|
||||
@ -144,15 +145,16 @@ class LinkToolbar extends Component {
|
||||
onChange={this.onChange}
|
||||
autoFocus
|
||||
/>
|
||||
{this.isEditing &&
|
||||
{this.isEditing && (
|
||||
<ToolbarButton onMouseDown={this.openLink}>
|
||||
<OpenIcon light />
|
||||
</ToolbarButton>}
|
||||
</ToolbarButton>
|
||||
)}
|
||||
<ToolbarButton onMouseDown={this.removeLink}>
|
||||
{this.isEditing ? <TrashIcon light /> : <CloseIcon light />}
|
||||
</ToolbarButton>
|
||||
</LinkEditor>
|
||||
{hasResults &&
|
||||
{hasResults && (
|
||||
<SearchResults>
|
||||
<ArrowKeyNavigation
|
||||
mode={ArrowKeyNavigation.mode.VERTICAL}
|
||||
@ -165,7 +167,8 @@ class LinkToolbar extends Component {
|
||||
return (
|
||||
<DocumentResult
|
||||
innerRef={ref =>
|
||||
index === 0 && this.setFirstDocumentRef(ref)}
|
||||
index === 0 && this.setFirstDocumentRef(ref)
|
||||
}
|
||||
document={document}
|
||||
key={document.id}
|
||||
onClick={ev => this.selectDocument(ev, document)}
|
||||
@ -173,14 +176,15 @@ class LinkToolbar extends Component {
|
||||
);
|
||||
})}
|
||||
</ArrowKeyNavigation>
|
||||
</SearchResults>}
|
||||
</SearchResults>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SearchResults = styled.div`
|
||||
background: #2F3336;
|
||||
background: #2f3336;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
width: 100%;
|
||||
@ -199,7 +203,7 @@ const LinkEditor = styled(Flex)`
|
||||
|
||||
const Input = styled.input`
|
||||
font-size: 15px;
|
||||
background: rgba(255,255,255,.1);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 2px;
|
||||
padding: 4px 8px;
|
||||
border: 0;
|
||||
|
@ -12,7 +12,7 @@ export default styled.button`
|
||||
background: none;
|
||||
transition: opacity 100ms ease-in-out;
|
||||
padding: 0;
|
||||
opacity: .7;
|
||||
opacity: 0.7;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
@ -22,5 +22,5 @@ export default styled.button`
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
${({ active }) => active && 'opacity: 1;'}
|
||||
${({ active }) => active && 'opacity: 1;'};
|
||||
`;
|
||||
|
@ -37,7 +37,10 @@ export default function KeyboardShortcuts() {
|
||||
const firstNode = state.document.nodes.first();
|
||||
if (firstNode === state.startBlock) return;
|
||||
|
||||
state = state.transform().toggleMark(type).apply();
|
||||
state = state
|
||||
.transform()
|
||||
.toggleMark(type)
|
||||
.apply();
|
||||
return state;
|
||||
},
|
||||
};
|
||||
|
@ -64,7 +64,10 @@ export default function MarkdownShortcuts() {
|
||||
}
|
||||
}
|
||||
|
||||
state = transform.extendToStartOf(startBlock).delete().apply();
|
||||
state = transform
|
||||
.extendToStartOf(startBlock)
|
||||
.delete()
|
||||
.apply();
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -113,7 +116,10 @@ export default function MarkdownShortcuts() {
|
||||
lastCodeTagIndex - shortcut.length
|
||||
);
|
||||
transform.addMark(mark);
|
||||
state = transform.collapseToEnd().removeMark(mark).apply();
|
||||
state = transform
|
||||
.collapseToEnd()
|
||||
.removeMark(mark)
|
||||
.apply();
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@ -212,7 +218,11 @@ export default function MarkdownShortcuts() {
|
||||
onTab(ev: SyntheticEvent, state: Object) {
|
||||
if (state.startBlock.type === 'heading1') {
|
||||
ev.preventDefault();
|
||||
return state.transform().splitBlock().setBlock('paragraph').apply();
|
||||
return state
|
||||
.transform()
|
||||
.splitBlock()
|
||||
.setBlock('paragraph')
|
||||
.apply();
|
||||
}
|
||||
},
|
||||
|
||||
@ -253,7 +263,11 @@ export default function MarkdownShortcuts() {
|
||||
}
|
||||
|
||||
ev.preventDefault();
|
||||
return state.transform().splitBlock().setBlock('paragraph').apply();
|
||||
return state
|
||||
.transform()
|
||||
.splitBlock()
|
||||
.setBlock('paragraph')
|
||||
.apply();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -9,12 +9,14 @@ export default function CheckboxIcon({
|
||||
}: Props & { checked: boolean }) {
|
||||
return (
|
||||
<Icon {...rest}>
|
||||
{checked
|
||||
? <path d="M8,5 L16,5 L16,5 C17.6568542,5 19,6.34314575 19,8 L19,16 C19,17.6568542 17.6568542,19 16,19 L8,19 L8,19 C6.34314575,19 5,17.6568542 5,16 L5,8 L5,8 C5,6.34314575 6.34314575,5 8,5 L8,5 Z M10.958729,12.8883948 L9.26824635,10.8598156 C8.91468227,10.4355387 8.28411757,10.3782146 7.85984067,10.7317787 C7.43556378,11.0853428 7.37823971,11.7159075 7.73180379,12.1401844 L10.2318038,15.1401844 C10.6450125,15.6360348 11.4127535,15.616362 11.8000251,15.1 L16.3000251,9.1 C16.6313959,8.6581722 16.5418529,8.03137085 16.1000251,7.7 C15.6581973,7.36862915 15.0313959,7.4581722 14.7000251,7.9 L10.958729,12.8883948 Z" />
|
||||
: <path
|
||||
d="M8,5 L16,5 L16,5 C17.6568542,5 19,6.34314575 19,8 L19,16 C19,17.6568542 17.6568542,19 16,19 L8,19 L8,19 C6.34314575,19 5,17.6568542 5,16 L5,8 L5,8 C5,6.34314575 6.34314575,5 8,5 L8,5 Z M8,7 C7.44771525,7 7,7.44771525 7,8 L7,16 C7,16.5522847 7.44771525,17 8,17 L16,17 C16.5522847,17 17,16.5522847 17,16 L17,8 C17,7.44771525 16.5522847,7 16,7 L8,7 Z"
|
||||
id="path-1"
|
||||
/>}
|
||||
{checked ? (
|
||||
<path d="M8,5 L16,5 L16,5 C17.6568542,5 19,6.34314575 19,8 L19,16 C19,17.6568542 17.6568542,19 16,19 L8,19 L8,19 C6.34314575,19 5,17.6568542 5,16 L5,8 L5,8 C5,6.34314575 6.34314575,5 8,5 L8,5 Z M10.958729,12.8883948 L9.26824635,10.8598156 C8.91468227,10.4355387 8.28411757,10.3782146 7.85984067,10.7317787 C7.43556378,11.0853428 7.37823971,11.7159075 7.73180379,12.1401844 L10.2318038,15.1401844 C10.6450125,15.6360348 11.4127535,15.616362 11.8000251,15.1 L16.3000251,9.1 C16.6313959,8.6581722 16.5418529,8.03137085 16.1000251,7.7 C15.6581973,7.36862915 15.0313959,7.4581722 14.7000251,7.9 L10.958729,12.8883948 Z" />
|
||||
) : (
|
||||
<path
|
||||
d="M8,5 L16,5 L16,5 C17.6568542,5 19,6.34314575 19,8 L19,16 C19,17.6568542 17.6568542,19 16,19 L8,19 L8,19 C6.34314575,19 5,17.6568542 5,16 L5,8 L5,8 C5,6.34314575 6.34314575,5 8,5 L8,5 Z M8,7 C7.44771525,7 7,7.44771525 7,8 L7,16 C7,16.5522847 7.44771525,17 8,17 L16,17 C16.5522847,17 17,16.5522847 17,16 L17,8 C17,7.44771525 16.5522847,7 16,7 L8,7 Z"
|
||||
id="path-1"
|
||||
/>
|
||||
)}
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
@ -9,9 +9,11 @@ export default function CollectionIcon({
|
||||
}: Props & { expanded: boolean }) {
|
||||
return (
|
||||
<Icon {...rest}>
|
||||
{expanded
|
||||
? <path d="M16.701875,4.16415178 L17,4.14285714 C18.1045695,4.06395932 19,5.02334914 19,6.28571429 L19,17.7142857 C19,18.9766509 18.1045695,19.9360407 17,19.8571429 L16.701875,19.8358482 C16.8928984,19.371917 17,18.8348314 17,18.25 L17,5.75 C17,5.16516859 16.8928984,4.62808299 16.701875,4.16415178 Z M14,3.36363636 C15.1045695,3.16280555 16,4.15126779 16,5.57142857 L16,18.4285714 C16,19.8487322 15.1045695,20.8371945 14,20.6363636 L7,19.3636364 C5.8954305,19.1628055 5,18.1045695 5,17 L5,7 C5,5.8954305 5.8954305,4.83719445 7,4.63636364 L14,3.36363636 Z M7.5,6.67532468 C7.22385763,6.71118732 7,6.97574633 7,7.26623377 L7,16.7337662 C7,17.0242537 7.22385763,17.2888127 7.5,17.3246753 L8.5,17.4545455 C8.77614237,17.4904081 9,17.272365 9,16.9675325 L9,7.03246753 C9,6.72763504 8.77614237,6.5095919 8.5,6.54545455 L7.5,6.67532468 Z" />
|
||||
: <path d="M7,4 L17,4 C18.1045695,4 19,4.8954305 19,6 L19,18 C19,19.1045695 18.1045695,20 17,20 L7,20 C5.8954305,20 5,19.1045695 5,18 L5,6 L5,6 C5,4.8954305 5.8954305,4 7,4 L7,4 Z M7.5,6 C7.22385763,6 7,6.22385763 7,6.5 L7,17.5 C7,17.7761424 7.22385763,18 7.5,18 L8.5,18 C8.77614237,18 9,17.7761424 9,17.5 L9,6.5 C9,6.22385763 8.77614237,6 8.5,6 L7.5,6 Z" />}
|
||||
{expanded ? (
|
||||
<path d="M16.701875,4.16415178 L17,4.14285714 C18.1045695,4.06395932 19,5.02334914 19,6.28571429 L19,17.7142857 C19,18.9766509 18.1045695,19.9360407 17,19.8571429 L16.701875,19.8358482 C16.8928984,19.371917 17,18.8348314 17,18.25 L17,5.75 C17,5.16516859 16.8928984,4.62808299 16.701875,4.16415178 Z M14,3.36363636 C15.1045695,3.16280555 16,4.15126779 16,5.57142857 L16,18.4285714 C16,19.8487322 15.1045695,20.8371945 14,20.6363636 L7,19.3636364 C5.8954305,19.1628055 5,18.1045695 5,17 L5,7 C5,5.8954305 5.8954305,4.83719445 7,4.63636364 L14,3.36363636 Z M7.5,6.67532468 C7.22385763,6.71118732 7,6.97574633 7,7.26623377 L7,16.7337662 C7,17.0242537 7.22385763,17.2888127 7.5,17.3246753 L8.5,17.4545455 C8.77614237,17.4904081 9,17.272365 9,16.9675325 L9,7.03246753 C9,6.72763504 8.77614237,6.5095919 8.5,6.54545455 L7.5,6.67532468 Z" />
|
||||
) : (
|
||||
<path d="M7,4 L17,4 C18.1045695,4 19,4.8954305 19,6 L19,18 C19,19.1045695 18.1045695,20 17,20 L7,20 C5.8954305,20 5,19.1045695 5,18 L5,6 L5,6 C5,4.8954305 5.8954305,4 7,4 L7,4 Z M7.5,6 C7.22385763,6 7,6.22385763 7,6.5 L7,17.5 C7,17.7761424 7.22385763,18 7.5,18 L8.5,18 C8.77614237,18 9,17.7761424 9,17.5 L9,6.5 C9,6.22385763 8.77614237,6 8.5,6 L7.5,6 Z" />
|
||||
)}
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
@ -28,9 +28,7 @@ const RealInput = styled.input`
|
||||
}
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
|
||||
`;
|
||||
const Wrapper = styled.div``;
|
||||
|
||||
export const Outline = styled(Flex)`
|
||||
display: flex;
|
||||
@ -44,7 +42,7 @@ export const Outline = styled(Flex)`
|
||||
font-weight: normal;
|
||||
|
||||
&:focus {
|
||||
border-color: ${color.slate}
|
||||
border-color: ${color.slate};
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -5,7 +5,8 @@ import { color } from 'shared/styles/constants';
|
||||
const Key = styled.kbd`
|
||||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font: 11px 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier,
|
||||
monospace;
|
||||
line-height: 10px;
|
||||
color: ${color.text};
|
||||
vertical-align: middle;
|
||||
|
@ -22,7 +22,7 @@ export const Label = styled(Flex)`
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: #9FA6AB;
|
||||
color: #9fa6ab;
|
||||
letter-spacing: 0.04em;
|
||||
`;
|
||||
|
||||
|
@ -43,7 +43,8 @@ type Props = {
|
||||
notifications?: React.Element<any>,
|
||||
};
|
||||
|
||||
@observer class Layout extends React.Component {
|
||||
@observer
|
||||
class Layout extends React.Component {
|
||||
props: Props;
|
||||
scrollable: ?HTMLDivElement;
|
||||
|
||||
@ -117,41 +118,42 @@ type Props = {
|
||||
<Flex auto>
|
||||
{auth.authenticated &&
|
||||
user &&
|
||||
team &&
|
||||
<Sidebar column editMode={ui.editMode}>
|
||||
<AccountMenu
|
||||
label={
|
||||
<HeaderBlock user={user} team={team}>
|
||||
<Avatar src={user.avatarUrl} />
|
||||
</HeaderBlock>
|
||||
}
|
||||
/>
|
||||
team && (
|
||||
<Sidebar column editMode={ui.editMode}>
|
||||
<AccountMenu
|
||||
label={
|
||||
<HeaderBlock user={user} team={team}>
|
||||
<Avatar src={user.avatarUrl} />
|
||||
</HeaderBlock>
|
||||
}
|
||||
/>
|
||||
|
||||
<Flex auto column>
|
||||
<Scrollable innerRef={this.setScrollableRef}>
|
||||
<LinkSection>
|
||||
<SidebarLink to="/dashboard" icon={<HomeIcon />}>
|
||||
Home
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/search" icon={<SearchIcon />}>
|
||||
Search
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/starred" icon={<StarredIcon />}>
|
||||
Starred
|
||||
</SidebarLink>
|
||||
</LinkSection>
|
||||
<LinkSection>
|
||||
<SidebarCollections
|
||||
history={this.props.history}
|
||||
location={this.props.location}
|
||||
activeDocument={documents.active}
|
||||
onCreateCollection={this.handleCreateCollection}
|
||||
activeDocumentRef={this.scrollToActiveDocument}
|
||||
/>
|
||||
</LinkSection>
|
||||
</Scrollable>
|
||||
</Flex>
|
||||
</Sidebar>}
|
||||
<Flex auto column>
|
||||
<Scrollable innerRef={this.setScrollableRef}>
|
||||
<LinkSection>
|
||||
<SidebarLink to="/dashboard" icon={<HomeIcon />}>
|
||||
Home
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/search" icon={<SearchIcon />}>
|
||||
Search
|
||||
</SidebarLink>
|
||||
<SidebarLink to="/starred" icon={<StarredIcon />}>
|
||||
Starred
|
||||
</SidebarLink>
|
||||
</LinkSection>
|
||||
<LinkSection>
|
||||
<SidebarCollections
|
||||
history={this.props.history}
|
||||
location={this.props.location}
|
||||
activeDocument={documents.active}
|
||||
onCreateCollection={this.handleCreateCollection}
|
||||
activeDocumentRef={this.scrollToActiveDocument}
|
||||
/>
|
||||
</LinkSection>
|
||||
</Scrollable>
|
||||
</Flex>
|
||||
</Sidebar>
|
||||
)}
|
||||
|
||||
<Content auto justify="center" editMode={ui.editMode}>
|
||||
{this.props.children}
|
||||
|
@ -43,14 +43,14 @@ const Header = styled(Flex)`
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
background: rgba(0,0,0,.05);
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
content: '';
|
||||
left: 24px;
|
||||
right: 24px;
|
||||
background: rgba(0,0,0,.075);
|
||||
background: rgba(0, 0, 0, 0.075);
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
|
@ -10,7 +10,8 @@ import DocumentDelete from 'scenes/DocumentDelete';
|
||||
import KeyboardShortcuts from 'scenes/KeyboardShortcuts';
|
||||
import Settings from 'scenes/Settings';
|
||||
|
||||
@observer class Modals extends Component {
|
||||
@observer
|
||||
class Modals extends Component {
|
||||
props: {
|
||||
ui: UiStore,
|
||||
};
|
||||
|
@ -31,7 +31,8 @@ type Props = {
|
||||
ui: UiStore,
|
||||
};
|
||||
|
||||
@observer class SidebarCollections extends Component {
|
||||
@observer
|
||||
class SidebarCollections extends Component {
|
||||
props: Props;
|
||||
|
||||
render() {
|
||||
@ -61,13 +62,14 @@ type Props = {
|
||||
/>
|
||||
))}
|
||||
|
||||
{collections.isLoaded &&
|
||||
{collections.isLoaded && (
|
||||
<SidebarLink
|
||||
onClick={this.props.onCreateCollection}
|
||||
icon={<PlusIcon />}
|
||||
>
|
||||
New collection…
|
||||
</SidebarLink>}
|
||||
</SidebarLink>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@ -82,7 +84,8 @@ type CollectionLinkProps = {
|
||||
prefetchDocument: (id: string) => Promise<void>,
|
||||
};
|
||||
|
||||
@observer class CollectionLink extends Component {
|
||||
@observer
|
||||
class CollectionLink extends Component {
|
||||
props: CollectionLinkProps;
|
||||
dropzoneRef;
|
||||
|
||||
@ -133,7 +136,7 @@ type CollectionLinkProps = {
|
||||
</CollectionAction>
|
||||
</Flex>
|
||||
|
||||
{expanded &&
|
||||
{expanded && (
|
||||
<Children column>
|
||||
{collection.documents.map(document => (
|
||||
<DocumentLink
|
||||
@ -146,7 +149,8 @@ type CollectionLinkProps = {
|
||||
depth={0}
|
||||
/>
|
||||
))}
|
||||
</Children>}
|
||||
</Children>
|
||||
)}
|
||||
</SidebarLink>
|
||||
</StyledDropToImport>
|
||||
);
|
||||
@ -172,11 +176,13 @@ const DocumentLink = observer(
|
||||
}: 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();
|
||||
@ -200,20 +206,22 @@ const DocumentLink = observer(
|
||||
to={document.url}
|
||||
expand={showChildren}
|
||||
expandedContent={
|
||||
document.children.length
|
||||
? <Children column>
|
||||
{document.children.map(childDocument => (
|
||||
<DocumentLink
|
||||
key={childDocument.id}
|
||||
history={history}
|
||||
document={childDocument}
|
||||
activeDocument={activeDocument}
|
||||
prefetchDocument={prefetchDocument}
|
||||
depth={depth + 1}
|
||||
/>
|
||||
))}
|
||||
</Children>
|
||||
: undefined
|
||||
document.children.length ? (
|
||||
<Children column>
|
||||
{document.children.map(childDocument => (
|
||||
<DocumentLink
|
||||
key={childDocument.id}
|
||||
history={history}
|
||||
document={childDocument}
|
||||
activeDocument={activeDocument}
|
||||
prefetchDocument={prefetchDocument}
|
||||
depth={depth + 1}
|
||||
/>
|
||||
))}
|
||||
</Children>
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
>
|
||||
{document.title}
|
||||
@ -228,10 +236,14 @@ const CollectionAction = styled.a`
|
||||
position: absolute;
|
||||
right: 0;
|
||||
color: ${color.slate};
|
||||
svg { opacity: .75; }
|
||||
svg {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg { opacity: 1; }
|
||||
svg {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -17,7 +17,7 @@ const StyledGoTo = styled(CollapsedIcon)`
|
||||
margin-bottom: -4px;
|
||||
margin-left: 1px;
|
||||
margin-right: -3px;
|
||||
${({ expanded }) => !expanded && 'transform: rotate(-90deg);'}
|
||||
${({ expanded }) => !expanded && 'transform: rotate(-90deg);'};
|
||||
`;
|
||||
|
||||
const IconWrapper = styled.span`
|
||||
@ -55,7 +55,8 @@ type Props = {
|
||||
iconColor?: string,
|
||||
};
|
||||
|
||||
@observer class SidebarLink extends Component {
|
||||
@observer
|
||||
class SidebarLink extends Component {
|
||||
props: Props;
|
||||
@observable expanded: boolean = false;
|
||||
|
||||
@ -67,13 +68,15 @@ type Props = {
|
||||
if (nextProps.expand) this.handleExpand();
|
||||
}
|
||||
|
||||
@action handleClick = (event: SyntheticEvent) => {
|
||||
@action
|
||||
handleClick = (event: SyntheticEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.expanded = !this.expanded;
|
||||
};
|
||||
|
||||
@action handleExpand = () => {
|
||||
@action
|
||||
handleExpand = () => {
|
||||
this.expanded = true;
|
||||
};
|
||||
|
||||
@ -91,8 +94,9 @@ type Props = {
|
||||
exact
|
||||
>
|
||||
{icon && <IconWrapper>{icon}</IconWrapper>}
|
||||
{expandedContent &&
|
||||
<StyledGoTo expanded={this.expanded} onClick={this.handleClick} />}
|
||||
{expandedContent && (
|
||||
<StyledGoTo expanded={this.expanded} onClick={this.handleClick} />
|
||||
)}
|
||||
<Content onClick={this.handleExpand}>{children}</Content>
|
||||
</Component>
|
||||
{this.expanded && expandedContent}
|
||||
|
@ -2,7 +2,8 @@
|
||||
import React from 'react';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
|
||||
@observer class LoadingIndicator extends React.Component {
|
||||
@observer
|
||||
class LoadingIndicator extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.ui.enableProgressBar();
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ const Container = styled.div`
|
||||
top: 0;
|
||||
z-index: 9999;
|
||||
|
||||
background-color: #03A9F4;
|
||||
background-color: #03a9f4;
|
||||
width: 100%;
|
||||
animation: ${loadingFrame} 4s ease-in-out infinite;
|
||||
animation-delay: 250ms;
|
||||
@ -30,7 +30,7 @@ const Container = styled.div`
|
||||
const Loader = styled.div`
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #03A9F4;
|
||||
background-color: #03a9f4;
|
||||
`;
|
||||
|
||||
export default LoadingIndicatorBar;
|
||||
|
@ -43,7 +43,9 @@ const Modal = ({
|
||||
>
|
||||
<Content column>
|
||||
{title && <h1>{title}</h1>}
|
||||
<Close onClick={onRequestClose}><CloseIcon size={32} /></Close>
|
||||
<Close onClick={onRequestClose}>
|
||||
<CloseIcon size={32} />
|
||||
</Close>
|
||||
{children}
|
||||
</Content>
|
||||
</StyledModal>
|
||||
@ -79,7 +81,7 @@ const Close = styled.a`
|
||||
position: fixed;
|
||||
top: 3rem;
|
||||
right: 3rem;
|
||||
opacity: .5;
|
||||
opacity: 0.5;
|
||||
color: ${color.text};
|
||||
|
||||
&:hover {
|
||||
|
@ -7,7 +7,9 @@ type Props = {
|
||||
};
|
||||
|
||||
const PageTitle = ({ title }: Props) => (
|
||||
<Helmet><title>{`${title} - Outline`}</title></Helmet>
|
||||
<Helmet>
|
||||
<title>{`${title} - Outline`}</title>
|
||||
</Helmet>
|
||||
);
|
||||
|
||||
export default PageTitle;
|
||||
|
@ -30,18 +30,19 @@ const StyledPopover = styled(BoundlessPopover)`
|
||||
position: absolute;
|
||||
|
||||
polygon:first-child {
|
||||
fill: rgba(0,0,0,.075);
|
||||
fill: rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
polygon {
|
||||
fill: #FFF;
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Dialog = styled.div`
|
||||
outline: none;
|
||||
background: #FFF;
|
||||
box-shadow: 0 0 0 1px rgba(0,0,0,.05), 0 8px 16px rgba(0,0,0,.1), 0 2px 4px rgba(0,0,0,.1);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 8px 16px rgba(0, 0, 0, 0.1),
|
||||
0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
line-height: 1.5;
|
||||
padding: 16px;
|
||||
|
@ -10,7 +10,8 @@ type Props = {
|
||||
redirectUri: string,
|
||||
};
|
||||
|
||||
@observer class SlackAuthLink extends React.Component {
|
||||
@observer
|
||||
class SlackAuthLink extends React.Component {
|
||||
props: Props;
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -5,7 +5,8 @@ import styled from 'styled-components';
|
||||
import { layout } from 'shared/styles/constants';
|
||||
import Toast from './components/Toast';
|
||||
|
||||
@observer class Toasts extends Component {
|
||||
@observer
|
||||
class Toasts extends Component {
|
||||
handleClose = index => {
|
||||
this.props.errors.remove(index);
|
||||
};
|
||||
|
@ -82,9 +82,7 @@ const Auth = ({ children }: AuthProps) => {
|
||||
|
||||
return (
|
||||
<Flex auto>
|
||||
<Provider {...authenticatedStores}>
|
||||
{children}
|
||||
</Provider>
|
||||
<Provider {...authenticatedStores}>{children}</Provider>
|
||||
</Flex>
|
||||
);
|
||||
} else {
|
||||
|
@ -6,7 +6,8 @@ import UiStore from 'stores/UiStore';
|
||||
import AuthStore from 'stores/AuthStore';
|
||||
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
|
||||
@observer class AccountMenu extends Component {
|
||||
@observer
|
||||
class AccountMenu extends Component {
|
||||
props: {
|
||||
label?: React$Element<any>,
|
||||
history: Object,
|
||||
@ -42,9 +43,7 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
<Link to="/developers">
|
||||
<DropdownMenuItem>API documentation</DropdownMenuItem>
|
||||
</Link>
|
||||
<DropdownMenuItem onClick={this.handleLogout}>
|
||||
Logout
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={this.handleLogout}>Logout</DropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ import MoreIcon from 'components/Icon/MoreIcon';
|
||||
import Flex from 'shared/components/Flex';
|
||||
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
|
||||
@observer class CollectionMenu extends Component {
|
||||
@observer
|
||||
class CollectionMenu extends Component {
|
||||
props: {
|
||||
label?: React$Element<*>,
|
||||
onOpen?: () => void,
|
||||
@ -44,7 +45,7 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
onOpen={onOpen}
|
||||
onClose={onClose}
|
||||
>
|
||||
{collection &&
|
||||
{collection && (
|
||||
<Flex column>
|
||||
<DropdownMenuItem onClick={this.onNewDocument}>
|
||||
New document
|
||||
@ -53,9 +54,11 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
Import document
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={this.onEdit}>Edit…</DropdownMenuItem>
|
||||
</Flex>}
|
||||
{allowDelete &&
|
||||
<DropdownMenuItem onClick={this.onDelete}>Delete…</DropdownMenuItem>}
|
||||
</Flex>
|
||||
)}
|
||||
{allowDelete && (
|
||||
<DropdownMenuItem onClick={this.onDelete}>Delete…</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ import MoreIcon from 'components/Icon/MoreIcon';
|
||||
import { documentMoveUrl } from 'utils/routeHelpers';
|
||||
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
|
||||
@observer class DocumentMenu extends Component {
|
||||
@observer
|
||||
class DocumentMenu extends Component {
|
||||
props: {
|
||||
ui: UiStore,
|
||||
label?: React$Element<any>,
|
||||
@ -50,11 +51,13 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
|
||||
return (
|
||||
<DropdownMenu label={label || <MoreIcon />}>
|
||||
{document.starred
|
||||
? <DropdownMenuItem onClick={this.handleUnstar}>
|
||||
Unstar
|
||||
</DropdownMenuItem>
|
||||
: <DropdownMenuItem onClick={this.handleStar}>Star</DropdownMenuItem>}
|
||||
{document.starred ? (
|
||||
<DropdownMenuItem onClick={this.handleUnstar}>
|
||||
Unstar
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<DropdownMenuItem onClick={this.handleStar}>Star</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={this.handleNewChild}
|
||||
title="Create a new child document for the current document"
|
||||
@ -65,10 +68,11 @@ import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
Download
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={this.handleMove}>Move…</DropdownMenuItem>
|
||||
{allowDelete &&
|
||||
{allowDelete && (
|
||||
<DropdownMenuItem onClick={this.handleDelete}>
|
||||
Delete…
|
||||
</DropdownMenuItem>}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
@ -27,19 +27,22 @@ class Collection extends BaseModel {
|
||||
|
||||
/* Computed */
|
||||
|
||||
@computed get entryUrl(): string {
|
||||
@computed
|
||||
get entryUrl(): string {
|
||||
return this.type === 'atlas' && this.documents.length > 0
|
||||
? this.documents[0].url
|
||||
: this.url;
|
||||
}
|
||||
|
||||
@computed get allowDelete(): boolean {
|
||||
@computed
|
||||
get allowDelete(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
|
||||
@action fetch = async () => {
|
||||
@action
|
||||
fetch = async () => {
|
||||
try {
|
||||
const res = await client.post('/collections.info', { id: this.id });
|
||||
invariant(res && res.data, 'API response should be available');
|
||||
@ -54,7 +57,8 @@ class Collection extends BaseModel {
|
||||
return this;
|
||||
};
|
||||
|
||||
@action save = async () => {
|
||||
@action
|
||||
save = async () => {
|
||||
if (this.isSaving) return this;
|
||||
this.isSaving = true;
|
||||
|
||||
@ -89,7 +93,8 @@ class Collection extends BaseModel {
|
||||
return true;
|
||||
};
|
||||
|
||||
@action delete = async () => {
|
||||
@action
|
||||
delete = async () => {
|
||||
try {
|
||||
await client.post('/collections.delete', { id: this.id });
|
||||
this.emit('collections.delete', {
|
||||
@ -102,7 +107,8 @@ class Collection extends BaseModel {
|
||||
return false;
|
||||
};
|
||||
|
||||
@action updateData(data: Object = {}) {
|
||||
@action
|
||||
updateData(data: Object = {}) {
|
||||
this.data = data;
|
||||
extendObservable(this, data);
|
||||
}
|
||||
|
@ -43,11 +43,13 @@ class Document extends BaseModel {
|
||||
|
||||
/* Computed */
|
||||
|
||||
@computed get modifiedSinceViewed(): boolean {
|
||||
@computed
|
||||
get modifiedSinceViewed(): boolean {
|
||||
return !!this.lastViewedAt && this.lastViewedAt < this.updatedAt;
|
||||
}
|
||||
|
||||
@computed get pathToDocument(): Array<{ id: string, title: string }> {
|
||||
@computed
|
||||
get pathToDocument(): Array<{ id: string, title: string }> {
|
||||
let path;
|
||||
const traveler = (nodes, previousPath) => {
|
||||
nodes.forEach(childNode => {
|
||||
@ -76,16 +78,19 @@ class Document extends BaseModel {
|
||||
return [];
|
||||
}
|
||||
|
||||
@computed get isEmpty(): boolean {
|
||||
@computed
|
||||
get isEmpty(): boolean {
|
||||
// Check if the document title has been modified and user generated content exists
|
||||
return this.text.replace(new RegExp(`^#$`), '').trim().length === 0;
|
||||
}
|
||||
|
||||
@computed get allowSave(): boolean {
|
||||
@computed
|
||||
get allowSave(): boolean {
|
||||
return !this.isEmpty && !this.isSaving;
|
||||
}
|
||||
|
||||
@computed get allowDelete(): boolean {
|
||||
@computed
|
||||
get allowDelete(): boolean {
|
||||
const collection = this.collection;
|
||||
return (
|
||||
collection &&
|
||||
@ -95,7 +100,8 @@ class Document extends BaseModel {
|
||||
);
|
||||
}
|
||||
|
||||
@computed get parentDocumentId(): ?string {
|
||||
@computed
|
||||
get parentDocumentId(): ?string {
|
||||
return this.pathToDocument.length > 1
|
||||
? this.pathToDocument[this.pathToDocument.length - 2].id
|
||||
: null;
|
||||
@ -103,7 +109,8 @@ class Document extends BaseModel {
|
||||
|
||||
/* Actions */
|
||||
|
||||
@action star = async () => {
|
||||
@action
|
||||
star = async () => {
|
||||
this.starred = true;
|
||||
try {
|
||||
await client.post('/documents.star', { id: this.id });
|
||||
@ -113,7 +120,8 @@ class Document extends BaseModel {
|
||||
}
|
||||
};
|
||||
|
||||
@action unstar = async () => {
|
||||
@action
|
||||
unstar = async () => {
|
||||
this.starred = false;
|
||||
try {
|
||||
await client.post('/documents.unstar', { id: this.id });
|
||||
@ -123,7 +131,8 @@ class Document extends BaseModel {
|
||||
}
|
||||
};
|
||||
|
||||
@action view = async () => {
|
||||
@action
|
||||
view = async () => {
|
||||
this.views++;
|
||||
try {
|
||||
await client.post('/views.create', { id: this.id });
|
||||
@ -132,7 +141,8 @@ class Document extends BaseModel {
|
||||
}
|
||||
};
|
||||
|
||||
@action fetch = async () => {
|
||||
@action
|
||||
fetch = async () => {
|
||||
try {
|
||||
const res = await client.post('/documents.info', { id: this.id });
|
||||
invariant(res && res.data, 'Document API response should be available');
|
||||
@ -145,7 +155,8 @@ class Document extends BaseModel {
|
||||
}
|
||||
};
|
||||
|
||||
@action save = async () => {
|
||||
@action
|
||||
save = async () => {
|
||||
if (this.isSaving) return this;
|
||||
this.isSaving = true;
|
||||
|
||||
@ -196,7 +207,8 @@ class Document extends BaseModel {
|
||||
return this;
|
||||
};
|
||||
|
||||
@action move = async (parentDocumentId: ?string) => {
|
||||
@action
|
||||
move = async (parentDocumentId: ?string) => {
|
||||
try {
|
||||
const res = await client.post('/documents.move', {
|
||||
id: this.id,
|
||||
@ -214,7 +226,8 @@ class Document extends BaseModel {
|
||||
return;
|
||||
};
|
||||
|
||||
@action delete = async () => {
|
||||
@action
|
||||
delete = async () => {
|
||||
try {
|
||||
await client.post('/documents.delete', { id: this.id });
|
||||
this.emit('documents.delete', {
|
||||
@ -232,7 +245,9 @@ class Document extends BaseModel {
|
||||
const a = window.document.createElement('a');
|
||||
a.textContent = 'download';
|
||||
a.download = `${this.title}.md`;
|
||||
a.href = `data:text/markdown;charset=UTF-8,${encodeURIComponent(this.text)}`;
|
||||
a.href = `data:text/markdown;charset=UTF-8,${encodeURIComponent(
|
||||
this.text
|
||||
)}`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,8 @@ type Props = {
|
||||
match: Object,
|
||||
};
|
||||
|
||||
@observer class CollectionScene extends Component {
|
||||
@observer
|
||||
class CollectionScene extends Component {
|
||||
props: Props;
|
||||
@observable collection: ?Collection;
|
||||
@observable isFetching = true;
|
||||
@ -56,11 +57,8 @@ type Props = {
|
||||
<NewDocumentContainer auto column justify="center">
|
||||
<h1>Create a document</h1>
|
||||
<HelpText>
|
||||
Publish your first document to start building the
|
||||
{' '}
|
||||
<strong>{this.collection.name}</strong>
|
||||
{' '}
|
||||
collection.
|
||||
Publish your first document to start building the{' '}
|
||||
<strong>{this.collection.name}</strong> collection.
|
||||
</HelpText>
|
||||
<Action>
|
||||
<Link to={newDocumentUrl(this.collection)}>
|
||||
@ -76,9 +74,11 @@ type Props = {
|
||||
|
||||
return (
|
||||
<CenteredContent>
|
||||
{this.isFetching
|
||||
? <LoadingListPlaceholder />
|
||||
: this.renderEmptyCollection()}
|
||||
{this.isFetching ? (
|
||||
<LoadingListPlaceholder />
|
||||
) : (
|
||||
this.renderEmptyCollection()
|
||||
)}
|
||||
</CenteredContent>
|
||||
);
|
||||
}
|
||||
|
@ -17,7 +17,8 @@ type Props = {
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|
||||
@observer class CollectionDelete extends Component {
|
||||
@observer
|
||||
class CollectionDelete extends Component {
|
||||
props: Props;
|
||||
@observable isDeleting: boolean;
|
||||
|
||||
@ -42,12 +43,9 @@ type Props = {
|
||||
<Flex column>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<HelpText>
|
||||
Are you sure? Deleting the
|
||||
{' '}
|
||||
<strong>{collection.name}</strong>
|
||||
{' '}
|
||||
collection is permanant and will also delete all of the documents within
|
||||
it, so be careful with that.
|
||||
Are you sure? Deleting the <strong>{collection.name}</strong>{' '}
|
||||
collection is permanant and will also delete all of the documents
|
||||
within it, so be careful with that.
|
||||
</HelpText>
|
||||
<Button type="submit" danger>
|
||||
{this.isDeleting ? 'Deleting…' : 'Delete'}
|
||||
|
@ -16,7 +16,8 @@ type Props = {
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|
||||
@observer class CollectionEdit extends Component {
|
||||
@observer
|
||||
class CollectionEdit extends Component {
|
||||
props: Props;
|
||||
@observable name: string;
|
||||
@observable color: string = '';
|
||||
|
@ -17,7 +17,8 @@ type Props = {
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|
||||
@observer class CollectionNew extends Component {
|
||||
@observer
|
||||
class CollectionNew extends Component {
|
||||
props: Props;
|
||||
@observable collection: Collection;
|
||||
@observable name: string = '';
|
||||
@ -56,8 +57,9 @@ type Props = {
|
||||
return (
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<HelpText>
|
||||
Collections are for grouping your Outline. They work best when organized
|
||||
around a topic or internal team — Product or Engineering for example.
|
||||
Collections are for grouping your Outline. They work best when
|
||||
organized around a topic or internal team — Product or Engineering for
|
||||
example.
|
||||
</HelpText>
|
||||
<Input
|
||||
type="text"
|
||||
|
@ -15,7 +15,7 @@ const Subheading = styled.h3`
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: #9FA6AB;
|
||||
color: #9fa6ab;
|
||||
letter-spacing: 0.04em;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 10px;
|
||||
@ -26,7 +26,8 @@ type Props = {
|
||||
documents: DocumentsStore,
|
||||
};
|
||||
|
||||
@observer class Dashboard extends Component {
|
||||
@observer
|
||||
class Dashboard extends Component {
|
||||
props: Props;
|
||||
@observable isLoaded: boolean = false;
|
||||
|
||||
@ -53,20 +54,24 @@ type Props = {
|
||||
<CenteredContent>
|
||||
<PageTitle title="Home" />
|
||||
<h1>Home</h1>
|
||||
{showContent
|
||||
? <Flex column>
|
||||
{recentlyViewedLoaded &&
|
||||
<Flex column>
|
||||
<Subheading>Recently viewed</Subheading>
|
||||
<DocumentList documents={documents.recentlyViewed} />
|
||||
</Flex>}
|
||||
{recentlyEditedLoaded &&
|
||||
<Flex column>
|
||||
<Subheading>Recently edited</Subheading>
|
||||
<DocumentList documents={documents.recentlyEdited} />
|
||||
</Flex>}
|
||||
</Flex>
|
||||
: <ListPlaceholder count={5} />}
|
||||
{showContent ? (
|
||||
<Flex column>
|
||||
{recentlyViewedLoaded && (
|
||||
<Flex column>
|
||||
<Subheading>Recently viewed</Subheading>
|
||||
<DocumentList documents={documents.recentlyViewed} />
|
||||
</Flex>
|
||||
)}
|
||||
{recentlyEditedLoaded && (
|
||||
<Flex column>
|
||||
<Subheading>Recently edited</Subheading>
|
||||
<DocumentList documents={documents.recentlyEdited} />
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
) : (
|
||||
<ListPlaceholder count={5} />
|
||||
)}
|
||||
</CenteredContent>
|
||||
);
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ type Props = {
|
||||
ui: UiStore,
|
||||
};
|
||||
|
||||
@observer class DocumentScene extends Component {
|
||||
@observer
|
||||
class DocumentScene extends Component {
|
||||
props: Props;
|
||||
savedTimeout: number;
|
||||
|
||||
@ -219,66 +220,74 @@ type Props = {
|
||||
{isMoving && document && <DocumentMove document={document} />}
|
||||
{titleText && <PageTitle title={titleText} />}
|
||||
{this.isLoading && <LoadingIndicator />}
|
||||
{isFetching &&
|
||||
{isFetching && (
|
||||
<CenteredContent>
|
||||
<LoadingState />
|
||||
</CenteredContent>}
|
||||
</CenteredContent>
|
||||
)}
|
||||
{!isFetching &&
|
||||
document &&
|
||||
<Flex justify="center" auto>
|
||||
<Prompt
|
||||
when={document.hasPendingChanges}
|
||||
message={DISCARD_CHANGES}
|
||||
/>
|
||||
<Editor
|
||||
key={document.id}
|
||||
text={document.text}
|
||||
emoji={document.emoji}
|
||||
onImageUploadStart={this.onImageUploadStart}
|
||||
onImageUploadStop={this.onImageUploadStop}
|
||||
onChange={this.onChange}
|
||||
onSave={this.onSave}
|
||||
onCancel={this.onDiscard}
|
||||
readOnly={!this.isEditing}
|
||||
/>
|
||||
<Meta align="center" justify="flex-end" readOnly={!this.isEditing}>
|
||||
<Flex align="center">
|
||||
{!isNew &&
|
||||
!this.isEditing &&
|
||||
<Collaborators document={document} />}
|
||||
<HeaderAction>
|
||||
{this.isEditing
|
||||
? <SaveAction
|
||||
document && (
|
||||
<Flex justify="center" auto>
|
||||
<Prompt
|
||||
when={document.hasPendingChanges}
|
||||
message={DISCARD_CHANGES}
|
||||
/>
|
||||
<Editor
|
||||
key={document.id}
|
||||
text={document.text}
|
||||
emoji={document.emoji}
|
||||
onImageUploadStart={this.onImageUploadStart}
|
||||
onImageUploadStop={this.onImageUploadStop}
|
||||
onChange={this.onChange}
|
||||
onSave={this.onSave}
|
||||
onCancel={this.onDiscard}
|
||||
readOnly={!this.isEditing}
|
||||
/>
|
||||
<Meta
|
||||
align="center"
|
||||
justify="flex-end"
|
||||
readOnly={!this.isEditing}
|
||||
>
|
||||
<Flex align="center">
|
||||
{!isNew &&
|
||||
!this.isEditing && <Collaborators document={document} />}
|
||||
<HeaderAction>
|
||||
{this.isEditing ? (
|
||||
<SaveAction
|
||||
isSaving={this.isSaving}
|
||||
onClick={this.onSave.bind(this, true)}
|
||||
disabled={
|
||||
!(this.document && this.document.allowSave) ||
|
||||
this.isSaving
|
||||
this.isSaving
|
||||
}
|
||||
isNew={!!isNew}
|
||||
/>
|
||||
: <a onClick={this.onClickEdit}>
|
||||
Edit
|
||||
</a>}
|
||||
</HeaderAction>
|
||||
{this.isEditing &&
|
||||
) : (
|
||||
<a onClick={this.onClickEdit}>Edit</a>
|
||||
)}
|
||||
</HeaderAction>
|
||||
{this.isEditing && (
|
||||
<HeaderAction>
|
||||
<a onClick={this.onDiscard}>Discard</a>
|
||||
</HeaderAction>
|
||||
)}
|
||||
{!this.isEditing && (
|
||||
<HeaderAction>
|
||||
<DocumentMenu document={document} />
|
||||
</HeaderAction>
|
||||
)}
|
||||
{!this.isEditing && <Separator />}
|
||||
<HeaderAction>
|
||||
<a onClick={this.onDiscard}>Discard</a>
|
||||
</HeaderAction>}
|
||||
{!this.isEditing &&
|
||||
<HeaderAction>
|
||||
<DocumentMenu document={document} />
|
||||
</HeaderAction>}
|
||||
{!this.isEditing && <Separator />}
|
||||
<HeaderAction>
|
||||
{!this.isEditing &&
|
||||
<a onClick={this.onClickNew}>
|
||||
<NewDocumentIcon />
|
||||
</a>}
|
||||
</HeaderAction>
|
||||
</Flex>
|
||||
</Meta>
|
||||
</Flex>}
|
||||
{!this.isEditing && (
|
||||
<a onClick={this.onClickNew}>
|
||||
<NewDocumentIcon />
|
||||
</a>
|
||||
)}
|
||||
</HeaderAction>
|
||||
</Flex>
|
||||
</Meta>
|
||||
</Flex>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -28,14 +28,16 @@ type Props = {
|
||||
collections: CollectionsStore,
|
||||
};
|
||||
|
||||
@observer class DocumentMove extends Component {
|
||||
@observer
|
||||
class DocumentMove extends Component {
|
||||
props: Props;
|
||||
firstDocument: HTMLElement;
|
||||
|
||||
@observable searchTerm: ?string;
|
||||
@observable isSaving: boolean;
|
||||
|
||||
@computed get searchIndex() {
|
||||
@computed
|
||||
get searchIndex() {
|
||||
const { document, collections } = this.props;
|
||||
const paths = collections.pathsToDocuments;
|
||||
const index = new Search('id');
|
||||
@ -54,7 +56,8 @@ type Props = {
|
||||
return index;
|
||||
}
|
||||
|
||||
@computed get results(): DocumentPath[] {
|
||||
@computed
|
||||
get results(): DocumentPath[] {
|
||||
const { document, collections } = this.props;
|
||||
|
||||
let results = [];
|
||||
@ -134,43 +137,46 @@ type Props = {
|
||||
return (
|
||||
<Modal isOpen onRequestClose={this.handleClose} title="Move document">
|
||||
{document &&
|
||||
collections.isLoaded &&
|
||||
<Flex column>
|
||||
<Section>
|
||||
<Labeled label="Current location">
|
||||
{this.renderPathToCurrentDocument()}
|
||||
</Labeled>
|
||||
</Section>
|
||||
collections.isLoaded && (
|
||||
<Flex column>
|
||||
<Section>
|
||||
<Labeled label="Current location">
|
||||
{this.renderPathToCurrentDocument()}
|
||||
</Labeled>
|
||||
</Section>
|
||||
|
||||
<Section column>
|
||||
<Labeled label="Choose a new location">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Filter by document name…"
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onChange={this.handleFilter}
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
</Labeled>
|
||||
<Flex column>
|
||||
<StyledArrowKeyNavigation
|
||||
mode={ArrowKeyNavigation.mode.VERTICAL}
|
||||
defaultActiveChildIndex={0}
|
||||
>
|
||||
{this.results.map((result, index) => (
|
||||
<PathToDocument
|
||||
key={result.id}
|
||||
result={result}
|
||||
document={document}
|
||||
ref={ref => index === 0 && this.setFirstDocumentRef(ref)}
|
||||
onSuccess={this.handleClose}
|
||||
/>
|
||||
))}
|
||||
</StyledArrowKeyNavigation>
|
||||
</Flex>
|
||||
</Section>
|
||||
</Flex>}
|
||||
<Section column>
|
||||
<Labeled label="Choose a new location">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Filter by document name…"
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onChange={this.handleFilter}
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
</Labeled>
|
||||
<Flex column>
|
||||
<StyledArrowKeyNavigation
|
||||
mode={ArrowKeyNavigation.mode.VERTICAL}
|
||||
defaultActiveChildIndex={0}
|
||||
>
|
||||
{this.results.map((result, index) => (
|
||||
<PathToDocument
|
||||
key={result.id}
|
||||
result={result}
|
||||
document={document}
|
||||
ref={ref =>
|
||||
index === 0 && this.setFirstDocumentRef(ref)
|
||||
}
|
||||
onSuccess={this.handleClose}
|
||||
/>
|
||||
))}
|
||||
</StyledArrowKeyNavigation>
|
||||
</Flex>
|
||||
</Section>
|
||||
</Flex>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ const ResultWrapper = styled.div`
|
||||
cursor: default;
|
||||
`;
|
||||
|
||||
const StyledGoToIcon = styled(GoToIcon)`
|
||||
`;
|
||||
const StyledGoToIcon = styled(GoToIcon)``;
|
||||
|
||||
const ResultWrapperLink = ResultWrapper.withComponent('a').extend`
|
||||
height: 32px;
|
||||
@ -50,7 +49,8 @@ type Props = {
|
||||
ref?: Function,
|
||||
};
|
||||
|
||||
@observer class PathToDocument extends React.Component {
|
||||
@observer
|
||||
class PathToDocument extends React.Component {
|
||||
props: Props;
|
||||
|
||||
handleClick = async (ev: SyntheticEvent) => {
|
||||
@ -83,12 +83,12 @@ type Props = {
|
||||
{result.path
|
||||
.map(doc => <span key={doc.id}>{doc.title}</span>)
|
||||
.reduce((prev, curr) => [prev, <StyledGoToIcon />, curr])}
|
||||
{document &&
|
||||
{document && (
|
||||
<Flex>
|
||||
{' '}
|
||||
<StyledGoToIcon />
|
||||
{' '}{document.title}
|
||||
</Flex>}
|
||||
<StyledGoToIcon /> {document.title}
|
||||
</Flex>
|
||||
)}
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ type Props = {
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|
||||
@observer class DocumentDelete extends Component {
|
||||
@observer
|
||||
class DocumentDelete extends Component {
|
||||
props: Props;
|
||||
@observable isDeleting: boolean;
|
||||
|
||||
@ -41,10 +42,7 @@ type Props = {
|
||||
<Flex column>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<HelpText>
|
||||
Are you sure? Deleting the
|
||||
{' '}
|
||||
<strong>{document.title}</strong>
|
||||
{' '}
|
||||
Are you sure? Deleting the <strong>{document.title}</strong>{' '}
|
||||
document is permanant and will also delete all of its history.
|
||||
</HelpText>
|
||||
<Button type="submit" danger>
|
||||
|
@ -14,7 +14,9 @@ class Error404 extends React.Component {
|
||||
|
||||
<p>We're unable to find the page you're accessing.</p>
|
||||
|
||||
<p>Maybe you want to try <Link to="/search">search</Link> instead?</p>
|
||||
<p>
|
||||
Maybe you want to try <Link to="/search">search</Link> instead?
|
||||
</p>
|
||||
</CenteredContent>
|
||||
);
|
||||
}
|
||||
|
@ -9,75 +9,123 @@ function KeyboardShortcuts() {
|
||||
return (
|
||||
<Flex column>
|
||||
<HelpText>
|
||||
Outline is designed to be super fast and easy to use.
|
||||
All of your usual keyboard shortcuts work here, and there
|
||||
Outline is designed to be super fast and easy to use. All of your usual
|
||||
keyboard shortcuts work here, and there
|
||||
{"'"}
|
||||
s Markdown too.
|
||||
</HelpText>
|
||||
|
||||
<h2>Navigation</h2>
|
||||
<List>
|
||||
<Keys><Key>e</Key></Keys>
|
||||
<Keys>
|
||||
<Key>e</Key>
|
||||
</Keys>
|
||||
<Label>Edit current document</Label>
|
||||
|
||||
<Keys><Key>m</Key></Keys>
|
||||
<Keys>
|
||||
<Key>m</Key>
|
||||
</Keys>
|
||||
<Label>Move current document</Label>
|
||||
|
||||
<Keys><Key>/</Key> or <Key>t</Key></Keys>
|
||||
<Keys>
|
||||
<Key>/</Key> or <Key>t</Key>
|
||||
</Keys>
|
||||
<Label>Jump to search</Label>
|
||||
|
||||
<Keys><Key>d</Key></Keys>
|
||||
<Keys>
|
||||
<Key>d</Key>
|
||||
</Keys>
|
||||
<Label>Jump to dashboard</Label>
|
||||
|
||||
<Keys><Key>⌘</Key> + <Key>/</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>/</Key>
|
||||
</Keys>
|
||||
<Label>Open this guide</Label>
|
||||
</List>
|
||||
|
||||
<h2>Editor</h2>
|
||||
<List>
|
||||
<Keys><Key>⌘</Key> + <Key>Enter</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>Enter</Key>
|
||||
</Keys>
|
||||
<Label>Save and exit document edit mode</Label>
|
||||
<Keys><Key>⌘</Key> + <Key>S</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>S</Key>
|
||||
</Keys>
|
||||
<Label>Save document and continue editing</Label>
|
||||
<Keys><Key>⌘</Key> + <Key>Esc</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>Esc</Key>
|
||||
</Keys>
|
||||
<Label>Cancel editing</Label>
|
||||
|
||||
<Keys><Key>⌘</Key> + <Key>b</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>b</Key>
|
||||
</Keys>
|
||||
<Label>Bold</Label>
|
||||
<Keys><Key>⌘</Key> + <Key>i</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>i</Key>
|
||||
</Keys>
|
||||
<Label>Italic</Label>
|
||||
<Keys><Key>⌘</Key> + <Key>u</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>u</Key>
|
||||
</Keys>
|
||||
<Label>Underline</Label>
|
||||
<Keys><Key>⌘</Key> + <Key>d</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>d</Key>
|
||||
</Keys>
|
||||
<Label>Strikethrough</Label>
|
||||
<Keys><Key>⌘</Key> + <Key>k</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>k</Key>
|
||||
</Keys>
|
||||
<Label>Link</Label>
|
||||
<Keys><Key>⌘</Key> + <Key>z</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>z</Key>
|
||||
</Keys>
|
||||
<Label>Undo</Label>
|
||||
<Keys><Key>⌘</Key> + <Key>Shift</Key> + <Key>z</Key></Keys>
|
||||
<Keys>
|
||||
<Key>⌘</Key> + <Key>Shift</Key> + <Key>z</Key>
|
||||
</Keys>
|
||||
<Label>Redo</Label>
|
||||
</List>
|
||||
|
||||
<h2>Markdown</h2>
|
||||
<List>
|
||||
<Keys><Key>#</Key> <Key>Space</Key></Keys>
|
||||
<Keys>
|
||||
<Key>#</Key> <Key>Space</Key>
|
||||
</Keys>
|
||||
<Label>Large header</Label>
|
||||
<Keys><Key>##</Key> <Key>Space</Key></Keys>
|
||||
<Keys>
|
||||
<Key>##</Key> <Key>Space</Key>
|
||||
</Keys>
|
||||
<Label>Medium header</Label>
|
||||
<Keys><Key>###</Key> <Key>Space</Key></Keys>
|
||||
<Keys>
|
||||
<Key>###</Key> <Key>Space</Key>
|
||||
</Keys>
|
||||
<Label>Small header</Label>
|
||||
|
||||
<Keys><Key>1.</Key> <Key>Space</Key></Keys>
|
||||
<Keys>
|
||||
<Key>1.</Key> <Key>Space</Key>
|
||||
</Keys>
|
||||
<Label>Numbered list</Label>
|
||||
<Keys><Key>-</Key> <Key>Space</Key></Keys>
|
||||
<Keys>
|
||||
<Key>-</Key> <Key>Space</Key>
|
||||
</Keys>
|
||||
<Label>Bulleted list</Label>
|
||||
<Keys><Key>[ ]</Key> <Key>Space</Key></Keys>
|
||||
<Keys>
|
||||
<Key>[ ]</Key> <Key>Space</Key>
|
||||
</Keys>
|
||||
<Label>Todo list</Label>
|
||||
<Keys><Key>></Key> <Key>Space</Key></Keys>
|
||||
<Keys>
|
||||
<Key>></Key> <Key>Space</Key>
|
||||
</Keys>
|
||||
<Label>Blockquote</Label>
|
||||
<Keys><Key>---</Key></Keys>
|
||||
<Keys>
|
||||
<Key>---</Key>
|
||||
</Keys>
|
||||
<Label>Horizontal divider</Label>
|
||||
<Keys><Key>{'```'}</Key></Keys>
|
||||
<Keys>
|
||||
<Key>{'```'}</Key>
|
||||
</Keys>
|
||||
<Label>Code block</Label>
|
||||
|
||||
<Keys>_italic_</Keys>
|
||||
@ -97,21 +145,21 @@ const List = styled.dl`
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const Keys = styled.dt`
|
||||
float: left;
|
||||
width: 25%;
|
||||
padding: 0 0 4px;
|
||||
margin: 0
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const Label = styled.dd`
|
||||
float: left;
|
||||
width: 75%;
|
||||
padding: 0 0 4px;
|
||||
margin: 0
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
export default KeyboardShortcuts;
|
||||
|
@ -54,7 +54,8 @@ const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
@observer class Search extends React.Component {
|
||||
@observer
|
||||
class Search extends React.Component {
|
||||
firstDocument: HTMLElement;
|
||||
props: Props;
|
||||
|
||||
@ -99,7 +100,8 @@ const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)`
|
||||
this.search(this.props.match.params.query);
|
||||
}, 250);
|
||||
|
||||
@action search = async (query: string) => {
|
||||
@action
|
||||
search = async (query: string) => {
|
||||
this.searchTerm = query;
|
||||
this.isFetching = true;
|
||||
|
||||
@ -141,11 +143,12 @@ const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)`
|
||||
<Container auto>
|
||||
<PageTitle title={this.title} />
|
||||
{this.isFetching && <LoadingIndicator />}
|
||||
{notFound &&
|
||||
{notFound && (
|
||||
<div>
|
||||
<h1>Not Found</h1>
|
||||
<p>We’re unable to find the page you’re accessing.</p>
|
||||
</div>}
|
||||
</div>
|
||||
)}
|
||||
<ResultsWrapper pinToTop={hasResults} column auto>
|
||||
<SearchField
|
||||
searchTerm={this.searchTerm}
|
||||
@ -165,7 +168,8 @@ const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)`
|
||||
return (
|
||||
<DocumentPreview
|
||||
innerRef={ref =>
|
||||
index === 0 && this.setFirstDocumentRef(ref)}
|
||||
index === 0 && this.setFirstDocumentRef(ref)
|
||||
}
|
||||
key={documentId}
|
||||
document={document}
|
||||
highlight={this.searchTerm}
|
||||
|
@ -53,10 +53,18 @@ const StyledInput = styled.input`
|
||||
outline: none;
|
||||
border: 0;
|
||||
|
||||
::-webkit-input-placeholder { color: ${color.slateLight}; }
|
||||
:-moz-placeholder { color: ${color.slateLight}; }
|
||||
::-moz-placeholder { color: ${color.slateLight}; }
|
||||
:-ms-input-placeholder { color: ${color.slateLight}; }
|
||||
::-webkit-input-placeholder {
|
||||
color: ${color.slateLight};
|
||||
}
|
||||
:-moz-placeholder {
|
||||
color: ${color.slateLight};
|
||||
}
|
||||
::-moz-placeholder {
|
||||
color: ${color.slateLight};
|
||||
}
|
||||
:-ms-input-placeholder {
|
||||
color: ${color.slateLight};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledIcon = styled(SearchIcon)`
|
||||
|
@ -14,7 +14,8 @@ import HelpText from 'components/HelpText';
|
||||
import { Label } from 'components/Labeled';
|
||||
import SlackAuthLink from 'components/SlackAuthLink';
|
||||
|
||||
@observer class Settings extends React.Component {
|
||||
@observer
|
||||
class Settings extends React.Component {
|
||||
store: SettingsStore;
|
||||
|
||||
constructor() {
|
||||
@ -27,12 +28,12 @@ import SlackAuthLink from 'components/SlackAuthLink';
|
||||
|
||||
return (
|
||||
<Flex column>
|
||||
{showSlackSettings &&
|
||||
{showSlackSettings && (
|
||||
<Section>
|
||||
<SectionLabel>Slack</SectionLabel>
|
||||
<HelpText>
|
||||
Connect Outline to your Slack to instantly search for your documents
|
||||
using <Code>/outline</Code> command.
|
||||
Connect Outline to your Slack to instantly search for your
|
||||
documents using <Code>/outline</Code> command.
|
||||
</HelpText>
|
||||
|
||||
<SlackAuthLink
|
||||
@ -47,16 +48,17 @@ import SlackAuthLink from 'components/SlackAuthLink';
|
||||
srcSet="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x"
|
||||
/>
|
||||
</SlackAuthLink>
|
||||
</Section>}
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Section>
|
||||
<SectionLabel>API Access</SectionLabel>
|
||||
<HelpText>
|
||||
Create API tokens to hack on your Outline.
|
||||
Learn more in <Link to="/developers">API documentation</Link>.
|
||||
Create API tokens to hack on your Outline. Learn more in{' '}
|
||||
<Link to="/developers">API documentation</Link>.
|
||||
</HelpText>
|
||||
|
||||
{this.store.apiKeys &&
|
||||
{this.store.apiKeys && (
|
||||
<Table>
|
||||
<tbody>
|
||||
{this.store.apiKeys &&
|
||||
@ -70,7 +72,8 @@ import SlackAuthLink from 'components/SlackAuthLink';
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>}
|
||||
</Table>
|
||||
)}
|
||||
<InlineForm
|
||||
placeholder="Token name"
|
||||
buttonLabel="Create token"
|
||||
@ -162,13 +165,13 @@ const Table = styled.table`
|
||||
const SectionLabel = styled(Label)`
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid #EAEBEA;
|
||||
border-bottom: 1px solid #eaebea;
|
||||
`;
|
||||
|
||||
const Code = styled.code`
|
||||
padding: 4px 6px;
|
||||
margin: 0 2px;
|
||||
background: #EAEBEA;
|
||||
background: #eaebea;
|
||||
border-radius: 4px;
|
||||
`;
|
||||
|
||||
|
@ -10,7 +10,8 @@ class SearchStore {
|
||||
|
||||
@observable isFetching: boolean = false;
|
||||
|
||||
@action fetchApiKeys = async () => {
|
||||
@action
|
||||
fetchApiKeys = async () => {
|
||||
this.isFetching = true;
|
||||
|
||||
try {
|
||||
@ -27,7 +28,8 @@ class SearchStore {
|
||||
this.isFetching = false;
|
||||
};
|
||||
|
||||
@action createApiKey = async () => {
|
||||
@action
|
||||
createApiKey = async () => {
|
||||
this.isFetching = true;
|
||||
|
||||
try {
|
||||
@ -46,7 +48,8 @@ class SearchStore {
|
||||
this.isFetching = false;
|
||||
};
|
||||
|
||||
@action deleteApiKey = async (id: string) => {
|
||||
@action
|
||||
deleteApiKey = async (id: string) => {
|
||||
this.isFetching = true;
|
||||
|
||||
try {
|
||||
@ -62,7 +65,8 @@ class SearchStore {
|
||||
this.isFetching = false;
|
||||
};
|
||||
|
||||
@action setKeyName = (value: SyntheticInputEvent) => {
|
||||
@action
|
||||
setKeyName = (value: SyntheticInputEvent) => {
|
||||
this.keyName = value.target.value;
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,8 @@ type Props = {
|
||||
onDelete: Function,
|
||||
};
|
||||
|
||||
@observer class ApiKeyRow extends React.Component {
|
||||
@observer
|
||||
class ApiKeyRow extends React.Component {
|
||||
props: Props;
|
||||
@observable disabled: boolean;
|
||||
|
||||
@ -28,7 +29,9 @@ type Props = {
|
||||
return (
|
||||
<tr>
|
||||
<td>{name}</td>
|
||||
<td><code>{secret}</code></td>
|
||||
<td>
|
||||
<code>{secret}</code>
|
||||
</td>
|
||||
<td>
|
||||
<Action role="button" onClick={this.onClick} disabled={disabled}>
|
||||
Action
|
||||
|
@ -15,7 +15,8 @@ type Props = {
|
||||
location: Location,
|
||||
};
|
||||
|
||||
@observer class SlackAuth extends React.Component {
|
||||
@observer
|
||||
class SlackAuth extends React.Component {
|
||||
props: Props;
|
||||
@observable redirectTo: string;
|
||||
|
||||
|
@ -8,7 +8,8 @@ import PageTitle from 'components/PageTitle';
|
||||
import DocumentList from 'components/DocumentList';
|
||||
import DocumentsStore from 'stores/DocumentsStore';
|
||||
|
||||
@observer class Starred extends Component {
|
||||
@observer
|
||||
class Starred extends Component {
|
||||
props: {
|
||||
documents: DocumentsStore,
|
||||
};
|
||||
|
@ -16,11 +16,13 @@ class AuthStore {
|
||||
|
||||
/* Computed */
|
||||
|
||||
@computed get authenticated(): boolean {
|
||||
@computed
|
||||
get authenticated(): boolean {
|
||||
return !!this.token;
|
||||
}
|
||||
|
||||
@computed get asJson(): string {
|
||||
@computed
|
||||
get asJson(): string {
|
||||
return JSON.stringify({
|
||||
user: this.user,
|
||||
team: this.team,
|
||||
@ -31,19 +33,24 @@ class AuthStore {
|
||||
|
||||
/* Actions */
|
||||
|
||||
@action logout = () => {
|
||||
@action
|
||||
logout = () => {
|
||||
this.user = null;
|
||||
this.token = null;
|
||||
Cookie.remove('loggedIn', { path: '/' });
|
||||
};
|
||||
|
||||
@action getOauthState = () => {
|
||||
const state = Math.random().toString(36).substring(7);
|
||||
@action
|
||||
getOauthState = () => {
|
||||
const state = Math.random()
|
||||
.toString(36)
|
||||
.substring(7);
|
||||
this.oauthState = state;
|
||||
return this.oauthState;
|
||||
};
|
||||
|
||||
@action authWithSlack = async (code: string, state: string) => {
|
||||
@action
|
||||
authWithSlack = async (code: string, state: string) => {
|
||||
if (state !== this.oauthState) {
|
||||
return {
|
||||
success: false,
|
||||
|
@ -43,20 +43,23 @@ class CollectionsStore {
|
||||
cache: CacheStore;
|
||||
ui: UiStore;
|
||||
|
||||
@computed get active(): ?Collection {
|
||||
@computed
|
||||
get active(): ?Collection {
|
||||
return this.ui.activeCollectionId
|
||||
? this.getById(this.ui.activeCollectionId)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@computed get orderedData(): Collection[] {
|
||||
@computed
|
||||
get orderedData(): Collection[] {
|
||||
return _.sortBy(this.data, 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* List of paths to each of the documents, where paths are composed of id and title/name pairs
|
||||
*/
|
||||
@computed get pathsToDocuments(): Array<DocumentPath> {
|
||||
@computed
|
||||
get pathsToDocuments(): Array<DocumentPath> {
|
||||
let results = [];
|
||||
const travelDocuments = (documentList, path) =>
|
||||
documentList.forEach(document => {
|
||||
@ -95,7 +98,8 @@ class CollectionsStore {
|
||||
|
||||
/* Actions */
|
||||
|
||||
@action fetchAll = async (): Promise<*> => {
|
||||
@action
|
||||
fetchAll = async (): Promise<*> => {
|
||||
try {
|
||||
const res = await this.client.post('/collections.list', {
|
||||
id: this.teamId,
|
||||
@ -111,7 +115,8 @@ class CollectionsStore {
|
||||
}
|
||||
};
|
||||
|
||||
@action fetchById = async (id: string): Promise<?Collection> => {
|
||||
@action
|
||||
fetchById = async (id: string): Promise<?Collection> => {
|
||||
let collection = this.getById(id);
|
||||
if (!collection) {
|
||||
try {
|
||||
@ -133,11 +138,13 @@ class CollectionsStore {
|
||||
return collection;
|
||||
};
|
||||
|
||||
@action add = (collection: Collection): void => {
|
||||
@action
|
||||
add = (collection: Collection): void => {
|
||||
this.data.push(collection);
|
||||
};
|
||||
|
||||
@action remove = (id: string): void => {
|
||||
@action
|
||||
remove = (id: string): void => {
|
||||
this.data.splice(this.data.indexOf(id), 1);
|
||||
};
|
||||
|
||||
|
@ -37,7 +37,8 @@ class DocumentsStore extends BaseStore {
|
||||
|
||||
/* Computed */
|
||||
|
||||
@computed get recentlyViewed(): Array<Document> {
|
||||
@computed
|
||||
get recentlyViewed(): Array<Document> {
|
||||
return _.take(
|
||||
_.filter(this.data.values(), ({ id }) =>
|
||||
this.recentlyViewedIds.includes(id)
|
||||
@ -46,15 +47,18 @@ class DocumentsStore extends BaseStore {
|
||||
);
|
||||
}
|
||||
|
||||
@computed get recentlyEdited(): Array<Document> {
|
||||
@computed
|
||||
get recentlyEdited(): Array<Document> {
|
||||
return _.take(_.orderBy(this.data.values(), 'updatedAt', 'desc'), 5);
|
||||
}
|
||||
|
||||
@computed get starred(): Array<Document> {
|
||||
@computed
|
||||
get starred(): Array<Document> {
|
||||
return _.filter(this.data.values(), 'starred');
|
||||
}
|
||||
|
||||
@computed get active(): ?Document {
|
||||
@computed
|
||||
get active(): ?Document {
|
||||
return this.ui.activeDocumentId
|
||||
? this.getById(this.ui.activeDocumentId)
|
||||
: undefined;
|
||||
@ -62,10 +66,8 @@ class DocumentsStore extends BaseStore {
|
||||
|
||||
/* Actions */
|
||||
|
||||
@action fetchAll = async (
|
||||
request: string = 'list',
|
||||
options: ?Object
|
||||
): Promise<*> => {
|
||||
@action
|
||||
fetchAll = async (request: string = 'list', options: ?Object): Promise<*> => {
|
||||
this.isFetching = true;
|
||||
|
||||
try {
|
||||
@ -86,11 +88,13 @@ class DocumentsStore extends BaseStore {
|
||||
}
|
||||
};
|
||||
|
||||
@action fetchRecentlyModified = async (options: ?Object): Promise<*> => {
|
||||
@action
|
||||
fetchRecentlyModified = async (options: ?Object): Promise<*> => {
|
||||
return await this.fetchAll('list', options);
|
||||
};
|
||||
|
||||
@action fetchRecentlyViewed = async (options: ?Object): Promise<*> => {
|
||||
@action
|
||||
fetchRecentlyViewed = async (options: ?Object): Promise<*> => {
|
||||
const data = await this.fetchAll('viewed', options);
|
||||
|
||||
runInAction('DocumentsStore#fetchRecentlyViewed', () => {
|
||||
@ -99,11 +103,13 @@ class DocumentsStore extends BaseStore {
|
||||
return data;
|
||||
};
|
||||
|
||||
@action fetchStarred = async (): Promise<*> => {
|
||||
@action
|
||||
fetchStarred = async (): Promise<*> => {
|
||||
await this.fetchAll('starred');
|
||||
};
|
||||
|
||||
@action search = async (query: string): Promise<*> => {
|
||||
@action
|
||||
search = async (query: string): Promise<*> => {
|
||||
const res = await client.get('/documents.search', { query });
|
||||
invariant(res && res.data, 'res or res.data missing');
|
||||
const { data } = res;
|
||||
@ -111,11 +117,13 @@ class DocumentsStore extends BaseStore {
|
||||
return data.map(documentData => documentData.id);
|
||||
};
|
||||
|
||||
@action prefetchDocument = async (id: string) => {
|
||||
@action
|
||||
prefetchDocument = async (id: string) => {
|
||||
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 (!prefetch) this.isFetching = true;
|
||||
|
||||
try {
|
||||
@ -137,11 +145,13 @@ class DocumentsStore extends BaseStore {
|
||||
}
|
||||
};
|
||||
|
||||
@action add = (document: Document): void => {
|
||||
@action
|
||||
add = (document: Document): void => {
|
||||
this.data.set(document.id, document);
|
||||
};
|
||||
|
||||
@action remove = (id: string): void => {
|
||||
@action
|
||||
remove = (id: string): void => {
|
||||
this.data.delete(id);
|
||||
};
|
||||
|
||||
|
@ -6,11 +6,13 @@ class ErrorsStore {
|
||||
|
||||
/* Actions */
|
||||
|
||||
@action add = (message: string): void => {
|
||||
@action
|
||||
add = (message: string): void => {
|
||||
this.data.push(message);
|
||||
};
|
||||
|
||||
@action remove = (index: number): void => {
|
||||
@action
|
||||
remove = (index: number): void => {
|
||||
this.data.splice(index, 1);
|
||||
};
|
||||
}
|
||||
|
@ -11,39 +11,47 @@ class UiStore {
|
||||
@observable editMode: boolean = false;
|
||||
|
||||
/* Actions */
|
||||
@action setActiveModal = (name: string, props: ?Object): void => {
|
||||
@action
|
||||
setActiveModal = (name: string, props: ?Object): void => {
|
||||
this.activeModalName = name;
|
||||
this.activeModalProps = props;
|
||||
};
|
||||
|
||||
@action clearActiveModal = (): void => {
|
||||
@action
|
||||
clearActiveModal = (): void => {
|
||||
this.activeModalName = undefined;
|
||||
this.activeModalProps = undefined;
|
||||
};
|
||||
|
||||
@action setActiveDocument = (document: Document): void => {
|
||||
@action
|
||||
setActiveDocument = (document: Document): void => {
|
||||
this.activeDocumentId = document.id;
|
||||
this.activeCollectionId = document.collection.id;
|
||||
};
|
||||
|
||||
@action clearActiveDocument = (): void => {
|
||||
@action
|
||||
clearActiveDocument = (): void => {
|
||||
this.activeDocumentId = undefined;
|
||||
this.activeCollectionId = undefined;
|
||||
};
|
||||
|
||||
@action enableEditMode() {
|
||||
@action
|
||||
enableEditMode() {
|
||||
this.editMode = true;
|
||||
}
|
||||
|
||||
@action disableEditMode() {
|
||||
@action
|
||||
disableEditMode() {
|
||||
this.editMode = false;
|
||||
}
|
||||
|
||||
@action enableProgressBar() {
|
||||
@action
|
||||
enableProgressBar() {
|
||||
this.progressBarVisible = true;
|
||||
}
|
||||
|
||||
@action disableProgressBar() {
|
||||
@action
|
||||
disableProgressBar() {
|
||||
this.progressBarVisible = false;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user