Change the settings to avoid using Session authentication for rest framework as it raise exceptions in case client and backend are on the same domain
On the filter, adapt to take into account new version of django_filters
import { Value } from 'slate';
import Plain from 'slate-plain-serializer';
import { Editor } from 'slate-react';
import React from 'react';
import { withNamespaces } from 'react-i18next';
import { connect } from 'react-redux';
import * as R from 'ramda';
import HtmlSerializer from './HtmlSerializer';
import './SlateEditor.css';
import { now } from '../../utils';
import Toolbar from './Toolbar';
import { getAutoSubmit } from '../../selectors/authSelectors';
/**
*
* @type {Component}
*/
class SlateEditor extends React.Component {
/**
* Deserialize the initial editor state.
*
* @type {Object}
*/
constructor(props) {
super(props);
this.state = {
value: props.note ? Value.fromJSON(JSON.parse(props.note.raw)) : Plain.deserialize(''),
startedAt: null,
finishedAt: null,
enterKeyValue: 0,
};
this.editorRef = React.createRef();
}
get editor() {
if(this.editorRef) {
return this.editorRef.current;
}
return null;
}
componentDidMount = () => {
this.focus();
}
/**
* On change, save the new state.
*
* @param {Change} change
*/
onChange = ({value, operations}) => {
const oldState = R.clone(this.state);
const newState = {
value
};
(operations || []).some((op) => {
if(['insert_text', 'remove_text', 'add_mark', 'remove_mark', 'set_mark', 'insert_node', 'merge_node', 'move_node', 'remove_node', 'set_node', 'split_node'].indexOf(op.type)>=0) {
const tsnow = now();
if(this.state.startedAt == null) {
newState.startedAt = tsnow;
}
newState.finishedAt = tsnow;
return true;
}
return false;
});
this.setState(newState, () => {
if (typeof this.props.onChange === 'function') {
this.props.onChange(R.clone(this.state), oldState, {value, operations});
}
})
}
asPlain = () => {
return Plain.serialize(this.state.value);
}
asRaw = () => {
return JSON.stringify(this.state.value.toJSON());
}
asHtml = () => {
return HtmlSerializer.serialize(this.state.value);
}
asCategories = () => {
return this.state.value.document.getMarksByType('category').map((mark) => mark.data.toJS()).toArray();
}
clear = () => {
const value = Plain.deserialize('');
this.setState({
value,
enterKeyValue: 0
})
}
focus = () => {
if(this.editor) {
this.editor.focus();
}
}
submitNote = () => {
this.setState({ enterKeyValue: 0 }, () => {
if (typeof this.props.submitNote === 'function') {
this.props.submitNote();
}
});
}
/**
* On key down, if it's a formatting command toggle a mark.
*
* @param {Event} e
* @param {Change} change
* @return {Change}
*/
onKeyUp = (e, editor, next) => {
const { value } = this.state;
const noteText = value.document.text.trim();
if(e.key === "Enter" && noteText.length !== 0) {
this.setState({ enterKeyValue: this.state.enterKeyValue + 1 });
} else if ( e.getModifierState() || (e.key !== "Control" && e.key !== "Shift" && e.key !== "Meta" && e.key !== "Alt") ) {
this.setState({ enterKeyValue: 0 });
}
return next();
}
/**
* On key down, if it's a formatting command toggle a mark.
*
* @param {Event} e
* @param {Change} change
* @return {Change}
*/
onKeyDown = (e, editor, next) => {
const { value, enterKeyValue } = this.state;
const { autoSubmit } = this.props;
const noteText = value.document.text.trim();
// we prevent empty first lines
if(e.key === "Enter" && noteText.length === 0) {
e.preventDefault();
return next();
}
// Enter submit the note
if(e.key === "Enter" && ( enterKeyValue === 2 || e.ctrlKey || autoSubmit ) && noteText.length !== 0) {
e.preventDefault();
this.submitNote();
return next();
}
if (!e.ctrlKey) {
return next();
}
e.preventDefault();
// Decide what to do based on the key code...
switch (e.key) {
default: {
break;
}
// When "B" is pressed, add a "bold" mark to the text.
case 'b': {
editor.toggleMark('bold');
break;
}
case 'i': {
// When "U" is pressed, add an "italic" mark to the text.
editor.toggleMark('italic');
break;
}
case 'u': {
// When "U" is pressed, add an "underline" mark to the text.
editor.toggleMark('underlined');
break;
}
}
return next();
}
// Add a `renderMark` method to render marks.
renderMark = (props, editor, next) => {
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>
case 'category':
let spanStyle = {
backgroundColor: mark.data.get('color')
};
return <span {...attributes} style={ spanStyle } >{children}</span>
default:
return next();
}
}
renderNode = (props, editor, next) => {
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>
default:
return next();
}
}
/**
* Render.
*
* @return {Element}
*/
render = () => (
<div className="bg-secondary mb-5">
<div className="sticky-top">
<Toolbar
value={this.state.value}
editor={this.editor}
note={this.props.note}
annotationCategories={this.props.annotationCategories}
isButtonDisabled={this.props.isButtonDisabled}
submitNote={this.submitNote}
/>
</div>
<div className="editor-slatejs p-2">
<Editor
ref={this.editorRef}
spellCheck
placeholder={this.props.t('slate_editor.placeholder')}
value={this.state.value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp}
renderMark={this.renderMark}
renderNode = {this.renderNode}
/>
</div>
</div>
);
}
/**
* Export.
*/
function mapStateToProps(state, props) {
const autoSubmit = getAutoSubmit(state);
return {
autoSubmit,
};
}
export default withNamespaces("", {
innerRef: (ref) => {
if(!ref) {
return;
}
const wrappedRef = ref.getWrappedInstance();
const editorRef = (wrappedRef && wrappedRef.props) ? wrappedRef.props.editorRef : null;
if(editorRef && editorRef.hasOwnProperty('current')) {
editorRef.current = wrappedRef;
}
}
})(connect(mapStateToProps, null, null, { withRef: true })(SlateEditor));