--- a/client/package.json Wed Sep 05 13:48:10 2018 +0200
+++ b/client/package.json Tue Sep 25 02:02:13 2018 +0200
@@ -5,6 +5,9 @@
"homepage": ".",
"dependencies": {
"@types/react-modal": "^3.2.1",
+ "@types/slate-html-serializer": "^0.6.2",
+ "@types/slate-plain-serializer": "^0.5.1",
+ "@types/slate-react": "^0.18.0",
"bootstrap": "^4.1.3",
"i18next": "^11.6.0",
"immutable": "^3.8.1",
@@ -27,7 +30,10 @@
"redux-persist-immutable": "^4.3.0",
"redux-persist-transform-immutable": "^4.3.0",
"redux-saga": "^0.15.6",
- "slate": "^0.20.1",
+ "slate": "^0.40.2",
+ "slate-html-serializer": "^0.7.2",
+ "slate-plain-serializer": "^0.6.2",
+ "slate-react": "^0.18.5",
"uuid": "^3.0.1",
"yarn": "^1.6.0"
},
--- a/client/src/AnnotationPlugin.js Wed Sep 05 13:48:10 2018 +0200
+++ b/client/src/AnnotationPlugin.js Tue Sep 25 02:02:13 2018 +0200
@@ -3,13 +3,12 @@
const { onChange } = options
return {
- onSelect(event, data, state, editor) {
+ onSelect(event, change) {
event.preventDefault()
- const selection = data.selection;
-
- const startOffset = selection.startOffset;
- const endOffset = selection.endOffset;
+ const { value } = change
+ const { selection } = value
+ const { start, end} = selection
if (selection.isCollapsed) {
return;
@@ -21,14 +20,14 @@
// Keep only the relevant nodes,
// i.e. nodes which are contained within selection
- state.document.nodes.forEach((node) => {
- if (selection.hasStartIn(node)) {
+ value.document.nodes.forEach((node) => {
+ if (start.isInNode(node)) {
hasStarted = true;
}
if (hasStarted && !hasEnded) {
nodes.push(node);
}
- if (selection.hasEndIn(node)) {
+ if (end.isAtEndOfNode(node)) {
hasEnded = true;
}
});
@@ -37,17 +36,17 @@
// Concatenate the nodes text
if (nodes.length === 1) {
- text = nodes[0].text.substring(startOffset, endOffset);
+ text = nodes[0].text.substring(start.offset, end.offset);
} else {
text = nodes.map((node) => {
- if (selection.hasStartIn(node)) return node.text.substring(startOffset);
- if (selection.hasEndIn(node)) return node.text.substring(0, endOffset);
+ if (start.isInNode(node)) return node.text.substring(start.offset);
+ if (end.isAtEndOfNode(node)) return node.text.substring(0, end.offset);
return node.text;
}).join('\n');
}
if (onChange) {
- onChange(text, startOffset, endOffset);
+ onChange(text, start.offset, end.offset);
}
}
--- a/client/src/HtmlSerializer.js Wed Sep 05 13:48:10 2018 +0200
+++ b/client/src/HtmlSerializer.js Tue Sep 25 02:02:13 2018 +0200
@@ -1,15 +1,18 @@
import React from 'react'
-import { Html } from 'slate'
+import Html from 'slate-html-serializer'
const BLOCK_TAGS = {
p: 'paragraph',
+ ul: 'bulleted-list',
+ ol: 'numbered-list',
+ li: 'list-item',
}
// Add a dictionary of mark tags.
const MARK_TAGS = {
em: 'italic',
strong: 'bold',
- u: 'underline',
+ u: 'underlined',
category: 'span'
}
@@ -20,16 +23,23 @@
const type = BLOCK_TAGS[el.tagName]
if (!type) return
return {
- kind: 'block',
+ object: 'block',
type: type,
- nodes: next(el.children)
+ nodes: next(el.childNodes)
}
},
- serialize(object, children) {
- if (object.kind !== 'block') return
- switch (object.type) {
+ serialize(obj, children) {
+ if (obj.object !== 'block') return
+ switch (obj.type) {
+ case 'numbered-list':
+ return <ol>{children}</ol>;
+ case 'bulleted-list':
+ return <ul>{children}</ul>;
+ case 'list-item':
+ return <li>{children}</li>;
case 'paragraph':
- case 'line': return <p>{children}</p>
+ case 'line':
+ return <p>{children}</p>
default: return;
}
}
@@ -40,18 +50,22 @@
const type = MARK_TAGS[el.tagName]
if (!type) return
return {
- kind: 'mark',
+ object: 'mark',
type: type,
- nodes: next(el.children)
+ nodes: next(el.childNodes)
}
},
- serialize(object, children) {
- if (object.kind !== 'mark') return
- switch (object.type) {
- case 'bold': return <strong>{children}</strong>
- case 'italic': return <em>{children}</em>
- case 'underline': return <u>{children}</u>
- case 'category': return <span style={{ backgroundColor: object.data.get('color') }}>{children}</span>
+ serialize(obj, children) {
+ if (obj.object !== 'mark') return
+ switch (obj.type) {
+ case 'bold':
+ return <strong>{children}</strong>
+ case 'italic':
+ return <em>{children}</em>
+ case 'underlined':
+ return <ins>{children}</ins>
+ case 'category':
+ return <span style={{ backgroundColor: obj.data.get('color') }}>{children}</span>
default: return;
}
}
--- a/client/src/components/NoteInput.js Wed Sep 05 13:48:10 2018 +0200
+++ b/client/src/components/NoteInput.js Tue Sep 25 02:02:13 2018 +0200
@@ -14,7 +14,7 @@
onEditorChange = (e) => {
this.setState({
- buttonDisabled: e.state.document.length === 0,
+ buttonDisabled: e.value.document === 0,
startedAt: e.startedAt,
finishedAt: e.finishedAt
});
@@ -26,7 +26,7 @@
const raw = this.refs.editor.asRaw();
const html = this.refs.editor.asHtml();
const categories = this.refs.editor.asCategories();
- const marginComment = this.marginComment.value;
+ // const marginComment = this.marginComment.value;
this.props.addNote(this.props.session, {
plain: plain,
@@ -35,7 +35,7 @@
startedAt: this.state.startedAt,
finishedAt: now(),
categories: categories,
- marginComment: marginComment,
+ // marginComment: marginComment,
});
this.refs.editor.clear();
@@ -54,8 +54,8 @@
render() {
return (
<form>
- <div className="editor">
- <div className="editor-left">
+ <div className="editor p-3 mb-3">
+ <div className="editor-left w-100 border-0 pl-3 pb-3 sticky-bottom">
<SlateEditor ref="editor"
onChange={this.onEditorChange}
onEnterKeyDown={this.onAddNoteClick}
@@ -64,15 +64,16 @@
isChecked={this.props.autoSubmit}
isButtonDisabled={this.state.buttonDisabled}
annotationCategories={ this.props.annotationCategories } />
+
</div>
- <div className="editor-right">
- <input type="textarea" className="form-control"
+ {/* <div className="editor-right w-25 pl-2 border-0 sticky-bottom">
+ <input type="textarea" className="form-control h-100"
name="margin"
- placeholder="Enter a margin comment for your note"
+ placeholder="Espace de commentaire pour votre note"
// inputRef={ ref => { this.marginComment = ref; } }
ref={(marginComment) => { this.marginComment = marginComment; }}
/>
- </div>
+ </div> */}
</div>
</form>
);
--- a/client/src/components/SlateEditor.js Wed Sep 05 13:48:10 2018 +0200
+++ b/client/src/components/SlateEditor.js Tue Sep 25 02:02:13 2018 +0200
@@ -1,4 +1,6 @@
-import { Editor, Plain, Raw } from 'slate'
+import { Value } from 'slate'
+import Plain from 'slate-plain-serializer'
+import { Editor } from 'slate-react'
import React from 'react'
import Portal from 'react-portal'
import Immutable from 'immutable'
@@ -41,11 +43,32 @@
fontStyle: 'italic'
},
underlined: {
- textDecoration: 'underline'
+ textDecoration: 'underlined'
}
}
}
+const initialValue = Value.fromJSON({
+ document: {
+ nodes: [
+ {
+ object: 'block',
+ type: 'paragraph',
+ nodes: [
+ {
+ object: 'text',
+ leaves: [
+ {
+ text: '',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+})
+
/**
* The rich text example.
*
@@ -74,8 +97,9 @@
plugins.push(annotationPlugin);
+
this.state = {
- state: props.note ? Raw.deserialize(props.note.raw) : Plain.deserialize(''),
+ value: props.note ? initialValue : Plain.deserialize(''),
startedAt: null,
finishedAt: null,
currentSelectionText: '',
@@ -97,44 +121,20 @@
this.updateMenu();
}
- /**
- * Check if the current selection has a mark with `type` in it.
+ /**
+ * On change, save the new state.
*
- * @param {String} type
- * @return {Boolean}
+ * @param {Change} change
*/
- hasMark = (type) => {
- const { state } = this.state
- return state.marks.some(mark => mark.type === type)
- }
-
- /**
- * Check if the any of the currently selected blocks are of `type`.
- *
- * @param {String} type
- * @return {Boolean}
- */
-
- hasBlock = (type) => {
- const { state } = this.state
- return state.blocks.some(node => node.type === type)
- }
-
- /**
- * On change, save the new state.
- *
- * @param {State} state
- */
-
- onChange = (state) => {
+ onChange = ({value}) => {
let newState = {
- state: state,
+ value: value,
startedAt: this.state.startedAt
};
- const isEmpty = state.document.length === 0;
+ const isEmpty = value.document.length === 0;
// Reset timers when the text is empty
if (isEmpty) {
@@ -158,16 +158,40 @@
}
}
+ /**
+ * Check if the current selection has a mark with `type` in it.
+ *
+ * @param {String} type
+ * @return {Boolean}
+ */
+
+ hasMark = type => {
+ const { value } = this.state
+ return value.activeMarks.some(mark => mark.type === type)
+}
+
+ /**
+ * Check if the any of the currently selected blocks are of `type`.
+ *
+ * @param {String} type
+ * @return {Boolean}
+ */
+
+ hasBlock = type => {
+ const { value } = this.state
+ return value.blocks.some(node => node.type === type)
+}
+
asPlain = () => {
- return Plain.serialize(this.state.state);
+ return Plain.serialize(this.state.value);
}
asRaw = () => {
- return Raw.serialize(this.state.state);
+ return JSON.stringify(this.state.value.toJSON());
}
asHtml = () => {
- return HtmlSerializer.serialize(this.state.state);
+ return HtmlSerializer.serialize(this.state.value);
}
asCategories = () => {
@@ -180,8 +204,8 @@
}
clear = () => {
- const state = Plain.deserialize('');
- this.onChange(state);
+ const value = Plain.deserialize('');
+ this.onChange({value});
}
focus = () => {
@@ -193,46 +217,48 @@
*
* @param {Event} e
* @param {Object} data
- * @param {State} state
- * @return {State}
+ * @param {Change} change
+ * @return {Change}
*/
- onKeyDown = (e, data, state) => {
+ onKeyDown = (e, change) => {
+ // if (data.key === 'enter' && this.props.isChecked && typeof this.props.onEnterKeyDown === 'function') {
+
+ // e.preventDefault();
+ // this.props.onEnterKeyDown();
+
+ // return change;
+ // }
+
+ // if (!data.isMod) return
+ if (!e.ctrlKey) return
+ // Decide what to do based on the key code...
+ switch (e.key) {
+ // When "B" is pressed, add a "bold" mark to the text.
+ case 'b': {
+ e.preventDefault()
+ change.toggleMark('bold')
- if (data.key === 'enter' && this.props.isChecked && typeof this.props.onEnterKeyDown === 'function') {
- e.preventDefault();
- this.props.onEnterKeyDown();
+ return true
+ }
+ case 'i': {
+ // When "U" is pressed, add an "italic" mark to the text.
+ e.preventDefault()
+ change.toggleMark('italic')
- return state;
+ return true
+ }
+ case 'u': {
+ // When "U" is pressed, add an "underline" mark to the text.
+ e.preventDefault()
+ change.toggleMark('underlined')
+
+ return true
+ }
+ }
}
- if (!data.isMod) return
- let mark
-
- switch (data.key) {
- case 'b':
- mark = 'bold'
- break
- case 'i':
- mark = 'italic'
- break
- case 'u':
- mark = 'underlined'
- break
- default:
- return
- }
-
- state = state
- .transform()
- .toggleMark(mark)
- .apply()
-
- e.preventDefault()
- return state
- }
-
- /**
+ /**
* When a mark button is clicked, toggle the current mark.
*
* @param {Event} e
@@ -240,10 +266,10 @@
*/
onClickMark = (e, type) => {
- e.preventDefault()
- const { state } = this.state
+
+ e.preventDefault()
+ const { value } = this.state
let { categories } = this.state
- const transform = state.transform()
let isPortalOpen = false;
@@ -251,24 +277,26 @@
// Can't use toggleMark here, because it expects the same object
// @see https://github.com/ianstormtaylor/slate/issues/873
if (this.hasMark('category')) {
- const categoryMarks = state.marks.filter(mark => mark.type === 'category')
+ const categoryMarks = value.activeMarks.filter(mark => mark.type === 'category')
categoryMarks.forEach(mark => {
const key = mark.data.get('key');
const text = mark.data.get('text');
categories = this.removeCategory(categories, key, text)
- transform.removeMark(mark)
+ const change = value.change().removeMark(mark)
+ this.onChange(change)
})
} else {
isPortalOpen = !this.state.isPortalOpen;
}
} else {
- transform.toggleMark(type)
+ const change = value.change().toggleMark(type)
+ this.onChange(change)
}
this.setState({
- state: transform.apply(),
+ state: value.change,
isPortalOpen: isPortalOpen,
categories: categories
})
@@ -283,9 +311,9 @@
onClickBlock = (e, type) => {
e.preventDefault()
- let { state } = this.state
- const transform = state.transform()
- const { document } = state
+ const { value } = this.state
+ const change = value.change()
+ const { document } = value
// Handle everything but list buttons.
if (type !== 'bulleted-list' && type !== 'numbered-list') {
@@ -293,43 +321,46 @@
const isList = this.hasBlock('list-item')
if (isList) {
- transform
- .setBlock(isActive ? DEFAULT_NODE : type)
+ change
+ .setBlocks(isActive ? DEFAULT_NODE : type)
.unwrapBlock('bulleted-list')
.unwrapBlock('numbered-list')
}
else {
- transform
- .setBlock(isActive ? DEFAULT_NODE : type)
+ change
+ .setBlocks(isActive ? DEFAULT_NODE : type)
}
}
// Handle the extra wrapping required for list buttons.
else {
const isList = this.hasBlock('list-item')
- const isType = state.blocks.some((block) => {
+ const isType = value.blocks.some((block) => {
return !!document.getClosest(block.key, parent => parent.type === type)
})
if (isList && isType) {
- transform
- .setBlock(DEFAULT_NODE)
+ change
+ .setBlocks(DEFAULT_NODE)
.unwrapBlock('bulleted-list')
.unwrapBlock('numbered-list')
+
} else if (isList) {
- transform
+ change
.unwrapBlock(type === 'bulleted-list' ? 'numbered-list' : 'bulleted-list')
.wrapBlock(type)
+
} else {
- transform
- .setBlock('list-item')
+ change
+ .setBlocks('list-item')
.wrapBlock(type)
+
}
}
- state = transform.apply()
- this.setState({ state })
+
+ this.onChange(change)
}
onPortalOpen = (portal) => {
@@ -338,25 +369,24 @@
}
onPortalClose = (portal) => {
- let { state } = this.state
- const transform = state.transform();
+ let { value } = this.state
this.setState({
- state: transform.apply(),
+ value: value.change,
isPortalOpen: false
})
}
onCategoryClick = (category) => {
- const { state, currentSelectionText, currentSelectionStart, currentSelectionEnd } = this.state;
+ const { value, currentSelectionText, currentSelectionStart, currentSelectionEnd } = this.state;
+ const change = value.change()
let { categories } = this.state;
- const transform = state.transform();
- const categoryMarks = state.marks.filter(mark => mark.type === 'category')
- categoryMarks.forEach(mark => transform.removeMark(mark));
+ const categoryMarks = value.activeMarks.filter(mark => mark.type === 'category')
+ categoryMarks.forEach(mark => change.removeMark(mark));
- transform.addMark({
+ change.addMark({
type: 'category',
data: {
text: currentSelectionText,
@@ -368,6 +398,7 @@
key: category.key
}
})
+ this.onChange(change)
Object.assign(category, {
text: currentSelectionText,
@@ -379,7 +410,7 @@
categories = categories.push(category);
this.setState({
- state: transform.apply(),
+ value: value,
isPortalOpen: false,
categories: categories
});
@@ -405,10 +436,12 @@
render = () => {
return (
- <div>
+ <div className="bg-secondary mb-5">
+ <div className="sticky-top">
{this.renderToolbar()}
+ </div>
{this.renderEditor()}
- </div>
+ </div>
)
}
@@ -420,25 +453,26 @@
renderToolbar = () => {
return (
- <div className="menu toolbar-menu">
- {this.renderMarkButton('bold', 'format_bold')}
- {this.renderMarkButton('italic', 'format_italic')}
- {this.renderMarkButton('underlined', 'format_underlined')}
- {this.renderMarkButton('category', 'label')}
+ <div className="menu toolbar-menu d-flex bg-secondary">
+ {this.renderMarkButton('bold', 'format_bold')}
+ {this.renderMarkButton('italic', 'format_italic')}
+ {this.renderMarkButton('underlined', 'format_underlined')}
+ {this.renderMarkButton('category', 'label')}
- {this.renderBlockButton('numbered-list', 'format_list_numbered')}
- {this.renderBlockButton('bulleted-list', 'format_list_bulleted')}
- {this.renderToolbarButtons()}
+ {this.renderBlockButton('numbered-list', 'format_list_numbered')}
+ {this.renderBlockButton('bulleted-list', 'format_list_bulleted')}
+
+ {this.renderToolbarButtons()}
</div>
)
}
renderToolbarCheckbox = () => {
return (
- <div className="checkbox">
- <label>
- <input type="checkbox" checked={this.props.isChecked} onChange={this.onCheckboxChange} /> <kbd>Entrée</kbd>= Ajouter une note
+ <div className="checkbox float-right">
+ <label className="mr-2">
+ <input type="checkbox" checked={this.props.isChecked} onChange={this.onCheckboxChange} /><small className="text-muted ml-1"> Appuyer sur <kbd className="bg-danger text-muted ml-1">Entrée</kbd> pour ajouter une note</small>
</label>
</div>
)
@@ -447,10 +481,10 @@
renderToolbarButtons = () => {
return (
<div>
- { !this.props.note && this.renderToolbarCheckbox() }
- <button type="button" className="btn btn-primary btn-lg" disabled={this.props.isButtonDisabled} onClick={this.onButtonClick}>
+ <button type="button" className="btn btn-primary btn-sm text-secondary float-right mr-5" disabled={this.props.isButtonDisabled} onClick={this.onButtonClick}>
{ this.props.note ? 'Save note' : 'Ajouter' }
</button>
+ { !this.props.note && this.renderToolbarCheckbox() }
</div>
);
}
@@ -467,13 +501,30 @@
const isActive = this.hasMark(type)
const onMouseDown = e => this.onClickMark(e, type)
+
return (
- <span className="button" onMouseDown={onMouseDown} data-active={isActive}>
+ <span className="button text-primary" onMouseDown={onMouseDown} data-active={isActive}>
<span className="material-icons">{icon}</span>
</span>
)
}
+ // Add a `renderMark` method to render marks.
+
+ renderMark = props => {
+ const { children, mark, attributes } = props
+
+ switch (mark.type) {
+ case 'bold':
+ return <strong {...attributes}>{children}</strong>
+ case 'code':
+ return <code {...attributes}>{children}</code>
+ case 'italic':
+ return <em {...attributes}>{children}</em>
+ case 'underlined':
+ return <ins {...attributes}>{children}</ins>
+ }
+ }
/**
* Render a block-toggling toolbar button.
*
@@ -483,16 +534,41 @@
*/
renderBlockButton = (type, icon) => {
- const isActive = this.hasBlock(type)
+ let isActive = this.hasBlock(type)
+
+ if (['numbered-list', 'bulleted-list'].includes(type)) {
+ const { value } = this.state
+ const parent = value.document.getParent(value.blocks.first().key)
+ isActive = this.hasBlock('list-item') && parent && parent.type === type
+ }
const onMouseDown = e => this.onClickBlock(e, type)
return (
- <span className="button" onMouseDown={onMouseDown} data-active={isActive}>
+ <span className="button text-primary" onMouseDown={onMouseDown} data-active={isActive}>
<span className="material-icons">{icon}</span>
</span>
)
}
+ renderNode = props => {
+ const { attributes, children, node } = props
+
+ switch (node.type) {
+ case 'block-quote':
+ return <blockquote {...attributes}>{children}</blockquote>
+ case 'bulleted-list':
+ return <ul {...attributes}>{children}</ul>
+ case 'heading-one':
+ return <h1 {...attributes}>{children}</h1>
+ case 'heading-two':
+ return <h2 {...attributes}>{children}</h2>
+ case 'list-item':
+ return <li {...attributes}>{children}</li>
+ case 'numbered-list':
+ return <ol {...attributes}>{children}</ol>
+ }
+}
+
/**
* Render the Slate editor.
*
@@ -501,19 +577,24 @@
renderEditor = () => {
return (
+ <div className="editor-wrapper sticky-bottom p-2">
<div className="editor-slatejs">
{this.renderHoveringMenu()}
<Editor
ref="editor"
spellCheck
- placeholder={'Enter some rich text...'}
+ autoFocus
+ placeholder={'Votre espace de prise de note...'}
schema={schema}
plugins={plugins}
- state={this.state.state}
+ value={this.state.value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
+ renderMark={this.renderMark}
+ renderNode = {this.renderNode}
/>
</div>
+ </div>
)
}
--- a/client/yarn.lock Wed Sep 05 13:48:10 2018 +0200
+++ b/client/yarn.lock Tue Sep 25 02:02:13 2018 +0200
@@ -28,6 +28,33 @@
"@types/prop-types" "*"
csstype "^2.2.0"
+"@types/slate-html-serializer@^0.6.2":
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/@types/slate-html-serializer/-/slate-html-serializer-0.6.2.tgz#7e4bf258bb52489cd392a8f26c2fe04eb97c7cfc"
+ dependencies:
+ "@types/react" "*"
+ "@types/slate" "*"
+
+"@types/slate-plain-serializer@^0.5.1":
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/@types/slate-plain-serializer/-/slate-plain-serializer-0.5.1.tgz#69f27bafbfc7c00739470c1ac4a066c42f6908ce"
+ dependencies:
+ "@types/slate" "*"
+
+"@types/slate-react@^0.18.0":
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/@types/slate-react/-/slate-react-0.18.0.tgz#f883a161888dd9bc18db23c4d1bd66b7ae9a2bb7"
+ dependencies:
+ "@types/react" "*"
+ "@types/slate" "*"
+ immutable "^3.8.2"
+
+"@types/slate@*":
+ version "0.40.0"
+ resolved "https://registry.yarnpkg.com/@types/slate/-/slate-0.40.0.tgz#cc1288bd3d7ba56444fecbab5a7c292d101d263f"
+ dependencies:
+ immutable "^3.8.2"
+
JSONStream@^1.3.2:
version "1.3.4"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.4.tgz#615bb2adb0cd34c8f4c447b5f6512fa1d8f16a2e"
@@ -1203,10 +1230,6 @@
version "4.1.3"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.3.tgz#0eb371af2c8448e8c210411d0cb824a6409a12be"
-bootstrap@^4.1.3:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.3.tgz#0eb371af2c8448e8c210411d0cb824a6409a12be"
-
boxen@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
@@ -2185,7 +2208,7 @@
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
-debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.2, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
+debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
@@ -2593,7 +2616,7 @@
es5-ext "^0.10.35"
es6-symbol "^3.1.1"
-es6-map@^0.1.3, es6-map@^0.1.4:
+es6-map@^0.1.3:
version "0.1.5"
resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0"
dependencies:
@@ -3908,7 +3931,7 @@
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
-immutable@^3.8.1:
+immutable@^3.8.1, immutable@^3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
@@ -4139,10 +4162,6 @@
version "1.0.3"
resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
-is-empty@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/is-empty/-/is-empty-1.2.0.tgz#de9bb5b278738a05a0b09a57e1fb4d4a341a9f6b"
-
is-equal-shallow@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
@@ -4201,6 +4220,10 @@
dependencies:
is-extglob "^2.1.1"
+is-hotkey@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.3.tgz#8a129eec16f3941bd4f37191e02b9c3e91950549"
+
is-in-browser@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
@@ -4374,6 +4397,10 @@
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+isomorphic-base64@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/isomorphic-base64/-/isomorphic-base64-1.0.2.tgz#f426aae82569ba8a4ec5ca73ad21a44ab1ee7803"
+
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
@@ -4811,10 +4838,6 @@
version "2.2.0"
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79"
-keycode@^2.1.2:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
-
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -5114,6 +5137,10 @@
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+lodash@^4.1.1:
+ version "4.17.11"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+
longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
@@ -5257,6 +5284,10 @@
dependencies:
mimic-fn "^1.0.0"
+memoize-one@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.0.2.tgz#3fb8db695aa14ab9c0f1644e1585a8806adc1aee"
+
memory-fs@^0.4.0, memory-fs@~0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -7085,6 +7116,10 @@
settle-promise "1.0.0"
source-map "0.5.6"
+react-immutable-proptypes@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#023d6f39bb15c97c071e9e60d00d136eac5fa0b4"
+
react-is@^16.3.2:
version "16.4.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.2.tgz#84891b56c2b6d9efdee577cc83501dfc5ecead88"
@@ -7943,24 +7978,72 @@
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
-slate@^0.20.1:
- version "0.20.7"
- resolved "https://registry.yarnpkg.com/slate/-/slate-0.20.7.tgz#083ca9074dc7fd3ad8863985e6d92ed76bdc9eff"
- dependencies:
- cheerio "^0.22.0"
- debug "^2.3.2"
+slate-base64-serializer@^0.2.63:
+ version "0.2.63"
+ resolved "https://registry.yarnpkg.com/slate-base64-serializer/-/slate-base64-serializer-0.2.63.tgz#b086dfce5145c29b8465dc54ff5493b726ddca07"
+ dependencies:
+ isomorphic-base64 "^1.0.2"
+
+slate-dev-environment@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/slate-dev-environment/-/slate-dev-environment-0.2.0.tgz#c43f4a5e13cccc16ad4c3015c8ad182b6e5b5b3a"
+ dependencies:
+ is-in-browser "^1.1.3"
+
+slate-dev-warning@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/slate-dev-warning/-/slate-dev-warning-0.0.1.tgz#f6c36731babea5e301b5bd504fe64911dd24200a"
+
+slate-hotkeys@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/slate-hotkeys/-/slate-hotkeys-0.2.3.tgz#843a467421c643b4a1a3c240957c8adcfa99deb3"
+ dependencies:
+ is-hotkey "^0.1.3"
+ slate-dev-environment "^0.2.0"
+
+slate-html-serializer@^0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/slate-html-serializer/-/slate-html-serializer-0.7.2.tgz#8b47cc24ece9b99be0d73bd5d55aeb3a66db73cb"
+ dependencies:
+ type-of "^2.0.1"
+
+slate-plain-serializer@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/slate-plain-serializer/-/slate-plain-serializer-0.6.2.tgz#8d406f9523d7ba219d14bf300d30d7e093430247"
+
+slate-prop-types@^0.4.61:
+ version "0.4.61"
+ resolved "https://registry.yarnpkg.com/slate-prop-types/-/slate-prop-types-0.4.61.tgz#141c109bed81b130dd03ab86dd7541b28d6d962a"
+
+slate-react@^0.18.5:
+ version "0.18.5"
+ resolved "https://registry.yarnpkg.com/slate-react/-/slate-react-0.18.5.tgz#089ae30cd19b7600a9b528b5ab0525038b500142"
+ dependencies:
+ debug "^3.1.0"
+ get-window "^1.1.1"
+ is-window "^1.0.2"
+ lodash "^4.1.1"
+ memoize-one "^4.0.0"
+ prop-types "^15.5.8"
+ react-immutable-proptypes "^2.1.0"
+ selection-is-backward "^1.0.0"
+ slate-base64-serializer "^0.2.63"
+ slate-dev-environment "^0.2.0"
+ slate-dev-warning "^0.0.1"
+ slate-hotkeys "^0.2.3"
+ slate-plain-serializer "^0.6.2"
+ slate-prop-types "^0.4.61"
+
+slate@^0.40.2:
+ version "0.40.2"
+ resolved "https://registry.yarnpkg.com/slate/-/slate-0.40.2.tgz#3adbd4bb66c16208b2dc3f0900b1857ea7912cb0"
+ dependencies:
+ debug "^3.1.0"
direction "^0.1.5"
- es6-map "^0.1.4"
esrever "^0.2.0"
- get-window "^1.1.1"
- immutable "^3.8.1"
- is-empty "^1.0.0"
- is-in-browser "^1.1.3"
- is-window "^1.0.2"
- keycode "^2.1.2"
- prop-types "^15.5.8"
- react-portal "^3.1.0"
- selection-is-backward "^1.0.0"
+ is-plain-object "^2.0.4"
+ lodash "^4.17.4"
+ slate-dev-warning "^0.0.1"
type-of "^2.0.1"
slice-ansi@0.0.4: