/*
* This file is part of ORY Editor.
*
* ORY Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ORY Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ORY Editor. If not, see .
*
* @license LGPL-3.0
* @copyright 2016-2018 Aeneas Rekkas
* @author Aeneas Rekkas
*
*/
import { createMuiTheme } from '@material-ui/core/styles';
import * as React from 'react';
import { Portal } from 'react-portal';
import isHotkey from 'is-hotkey';
import { NextType } from '../types/next';
import { Editor, getEventTransfer } from 'slate-react';
import { BottomToolbar, ThemeProvider } from 'ory-editor-ui';
import { placeholder } from '../const';
import debounce from 'lodash.debounce';
import { html as serializer } from '../hooks';
import { SlateProps } from './../types/component';
import { Value, Editor as CoreEditor } from 'slate';
import { Cancelable } from 'lodash';
const theme = createMuiTheme({
palette: {
type: 'dark',
},
typography: {
useNextVariants: true,
},
});
export interface SlateState {
editorState?: Value;
}
class Slate extends React.Component {
private toolbar: React.RefObject;
private editor: React.RefObject;
private flushStateDebounced: (() => void) & Cancelable;
constructor(props: SlateProps) {
super(props);
this.state = {};
this.editor = React.createRef();
this.toolbar = React.createRef();
this.flushStateDebounced = debounce(this.flushState, 1000, {
leading: true,
trailing: true,
maxWait: 10000,
});
}
componentDidMount = () => {
this.updateToolbar();
}
flushState = () => {
if (this.state.editorState) {
this.props.onChange({ editorState: this.state.editorState });
}
}
getState() {
return this.state.editorState !== undefined
? this.state.editorState
: this.props.state.editorState;
}
onStateChange = ({ value }: { value: Value }) => {
this.setState(
{
editorState: value,
},
() => {
this.updateToolbar();
}
);
this.flushStateDebounced();
}
updateToolbar = () => {
const editorState = this.getState();
const toolbar = this.toolbar.current;
if (
!toolbar ||
editorState.selection.isBlurred ||
editorState.selection.isCollapsed
) {
return;
}
let s = window.getSelection();
let oRange = s.getRangeAt(0); // get the text range
let oRect = oRange.getBoundingClientRect();
if (oRect) {
const { left, top, width } = oRect;
toolbar.style.opacity = '1';
toolbar.style.top = `${top + window.scrollY - toolbar.offsetHeight}px`;
toolbar.style.left = `${left +
window.scrollX -
toolbar.offsetWidth / 2 +
width / 2}px`;
}
}
onPaste = (e: Event, editor: CoreEditor, next: NextType) => {
const transfer = getEventTransfer(e);
if (transfer.type !== 'html') {
return next();
}
// tslint:disable-next-line:no-any
const { document } = serializer.deserialize((transfer as any).html);
return editor.insertFragment(document);
}
onKeyDown = (
e: KeyboardEvent,
editor: CoreEditor,
next: NextType
): boolean => {
// we need to prevent slate from handling undo and redo
if (isHotkey(['mod+z', 'mod+y'], e)) {
this.setState({ editorState: undefined });
return true;
}
if (isHotkey('shift+enter', e)) {
e.preventDefault();
editor.insertText('\n');
return true;
}
return next();
}
render() {
const {
focused,
readOnly,
plugins,
HoverButtons,
ToolbarButtons,
} = this.props;
const editorState = this.getState();
const isOpened =
editorState.selection.isExpanded && editorState.selection.isFocused;
return (
{focused && (
{/* ory-prevent-blur is required to prevent global blurring */}
)}
}
onChange={this.onStateChange}
onKeyDown={this.onKeyDown}
readOnly={Boolean(readOnly)}
className="ory-plugins-content-slate-container"
// onBlur={this.onBlur}
// onFocus={this.onFocus}
value={editorState}
plugins={plugins}
onPaste={this.onPaste}
placeholder={placeholder}
/>
{readOnly ? null : (
)}
);
}
}
export default Slate;