Merge master

This commit is contained in:
Tom Moor
2017-09-11 22:49:53 -07:00
55 changed files with 791 additions and 1183 deletions

View File

@@ -1,36 +1,37 @@
// @flow
import React, { PropTypes } from 'react';
import React from 'react';
import { observer } from 'mobx-react';
import Flex from 'components/Flex';
import classNames from 'classnames/bind';
import styles from './Alert.scss';
import styled from 'styled-components';
import { color } from 'styles/constants';
const cx = classNames.bind(styles);
type Props = {
children: React.Element<*>,
type?: 'info' | 'success' | 'warning' | 'danger' | 'offline',
};
class Alert extends React.Component {
static propTypes = {
children: PropTypes.node.isRequired,
danger: PropTypes.bool,
warning: PropTypes.bool,
success: PropTypes.bool,
@observer class Alert extends React.Component {
props: Props;
defaultProps = {
type: 'info',
};
render() {
let alertType;
if (this.props.danger) alertType = 'danger';
if (this.props.warning) alertType = 'warning';
if (this.props.success) alertType = 'success';
if (!alertType) alertType = 'info'; // default
return (
<Flex
align="center"
justify="center"
className={cx(styles.container, styles[alertType])}
>
<Container align="center" justify="center" type={this.props.type}>
{this.props.children}
</Flex>
</Container>
);
}
}
const Container = styled(Flex)`
height: $headerHeight;
color: #ffffff;
font-size: 14px;
line-height: 1;
background-color: ${({ type }) => color[type]};
`;
export default Alert;

View File

@@ -1,28 +0,0 @@
@import '~styles/constants.scss';
.container {
height: $headerHeight;
color: #ffffff;
font-size: 14px;
line-height: 1;
}
.danger {
background-color: #f04124;
}
.warning {
background-color: #f08a24;
}
.success {
background-color: #43AC6A;
}
.info {
background-color: #a0d3e8;
}
.offline {
background-color: #000000;
}

View File

@@ -1,23 +0,0 @@
/* eslint-disable */
import React from 'react';
import Alert from '.';
test('renders default as info', () => {
snap(<Alert>default</Alert>);
});
test('renders success', () => {
snap(<Alert success>success</Alert>);
});
test('renders info', () => {
snap(<Alert info>info</Alert>);
});
test('renders warning', () => {
snap(<Alert warning>warning</Alert>);
});
test('renders danger', () => {
snap(<Alert danger>danger</Alert>);
});

View File

@@ -1,51 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders danger 1`] = `
<Flex
align="center"
className="container danger"
justify="center"
>
danger
</Flex>
`;
exports[`renders default as info 1`] = `
<Flex
align="center"
className="container info"
justify="center"
>
default
</Flex>
`;
exports[`renders info 1`] = `
<Flex
align="center"
className="container info"
justify="center"
>
info
</Flex>
`;
exports[`renders success 1`] = `
<Flex
align="center"
className="container success"
justify="center"
>
success
</Flex>
`;
exports[`renders warning 1`] = `
<Flex
align="center"
className="container warning"
justify="center"
>
warning
</Flex>
`;

View File

@@ -25,15 +25,20 @@ const Collaborators = function({ document }: { document: Document }) {
return (
<Avatars>
<Tooltip tooltip={tooltip} placement="bottom">
<StyledTooltip tooltip={tooltip} placement="bottom">
{collaborators.map(user => (
<Avatar key={user.id} src={user.avatarUrl} />
))}
</Tooltip>
</StyledTooltip>
</Avatars>
);
};
const StyledTooltip = styled(Tooltip)`
display: flex;
flex-direction: row-reverse;
`;
const Avatars = styled(Flex)`
flex-direction: row-reverse;
height: 26px;
@@ -45,7 +50,7 @@ const Avatar = styled.img`
flex-shrink: 0;
border-radius: 50%;
border: 2px solid ${color.white};
margin-right: -13px;
margin-right: -10px;
&:first-child {
margin-right: 0;

View File

@@ -1,48 +0,0 @@
// @flow
import React from 'react';
import { observer } from 'mobx-react';
import { Link } from 'react-router-dom';
import moment from 'moment';
import styles from './Collection.scss';
@observer class Collection extends React.Component {
static propTypes = {
data: React.PropTypes.object.isRequired,
};
render() {
const data = this.props.data;
return (
<div className={styles.container}>
<h2>
<Link to={data.url} className={styles.atlasLink}>{data.name}</Link>
</h2>
{data.recentDocuments.length > 0
? data.recentDocuments.map(document => {
return (
<Link
key={document.id}
to={document.url}
className={styles.link}
>
<h3 className={styles.title}>{document.title}</h3>
<span className={styles.timestamp}>
{moment(document.updatedAt).fromNow()}
</span>
</Link>
);
})
: <div className={styles.description}>
No documents. Why not
{' '}
<Link to={`${data.url}/new`}>create one</Link>
?
</div>}
</div>
);
}
}
export default Collection;

View File

@@ -1,41 +0,0 @@
@import '~styles/constants.scss';
.container {
display: flex;
flex: 1;
flex-direction: column;
padding-bottom: 40px;
margin-bottom: 20px;
border-bottom: 1px solid #eee;
}
.atlasLink {
text-decoration: none;
color: $textColor;
}
.description {
color: #aaa;
}
.link {
display: flex;
flex: 1;
justify-content: space-between;
margin-bottom: 20px;
text-decoration: none;
}
.title {
font-weight: normal;
font-size: 15px;
color: $textColor;
margin: 0;
}
.timestamp {
font-size: 13px;
color: #ccc;
}

View File

@@ -1,21 +0,0 @@
// @flow
import React from 'react';
import { observer } from 'mobx-react';
import moment from 'moment';
import { Link } from 'react-router-dom';
import styles from './DocumentLink.scss';
const DocumentLink = observer(props => {
return (
<Link to={props.document.url} className={styles.link}>
<h3 className={styles.title}>{props.document.title}</h3>
<span className={styles.timestamp}>
{moment(props.document.updatedAt).fromNow()}
</span>
</Link>
);
});
export default DocumentLink;

View File

@@ -1,23 +0,0 @@
@import '~styles/constants.scss';
.link {
display: flex;
flex: 1;
justify-content: space-between;
margin-bottom: 20px;
text-decoration: none;
}
.title {
font-weight: normal;
font-size: 15px;
color: $textColor;
margin: 0;
}
.timestamp {
font-size: 13px;
color: #ccc;
}

View File

@@ -1,3 +0,0 @@
// @flow
import DocumentLink from './DocumentLink';
export default DocumentLink;

View File

@@ -1,3 +0,0 @@
// @flow
import Collection from './Collection';
export default Collection;

View File

@@ -1,10 +1,17 @@
// @flow
import React from 'react';
import styles from './Divider.scss';
import styled from 'styled-components';
import Flex from 'components/Flex';
const Divider = () => {
return <div className={styles.divider}><span /></div>;
return <Flex auto justify="center"><Content /></Flex>;
};
const Content = styled.span`
display: flex;
width: 50%;
margin: 20px 0;
border-bottom: 1px solid #eee;
`;
export default Divider;

View File

@@ -1,13 +0,0 @@
.divider {
display: flex;
flex: 1;
justify-content: center;
span {
display: flex;
width: 50%;
margin: 20px 0;
border-bottom: 1px solid #eee;
}
}

View File

@@ -15,13 +15,18 @@ type Props = {
innerRef?: Function,
};
const StyledStar = styled(Icon).attrs({
const StyledStar = styled(({ solid, ...props }) => <Icon {...props} />).attrs({
type: 'Star',
color: color.text,
})`
width: 16px;
height: 16px;
top: 1px;
margin-left: 4px;
opacity: ${props => (props.solid ? '1 !important' : 0)};
transition: opacity 100ms ease-in-out;
${props => props.solid && 'polygon { fill: #000};'}
`;
const DocumentLink = styled(Link)`

View File

@@ -3,20 +3,17 @@ import React, { Component } from 'react';
import { observer } from 'mobx-react';
import { Editor, Plain } from 'slate';
import keydown from 'react-keydown';
import classnames from 'classnames/bind';
import type { Document, State, Editor as EditorType } from './types';
import getDataTransferFiles from 'utils/getDataTransferFiles';
import Flex from 'components/Flex';
import ClickablePadding from './components/ClickablePadding';
import Toolbar from './components/Toolbar';
import Placeholder from './components/Placeholder';
import Markdown from './serializer';
import createSchema from './schema';
import createPlugins from './plugins';
import insertImage from './insertImage';
import styled from 'styled-components';
import styles from './Editor.scss';
const cx = classnames.bind(styles);
type Props = {
text: string,
@@ -188,11 +185,10 @@ type KeyData = {
<MaxWidth column auto>
<Header onClick={this.focusAtStart} readOnly={this.props.readOnly} />
<Toolbar state={this.state.state} onChange={this.onChange} />
<Editor
ref={ref => (this.editor = ref)}
<StyledEditor
innerRef={ref => (this.editor = ref)}
placeholder="Start with a title…"
bodyPlaceholder="Insert witty platitude here"
className={cx(styles.editor, { readOnly: this.props.readOnly })}
schema={this.schema}
plugins={this.plugins}
emoji={this.props.emoji}
@@ -225,4 +221,106 @@ const Header = styled(Flex)`
${({ readOnly }) => !readOnly && 'cursor: text;'}
`;
const StyledEditor = styled(Editor)`
font-weight: 400;
font-size: 1em;
line-height: 1.7em;
width: 100%;
color: #1b2830;
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 500;
.anchor {
visibility: hidden;
color: #dedede;
padding-left: 0.25em;
}
&:hover {
.anchor {
visibility: visible;
&:hover {
color: #cdcdcd;
}
}
}
}
h1:first-of-type {
${Placeholder} {
visibility: visible;
}
}
p:first-of-type {
${Placeholder} {
visibility: visible;
}
}
ul,
ol {
margin: 1em 0.1em;
padding-left: 1em;
ul,
ol {
margin: 0.1em;
}
}
p {
position: relative;
}
li p {
display: inline;
margin: 0;
}
.todoList {
list-style: none;
padding-left: 0;
.todoList {
padding-left: 1em;
}
}
.todo {
span:last-child:focus {
outline: none;
}
}
blockquote {
border-left: 3px solid #efefef;
padding-left: 10px;
}
table {
border-collapse: collapse;
}
tr {
border-bottom: 1px solid #eee;
}
th {
font-weight: bold;
}
th,
td {
padding: 5px 20px 5px 0;
}
`;
export default MarkdownEditor;

View File

@@ -1,131 +0,0 @@
.editor {
font-weight: 400;
font-size: 1em;
line-height: 1.7em;
width: 100%;
color: #1b2830;
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 500;
.anchor {
visibility: hidden;
color: #dedede;
padding-left: .25em;
}
&:hover {
.anchor {
visibility: visible;
&:hover {
color: #cdcdcd;
}
}
}
}
h1:first-of-type {
.placeholder {
visibility: visible;
}
}
p:first-of-type {
.placeholder {
visibility: visible;
}
}
ul,
ol {
margin: 1em .1em;
padding-left: 1em;
ul,
ol {
margin: .1em;
}
}
p {
position: relative;
}
li p {
display: inline;
margin: 0;
}
.todoList {
list-style: none;
padding-left: 0;
.todoList {
padding-left: 1em;
}
}
.todo {
span:last-child:focus {
outline: none;
}
}
blockquote {
border-left: 3px solid #efefef;
padding-left: 10px;
}
table {
border-collapse: collapse;
}
tr {
border-bottom: 1px solid #eee;
}
th {
font-weight: bold;
}
th,
td {
padding: 5px 20px 5px 0;
}
}
.readOnly {
cursor: default;
}
.title {
position: relative;
}
.placeholder {
position: absolute;
top: 0;
visibility: hidden;
pointer-events: none;
user-select: none;
color: #B1BECC;
}
@media all and (max-width: 2000px) and (min-width: 960px) {
.container {
// margin-top: 48px;
font-size: 1.1em;
}
}
@media all and (max-width: 960px) {
.container {
font-size: 0.9em;
}
}

View File

@@ -5,7 +5,7 @@ import styled from 'styled-components';
import _ from 'lodash';
import slug from 'slug';
import type { Node, Editor } from '../types';
import styles from '../Editor.scss';
import Placeholder from './Placeholder';
type Props = {
children: React$Element<any>,
@@ -22,6 +22,27 @@ const Wrapper = styled.div`
margin-left: ${props => (props.hasEmoji ? '-1.2em' : 0)}
`;
const Anchor = styled.a`
visibility: hidden;
padding-left: .25em;
color: #dedede;
&:hover {
color: #cdcdcd;
}
`;
// $FlowIssue I don't know
const titleStyles = component => styled(component)`
position: relative;
&:hover {
${Anchor} {
visibility: visible;
}
}
`;
function Heading(props: Props) {
const {
parent,
@@ -37,21 +58,20 @@ function Heading(props: Props) {
const showPlaceholder = placeholder && firstHeading && !node.text;
const slugish = _.escape(`${component}-${slug(node.text)}`);
const showHash = readOnly && !!slugish;
const Component = component;
const Component = titleStyles(component);
const emoji = editor.props.emoji || '';
const title = node.text.trim();
const startsWithEmojiAndSpace =
emoji && title.match(new RegExp(`^${emoji}\\s`));
return (
<Component className={styles.title}>
<Component>
<Wrapper hasEmoji={startsWithEmojiAndSpace}>{children}</Wrapper>
{showPlaceholder &&
<span className={styles.placeholder} contentEditable={false}>
<Placeholder contentEditable={false}>
{editor.props.placeholder}
</span>}
{showHash &&
<a name={slugish} className={styles.anchor} href={`#${slugish}`}>#</a>}
</Placeholder>}
{showHash && <Anchor name={slugish} href={`#${slugish}`}>#</Anchor>}
</Component>
);
}

View File

@@ -2,7 +2,7 @@
import React from 'react';
import { Document } from 'slate';
import type { Props } from '../types';
import styles from '../Editor.scss';
import Placeholder from './Placeholder';
export default function Link({
attributes,
@@ -26,9 +26,9 @@ export default function Link({
<p>
{children}
{showPlaceholder &&
<span className={styles.placeholder} contentEditable={false}>
<Placeholder contentEditable={false}>
{editor.props.bodyPlaceholder}
</span>}
</Placeholder>}
</p>
);
}

View File

@@ -0,0 +1,11 @@
// @flow
import styled from 'styled-components';
export default styled.span`
position: absolute;
top: 0;
visibility: hidden;
pointer-events: none;
user-select: none;
color: #B1BECC;
`;

View File

@@ -1,7 +1,7 @@
// @flow
import React, { Component } from 'react';
import styled from 'styled-components';
import type { Props } from '../types';
import styles from '../Editor.scss';
export default class TodoItem extends Component {
props: Props & { checked: boolean };
@@ -22,7 +22,7 @@ export default class TodoItem extends Component {
const { children, checked, readOnly } = this.props;
return (
<li contentEditable={false} className={styles.todo}>
<StyledLi contentEditable={false}>
<input
type="checkbox"
checked={checked}
@@ -33,7 +33,17 @@ export default class TodoItem extends Component {
<span contentEditable={!readOnly} suppressContentEditableWarning>
{children}
</span>
</li>
</StyledLi>
);
}
}
const StyledLi = styled.li`
input {
margin-right: 0.25em;
}
&:last-child:focus {
outline: none;
}
`;

View File

@@ -1,12 +1,11 @@
// @flow
import React, { Component } from 'react';
import Portal from 'react-portal';
import classnames from 'classnames';
import styled from 'styled-components';
import _ from 'lodash';
import type { State } from '../../types';
import FormattingToolbar from './components/FormattingToolbar';
import LinkToolbar from './components/LinkToolbar';
import styles from './Toolbar.scss';
export default class Toolbar extends Component {
props: {
@@ -112,9 +111,6 @@ export default class Toolbar extends Component {
render() {
const link = this.state.link;
const classes = classnames(styles.menu, {
[styles.active]: this.state.active,
});
const style = {
top: this.state.top,
@@ -123,7 +119,7 @@ export default class Toolbar extends Component {
return (
<Portal isOpened>
<div className={classes} style={style} ref={this.setRef}>
<Menu active={this.state.active} innerRef={this.setRef} style={style}>
{link &&
<LinkToolbar
{...this.props}
@@ -135,8 +131,28 @@ export default class Toolbar extends Component {
onCreateLink={this.handleFocus}
{...this.props}
/>}
</div>
</Menu>
</Portal>
);
}
}
const Menu = styled.div`
padding: 8px 16px;
position: absolute;
z-index: 1;
top: -10000px;
left: -10000px;
opacity: 0;
background-color: #222;
border-radius: 4px;
transition: opacity 250ms ease-in-out, transform 250ms ease-in-out;
line-height: 0;
height: 40px;
min-width: 260px;
${({ active }) => active && `
transform: translateY(-6px);
opacity: 1;
`}
`;

View File

@@ -1,62 +0,0 @@
.menu {
padding: 8px 16px;
position: absolute;
z-index: 1;
top: -10000px;
left: -10000px;
opacity: 0;
background-color: #222;
border-radius: 4px;
transition: opacity 250ms ease-in-out, transform 250ms ease-in-out;
line-height: 0;
height: 40px;
min-width: 260px;
}
.active {
transform: translateY(-6px);
opacity: 1;
}
.linkEditor {
display: flex;
margin-left: -8px;
margin-right: -8px;
input {
background: rgba(255,255,255,.1);
border-radius: 2px;
padding: 5px 8px;
border: 0;
margin: 0;
outline: none;
color: #fff;
flex-grow: 1;
}
}
.button {
display: inline-block;
flex: 0;
width: 24px;
height: 24px;
cursor: pointer;
margin-left: 10px;
border: none;
background: none;
transition: opacity 100ms ease-in-out;
padding: 0;
opacity: .7;
&:first-child {
margin-left: 0;
}
&:hover {
opacity: 1;
}
&[data-active="true"] {
opacity: 1;
}
}

View File

@@ -1,7 +1,7 @@
// @flow
import React, { Component } from 'react';
import styles from '../Toolbar.scss';
import type { State } from '../../../types';
import ToolbarButton from './ToolbarButton';
import BoldIcon from 'components/Icon/BoldIcon';
import CodeIcon from 'components/Icon/CodeIcon';
import Heading1Icon from 'components/Icon/Heading1Icon';
@@ -68,13 +68,9 @@ export default class FormattingToolbar extends Component {
const onMouseDown = ev => this.onClickMark(ev, type);
return (
<button
className={styles.button}
onMouseDown={onMouseDown}
data-active={isActive}
>
<ToolbarButton onMouseDown={onMouseDown} active={isActive}>
<IconClass light />
</button>
</ToolbarButton>
);
};
@@ -84,13 +80,9 @@ export default class FormattingToolbar extends Component {
this.onClickBlock(ev, isActive ? 'paragraph' : type);
return (
<button
className={styles.button}
onMouseDown={onMouseDown}
data-active={isActive}
>
<ToolbarButton onMouseDown={onMouseDown} active={isActive}>
<IconClass light />
</button>
</ToolbarButton>
);
};
@@ -103,9 +95,9 @@ export default class FormattingToolbar extends Component {
{this.renderBlockButton('heading2', Heading2Icon)}
{this.renderBlockButton('bulleted-list', BulletedListIcon)}
{this.renderMarkButton('code', CodeIcon)}
<button className={styles.button} onMouseDown={this.onCreateLink}>
<ToolbarButton onMouseDown={this.onCreateLink}>
<LinkIcon light />
</button>
</ToolbarButton>
</span>
);
}

View File

@@ -1,9 +1,11 @@
// @flow
import React, { Component } from 'react';
import styled from 'styled-components';
import ToolbarButton from './ToolbarButton';
import type { State } from '../../../types';
import keydown from 'react-keydown';
import styles from '../Toolbar.scss';
import Icon from 'components/Icon';
import Flex from 'components/Flex';
@keydown
export default class LinkToolbar extends Component {
@@ -20,7 +22,7 @@ export default class LinkToolbar extends Component {
case 13: // enter
ev.preventDefault();
return this.save(ev.target.value);
case 26: // escape
case 27: // escape
return this.input.blur();
default:
}
@@ -48,19 +50,35 @@ export default class LinkToolbar extends Component {
render() {
const href = this.props.link.data.get('href');
return (
<span className={styles.linkEditor}>
<input
ref={ref => (this.input = ref)}
<LinkEditor>
<Input
innerRef={ref => (this.input = ref)}
defaultValue={href}
placeholder="http://"
onBlur={this.props.onBlur}
onKeyDown={this.onKeyDown}
autoFocus
/>
<button className={styles.button} onMouseDown={this.removeLink}>
<ToolbarButton onMouseDown={this.removeLink}>
<Icon type="X" light />
</button>
</span>
</ToolbarButton>
</LinkEditor>
);
}
}
const LinkEditor = styled(Flex)`
margin-left: -8px;
margin-right: -8px;
`;
const Input = styled.input`
background: rgba(255,255,255,.1);
border-radius: 2px;
padding: 5px 8px;
border: 0;
margin: 0;
outline: none;
color: #fff;
flex-grow: 1;
`;

View File

@@ -0,0 +1,26 @@
// @flow
import styled from 'styled-components';
export default styled.button`
display: inline-block;
flex: 0;
width: 24px;
height: 24px;
cursor: pointer;
margin-left: 10px;
border: none;
background: none;
transition: opacity 100ms ease-in-out;
padding: 0;
opacity: .7;
&:first-child {
margin-left: 0;
}
&:hover {
opacity: 1;
}
${({ active }) => active && 'opacity: 1;'}
`;

View File

@@ -1,5 +1,6 @@
// @flow
import React from 'react';
import styled from 'styled-components';
import Code from './components/Code';
import InlineCode from './components/InlineCode';
import Image from './components/Image';
@@ -8,7 +9,15 @@ import ListItem from './components/ListItem';
import Heading from './components/Heading';
import Paragraph from './components/Paragraph';
import type { Props, Node, Transform } from './types';
import styles from './Editor.scss';
const TodoList = styled.ul`
list-style: none;
padding-left: 0;
ul {
padding-left: 1em;
}
`;
const createSchema = () => {
return {
@@ -29,9 +38,7 @@ const createSchema = () => {
'horizontal-rule': (props: Props) => <hr />,
'bulleted-list': (props: Props) => <ul>{props.children}</ul>,
'ordered-list': (props: Props) => <ol>{props.children}</ol>,
'todo-list': (props: Props) => (
<ul className={styles.todoList}>{props.children}</ul>
),
'todo-list': (props: Props) => <TodoList>{props.children}</TodoList>,
table: (props: Props) => <table>{props.children}</table>,
'table-row': (props: Props) => <tr>{props.children}</tr>,
'table-head': (props: Props) => <th>{props.children}</th>,

View File

@@ -3,7 +3,6 @@ import styled from 'styled-components';
import { color } from 'styles/constants';
const HelpText = styled.p`
user-select: none;
color: ${color.slateDark};
`;

View File

@@ -1,11 +1,16 @@
// @flow
import React from 'react';
import _ from 'lodash';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import styled from 'styled-components';
import Mask from './components/Mask';
import Flex from 'components/Flex';
export default (props: Object) => {
type Props = {
count?: number,
};
const ListPlaceHolder = ({ count }: Props) => {
return (
<ReactCSSTransitionGroup
transitionName="fadeIn"
@@ -16,14 +21,12 @@ export default (props: Object) => {
transitionEnter
transitionLeave
>
<Item column auto>
<Mask header />
<Mask />
</Item>
<Item column auto>
<Mask header />
<Mask />
</Item>
{_.times(count || 2, index => (
<Item key={index} column auto>
<Mask header />
<Mask />
</Item>
))}
</ReactCSSTransitionGroup>
);
};
@@ -31,3 +34,5 @@ export default (props: Object) => {
const Item = styled(Flex)`
padding: 18px 0;
`;
export default ListPlaceHolder;

View File

@@ -1,77 +0,0 @@
// @flow
import React from 'react';
import { State, Document, Editor } from 'slate';
import MarkdownSerializer from '../Editor/serializer';
import type { State as StateType } from '../Editor/types';
import schema from '../Editor/schema';
import styles from '../Editor/Editor.scss';
type Props = {
text: string,
className: string,
limit: number,
};
function filterDocumentState({ state, characterLimit, nodeLimit }) {
const { document } = state;
if (document.text.length <= characterLimit) {
return state;
}
let totalCharacters = 0;
let totalNodes = 0;
const nodes = document.nodes.filter(childNode => {
if (childNode.text.length + totalCharacters <= characterLimit) {
totalCharacters += childNode.text.length;
if (totalNodes++ <= nodeLimit) {
return true;
}
}
return false;
});
return State.create({
document: Document.create({
...document,
nodes: nodes,
}),
});
}
class Markdown extends React.Component {
props: Props;
state: {
state: StateType,
};
constructor(props: Props) {
super(props);
const state = MarkdownSerializer.deserialize(props.text);
const options = {
state,
characterLimit: props.limit,
nodeLimit: 5,
};
this.state = {
state: filterDocumentState(options),
};
}
render() {
return (
<span className={this.props.className}>
<Editor
className={styles.editor}
schema={schema}
state={this.state.state}
readOnly
/>
</span>
);
}
}
export default Markdown;

View File

@@ -1,3 +0,0 @@
// @flow
import Markdown from './Markdown';
export default Markdown;