Slate 30
This commit is contained in:
@ -8,6 +8,9 @@
|
|||||||
.*/node_modules/polished/.*
|
.*/node_modules/polished/.*
|
||||||
.*/node_modules/react-side-effect/.*
|
.*/node_modules/react-side-effect/.*
|
||||||
.*/node_modules/fbjs/.*
|
.*/node_modules/fbjs/.*
|
||||||
|
.*/node_modules/slate-edit-code/.*
|
||||||
|
.*/node_modules/slate-edit-list/.*
|
||||||
|
.*/node_modules/slate-prism/.*
|
||||||
.*/node_modules/config-chain/.*
|
.*/node_modules/config-chain/.*
|
||||||
*.test.js
|
*.test.js
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ const Collaborators = ({ document }: Props) => {
|
|||||||
<Avatars>
|
<Avatars>
|
||||||
<StyledTooltip tooltip={tooltip} placement="bottom">
|
<StyledTooltip tooltip={tooltip} placement="bottom">
|
||||||
{collaborators.map(user => (
|
{collaborators.map(user => (
|
||||||
<AvatarWrapper>
|
<AvatarWrapper key={user.id}>
|
||||||
<Avatar key={user.id} src={user.avatarUrl} />
|
<Avatar src={user.avatarUrl} />
|
||||||
</AvatarWrapper>
|
</AvatarWrapper>
|
||||||
))}
|
))}
|
||||||
</StyledTooltip>
|
</StyledTooltip>
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { observable } from 'mobx';
|
import { observable } from 'mobx';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Editor, Plain } from 'slate';
|
import { Editor } from 'slate-react';
|
||||||
|
import type { state, props, change } from 'slate-prop-types';
|
||||||
|
import Plain from 'slate-plain-serializer';
|
||||||
import keydown from 'react-keydown';
|
import keydown from 'react-keydown';
|
||||||
import type { State, Editor as EditorType } from './types';
|
|
||||||
import getDataTransferFiles from 'utils/getDataTransferFiles';
|
import getDataTransferFiles from 'utils/getDataTransferFiles';
|
||||||
import Flex from 'shared/components/Flex';
|
import Flex from 'shared/components/Flex';
|
||||||
import ClickablePadding from './components/ClickablePadding';
|
import ClickablePadding from './components/ClickablePadding';
|
||||||
@ -13,18 +14,19 @@ import BlockInsert from './components/BlockInsert';
|
|||||||
import Placeholder from './components/Placeholder';
|
import Placeholder from './components/Placeholder';
|
||||||
import Contents from './components/Contents';
|
import Contents from './components/Contents';
|
||||||
import Markdown from './serializer';
|
import Markdown from './serializer';
|
||||||
import createSchema from './schema';
|
|
||||||
import createPlugins from './plugins';
|
import createPlugins from './plugins';
|
||||||
import insertImage from './insertImage';
|
import insertImage from './insertImage';
|
||||||
|
import renderMark from './marks';
|
||||||
|
import createRenderNode from './nodes';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
text: string,
|
text: string,
|
||||||
onChange: Function,
|
onChange: change => *,
|
||||||
onSave: Function,
|
onSave: (redirect?: boolean) => *,
|
||||||
onCancel: Function,
|
onCancel: () => void,
|
||||||
onImageUploadStart: Function,
|
onImageUploadStart: () => void,
|
||||||
onImageUploadStop: Function,
|
onImageUploadStop: () => void,
|
||||||
emoji?: string,
|
emoji?: string,
|
||||||
readOnly: boolean,
|
readOnly: boolean,
|
||||||
};
|
};
|
||||||
@ -37,15 +39,15 @@ type KeyData = {
|
|||||||
@observer
|
@observer
|
||||||
class MarkdownEditor extends Component {
|
class MarkdownEditor extends Component {
|
||||||
props: Props;
|
props: Props;
|
||||||
editor: EditorType;
|
editor: Editor;
|
||||||
schema: Object;
|
renderNode: props => *;
|
||||||
plugins: Array<Object>;
|
plugins: Object[];
|
||||||
@observable editorState: State;
|
@observable editorValue: state;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.schema = createSchema({
|
this.renderNode = createRenderNode({
|
||||||
onInsertImage: this.insertImageFile,
|
onInsertImage: this.insertImageFile,
|
||||||
onChange: this.onChange,
|
onChange: this.onChange,
|
||||||
});
|
});
|
||||||
@ -55,9 +57,9 @@ class MarkdownEditor extends Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (props.text.trim().length) {
|
if (props.text.trim().length) {
|
||||||
this.editorState = Markdown.deserialize(props.text);
|
this.editorValue = Markdown.deserialize(props.text);
|
||||||
} else {
|
} else {
|
||||||
this.editorState = Plain.deserialize('');
|
this.editorValue = Plain.deserialize('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,12 +79,11 @@ class MarkdownEditor extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange = (editorState: State) => {
|
onChange = (change: change) => {
|
||||||
if (this.editorState !== editorState) {
|
if (this.editorValue !== change.value) {
|
||||||
this.props.onChange(Markdown.serialize(editorState));
|
this.props.onChange(Markdown.serialize(change.value));
|
||||||
}
|
}
|
||||||
|
this.editorValue = change.value;
|
||||||
this.editorState = editorState;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDrop = async (ev: SyntheticEvent) => {
|
handleDrop = async (ev: SyntheticEvent) => {
|
||||||
@ -103,17 +104,16 @@ class MarkdownEditor extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
insertImageFile = async (file: window.File) => {
|
insertImageFile = async (file: window.File) => {
|
||||||
const state = this.editor.getState();
|
this.editor.change(
|
||||||
let transform = state.transform();
|
async change =>
|
||||||
|
await insertImage(
|
||||||
transform = await insertImage(
|
change,
|
||||||
transform,
|
|
||||||
file,
|
file,
|
||||||
this.editor,
|
this.editor,
|
||||||
this.props.onImageUploadStart,
|
this.props.onImageUploadStart,
|
||||||
this.props.onImageUploadStop
|
this.props.onImageUploadStop
|
||||||
|
)
|
||||||
);
|
);
|
||||||
this.editor.onChange(transform.apply());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
cancelEvent = (ev: SyntheticEvent) => {
|
cancelEvent = (ev: SyntheticEvent) => {
|
||||||
@ -136,7 +136,7 @@ class MarkdownEditor extends Component {
|
|||||||
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.props.onSave({ redirect: false });
|
this.props.onSave(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keydown('esc')
|
@keydown('esc')
|
||||||
@ -146,37 +146,33 @@ class MarkdownEditor extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handling of keyboard shortcuts within editor focus
|
// Handling of keyboard shortcuts within editor focus
|
||||||
onKeyDown = (ev: SyntheticKeyboardEvent, data: KeyData, state: State) => {
|
onKeyDown = (ev: SyntheticKeyboardEvent, data: KeyData, change: change) => {
|
||||||
if (!data.isMeta) return;
|
if (!data.isMeta) return;
|
||||||
|
|
||||||
switch (data.key) {
|
switch (data.key) {
|
||||||
case 's':
|
case 's':
|
||||||
this.onSave(ev);
|
this.onSave(ev);
|
||||||
return state;
|
return change;
|
||||||
case 'enter':
|
case 'enter':
|
||||||
this.onSaveAndExit(ev);
|
this.onSaveAndExit(ev);
|
||||||
return state;
|
return change;
|
||||||
case 'escape':
|
case 'escape':
|
||||||
this.onCancel();
|
this.onCancel();
|
||||||
return state;
|
return change;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
focusAtStart = () => {
|
focusAtStart = () => {
|
||||||
const state = this.editor.getState();
|
this.editor.change(change =>
|
||||||
const transform = state.transform();
|
change.collapseToStartOf(change.value.document).focus()
|
||||||
transform.collapseToStartOf(state.document);
|
);
|
||||||
transform.focus();
|
|
||||||
this.editorState = transform.apply();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
focusAtEnd = () => {
|
focusAtEnd = () => {
|
||||||
const state = this.editor.getState();
|
this.editor.change(change =>
|
||||||
const transform = state.transform();
|
change.collapseToEndOf(change.value.document).focus()
|
||||||
transform.collapseToEndOf(state.document);
|
);
|
||||||
transform.focus();
|
|
||||||
this.editorState = transform.apply();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
@ -193,14 +189,15 @@ class MarkdownEditor extends Component {
|
|||||||
>
|
>
|
||||||
<MaxWidth column auto>
|
<MaxWidth column auto>
|
||||||
<Header onClick={this.focusAtStart} readOnly={readOnly} />
|
<Header onClick={this.focusAtStart} readOnly={readOnly} />
|
||||||
{readOnly && <Contents state={this.editorState} />}
|
{readOnly && this.editor && <Contents editor={this.editor} />}
|
||||||
{!readOnly && (
|
{!readOnly &&
|
||||||
<Toolbar state={this.editorState} onChange={this.onChange} />
|
this.editor && (
|
||||||
|
<Toolbar value={this.editorValue} editor={this.editor} />
|
||||||
)}
|
)}
|
||||||
{!readOnly && (
|
{!readOnly &&
|
||||||
|
this.editor && (
|
||||||
<BlockInsert
|
<BlockInsert
|
||||||
state={this.editorState}
|
editor={this.editor}
|
||||||
onChange={this.onChange}
|
|
||||||
onInsertImage={this.insertImageFile}
|
onInsertImage={this.insertImageFile}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -208,10 +205,11 @@ class MarkdownEditor extends Component {
|
|||||||
innerRef={ref => (this.editor = ref)}
|
innerRef={ref => (this.editor = ref)}
|
||||||
placeholder="Start with a title…"
|
placeholder="Start with a title…"
|
||||||
bodyPlaceholder="…the rest is your canvas"
|
bodyPlaceholder="…the rest is your canvas"
|
||||||
schema={this.schema}
|
|
||||||
plugins={this.plugins}
|
plugins={this.plugins}
|
||||||
emoji={emoji}
|
emoji={emoji}
|
||||||
state={this.editorState}
|
value={this.editorValue}
|
||||||
|
renderNode={this.renderNode}
|
||||||
|
renderMark={renderMark}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Portal } from 'react-portal';
|
import { Portal } from 'react-portal';
|
||||||
import { findDOMNode, Node } from 'slate';
|
import { Node } from 'slate';
|
||||||
|
import { Editor, findDOMNode } from 'slate-react';
|
||||||
import { observable } from 'mobx';
|
import { observable } from 'mobx';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { color } from 'shared/styles/constants';
|
import { color } from 'shared/styles/constants';
|
||||||
import PlusIcon from 'components/Icon/PlusIcon';
|
import PlusIcon from 'components/Icon/PlusIcon';
|
||||||
import type { State } from '../types';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
state: State,
|
editor: Editor,
|
||||||
onChange: Function,
|
|
||||||
onInsertImage: File => Promise<*>,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function findClosestRootNode(state, ev) {
|
function findClosestRootNode(state, ev) {
|
||||||
@ -53,7 +51,7 @@ export default class BlockInsert extends Component {
|
|||||||
|
|
||||||
handleMouseMove = (ev: SyntheticMouseEvent) => {
|
handleMouseMove = (ev: SyntheticMouseEvent) => {
|
||||||
const windowWidth = window.innerWidth / 2.5;
|
const windowWidth = window.innerWidth / 2.5;
|
||||||
const result = findClosestRootNode(this.props.state, ev);
|
const result = findClosestRootNode(this.props.editor.value, ev);
|
||||||
const movementThreshold = 200;
|
const movementThreshold = 200;
|
||||||
|
|
||||||
this.mouseMovementSinceClick +=
|
this.mouseMovementSinceClick +=
|
||||||
@ -70,7 +68,7 @@ export default class BlockInsert extends Component {
|
|||||||
this.closestRootNode = result.node;
|
this.closestRootNode = result.node;
|
||||||
|
|
||||||
// do not show block menu on title heading or editor
|
// do not show block menu on title heading or editor
|
||||||
const firstNode = this.props.state.document.nodes.first();
|
const firstNode = this.props.editor.value.document.nodes.first();
|
||||||
if (result.node === firstNode || result.node.type === 'block-toolbar') {
|
if (result.node === firstNode || result.node.type === 'block-toolbar') {
|
||||||
this.left = -1000;
|
this.left = -1000;
|
||||||
} else {
|
} else {
|
||||||
@ -89,23 +87,22 @@ export default class BlockInsert extends Component {
|
|||||||
this.mouseMovementSinceClick = 0;
|
this.mouseMovementSinceClick = 0;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
|
||||||
const { state } = this.props;
|
const { editor } = this.props;
|
||||||
const type = { type: 'block-toolbar', isVoid: true };
|
const type = { type: 'block-toolbar', isVoid: true };
|
||||||
let transform = state.transform();
|
|
||||||
|
|
||||||
|
editor.change(change => {
|
||||||
// remove any existing toolbars in the document as a fail safe
|
// remove any existing toolbars in the document as a fail safe
|
||||||
state.document.nodes.forEach(node => {
|
editor.value.document.nodes.forEach(node => {
|
||||||
if (node.type === 'block-toolbar') {
|
if (node.type === 'block-toolbar') {
|
||||||
transform.removeNodeByKey(node.key);
|
change.removeNodeByKey(node.key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
transform
|
change
|
||||||
.collapseToStartOf(this.closestRootNode)
|
.collapseToStartOf(this.closestRootNode)
|
||||||
.collapseToEndOfPreviousBlock()
|
.collapseToEndOfPreviousBlock()
|
||||||
.insertBlock(type);
|
.insertBlock(type);
|
||||||
|
});
|
||||||
this.props.onChange(transform.apply());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import type { props } from 'slate-prop-types';
|
||||||
import CopyButton from './CopyButton';
|
import CopyButton from './CopyButton';
|
||||||
import { color } from 'shared/styles/constants';
|
import { color } from 'shared/styles/constants';
|
||||||
import type { Props } from '../types';
|
|
||||||
|
|
||||||
export default function Code({ children, node, readOnly, attributes }: Props) {
|
export default function Code({ children, node, readOnly, attributes }: props) {
|
||||||
const language = node.data.get('language') || 'javascript';
|
const language = node.data.get('language') || 'javascript';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { observable } from 'mobx';
|
import { observable } from 'mobx';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
import { Editor } from 'slate-react';
|
||||||
|
import type { state, block } from 'slate-prop-types';
|
||||||
import { List } from 'immutable';
|
import { List } from 'immutable';
|
||||||
import { color } from 'shared/styles/constants';
|
import { color } from 'shared/styles/constants';
|
||||||
import headingToSlug from '../headingToSlug';
|
import headingToSlug from '../headingToSlug';
|
||||||
import type { State, Block } from '../types';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
state: State,
|
editor: Editor,
|
||||||
};
|
};
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
@ -53,10 +54,10 @@ class Contents extends Component {
|
|||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
get headings(): List<Block> {
|
get headings(): List<block> {
|
||||||
const { state } = this.props;
|
const { editor } = this.props;
|
||||||
|
|
||||||
return state.document.nodes.filter((node: Block) => {
|
return editor.value.document.nodes.filter((node: block) => {
|
||||||
if (!node.text) return false;
|
if (!node.text) return false;
|
||||||
return node.type.match(/^heading/);
|
return node.type.match(/^heading/);
|
||||||
});
|
});
|
||||||
@ -74,7 +75,7 @@ class Contents extends Component {
|
|||||||
const active = this.activeHeading === slug;
|
const active = this.activeHeading === slug;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem type={heading.type} active={active}>
|
<ListItem type={heading.type} active={active} key={slug}>
|
||||||
<Anchor href={`#${slug}`} active={active}>
|
<Anchor href={`#${slug}`} active={active}>
|
||||||
{heading.text}
|
{heading.text}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Document } from 'slate';
|
import { Document } from 'slate';
|
||||||
|
import { Editor } from 'slate-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import type { node } from 'slate-prop-types';
|
||||||
import headingToSlug from '../headingToSlug';
|
import headingToSlug from '../headingToSlug';
|
||||||
import type { Node, Editor } from '../types';
|
|
||||||
import Placeholder from './Placeholder';
|
import Placeholder from './Placeholder';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React$Element<*>,
|
children: React$Element<*>,
|
||||||
placeholder?: boolean,
|
placeholder?: boolean,
|
||||||
parent: Node,
|
parent: node,
|
||||||
node: Node,
|
node: node,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
readOnly: boolean,
|
readOnly: boolean,
|
||||||
component?: string,
|
component?: string,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import type { Props } from '../types';
|
import type { props } from 'slate-prop-types';
|
||||||
import { color } from 'shared/styles/constants';
|
import { color } from 'shared/styles/constants';
|
||||||
|
|
||||||
function HorizontalRule(props: Props) {
|
function HorizontalRule(props: props) {
|
||||||
const { state, node, attributes } = props;
|
const { state, node, attributes } = props;
|
||||||
const active = state.isFocused && state.selection.hasEdgeIn(node);
|
const active = state.isFocused && state.selection.hasEdgeIn(node);
|
||||||
return <StyledHr active={active} {...attributes} />;
|
return <StyledHr active={active} {...attributes} />;
|
||||||
|
@ -2,23 +2,23 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import ImageZoom from 'react-medium-image-zoom';
|
import ImageZoom from 'react-medium-image-zoom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import type { Props } from '../types';
|
import type { props } from 'slate-prop-types';
|
||||||
import { color } from 'shared/styles/constants';
|
import { color } from 'shared/styles/constants';
|
||||||
|
|
||||||
class Image extends Component {
|
class Image extends Component {
|
||||||
props: Props;
|
props: props;
|
||||||
|
|
||||||
handleChange = (ev: SyntheticInputEvent) => {
|
handleChange = (ev: SyntheticInputEvent) => {
|
||||||
const alt = ev.target.value;
|
const alt = ev.target.value;
|
||||||
const { editor, node } = this.props;
|
const { editor, node } = this.props;
|
||||||
const data = node.data.toObject();
|
const data = node.data.toObject();
|
||||||
const state = editor
|
|
||||||
.getState()
|
|
||||||
.transform()
|
|
||||||
.setNodeByKey(node.key, { data: { ...data, alt } })
|
|
||||||
.apply();
|
|
||||||
|
|
||||||
editor.onChange(state);
|
editor.onChange(
|
||||||
|
editor
|
||||||
|
.getState()
|
||||||
|
.change()
|
||||||
|
.setNodeByKey(node.key, { data: { ...data, alt } })
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = (ev: SyntheticInputEvent) => {
|
handleClick = (ev: SyntheticInputEvent) => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link as InternalLink } from 'react-router-dom';
|
import { Link as InternalLink } from 'react-router-dom';
|
||||||
import type { Props } from '../types';
|
import type { props } from 'slate-prop-types';
|
||||||
|
|
||||||
function getPathFromUrl(href: string) {
|
function getPathFromUrl(href: string) {
|
||||||
if (href[0] === '/') return href;
|
if (href[0] === '/') return href;
|
||||||
@ -14,7 +14,7 @@ function getPathFromUrl(href: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOutlineUrl(href: string) {
|
function isInternalUrl(href: string) {
|
||||||
if (href[0] === '/') return true;
|
if (href[0] === '/') return true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -26,11 +26,11 @@ function isOutlineUrl(href: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Link({ attributes, node, children, readOnly }: Props) {
|
export default function Link({ attributes, node, children, readOnly }: props) {
|
||||||
const href = node.data.get('href');
|
const href = node.data.get('href');
|
||||||
const path = getPathFromUrl(href);
|
const path = getPathFromUrl(href);
|
||||||
|
|
||||||
if (isOutlineUrl(href) && readOnly) {
|
if (isInternalUrl(href) && readOnly) {
|
||||||
return (
|
return (
|
||||||
<InternalLink {...attributes} to={path}>
|
<InternalLink {...attributes} to={path}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { Props } from '../types';
|
import type { props } from 'slate-prop-types';
|
||||||
import TodoItem from './TodoItem';
|
import TodoItem from './TodoItem';
|
||||||
|
|
||||||
export default function ListItem({
|
export default function ListItem({
|
||||||
@ -8,7 +8,7 @@ export default function ListItem({
|
|||||||
node,
|
node,
|
||||||
attributes,
|
attributes,
|
||||||
...props
|
...props
|
||||||
}: Props) {
|
}: props) {
|
||||||
const checked = node.data.get('checked');
|
const checked = node.data.get('checked');
|
||||||
|
|
||||||
if (checked !== undefined) {
|
if (checked !== undefined) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Document } from 'slate';
|
import { Document } from 'slate';
|
||||||
import type { Props } from '../types';
|
import type { props } from 'slate-prop-types';
|
||||||
import Placeholder from './Placeholder';
|
import Placeholder from './Placeholder';
|
||||||
|
|
||||||
export default function Link({
|
export default function Link({
|
||||||
@ -11,7 +11,7 @@ export default function Link({
|
|||||||
parent,
|
parent,
|
||||||
children,
|
children,
|
||||||
readOnly,
|
readOnly,
|
||||||
}: Props) {
|
}: props) {
|
||||||
const parentIsDocument = parent instanceof Document;
|
const parentIsDocument = parent instanceof Document;
|
||||||
const firstParagraph = parent && parent.nodes.get(1) === node;
|
const firstParagraph = parent && parent.nodes.get(1) === node;
|
||||||
const lastParagraph = parent && parent.nodes.last() === node;
|
const lastParagraph = parent && parent.nodes.last() === node;
|
||||||
|
@ -2,21 +2,20 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { color } from 'shared/styles/constants';
|
import { color } from 'shared/styles/constants';
|
||||||
import type { Props } from '../types';
|
import type { props } from 'slate-prop-types';
|
||||||
|
|
||||||
export default class TodoItem extends Component {
|
export default class TodoItem extends Component {
|
||||||
props: Props & { checked: boolean };
|
props: props & { checked: boolean };
|
||||||
|
|
||||||
handleChange = (ev: SyntheticInputEvent) => {
|
handleChange = (ev: SyntheticInputEvent) => {
|
||||||
const checked = ev.target.checked;
|
const checked = ev.target.checked;
|
||||||
const { editor, node } = this.props;
|
const { editor, node } = this.props;
|
||||||
const state = editor
|
const change = editor
|
||||||
.getState()
|
.getState()
|
||||||
.transform()
|
.change()
|
||||||
.setNodeByKey(node.key, { data: { checked } })
|
.setNodeByKey(node.key, { data: { checked } });
|
||||||
.apply();
|
|
||||||
|
|
||||||
editor.onChange(state);
|
editor.onChange(change);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -13,12 +13,12 @@ import HorizontalRuleIcon from 'components/Icon/HorizontalRuleIcon';
|
|||||||
import TodoListIcon from 'components/Icon/TodoListIcon';
|
import TodoListIcon from 'components/Icon/TodoListIcon';
|
||||||
import Flex from 'shared/components/Flex';
|
import Flex from 'shared/components/Flex';
|
||||||
import ToolbarButton from './components/ToolbarButton';
|
import ToolbarButton from './components/ToolbarButton';
|
||||||
import type { Props as BaseProps } from '../../types';
|
import type { props } from 'slate-prop-types';
|
||||||
import { color } from 'shared/styles/constants';
|
import { color } from 'shared/styles/constants';
|
||||||
import { fadeIn } from 'shared/styles/animations';
|
import { fadeIn } from 'shared/styles/animations';
|
||||||
import { splitAndInsertBlock } from '../../transforms';
|
import { splitAndInsertBlock } from '../../transforms';
|
||||||
|
|
||||||
type Props = BaseProps & {
|
type Props = props & {
|
||||||
onInsertImage: Function,
|
onInsertImage: Function,
|
||||||
onChange: Function,
|
onChange: Function,
|
||||||
};
|
};
|
||||||
@ -34,16 +34,15 @@ class BlockToolbar extends Component {
|
|||||||
file: HTMLInputElement;
|
file: HTMLInputElement;
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
componentWillReceiveProps(nextProps: Props) {
|
||||||
const wasActive = this.props.state.selection.hasEdgeIn(this.props.node);
|
const { editor } = this.props;
|
||||||
const isActive = nextProps.state.selection.hasEdgeIn(nextProps.node);
|
const wasActive = editor.value.selection.hasEdgeIn(this.props.node);
|
||||||
|
const isActive = nextProps.editor.value.selection.hasEdgeIn(nextProps.node);
|
||||||
const becameInactive = !isActive && wasActive;
|
const becameInactive = !isActive && wasActive;
|
||||||
|
|
||||||
if (becameInactive) {
|
if (becameInactive) {
|
||||||
const state = nextProps.state
|
nextProps.editor.change(change =>
|
||||||
.transform()
|
change.removeNodeByKey(nextProps.node.key)
|
||||||
.removeNodeByKey(nextProps.node.key)
|
);
|
||||||
.apply();
|
|
||||||
this.props.onChange(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,24 +51,25 @@ class BlockToolbar extends Component {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
const state = this.props.state
|
this.props.editor.change(change =>
|
||||||
.transform()
|
change.removeNodeByKey(this.props.node.key)
|
||||||
.removeNodeByKey(this.props.node.key)
|
);
|
||||||
.apply();
|
|
||||||
this.props.onChange(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insertBlock = (options: Options) => {
|
insertBlock = (options: Options) => {
|
||||||
const { state } = this.props;
|
const { editor } = this.props;
|
||||||
let transform = splitAndInsertBlock(state.transform(), state, options);
|
|
||||||
|
|
||||||
state.document.nodes.forEach(node => {
|
editor.change(change => {
|
||||||
|
splitAndInsertBlock(change, options);
|
||||||
|
|
||||||
|
change.value.document.nodes.forEach(node => {
|
||||||
if (node.type === 'block-toolbar') {
|
if (node.type === 'block-toolbar') {
|
||||||
transform.removeNodeByKey(node.key);
|
change.removeNodeByKey(node.key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.onChange(transform.focus().apply());
|
change.focus();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClickBlock = (ev: SyntheticEvent, type: string) => {
|
handleClickBlock = (ev: SyntheticEvent, type: string) => {
|
||||||
@ -126,8 +126,9 @@ class BlockToolbar extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { state, attributes, node } = this.props;
|
const { editor, attributes, node } = this.props;
|
||||||
const active = state.isFocused && state.selection.hasEdgeIn(node);
|
const active =
|
||||||
|
editor.value.isFocused && editor.value.selection.hasEdgeIn(node);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Bar active={active} {...attributes}>
|
<Bar active={active} {...attributes}>
|
||||||
|
@ -3,9 +3,10 @@ import React, { Component } from 'react';
|
|||||||
import { observable } from 'mobx';
|
import { observable } from 'mobx';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Portal } from 'react-portal';
|
import { Portal } from 'react-portal';
|
||||||
|
import { Editor } from 'slate-react';
|
||||||
|
import type { value } from 'slate-prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import type { State } from '../../types';
|
|
||||||
import FormattingToolbar from './components/FormattingToolbar';
|
import FormattingToolbar from './components/FormattingToolbar';
|
||||||
import LinkToolbar from './components/LinkToolbar';
|
import LinkToolbar from './components/LinkToolbar';
|
||||||
|
|
||||||
@ -18,8 +19,8 @@ export default class Toolbar extends Component {
|
|||||||
@observable left: string = '';
|
@observable left: string = '';
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
state: State,
|
editor: Editor,
|
||||||
onChange: (state: State) => void,
|
value: value,
|
||||||
};
|
};
|
||||||
|
|
||||||
menu: HTMLElement;
|
menu: HTMLElement;
|
||||||
@ -41,11 +42,11 @@ export default class Toolbar extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
get linkInSelection(): any {
|
get linkInSelection(): any {
|
||||||
const { state } = this.props;
|
const { value } = this.props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selectedLinks = state.startBlock
|
const selectedLinks = value.startBlock
|
||||||
.getInlinesAtRange(state.selection)
|
.getInlinesAtRange(value.selection)
|
||||||
.filter(node => node.type === 'link');
|
.filter(node => node.type === 'link');
|
||||||
if (selectedLinks.size) {
|
if (selectedLinks.size) {
|
||||||
return selectedLinks.first();
|
return selectedLinks.first();
|
||||||
@ -56,10 +57,10 @@ export default class Toolbar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update = () => {
|
update = () => {
|
||||||
const { state } = this.props;
|
const { value } = this.props;
|
||||||
const link = this.linkInSelection;
|
const link = this.linkInSelection;
|
||||||
|
|
||||||
if (state.isBlurred || (state.isCollapsed && !link)) {
|
if (value.isBlurred || (value.isCollapsed && !link)) {
|
||||||
if (this.active && !this.focused) {
|
if (this.active && !this.focused) {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.link = undefined;
|
this.link = undefined;
|
||||||
@ -70,11 +71,11 @@ export default class Toolbar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// don't display toolbar for document title
|
// don't display toolbar for document title
|
||||||
const firstNode = state.document.nodes.first();
|
const firstNode = value.document.nodes.first();
|
||||||
if (firstNode === state.startBlock) return;
|
if (firstNode === value.startBlock) return;
|
||||||
|
|
||||||
// don't display toolbar for code blocks
|
// don't display toolbar for code blocks
|
||||||
if (state.startBlock.type === 'code') return;
|
if (value.startBlock.type === 'code') return;
|
||||||
|
|
||||||
this.active = true;
|
this.active = true;
|
||||||
this.focused = !!link;
|
this.focused = !!link;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import type { State } from '../../../types';
|
import { Editor } from 'slate-react';
|
||||||
import ToolbarButton from './ToolbarButton';
|
import ToolbarButton from './ToolbarButton';
|
||||||
import BoldIcon from 'components/Icon/BoldIcon';
|
import BoldIcon from 'components/Icon/BoldIcon';
|
||||||
import CodeIcon from 'components/Icon/CodeIcon';
|
import CodeIcon from 'components/Icon/CodeIcon';
|
||||||
@ -13,8 +13,7 @@ import StrikethroughIcon from 'components/Icon/StrikethroughIcon';
|
|||||||
|
|
||||||
class FormattingToolbar extends Component {
|
class FormattingToolbar extends Component {
|
||||||
props: {
|
props: {
|
||||||
state: State,
|
editor: Editor,
|
||||||
onChange: Function,
|
|
||||||
onCreateLink: Function,
|
onCreateLink: Function,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,11 +24,11 @@ class FormattingToolbar extends Component {
|
|||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
hasMark = (type: string) => {
|
hasMark = (type: string) => {
|
||||||
return this.props.state.marks.some(mark => mark.type === type);
|
return this.props.editor.value.marks.some(mark => mark.type === type);
|
||||||
};
|
};
|
||||||
|
|
||||||
isBlock = (type: string) => {
|
isBlock = (type: string) => {
|
||||||
return this.props.state.startBlock.type === type;
|
return this.props.editor.value.startBlock.type === type;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,37 +39,23 @@ class FormattingToolbar extends Component {
|
|||||||
*/
|
*/
|
||||||
onClickMark = (ev: SyntheticEvent, type: string) => {
|
onClickMark = (ev: SyntheticEvent, type: string) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
let { state } = this.props;
|
this.props.editor.change(change => change.toggleMark(type));
|
||||||
|
|
||||||
state = state
|
|
||||||
.transform()
|
|
||||||
.toggleMark(type)
|
|
||||||
.apply();
|
|
||||||
this.props.onChange(state);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onClickBlock = (ev: SyntheticEvent, type: string) => {
|
onClickBlock = (ev: SyntheticEvent, type: string) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
let { state } = this.props;
|
this.props.editor.change(change => change.setBlock(type));
|
||||||
|
|
||||||
state = state
|
|
||||||
.transform()
|
|
||||||
.setBlock(type)
|
|
||||||
.apply();
|
|
||||||
this.props.onChange(state);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onCreateLink = (ev: SyntheticEvent) => {
|
onCreateLink = (ev: SyntheticEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
let { state } = this.props;
|
|
||||||
const data = { href: '' };
|
const data = { href: '' };
|
||||||
state = state
|
this.props.editor.change(change => {
|
||||||
.transform()
|
change.wrapInline({ type: 'link', data });
|
||||||
.wrapInline({ type: 'link', data })
|
|
||||||
.apply();
|
|
||||||
this.props.onChange(state);
|
|
||||||
this.props.onCreateLink();
|
this.props.onCreateLink();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderMarkButton = (type: string, IconClass: Function) => {
|
renderMarkButton = (type: string, IconClass: Function) => {
|
||||||
|
@ -4,11 +4,12 @@ import ReactDOM from 'react-dom';
|
|||||||
import { observable, action } from 'mobx';
|
import { observable, action } from 'mobx';
|
||||||
import { observer, inject } from 'mobx-react';
|
import { observer, inject } from 'mobx-react';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
|
import { Editor } from 'slate-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import ArrowKeyNavigation from 'boundless-arrow-key-navigation';
|
import ArrowKeyNavigation from 'boundless-arrow-key-navigation';
|
||||||
|
import type { change } from 'slate-prop-types';
|
||||||
import ToolbarButton from './ToolbarButton';
|
import ToolbarButton from './ToolbarButton';
|
||||||
import DocumentResult from './DocumentResult';
|
import DocumentResult from './DocumentResult';
|
||||||
import type { State } from '../../../types';
|
|
||||||
import DocumentsStore from 'stores/DocumentsStore';
|
import DocumentsStore from 'stores/DocumentsStore';
|
||||||
import keydown from 'react-keydown';
|
import keydown from 'react-keydown';
|
||||||
import CloseIcon from 'components/Icon/CloseIcon';
|
import CloseIcon from 'components/Icon/CloseIcon';
|
||||||
@ -23,11 +24,11 @@ class LinkToolbar extends Component {
|
|||||||
firstDocument: HTMLElement;
|
firstDocument: HTMLElement;
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
state: State,
|
editor: Editor,
|
||||||
link: Object,
|
link: Object,
|
||||||
documents: DocumentsStore,
|
documents: DocumentsStore,
|
||||||
onBlur: () => void,
|
onBlur: () => void,
|
||||||
onChange: State => void,
|
onChange: change => *,
|
||||||
};
|
};
|
||||||
|
|
||||||
@observable isEditing: boolean = false;
|
@observable isEditing: boolean = false;
|
||||||
@ -112,17 +113,14 @@ class LinkToolbar extends Component {
|
|||||||
|
|
||||||
save = (href: string) => {
|
save = (href: string) => {
|
||||||
href = href.trim();
|
href = href.trim();
|
||||||
const { state } = this.props;
|
this.props.editor.change(change => {
|
||||||
const transform = state.transform();
|
|
||||||
|
|
||||||
if (href) {
|
if (href) {
|
||||||
transform.setInline({ type: 'link', data: { href } });
|
change.setInline({ type: 'link', data: { href } });
|
||||||
} else {
|
} else {
|
||||||
transform.unwrapInline('link');
|
change.unwrapInline('link');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onChange(transform.apply());
|
|
||||||
this.props.onBlur();
|
this.props.onBlur();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
setFirstDocumentRef = ref => {
|
setFirstDocumentRef = ref => {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { escape } from 'lodash';
|
import { escape } from 'lodash';
|
||||||
import type { Node } from './types';
|
import type { node } from 'slate-prop-types';
|
||||||
import slug from 'slug';
|
import slug from 'slug';
|
||||||
|
|
||||||
export default function headingToSlug(node: Node) {
|
export default function headingToSlug(node: node) {
|
||||||
const level = node.type.replace('heading', 'h');
|
const level = node.type.replace('heading', 'h');
|
||||||
return escape(`${level}-${slug(node.text)}-${node.key}`);
|
return escape(`${level}-${slug(node.text)}-${node.key}`);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
import uploadFile from 'utils/uploadFile';
|
import uploadFile from 'utils/uploadFile';
|
||||||
import type { Editor, Transform } from './types';
|
import { Editor } from 'slate-react';
|
||||||
|
import type { change } from 'slate-prop-types';
|
||||||
|
|
||||||
export default async function insertImageFile(
|
export default async function insertImageFile(
|
||||||
transform: Transform,
|
change: change,
|
||||||
file: window.File,
|
file: window.File,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
onImageUploadStart: () => void,
|
onImageUploadStart: () => void,
|
||||||
@ -21,7 +22,7 @@ export default async function insertImageFile(
|
|||||||
const src = reader.result;
|
const src = reader.result;
|
||||||
|
|
||||||
// insert into document as uploading placeholder
|
// insert into document as uploading placeholder
|
||||||
const state = transform
|
const state = change
|
||||||
.insertBlock({
|
.insertBlock({
|
||||||
type: 'image',
|
type: 'image',
|
||||||
isVoid: true,
|
isVoid: true,
|
||||||
@ -36,11 +37,11 @@ export default async function insertImageFile(
|
|||||||
const asset = await uploadFile(file);
|
const asset = await uploadFile(file);
|
||||||
const src = asset.url;
|
const src = asset.url;
|
||||||
|
|
||||||
// we dont use the original transform provided to the callback here
|
// we dont use the original change provided to the callback here
|
||||||
// as the state may have changed significantly in the time it took to
|
// as the state may have changed significantly in the time it took to
|
||||||
// upload the file.
|
// upload the file.
|
||||||
const state = editor.getState();
|
const state = editor.getState();
|
||||||
const finalTransform = state.transform();
|
const finalTransform = state.change();
|
||||||
const placeholder = state.document.findDescendant(
|
const placeholder = state.document.findDescendant(
|
||||||
node => node.data && node.data.get('id') === id
|
node => node.data && node.data.get('id') === id
|
||||||
);
|
);
|
||||||
|
22
app/components/Editor/marks.js
Normal file
22
app/components/Editor/marks.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import InlineCode from './components/InlineCode';
|
||||||
|
import type { props } from 'slate-prop-types';
|
||||||
|
|
||||||
|
export default function renderMark(props: props) {
|
||||||
|
switch (props.mark.type) {
|
||||||
|
case 'bold':
|
||||||
|
return <strong>{props.children}</strong>;
|
||||||
|
case 'code':
|
||||||
|
return <InlineCode>{props.children}</InlineCode>;
|
||||||
|
case 'italic':
|
||||||
|
return <em>{props.children}</em>;
|
||||||
|
case 'underlined':
|
||||||
|
return <u>{props.children}</u>;
|
||||||
|
case 'deleted':
|
||||||
|
return <del>{props.children}</del>;
|
||||||
|
case 'added':
|
||||||
|
return <mark>{props.children}</mark>;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
75
app/components/Editor/nodes.js
Normal file
75
app/components/Editor/nodes.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import Code from './components/Code';
|
||||||
|
import BlockToolbar from './components/Toolbar/BlockToolbar';
|
||||||
|
import HorizontalRule from './components/HorizontalRule';
|
||||||
|
import Image from './components/Image';
|
||||||
|
import Link from './components/Link';
|
||||||
|
import ListItem from './components/ListItem';
|
||||||
|
import TodoList from './components/TodoList';
|
||||||
|
import {
|
||||||
|
Heading1,
|
||||||
|
Heading2,
|
||||||
|
Heading3,
|
||||||
|
Heading4,
|
||||||
|
Heading5,
|
||||||
|
Heading6,
|
||||||
|
} from './components/Heading';
|
||||||
|
import Paragraph from './components/Paragraph';
|
||||||
|
import type { props } from 'slate-prop-types';
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
onInsertImage: *,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function createRenderNode({ onChange, onInsertImage }: Options) {
|
||||||
|
return function renderNode(props: props) {
|
||||||
|
const { attributes } = props;
|
||||||
|
|
||||||
|
switch (props.node.type) {
|
||||||
|
case 'paragraph':
|
||||||
|
return <Paragraph {...props} />;
|
||||||
|
case 'block-toolbar':
|
||||||
|
return <BlockToolbar onInsertImage={onInsertImage} {...props} />;
|
||||||
|
case 'block-quote':
|
||||||
|
return <blockquote {...attributes}>{props.children}</blockquote>;
|
||||||
|
case 'bulleted-list':
|
||||||
|
return <ul {...attributes}>{props.children}</ul>;
|
||||||
|
case 'ordered-list':
|
||||||
|
return <ol {...attributes}>{props.children}</ol>;
|
||||||
|
case 'todo-list':
|
||||||
|
return <TodoList {...attributes}>{props.children}</TodoList>;
|
||||||
|
case 'table':
|
||||||
|
return <table {...attributes}>{props.children}</table>;
|
||||||
|
case 'table-row':
|
||||||
|
return <tr {...attributes}>{props.children}</tr>;
|
||||||
|
case 'table-head':
|
||||||
|
return <th {...attributes}>{props.children}</th>;
|
||||||
|
case 'table-cell':
|
||||||
|
return <td {...attributes}>{props.children}</td>;
|
||||||
|
case 'list-item':
|
||||||
|
return <ListItem {...props} />;
|
||||||
|
case 'horizontal-rule':
|
||||||
|
return <HorizontalRule {...props} />;
|
||||||
|
case 'code':
|
||||||
|
return <Code {...props} />;
|
||||||
|
case 'image':
|
||||||
|
return <Image {...props} />;
|
||||||
|
case 'link':
|
||||||
|
return <Link {...props} />;
|
||||||
|
case 'heading1':
|
||||||
|
return <Heading1 placeholder {...props} />;
|
||||||
|
case 'heading2':
|
||||||
|
return <Heading2 {...props} />;
|
||||||
|
case 'heading3':
|
||||||
|
return <Heading3 {...props} />;
|
||||||
|
case 'heading4':
|
||||||
|
return <Heading4 {...props} />;
|
||||||
|
case 'heading5':
|
||||||
|
return <Heading5 {...props} />;
|
||||||
|
case 'heading6':
|
||||||
|
return <Heading6 {...props} />;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import DropOrPasteImages from '@tommoor/slate-drop-or-paste-images';
|
// import DropOrPasteImages from '@tommoor/slate-drop-or-paste-images';
|
||||||
import PasteLinkify from 'slate-paste-linkify';
|
import PasteLinkify from 'slate-paste-linkify';
|
||||||
import CollapseOnEscape from 'slate-collapse-on-escape';
|
import CollapseOnEscape from 'slate-collapse-on-escape';
|
||||||
import TrailingBlock from 'slate-trailing-block';
|
import TrailingBlock from 'slate-trailing-block';
|
||||||
@ -8,7 +8,7 @@ import Prism from 'slate-prism';
|
|||||||
import EditList from './plugins/EditList';
|
import EditList from './plugins/EditList';
|
||||||
import KeyboardShortcuts from './plugins/KeyboardShortcuts';
|
import KeyboardShortcuts from './plugins/KeyboardShortcuts';
|
||||||
import MarkdownShortcuts from './plugins/MarkdownShortcuts';
|
import MarkdownShortcuts from './plugins/MarkdownShortcuts';
|
||||||
import insertImage from './insertImage';
|
// import insertImage from './insertImage';
|
||||||
|
|
||||||
const onlyInCode = node => node.type === 'code';
|
const onlyInCode = node => node.type === 'code';
|
||||||
|
|
||||||
@ -23,18 +23,18 @@ const createPlugins = ({ onImageUploadStart, onImageUploadStop }: Options) => {
|
|||||||
type: 'link',
|
type: 'link',
|
||||||
collapseTo: 'end',
|
collapseTo: 'end',
|
||||||
}),
|
}),
|
||||||
DropOrPasteImages({
|
// DropOrPasteImages({
|
||||||
extensions: ['png', 'jpg', 'gif'],
|
// extensions: ['png', 'jpg', 'gif'],
|
||||||
applyTransform: (transform, file, editor) => {
|
// applyTransform: (transform, file, editor) => {
|
||||||
return insertImage(
|
// return insertImage(
|
||||||
transform,
|
// transform,
|
||||||
file,
|
// file,
|
||||||
editor,
|
// editor,
|
||||||
onImageUploadStart,
|
// onImageUploadStart,
|
||||||
onImageUploadStop
|
// onImageUploadStop
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
}),
|
// }),
|
||||||
EditList,
|
EditList,
|
||||||
EditCode({
|
EditCode({
|
||||||
onlyIn: onlyInCode,
|
onlyIn: onlyInCode,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
import type { change } from 'slate-prop-types';
|
||||||
|
|
||||||
export default function KeyboardShortcuts() {
|
export default function KeyboardShortcuts() {
|
||||||
return {
|
return {
|
||||||
@ -10,38 +11,32 @@ export default function KeyboardShortcuts() {
|
|||||||
* @param {State} state
|
* @param {State} state
|
||||||
* @return {State or Null} state
|
* @return {State or Null} state
|
||||||
*/
|
*/
|
||||||
onKeyDown(ev: SyntheticEvent, data: Object, state: Object) {
|
onKeyDown(ev: SyntheticEvent, data: Object, change: change) {
|
||||||
if (!data.isMeta) return null;
|
if (!data.isMeta) return null;
|
||||||
|
|
||||||
switch (data.key) {
|
switch (data.key) {
|
||||||
case 'b':
|
case 'b':
|
||||||
return this.toggleMark(state, 'bold');
|
return this.toggleMark(change, 'bold');
|
||||||
case 'i':
|
case 'i':
|
||||||
return this.toggleMark(state, 'italic');
|
return this.toggleMark(change, 'italic');
|
||||||
case 'u':
|
case 'u':
|
||||||
return this.toggleMark(state, 'underlined');
|
return this.toggleMark(change, 'underlined');
|
||||||
case 'd':
|
case 'd':
|
||||||
return this.toggleMark(state, 'deleted');
|
return this.toggleMark(change, 'deleted');
|
||||||
case 'k':
|
case 'k':
|
||||||
return state
|
return change.wrapInline({ type: 'link', data: { href: '' } });
|
||||||
.transform()
|
|
||||||
.wrapInline({ type: 'link', data: { href: '' } })
|
|
||||||
.apply();
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleMark(state: Object, type: string) {
|
toggleMark(change: change, type: string) {
|
||||||
|
const { state } = change;
|
||||||
// don't allow formatting of document title
|
// don't allow formatting of document title
|
||||||
const firstNode = state.document.nodes.first();
|
const firstNode = state.document.nodes.first();
|
||||||
if (firstNode === state.startBlock) return;
|
if (firstNode === state.startBlock) return;
|
||||||
|
|
||||||
state = state
|
return state.change().toggleMark(type);
|
||||||
.transform()
|
|
||||||
.toggleMark(type)
|
|
||||||
.apply();
|
|
||||||
return state;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
import type { change } from 'slate-prop-types';
|
||||||
|
|
||||||
|
type KeyData = {
|
||||||
|
isMeta: boolean,
|
||||||
|
key: string,
|
||||||
|
};
|
||||||
|
|
||||||
const inlineShortcuts = [
|
const inlineShortcuts = [
|
||||||
{ mark: 'bold', shortcut: '**' },
|
{ mark: 'bold', shortcut: '**' },
|
||||||
{ mark: 'bold', shortcut: '__' },
|
{ mark: 'bold', shortcut: '__' },
|
||||||
@ -14,20 +21,20 @@ export default function MarkdownShortcuts() {
|
|||||||
/**
|
/**
|
||||||
* On key down, check for our specific key shortcuts.
|
* On key down, check for our specific key shortcuts.
|
||||||
*/
|
*/
|
||||||
onKeyDown(ev: SyntheticEvent, data: Object, state: Object) {
|
onKeyDown(ev: SyntheticEvent, data: KeyData, change: change) {
|
||||||
switch (data.key) {
|
switch (data.key) {
|
||||||
case '-':
|
case '-':
|
||||||
return this.onDash(ev, state);
|
return this.onDash(ev, change);
|
||||||
case '`':
|
case '`':
|
||||||
return this.onBacktick(ev, state);
|
return this.onBacktick(ev, change);
|
||||||
case 'tab':
|
case 'tab':
|
||||||
return this.onTab(ev, state);
|
return this.onTab(ev, change);
|
||||||
case 'space':
|
case 'space':
|
||||||
return this.onSpace(ev, state);
|
return this.onSpace(ev, change);
|
||||||
case 'backspace':
|
case 'backspace':
|
||||||
return this.onBackspace(ev, state);
|
return this.onBackspace(ev, change);
|
||||||
case 'enter':
|
case 'enter':
|
||||||
return this.onEnter(ev, state);
|
return this.onEnter(ev, change);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -37,7 +44,8 @@ export default function MarkdownShortcuts() {
|
|||||||
* On space, if it was after an auto-markdown shortcut, convert the current
|
* On space, if it was after an auto-markdown shortcut, convert the current
|
||||||
* node into the shortcut's corresponding type.
|
* node into the shortcut's corresponding type.
|
||||||
*/
|
*/
|
||||||
onSpace(ev: SyntheticEvent, state: Object) {
|
onSpace(ev: SyntheticEvent, change: change) {
|
||||||
|
const { state } = change;
|
||||||
if (state.isExpanded) return;
|
if (state.isExpanded) return;
|
||||||
const { startBlock, startOffset } = state;
|
const { startBlock, startOffset } = state;
|
||||||
const chars = startBlock.text.slice(0, startOffset).trim();
|
const chars = startBlock.text.slice(0, startOffset).trim();
|
||||||
@ -50,25 +58,19 @@ export default function MarkdownShortcuts() {
|
|||||||
let checked;
|
let checked;
|
||||||
if (chars === '[x]') checked = true;
|
if (chars === '[x]') checked = true;
|
||||||
if (chars === '[ ]') checked = false;
|
if (chars === '[ ]') checked = false;
|
||||||
const transform = state
|
const change = state.change().setBlock({ type, data: { checked } });
|
||||||
.transform()
|
|
||||||
.setBlock({ type, data: { checked } });
|
|
||||||
|
|
||||||
if (type === 'list-item') {
|
if (type === 'list-item') {
|
||||||
if (checked !== undefined) {
|
if (checked !== undefined) {
|
||||||
transform.wrapBlock('todo-list');
|
change.wrapBlock('todo-list');
|
||||||
} else if (chars === '1.') {
|
} else if (chars === '1.') {
|
||||||
transform.wrapBlock('ordered-list');
|
change.wrapBlock('ordered-list');
|
||||||
} else {
|
} else {
|
||||||
transform.wrapBlock('bulleted-list');
|
change.wrapBlock('bulleted-list');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = transform
|
return change.extendToStartOf(startBlock).delete();
|
||||||
.extendToStartOf(startBlock)
|
|
||||||
.delete()
|
|
||||||
.apply();
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of inlineShortcuts) {
|
for (const key of inlineShortcuts) {
|
||||||
@ -97,35 +99,32 @@ export default function MarkdownShortcuts() {
|
|||||||
|
|
||||||
// if we have multiple tags then mark the text between as inline code
|
// if we have multiple tags then mark the text between as inline code
|
||||||
if (inlineTags.length > 1) {
|
if (inlineTags.length > 1) {
|
||||||
const transform = state.transform();
|
const change = state.change();
|
||||||
const firstText = startBlock.getFirstText();
|
const firstText = startBlock.getFirstText();
|
||||||
const firstCodeTagIndex = inlineTags[0];
|
const firstCodeTagIndex = inlineTags[0];
|
||||||
const lastCodeTagIndex = inlineTags[inlineTags.length - 1];
|
const lastCodeTagIndex = inlineTags[inlineTags.length - 1];
|
||||||
transform.removeTextByKey(
|
change.removeTextByKey(
|
||||||
firstText.key,
|
firstText.key,
|
||||||
lastCodeTagIndex,
|
lastCodeTagIndex,
|
||||||
shortcut.length
|
shortcut.length
|
||||||
);
|
);
|
||||||
transform.removeTextByKey(
|
change.removeTextByKey(
|
||||||
firstText.key,
|
firstText.key,
|
||||||
firstCodeTagIndex,
|
firstCodeTagIndex,
|
||||||
shortcut.length
|
shortcut.length
|
||||||
);
|
);
|
||||||
transform.moveOffsetsTo(
|
change.moveOffsetsTo(
|
||||||
firstCodeTagIndex,
|
firstCodeTagIndex,
|
||||||
lastCodeTagIndex - shortcut.length
|
lastCodeTagIndex - shortcut.length
|
||||||
);
|
);
|
||||||
transform.addMark(mark);
|
change.addMark(mark);
|
||||||
state = transform
|
return change.collapseToEnd().removeMark(mark);
|
||||||
.collapseToEnd()
|
|
||||||
.removeMark(mark)
|
|
||||||
.apply();
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onDash(ev: SyntheticEvent, state: Object) {
|
onDash(ev: SyntheticEvent, change: change) {
|
||||||
|
const { state } = change;
|
||||||
if (state.isExpanded) return;
|
if (state.isExpanded) return;
|
||||||
const { startBlock, startOffset } = state;
|
const { startBlock, startOffset } = state;
|
||||||
const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '');
|
const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '');
|
||||||
@ -133,7 +132,7 @@ export default function MarkdownShortcuts() {
|
|||||||
if (chars === '--') {
|
if (chars === '--') {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
return state
|
return state
|
||||||
.transform()
|
.change()
|
||||||
.extendToStartOf(startBlock)
|
.extendToStartOf(startBlock)
|
||||||
.delete()
|
.delete()
|
||||||
.setBlock({
|
.setBlock({
|
||||||
@ -141,12 +140,12 @@ export default function MarkdownShortcuts() {
|
|||||||
isVoid: true,
|
isVoid: true,
|
||||||
})
|
})
|
||||||
.collapseToStartOfNextBlock()
|
.collapseToStartOfNextBlock()
|
||||||
.insertBlock('paragraph')
|
.insertBlock('paragraph');
|
||||||
.apply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onBacktick(ev: SyntheticEvent, state: Object) {
|
onBacktick(ev: SyntheticEvent, change: change) {
|
||||||
|
const { state } = change;
|
||||||
if (state.isExpanded) return;
|
if (state.isExpanded) return;
|
||||||
const { startBlock, startOffset } = state;
|
const { startBlock, startOffset } = state;
|
||||||
const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '');
|
const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '');
|
||||||
@ -154,18 +153,18 @@ export default function MarkdownShortcuts() {
|
|||||||
if (chars === '``') {
|
if (chars === '``') {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
return state
|
return state
|
||||||
.transform()
|
.change()
|
||||||
.extendToStartOf(startBlock)
|
.extendToStartOf(startBlock)
|
||||||
.delete()
|
.delete()
|
||||||
.setBlock({
|
.setBlock({
|
||||||
type: 'code',
|
type: 'code',
|
||||||
})
|
});
|
||||||
.apply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onBackspace(ev: SyntheticEvent, state: Object) {
|
onBackspace(ev: SyntheticEvent, change: change) {
|
||||||
if (state.isExpanded) return;
|
const { state } = change;
|
||||||
|
if (change.isExpanded) return;
|
||||||
const { startBlock, selection, startOffset } = state;
|
const { startBlock, selection, startOffset } = state;
|
||||||
|
|
||||||
// If at the start of a non-paragraph, convert it back into a paragraph
|
// If at the start of a non-paragraph, convert it back into a paragraph
|
||||||
@ -173,13 +172,11 @@ export default function MarkdownShortcuts() {
|
|||||||
if (startBlock.type === 'paragraph') return;
|
if (startBlock.type === 'paragraph') return;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
const transform = state.transform().setBlock('paragraph');
|
const change = state.change().setBlock('paragraph');
|
||||||
|
|
||||||
if (startBlock.type === 'list-item')
|
if (startBlock.type === 'list-item')
|
||||||
transform.unwrapBlock('bulleted-list');
|
change.unwrapBlock('bulleted-list');
|
||||||
|
return change;
|
||||||
state = transform.apply();
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If at the end of a code mark hitting backspace should remove the mark
|
// If at the end of a code mark hitting backspace should remove the mark
|
||||||
@ -198,15 +195,14 @@ export default function MarkdownShortcuts() {
|
|||||||
.reverse()
|
.reverse()
|
||||||
.takeUntil((v, k) => !v.marks.some(mark => mark.type === 'code'));
|
.takeUntil((v, k) => !v.marks.some(mark => mark.type === 'code'));
|
||||||
|
|
||||||
const transform = state.transform();
|
return state
|
||||||
transform.removeMarkByKey(
|
.change()
|
||||||
|
.removeMarkByKey(
|
||||||
textNode.key,
|
textNode.key,
|
||||||
state.startOffset - charsInCodeBlock.size,
|
change.startOffset - charsInCodeBlock.size,
|
||||||
state.startOffset,
|
change.startOffset,
|
||||||
'code'
|
'code'
|
||||||
);
|
);
|
||||||
state = transform.apply();
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -215,14 +211,15 @@ export default function MarkdownShortcuts() {
|
|||||||
* On tab, if at the end of the heading jump to the main body content
|
* On tab, if at the end of the heading jump to the main body content
|
||||||
* as if it is another input field (act the same as enter).
|
* as if it is another input field (act the same as enter).
|
||||||
*/
|
*/
|
||||||
onTab(ev: SyntheticEvent, state: Object) {
|
onTab(ev: SyntheticEvent, change: change) {
|
||||||
|
const { state } = change;
|
||||||
|
|
||||||
if (state.startBlock.type === 'heading1') {
|
if (state.startBlock.type === 'heading1') {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
return state
|
return state
|
||||||
.transform()
|
.change()
|
||||||
.splitBlock()
|
.splitBlock()
|
||||||
.setBlock('paragraph')
|
.setBlock('paragraph');
|
||||||
.apply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -230,11 +227,12 @@ export default function MarkdownShortcuts() {
|
|||||||
* On return, if at the end of a node type that should not be extended,
|
* On return, if at the end of a node type that should not be extended,
|
||||||
* create a new paragraph below it.
|
* create a new paragraph below it.
|
||||||
*/
|
*/
|
||||||
onEnter(ev: SyntheticEvent, state: Object) {
|
onEnter(ev: SyntheticEvent, change: change) {
|
||||||
|
const { state } = change;
|
||||||
if (state.isExpanded) return;
|
if (state.isExpanded) return;
|
||||||
const { startBlock, startOffset, endOffset } = state;
|
const { startBlock, startOffset, endOffset } = state;
|
||||||
if (startOffset === 0 && startBlock.length === 0)
|
if (startOffset === 0 && startBlock.length === 0)
|
||||||
return this.onBackspace(ev, state);
|
return this.onBackspace(ev, change);
|
||||||
if (endOffset !== startBlock.length) return;
|
if (endOffset !== startBlock.length) return;
|
||||||
|
|
||||||
// Hitting enter while an image is selected should jump caret below and
|
// Hitting enter while an image is selected should jump caret below and
|
||||||
@ -242,10 +240,9 @@ export default function MarkdownShortcuts() {
|
|||||||
if (startBlock.type === 'image') {
|
if (startBlock.type === 'image') {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
return state
|
return state
|
||||||
.transform()
|
.change()
|
||||||
.collapseToEnd()
|
.collapseToEnd()
|
||||||
.insertBlock('paragraph')
|
.insertBlock('paragraph');
|
||||||
.apply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hitting enter in a heading or blockquote will split the node at that
|
// Hitting enter in a heading or blockquote will split the node at that
|
||||||
@ -264,10 +261,9 @@ export default function MarkdownShortcuts() {
|
|||||||
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
return state
|
return state
|
||||||
.transform()
|
.change()
|
||||||
.splitBlock()
|
.splitBlock()
|
||||||
.setBlock('paragraph')
|
.setBlock('paragraph');
|
||||||
.apply();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,133 +1,133 @@
|
|||||||
// @flow
|
// // @flow
|
||||||
import React from 'react';
|
// import React from 'react';
|
||||||
import Code from './components/Code';
|
// import Code from './components/Code';
|
||||||
import HorizontalRule from './components/HorizontalRule';
|
// import HorizontalRule from './components/HorizontalRule';
|
||||||
import InlineCode from './components/InlineCode';
|
// import InlineCode from './components/InlineCode';
|
||||||
import Image from './components/Image';
|
// import Image from './components/Image';
|
||||||
import Link from './components/Link';
|
// import Link from './components/Link';
|
||||||
import ListItem from './components/ListItem';
|
// import ListItem from './components/ListItem';
|
||||||
import TodoList from './components/TodoList';
|
// import TodoList from './components/TodoList';
|
||||||
import {
|
// import {
|
||||||
Heading1,
|
// Heading1,
|
||||||
Heading2,
|
// Heading2,
|
||||||
Heading3,
|
// Heading3,
|
||||||
Heading4,
|
// Heading4,
|
||||||
Heading5,
|
// Heading5,
|
||||||
Heading6,
|
// Heading6,
|
||||||
} from './components/Heading';
|
// } from './components/Heading';
|
||||||
import Paragraph from './components/Paragraph';
|
// import Paragraph from './components/Paragraph';
|
||||||
import BlockToolbar from './components/Toolbar/BlockToolbar';
|
// import BlockToolbar from './components/Toolbar/BlockToolbar';
|
||||||
import type { Props, Node, Transform } from './types';
|
// import type { Props, Node, Transform } from './types';
|
||||||
|
//
|
||||||
type Options = {
|
// type Options = {
|
||||||
onInsertImage: Function,
|
// onInsertImage: Function,
|
||||||
onChange: Function,
|
// onChange: Function,
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
const createSchema = ({ onInsertImage, onChange }: Options) => {
|
// const createSchema = ({ onInsertImage, onChange }: Options) => {
|
||||||
return {
|
// return {
|
||||||
marks: {
|
// marks: {
|
||||||
bold: (props: Props) => <strong>{props.children}</strong>,
|
// bold: (props: Props) => <strong>{props.children}</strong>,
|
||||||
code: (props: Props) => <InlineCode>{props.children}</InlineCode>,
|
// code: (props: Props) => <InlineCode>{props.children}</InlineCode>,
|
||||||
italic: (props: Props) => <em>{props.children}</em>,
|
// italic: (props: Props) => <em>{props.children}</em>,
|
||||||
underlined: (props: Props) => <u>{props.children}</u>,
|
// underlined: (props: Props) => <u>{props.children}</u>,
|
||||||
deleted: (props: Props) => <del>{props.children}</del>,
|
// deleted: (props: Props) => <del>{props.children}</del>,
|
||||||
added: (props: Props) => <mark>{props.children}</mark>,
|
// added: (props: Props) => <mark>{props.children}</mark>,
|
||||||
},
|
// },
|
||||||
|
//
|
||||||
nodes: {
|
// nodes: {
|
||||||
'block-toolbar': (props: Props) => (
|
// 'block-toolbar': (props: Props) => (
|
||||||
<BlockToolbar
|
// <BlockToolbar
|
||||||
onChange={onChange}
|
// onChange={onChange}
|
||||||
onInsertImage={onInsertImage}
|
// onInsertImage={onInsertImage}
|
||||||
{...props}
|
// {...props}
|
||||||
/>
|
// />
|
||||||
),
|
// ),
|
||||||
paragraph: (props: Props) => <Paragraph {...props} />,
|
// paragraph: (props: Props) => <Paragraph {...props} />,
|
||||||
'block-quote': (props: Props) => (
|
// 'block-quote': (props: Props) => (
|
||||||
<blockquote {...props.attributes}>{props.children}</blockquote>
|
// <blockquote {...props.attributes}>{props.children}</blockquote>
|
||||||
),
|
// ),
|
||||||
'horizontal-rule': HorizontalRule,
|
// 'horizontal-rule': HorizontalRule,
|
||||||
'bulleted-list': (props: Props) => (
|
// 'bulleted-list': (props: Props) => (
|
||||||
<ul {...props.attributes}>{props.children}</ul>
|
// <ul {...props.attributes}>{props.children}</ul>
|
||||||
),
|
// ),
|
||||||
'ordered-list': (props: Props) => (
|
// 'ordered-list': (props: Props) => (
|
||||||
<ol {...props.attributes}>{props.children}</ol>
|
// <ol {...props.attributes}>{props.children}</ol>
|
||||||
),
|
// ),
|
||||||
'todo-list': (props: Props) => (
|
// 'todo-list': (props: Props) => (
|
||||||
<TodoList {...props.attributes}>{props.children}</TodoList>
|
// <TodoList {...props.attributes}>{props.children}</TodoList>
|
||||||
),
|
// ),
|
||||||
table: (props: Props) => (
|
// table: (props: Props) => (
|
||||||
<table {...props.attributes}>{props.children}</table>
|
// <table {...props.attributes}>{props.children}</table>
|
||||||
),
|
// ),
|
||||||
'table-row': (props: Props) => (
|
// 'table-row': (props: Props) => (
|
||||||
<tr {...props.attributes}>{props.children}</tr>
|
// <tr {...props.attributes}>{props.children}</tr>
|
||||||
),
|
// ),
|
||||||
'table-head': (props: Props) => (
|
// 'table-head': (props: Props) => (
|
||||||
<th {...props.attributes}>{props.children}</th>
|
// <th {...props.attributes}>{props.children}</th>
|
||||||
),
|
// ),
|
||||||
'table-cell': (props: Props) => (
|
// 'table-cell': (props: Props) => (
|
||||||
<td {...props.attributes}>{props.children}</td>
|
// <td {...props.attributes}>{props.children}</td>
|
||||||
),
|
// ),
|
||||||
code: Code,
|
// code: Code,
|
||||||
image: Image,
|
// image: Image,
|
||||||
link: Link,
|
// link: Link,
|
||||||
'list-item': ListItem,
|
// 'list-item': ListItem,
|
||||||
heading1: (props: Props) => <Heading1 placeholder {...props} />,
|
// heading1: (props: Props) => <Heading1 placeholder {...props} />,
|
||||||
heading2: (props: Props) => <Heading2 {...props} />,
|
// heading2: (props: Props) => <Heading2 {...props} />,
|
||||||
heading3: (props: Props) => <Heading3 {...props} />,
|
// heading3: (props: Props) => <Heading3 {...props} />,
|
||||||
heading4: (props: Props) => <Heading4 {...props} />,
|
// heading4: (props: Props) => <Heading4 {...props} />,
|
||||||
heading5: (props: Props) => <Heading5 {...props} />,
|
// heading5: (props: Props) => <Heading5 {...props} />,
|
||||||
heading6: (props: Props) => <Heading6 {...props} />,
|
// heading6: (props: Props) => <Heading6 {...props} />,
|
||||||
},
|
// },
|
||||||
|
//
|
||||||
rules: [
|
// rules: [
|
||||||
// ensure first node is always a heading
|
// // ensure first node is always a heading
|
||||||
{
|
// {
|
||||||
match: (node: Node) => {
|
// match: (node: Node) => {
|
||||||
return node.kind === 'document';
|
// return node.kind === 'document';
|
||||||
},
|
// },
|
||||||
validate: (document: Node) => {
|
// validate: (document: Node) => {
|
||||||
const firstNode = document.nodes.first();
|
// const firstNode = document.nodes.first();
|
||||||
return firstNode && firstNode.type === 'heading1' ? null : firstNode;
|
// return firstNode && firstNode.type === 'heading1' ? null : firstNode;
|
||||||
},
|
// },
|
||||||
normalize: (transform: Transform, document: Node, firstNode: Node) => {
|
// normalize: (transform: Transform, document: Node, firstNode: Node) => {
|
||||||
transform.setBlock({ type: 'heading1' });
|
// transform.setBlock({ type: 'heading1' });
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
|
//
|
||||||
// automatically removes any marks in first heading
|
// // automatically removes any marks in first heading
|
||||||
{
|
// {
|
||||||
match: (node: Node) => {
|
// match: (node: Node) => {
|
||||||
return node.kind === 'heading1';
|
// return node.kind === 'heading1';
|
||||||
},
|
// },
|
||||||
validate: (heading: Node) => {
|
// validate: (heading: Node) => {
|
||||||
const hasMarks = heading.getMarks().isEmpty();
|
// const hasMarks = heading.getMarks().isEmpty();
|
||||||
const hasInlines = heading.getInlines().isEmpty();
|
// const hasInlines = heading.getInlines().isEmpty();
|
||||||
|
//
|
||||||
return !(hasMarks && hasInlines);
|
// return !(hasMarks && hasInlines);
|
||||||
},
|
// },
|
||||||
normalize: (transform: Transform, heading: Node) => {
|
// normalize: (transform: Transform, heading: Node) => {
|
||||||
transform.unwrapInlineByKey(heading.key);
|
// transform.unwrapInlineByKey(heading.key);
|
||||||
|
//
|
||||||
heading.getMarks().forEach(mark => {
|
// heading.getMarks().forEach(mark => {
|
||||||
heading.nodes.forEach(textNode => {
|
// heading.nodes.forEach(textNode => {
|
||||||
if (textNode.kind === 'text') {
|
// if (textNode.kind === 'text') {
|
||||||
transform.removeMarkByKey(
|
// transform.removeMarkByKey(
|
||||||
textNode.key,
|
// textNode.key,
|
||||||
0,
|
// 0,
|
||||||
textNode.text.length,
|
// textNode.text.length,
|
||||||
mark
|
// mark
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
return transform;
|
// return transform;
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
export default createSchema;
|
// export default createSchema;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
import type { change } from 'slate-prop-types';
|
||||||
import EditList from './plugins/EditList';
|
import EditList from './plugins/EditList';
|
||||||
import type { State, Transform } from './types';
|
|
||||||
|
|
||||||
const { transforms } = EditList;
|
const { transforms } = EditList;
|
||||||
|
|
||||||
@ -10,28 +10,25 @@ type Options = {
|
|||||||
append?: string | Object,
|
append?: string | Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function splitAndInsertBlock(
|
export function splitAndInsertBlock(change: change, options: Options) {
|
||||||
transform: Transform,
|
|
||||||
state: State,
|
|
||||||
options: Options
|
|
||||||
) {
|
|
||||||
const { type, wrapper, append } = options;
|
const { type, wrapper, append } = options;
|
||||||
const { document } = state;
|
const { value } = change;
|
||||||
const parent = document.getParent(state.startBlock.key);
|
const { document } = value;
|
||||||
|
const parent = document.getParent(value.startBlock.key);
|
||||||
|
|
||||||
// lists get some special treatment
|
// lists get some special treatment
|
||||||
if (parent && parent.type === 'list-item') {
|
if (parent && parent.type === 'list-item') {
|
||||||
transform = transforms.unwrapList(
|
change = transforms.unwrapList(
|
||||||
transforms
|
transforms
|
||||||
.splitListItem(transform.collapseToStart())
|
.splitListItem(change.collapseToStart())
|
||||||
.collapseToEndOfPreviousBlock()
|
.collapseToEndOfPreviousBlock()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
transform = transform.insertBlock(type);
|
change = change.insertBlock(type);
|
||||||
|
|
||||||
if (wrapper) transform = transform.wrapBlock(wrapper);
|
if (wrapper) change = change.wrapBlock(wrapper);
|
||||||
if (append) transform = transform.insertBlock(append);
|
if (append) change = change.insertBlock(append);
|
||||||
|
|
||||||
return transform;
|
return change;
|
||||||
}
|
}
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
// @flow
|
|
||||||
import { List, Set, Map } from 'immutable';
|
|
||||||
import { Selection } from 'slate';
|
|
||||||
|
|
||||||
export type NodeTransform = {
|
|
||||||
addMarkByKey: Function,
|
|
||||||
insertNodeByKey: Function,
|
|
||||||
insertTextByKey: Function,
|
|
||||||
moveNodeByKey: Function,
|
|
||||||
removeMarkByKey: Function,
|
|
||||||
removeNodeByKey: Function,
|
|
||||||
removeTextByKey: Function,
|
|
||||||
setMarkByKey: Function,
|
|
||||||
setNodeByKey: Function,
|
|
||||||
splitNodeByKey: Function,
|
|
||||||
unwrapInlineByKey: Function,
|
|
||||||
unwrapBlockByKey: Function,
|
|
||||||
unwrapNodeByKey: Function,
|
|
||||||
wrapBlockByKey: Function,
|
|
||||||
wrapInlineByKey: Function,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type StateTransform = {
|
|
||||||
deleteBackward: Function,
|
|
||||||
deleteForward: Function,
|
|
||||||
delete: Function,
|
|
||||||
insertBlock: Function,
|
|
||||||
insertFragment: Function,
|
|
||||||
insertInline: Function,
|
|
||||||
insertText: Function,
|
|
||||||
addMark: Function,
|
|
||||||
setBlock: Function,
|
|
||||||
setInline: Function,
|
|
||||||
splitBlock: Function,
|
|
||||||
splitInline: Function,
|
|
||||||
removeMark: Function,
|
|
||||||
toggleMark: Function,
|
|
||||||
unwrapBlock: Function,
|
|
||||||
unwrapInline: Function,
|
|
||||||
wrapBlock: Function,
|
|
||||||
wrapInline: Function,
|
|
||||||
wrapText: Function,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SelectionTransform = {
|
|
||||||
collapseToStart: Function,
|
|
||||||
collapseToEnd: Function,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Transform = NodeTransform & StateTransform & SelectionTransform;
|
|
||||||
|
|
||||||
export type Editor = {
|
|
||||||
props: Object,
|
|
||||||
className: string,
|
|
||||||
onChange: Function,
|
|
||||||
onDocumentChange: Function,
|
|
||||||
onSelectionChange: Function,
|
|
||||||
plugins: Array<Object>,
|
|
||||||
readOnly: boolean,
|
|
||||||
state: Object,
|
|
||||||
style: Object,
|
|
||||||
placeholder?: string,
|
|
||||||
placeholderClassName?: string,
|
|
||||||
placeholderStyle?: string,
|
|
||||||
blur: Function,
|
|
||||||
focus: Function,
|
|
||||||
getSchema: Function,
|
|
||||||
getState: Function,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Node = {
|
|
||||||
key: string,
|
|
||||||
kind: string,
|
|
||||||
type: string,
|
|
||||||
length: number,
|
|
||||||
text: string,
|
|
||||||
data: Map<string, any>,
|
|
||||||
nodes: List<Node>,
|
|
||||||
getMarks: Function,
|
|
||||||
getBlocks: Function,
|
|
||||||
getParent: Function,
|
|
||||||
getInlines: Function,
|
|
||||||
getInlinesAtRange: Function,
|
|
||||||
setBlock: Function,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Block = Node & {
|
|
||||||
type: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Document = Node;
|
|
||||||
|
|
||||||
export type State = {
|
|
||||||
document: Document,
|
|
||||||
selection: Selection,
|
|
||||||
startBlock: Block,
|
|
||||||
endBlock: Block,
|
|
||||||
startText: Node,
|
|
||||||
endText: Node,
|
|
||||||
marks: Set<*>,
|
|
||||||
blocks: List<Block>,
|
|
||||||
fragment: Document,
|
|
||||||
lines: List<Node>,
|
|
||||||
tests: List<Node>,
|
|
||||||
startBlock: Block,
|
|
||||||
transform: Function,
|
|
||||||
isBlurred: Function,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Props = {
|
|
||||||
node: Node,
|
|
||||||
parent?: Node,
|
|
||||||
attributes?: Object,
|
|
||||||
state: State,
|
|
||||||
editor: Editor,
|
|
||||||
readOnly?: boolean,
|
|
||||||
children?: React$Element<any>,
|
|
||||||
};
|
|
20
package.json
20
package.json
@ -105,6 +105,7 @@
|
|||||||
"history": "3.0.0",
|
"history": "3.0.0",
|
||||||
"html-webpack-plugin": "2.17.0",
|
"html-webpack-plugin": "2.17.0",
|
||||||
"http-errors": "1.4.0",
|
"http-errors": "1.4.0",
|
||||||
|
"immutable": "^3.8.2",
|
||||||
"imports-loader": "0.6.5",
|
"imports-loader": "0.6.5",
|
||||||
"invariant": "^2.2.2",
|
"invariant": "^2.2.2",
|
||||||
"isomorphic-fetch": "2.2.1",
|
"isomorphic-fetch": "2.2.1",
|
||||||
@ -160,14 +161,17 @@
|
|||||||
"sequelize": "^4.3.1",
|
"sequelize": "^4.3.1",
|
||||||
"sequelize-cli": "^2.7.0",
|
"sequelize-cli": "^2.7.0",
|
||||||
"sequelize-encrypted": "0.1.0",
|
"sequelize-encrypted": "0.1.0",
|
||||||
"slate": "^0.21.4",
|
"slate": "^0.29.0",
|
||||||
"slate-collapse-on-escape": "^0.2.1",
|
"slate-collapse-on-escape": "^0.6.0",
|
||||||
"slate-edit-code": "^0.10.2",
|
"slate-edit-code": "^0.13.2",
|
||||||
"slate-edit-list": "^0.7.0",
|
"slate-edit-list": "^0.10.1",
|
||||||
"slate-md-serializer": "0.5.6",
|
"slate-md-serializer": "1.0.1",
|
||||||
"slate-paste-linkify": "^0.2.1",
|
"slate-paste-linkify": "^0.5.0",
|
||||||
"slate-prism": "^0.2.2",
|
"slate-plain-serializer": "^0.4.12",
|
||||||
"slate-trailing-block": "^0.2.4",
|
"slate-prism": "^0.4.0",
|
||||||
|
"slate-prop-types": "^0.4.12",
|
||||||
|
"slate-react": "^0.10.19",
|
||||||
|
"slate-trailing-block": "^0.4.0",
|
||||||
"slug": "0.9.1",
|
"slug": "0.9.1",
|
||||||
"string-hash": "^1.1.0",
|
"string-hash": "^1.1.0",
|
||||||
"string-replace-to-array": "^1.0.3",
|
"string-replace-to-array": "^1.0.3",
|
||||||
|
139
yarn.lock
139
yarn.lock
@ -2664,7 +2664,7 @@ es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1:
|
|||||||
es5-ext "^0.10.14"
|
es5-ext "^0.10.14"
|
||||||
es6-symbol "^3.1"
|
es6-symbol "^3.1"
|
||||||
|
|
||||||
es6-map@^0.1.3, es6-map@^0.1.4:
|
es6-map@^0.1.3:
|
||||||
version "0.1.5"
|
version "0.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0"
|
resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3467,7 +3467,7 @@ get-caller-file@^1.0.1:
|
|||||||
|
|
||||||
get-document@1:
|
get-document@1:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/get-document/-/get-document-1.0.0.tgz#4821bce66f1c24cb0331602be6cb6b12c4f01c4b"
|
resolved "https://registry.npmjs.org/get-document/-/get-document-1.0.0.tgz#4821bce66f1c24cb0331602be6cb6b12c4f01c4b"
|
||||||
|
|
||||||
get-stdin@^4.0.1:
|
get-stdin@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
@ -3479,7 +3479,7 @@ get-stream@^3.0.0:
|
|||||||
|
|
||||||
get-window@^1.1.1:
|
get-window@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/get-window/-/get-window-1.1.1.tgz#0750f8970c88a54ac1294deb97add9568b3db594"
|
resolved "https://registry.npmjs.org/get-window/-/get-window-1.1.1.tgz#0750f8970c88a54ac1294deb97add9568b3db594"
|
||||||
dependencies:
|
dependencies:
|
||||||
get-document "1"
|
get-document "1"
|
||||||
|
|
||||||
@ -4125,9 +4125,9 @@ immediate@~3.0.5:
|
|||||||
version "3.0.6"
|
version "3.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||||
|
|
||||||
immutable@^3.8.1:
|
immutable@^3.8.2:
|
||||||
version "3.8.1"
|
version "3.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2"
|
resolved "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
|
||||||
|
|
||||||
imports-loader@0.6.5:
|
imports-loader@0.6.5:
|
||||||
version "0.6.5"
|
version "0.6.5"
|
||||||
@ -4366,6 +4366,10 @@ is-hexadecimal@^1.0.0:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz#6e084bbc92061fbb0971ec58b6ce6d404e24da69"
|
resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz#6e084bbc92061fbb0971ec58b6ce6d404e24da69"
|
||||||
|
|
||||||
|
is-hotkey@^0.1.1:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.1.tgz#b279a2fd108391be9aa93c6cb317f50357da549a"
|
||||||
|
|
||||||
is-image@^1.0.1:
|
is-image@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-image/-/is-image-1.0.1.tgz#6fd51a752a1a111506d060d952118b0b989b426e"
|
resolved "https://registry.yarnpkg.com/is-image/-/is-image-1.0.1.tgz#6fd51a752a1a111506d060d952118b0b989b426e"
|
||||||
@ -4374,7 +4378,7 @@ is-image@^1.0.1:
|
|||||||
|
|
||||||
is-in-browser@^1.1.3:
|
is-in-browser@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
|
resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
|
||||||
|
|
||||||
is-lower-case@^1.1.0:
|
is-lower-case@^1.1.0:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
@ -4435,7 +4439,7 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
|
|||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||||
|
|
||||||
is-plain-object@^2.0.1, is-plain-object@^2.0.3:
|
is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4531,7 +4535,7 @@ is-whitespace-character@^1.0.0:
|
|||||||
|
|
||||||
is-window@^1.0.2:
|
is-window@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d"
|
resolved "https://registry.npmjs.org/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d"
|
||||||
|
|
||||||
is-windows@^0.2.0:
|
is-windows@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
@ -4571,6 +4575,10 @@ isobject@^3.0.0, isobject@^3.0.1:
|
|||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||||
|
|
||||||
|
isomorphic-base64@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/isomorphic-base64/-/isomorphic-base64-1.0.2.tgz#f426aae82569ba8a4ec5ca73ad21a44ab1ee7803"
|
||||||
|
|
||||||
isomorphic-fetch@2.2.1, isomorphic-fetch@^2.1.1:
|
isomorphic-fetch@2.2.1, isomorphic-fetch@^2.1.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
|
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
|
||||||
@ -5054,7 +5062,7 @@ jws@^3.0.0, jws@^3.1.4:
|
|||||||
|
|
||||||
keycode@^2.1.2:
|
keycode@^2.1.2:
|
||||||
version "2.1.9"
|
version "2.1.9"
|
||||||
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.9.tgz#964a23c54e4889405b4861a5c9f0480d45141dfa"
|
resolved "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz#964a23c54e4889405b4861a5c9f0480d45141dfa"
|
||||||
|
|
||||||
keygrip@~1.0.2:
|
keygrip@~1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -5670,6 +5678,10 @@ lodash.templatesettings@^3.0.0:
|
|||||||
lodash._reinterpolate "^3.0.0"
|
lodash._reinterpolate "^3.0.0"
|
||||||
lodash.escape "^3.0.0"
|
lodash.escape "^3.0.0"
|
||||||
|
|
||||||
|
lodash.throttle@^4.1.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
|
||||||
|
|
||||||
lodash.toarray@^4.4.0:
|
lodash.toarray@^4.4.0:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
|
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
|
||||||
@ -7403,6 +7415,10 @@ react-helmet@^5.2.0:
|
|||||||
prop-types "^15.5.4"
|
prop-types "^15.5.4"
|
||||||
react-side-effect "^1.1.0"
|
react-side-effect "^1.1.0"
|
||||||
|
|
||||||
|
react-immutable-proptypes@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#023d6f39bb15c97c071e9e60d00d136eac5fa0b4"
|
||||||
|
|
||||||
react-keydown@^1.7.3:
|
react-keydown@^1.7.3:
|
||||||
version "1.9.4"
|
version "1.9.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-keydown/-/react-keydown-1.9.4.tgz#22718ac95edb64dd840dfc4350abf7e693ea0b7f"
|
resolved "https://registry.yarnpkg.com/react-keydown/-/react-keydown-1.9.4.tgz#22718ac95edb64dd840dfc4350abf7e693ea0b7f"
|
||||||
@ -7431,8 +7447,8 @@ react-modal@^3.1.2:
|
|||||||
prop-types "^15.5.10"
|
prop-types "^15.5.10"
|
||||||
|
|
||||||
react-portal@^3.1.0:
|
react-portal@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-3.1.0.tgz#865c44fb72a1da106c649206936559ce891ee899"
|
resolved "https://registry.npmjs.org/react-portal/-/react-portal-3.2.0.tgz#4224e19b2b05d5cbe730a7ba0e34ec7585de0043"
|
||||||
dependencies:
|
dependencies:
|
||||||
prop-types "^15.5.8"
|
prop-types "^15.5.8"
|
||||||
|
|
||||||
@ -7993,7 +8009,7 @@ select@^1.1.2:
|
|||||||
|
|
||||||
selection-is-backward@^1.0.0:
|
selection-is-backward@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/selection-is-backward/-/selection-is-backward-1.0.0.tgz#97a54633188a511aba6419fc5c1fa91b467e6be1"
|
resolved "https://registry.npmjs.org/selection-is-backward/-/selection-is-backward-1.0.0.tgz#97a54633188a511aba6419fc5c1fa91b467e6be1"
|
||||||
|
|
||||||
semver-diff@^2.0.0:
|
semver-diff@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
@ -8189,64 +8205,99 @@ slash@^1.0.0:
|
|||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
||||||
|
|
||||||
slate-collapse-on-escape@^0.2.1:
|
slate-base64-serializer@^0.2.14:
|
||||||
version "0.2.1"
|
version "0.2.14"
|
||||||
resolved "https://registry.yarnpkg.com/slate-collapse-on-escape/-/slate-collapse-on-escape-0.2.1.tgz#988f474439f0a21f94cc0016da52ea3c1a061100"
|
resolved "https://registry.npmjs.org/slate-base64-serializer/-/slate-base64-serializer-0.2.14.tgz#9970bfddde069f76ba78a5ac644a1de352e8ba42"
|
||||||
|
dependencies:
|
||||||
|
isomorphic-base64 "^1.0.2"
|
||||||
|
|
||||||
|
slate-collapse-on-escape@^0.6.0:
|
||||||
|
version "0.6.0"
|
||||||
|
resolved "https://registry.npmjs.org/slate-collapse-on-escape/-/slate-collapse-on-escape-0.6.0.tgz#668f2318770608a09a25a95623f5fb4329a277bf"
|
||||||
dependencies:
|
dependencies:
|
||||||
to-pascal-case "^1.0.0"
|
to-pascal-case "^1.0.0"
|
||||||
|
|
||||||
slate-edit-code@^0.10.2:
|
slate-dev-logger@^0.1.25, slate-dev-logger@^0.1.36:
|
||||||
version "0.10.3"
|
version "0.1.36"
|
||||||
resolved "https://registry.yarnpkg.com/slate-edit-code/-/slate-edit-code-0.10.3.tgz#c8211a050a127cdccc9174209f778fe489659801"
|
resolved "https://registry.npmjs.org/slate-dev-logger/-/slate-dev-logger-0.1.36.tgz#ecdb37dbf944dfc742bab23b6a20d5a0472db95e"
|
||||||
|
|
||||||
|
slate-edit-code@^0.13.2:
|
||||||
|
version "0.13.2"
|
||||||
|
resolved "https://registry.npmjs.org/slate-edit-code/-/slate-edit-code-0.13.2.tgz#682a7640da076906e5b4a4c73ec0e46d31d92c62"
|
||||||
dependencies:
|
dependencies:
|
||||||
detect-indent "^4.0.0"
|
detect-indent "^4.0.0"
|
||||||
detect-newline "^2.1.0"
|
detect-newline "^2.1.0"
|
||||||
ends-with "^0.2.0"
|
ends-with "^0.2.0"
|
||||||
immutable "^3.8.1"
|
is-hotkey "^0.1.1"
|
||||||
|
|
||||||
slate-edit-list@^0.7.0:
|
slate-edit-list@^0.10.1:
|
||||||
version "0.7.1"
|
version "0.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/slate-edit-list/-/slate-edit-list-0.7.1.tgz#84ee960d2d5b5a20ce267ad9df894395a91b93d5"
|
resolved "https://registry.npmjs.org/slate-edit-list/-/slate-edit-list-0.10.1.tgz#9c6a142a314b0ff22a327f1b50c8f5c85468cb17"
|
||||||
|
|
||||||
slate-md-serializer@0.5.6:
|
slate-md-serializer@1.0.1:
|
||||||
version "0.5.6"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/slate-md-serializer/-/slate-md-serializer-0.5.6.tgz#88048ae62757ce3aaf1096ebd4200c58fdd1e86c"
|
resolved "https://registry.npmjs.org/slate-md-serializer/-/slate-md-serializer-1.0.1.tgz#10fb8118bf0b97addaf9d7fd77c1b19f3d767309"
|
||||||
|
|
||||||
slate-paste-linkify@^0.2.1:
|
slate-paste-linkify@^0.5.0:
|
||||||
version "0.2.1"
|
version "0.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/slate-paste-linkify/-/slate-paste-linkify-0.2.1.tgz#4647b5207b910d2d084f7d5d256384869b0a9c75"
|
resolved "https://registry.npmjs.org/slate-paste-linkify/-/slate-paste-linkify-0.5.0.tgz#fbb52216ae4c02475b42b3de3609de0ad6b07fd1"
|
||||||
dependencies:
|
dependencies:
|
||||||
is-url "^1.2.2"
|
is-url "^1.2.2"
|
||||||
to-pascal-case "^1.0.0"
|
to-pascal-case "^1.0.0"
|
||||||
|
|
||||||
slate-prism@^0.2.2:
|
slate-plain-serializer@^0.4.12:
|
||||||
version "0.2.2"
|
version "0.4.12"
|
||||||
resolved "https://registry.yarnpkg.com/slate-prism/-/slate-prism-0.2.2.tgz#0c9d5c2bee0e94a6df5fc564b7a99f6b8e1ea492"
|
resolved "https://registry.npmjs.org/slate-plain-serializer/-/slate-plain-serializer-0.4.12.tgz#802c246bbb7f5c03170a6b3b862251ec51d39cb6"
|
||||||
|
dependencies:
|
||||||
|
slate-dev-logger "^0.1.36"
|
||||||
|
|
||||||
|
slate-prism@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.npmjs.org/slate-prism/-/slate-prism-0.4.0.tgz#9d43b1fafa4c3a8e3bceaa8dbc41dd5ca39445a9"
|
||||||
dependencies:
|
dependencies:
|
||||||
prismjs "^1.6.0"
|
prismjs "^1.6.0"
|
||||||
|
|
||||||
slate-trailing-block@^0.2.4:
|
slate-prop-types@^0.4.12:
|
||||||
version "0.2.4"
|
version "0.4.12"
|
||||||
resolved "https://registry.yarnpkg.com/slate-trailing-block/-/slate-trailing-block-0.2.4.tgz#6ce9525fa15f9f098d810d9312a4267799cd0e12"
|
resolved "https://registry.npmjs.org/slate-prop-types/-/slate-prop-types-0.4.12.tgz#a600030e8083fbc4a195ed98657e0d4e45caf93b"
|
||||||
|
dependencies:
|
||||||
|
slate-dev-logger "^0.1.36"
|
||||||
|
|
||||||
slate@^0.21.4:
|
slate-react@^0.10.19:
|
||||||
version "0.21.4"
|
version "0.10.19"
|
||||||
resolved "https://registry.yarnpkg.com/slate/-/slate-0.21.4.tgz#ae6113379cd838b7ec68ecd94834ce9741bc36f3"
|
resolved "https://registry.npmjs.org/slate-react/-/slate-react-0.10.19.tgz#917dac40634aeb0dc973100d492e11fc450c80ee"
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "^2.3.2"
|
debug "^2.3.2"
|
||||||
direction "^0.1.5"
|
|
||||||
es6-map "^0.1.4"
|
|
||||||
esrever "^0.2.0"
|
|
||||||
get-window "^1.1.1"
|
get-window "^1.1.1"
|
||||||
immutable "^3.8.1"
|
is-hotkey "^0.1.1"
|
||||||
is-empty "^1.0.0"
|
|
||||||
is-in-browser "^1.1.3"
|
is-in-browser "^1.1.3"
|
||||||
is-window "^1.0.2"
|
is-window "^1.0.2"
|
||||||
keycode "^2.1.2"
|
keycode "^2.1.2"
|
||||||
lodash "^4.17.4"
|
lodash.throttle "^4.1.1"
|
||||||
prop-types "^15.5.8"
|
prop-types "^15.5.8"
|
||||||
|
react-immutable-proptypes "^2.1.0"
|
||||||
react-portal "^3.1.0"
|
react-portal "^3.1.0"
|
||||||
selection-is-backward "^1.0.0"
|
selection-is-backward "^1.0.0"
|
||||||
|
slate-base64-serializer "^0.2.14"
|
||||||
|
slate-dev-logger "^0.1.36"
|
||||||
|
slate-plain-serializer "^0.4.12"
|
||||||
|
slate-prop-types "^0.4.12"
|
||||||
|
|
||||||
|
slate-trailing-block@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.npmjs.org/slate-trailing-block/-/slate-trailing-block-0.4.0.tgz#92573d7729e4c2a05c45521616ae2db28197476c"
|
||||||
|
|
||||||
|
slate@^0.29.0:
|
||||||
|
version "0.29.1"
|
||||||
|
resolved "https://registry.npmjs.org/slate/-/slate-0.29.1.tgz#a9df98158e67f92456b9b8f38fb6d279ba8f9f7e"
|
||||||
|
dependencies:
|
||||||
|
debug "^2.3.2"
|
||||||
|
direction "^0.1.5"
|
||||||
|
esrever "^0.2.0"
|
||||||
|
is-empty "^1.0.0"
|
||||||
|
is-plain-object "^2.0.4"
|
||||||
|
lodash "^4.17.4"
|
||||||
|
slate-dev-logger "^0.1.25"
|
||||||
type-of "^2.0.1"
|
type-of "^2.0.1"
|
||||||
|
|
||||||
slice-ansi@0.0.4:
|
slice-ansi@0.0.4:
|
||||||
|
Reference in New Issue
Block a user