@edtr-io/plugin-text
Version:
1,756 lines (1,537 loc) • 85.2 kB
JavaScript
import { styled, usePluginTheme, createPluginTheme } from '@edtr-io/ui';
import { createElement, Component, Fragment, useRef, useState, useEffect, useContext, useMemo, useCallback, createRef } from 'react';
import { Data, Value, Range } from 'slate';
import { EdtrIcon, edtrTextControls, InlineSettings, InlineInput, Icon, faExternalLinkAlt, styled as styled$1, createIcon, faParagraph, Overlay, HoveringOverlay, InlineCheckbox, faQuestionCircle, BottomToolbar } from '@edtr-io/editor-ui';
import { find, times } from 'ramda';
import { canUseDOM } from 'exenv';
import isHotkey, { isHotkey as isHotkey$1 } from 'is-hotkey';
import { useEditorFocus, useStore, OverlayContext, selectors, StateType } from '@edtr-io/core';
import List from '@convertkit/slate-lists';
import { findNode, Editor } from 'slate-react';
import KaTeX from 'katex';
import { HotKeys } from 'react-hotkeys';
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
var colorMark = '@splish-me/color';
var getActiveMarks = function getActiveMarks(editor) {
return editor.value.document.getActiveMarksAtRange(getTrimmedSelectionRange(editor));
};
var createIsColor = function createIsColor(colorIndex) {
return function (editor) {
return getActiveMarks(editor).some(function (mark) {
if (!mark) {
return false;
}
if (typeof colorIndex === 'undefined') {
return mark.type === colorMark;
}
return mark.type === colorMark && mark.data.get('colorIndex') == colorIndex;
});
};
};
var removeColor = function removeColor(editor) {
return editor.value.marks.toArray().filter(function (mark) {
return mark.type === colorMark;
}).reduce(function (changedEditor, mark) {
return editor.removeMark(mark);
}, editor);
};
var createToggleColor = function createToggleColor(colorIndex) {
return function (editor) {
if (createIsColor(colorIndex)(editor)) {
return removeColor(editor);
}
return removeColor(editor).addMark({
type: colorMark,
data: {
colorIndex: colorIndex
}
});
};
};
var getColorIndex = function getColorIndex(editor) {
if (!createIsColor()(editor)) {
return undefined;
} else {
var mark = getActiveMarks(editor).find(function (mark) {
return mark ? mark.type === colorMark : false;
});
return mark.data.get('colorIndex');
}
};
var Color =
/*#__PURE__*/
styled.span(function (props) {
var theme = createTextPluginTheme(name, props.theme);
var colors = theme.plugins.colors.colors;
return {
color: colors[props.colorIndex % colors.length]
};
});
var DefaultEditorComponent =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(DefaultEditorComponent, _React$Component);
function DefaultEditorComponent() {
return _React$Component.apply(this, arguments) || this;
}
var _proto = DefaultEditorComponent.prototype;
_proto.render = function render() {
var _this$props = this.props,
attributes = _this$props.attributes,
children = _this$props.children,
colorIndex = _this$props.colorIndex;
return createElement(Color, Object.assign({
colorIndex: colorIndex
}, attributes), children);
};
return DefaultEditorComponent;
}(Component);
var DefaultRendererComponent =
/*#__PURE__*/
function (_React$Component2) {
_inheritsLoose(DefaultRendererComponent, _React$Component2);
function DefaultRendererComponent() {
return _React$Component2.apply(this, arguments) || this;
}
var _proto2 = DefaultRendererComponent.prototype;
_proto2.render = function render() {
var _this$props2 = this.props,
children = _this$props2.children,
colorIndex = _this$props2.colorIndex;
return createElement(Color, {
colorIndex: colorIndex
}, children);
};
return DefaultRendererComponent;
}(Component);
var createColorPlugin = function createColorPlugin(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
_ref$EditorComponent = _ref.EditorComponent,
EditorComponent = _ref$EditorComponent === void 0 ? DefaultEditorComponent : _ref$EditorComponent,
_ref$RenderComponent = _ref.RenderComponent,
RenderComponent = _ref$RenderComponent === void 0 ? DefaultRendererComponent : _ref$RenderComponent;
return function () {
// TODO: deserialize
return {
serialize: function serialize(obj, children) {
var mark = obj;
if (mark.object === 'mark') {
var colorIndex = mark.data.get('colorIndex');
return createElement(RenderComponent, {
mark: mark,
colorIndex: colorIndex
}, children);
}
},
renderMark: function renderMark(props, _editor, next) {
var mark = props.mark;
if (mark.object === 'mark' && mark.type === colorMark) {
var colorIndex = mark.data.get('colorIndex');
return createElement(EditorComponent, Object.assign({
colorIndex: colorIndex
}, props));
}
return next();
}
};
};
};
var Button =
/*#__PURE__*/
styled.button(function (props) {
var theme = createTextPluginTheme(props.name, props.theme);
return {
backgroundColor: props.active ? theme.active.backgroundColor : theme.backgroundColor,
cursor: 'pointer',
boxShadow: props.active ? 'inset 0 1px 3px 0 rgba(0,0,0,0.50)' : undefined,
color: props.active ? theme.active.color : theme.color,
outline: 'none',
height: '25px',
border: 'none',
borderRadius: '4px',
margin: '5px',
padding: '0px',
width: '25px',
'&:hover': {
color: theme.hoverColor
}
};
});
var ColorControls = function ColorControls(props) {
var theme = usePluginTheme(props.name, textPluginThemeFactory);
var _theme$plugins$colors = theme.plugins.colors,
colors = _theme$plugins$colors.colors,
defaultColor = _theme$plugins$colors.defaultColor;
return createElement(Fragment, null, createElement(Button, {
active: !createIsColor()(props.editor),
name: props.name,
onClick: function onClick() {
removeColor(props.editor).moveToEnd().focus();
props.switchControls(VisibleControls.All);
props.onChange(props.editor);
},
title: "Farbe zur\xFCcksetzen"
}, createElement(ColorPaletteIcon, {
color: defaultColor
})), colors.map(function (color, index) {
return createElement(Button, {
key: index,
active: createIsColor(index)(props.editor),
name: props.name,
onClick: function onClick() {
trimSelection(props.editor);
createToggleColor(index)(props.editor).moveToEnd().focus();
props.switchControls(VisibleControls.All);
props.onChange(props.editor);
},
title: "Einf\xE4rben"
}, createElement(ColorPaletteIcon, {
color: color
}));
}), createElement(Button, {
name: props.name,
onClick: function onClick() {
return props.switchControls(VisibleControls.All);
},
title: "Untermen\xFC schlie\xDFen"
}, createElement(EdtrIcon, {
icon: edtrTextControls.close
})));
};
var ColorPaletteIcon =
/*#__PURE__*/
styled.div(function (props) {
return {
display: 'inline-block',
backgroundColor: props.color,
borderRadius: ' 100%',
width: '19px',
height: '19px',
margin: '3px',
verticalAlign: 'middle'
};
});
var ColoredText =
/*#__PURE__*/
styled.span({
position: 'relative',
verticalAlign: 'middle',
display: 'inline-block'
});
var FlexContainer =
/*#__PURE__*/
styled.span({
display: 'flex',
alignItems: 'center',
flexDirection: 'column'
});
var Line =
/*#__PURE__*/
styled.span(function (props) {
var theme = createTextPluginTheme(name, props.theme);
var _theme$plugins$colors2 = theme.plugins.colors,
colors = _theme$plugins$colors2.colors,
defaultColor = _theme$plugins$colors2.defaultColor;
return {
border: "2px solid " + (props.index === undefined ? defaultColor : colors[props.index % colors.length]),
borderRadius: '4px',
bottom: '0',
width: '80%',
position: 'absolute'
};
});
var ColoredTextIcon = function ColoredTextIcon(props) {
return createElement(ColoredText, null, createElement(FlexContainer, null, createElement(EdtrIcon, {
icon: edtrTextControls.colorText
}), createElement(Line, {
index: props.index
})));
};
var isBlockquote = function isBlockquote(editor, pluginClosure) {
return !!(pluginClosure.current && pluginClosure.current.parent && pluginClosure.current.parent.name === 'blockquote');
};
var createBlockquote = function createBlockquote(editor, name) {
editor.command('replaceWithPlugin', {
plugin: 'blockquote',
state: {
plugin: name,
state: editor.value.toJSON()
}
});
};
var removeBlockquote = function removeBlockquote(editor, pluginClosure) {
if (pluginClosure.current && isBlockquote(editor, pluginClosure)) {
return editor.command('unwrapParent');
}
};
var createHeadingNode = function createHeadingNode(level) {
return "@splish-me/h" + level;
};
var Heading = function Heading(_ref) {
var level = _ref.level,
children = _ref.children,
props = _objectWithoutPropertiesLoose(_ref, ["level", "children"]);
if (level <= 6 && level >= 1) return createElement("h" + level, props, children);else return createElement("h6", Object.assign({}, props), children);
};
var DefaultEditorComponent$1 =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(DefaultEditorComponent, _React$Component);
function DefaultEditorComponent() {
return _React$Component.apply(this, arguments) || this;
}
var _proto = DefaultEditorComponent.prototype;
_proto.render = function render() {
var _this$props = this.props,
attributes = _this$props.attributes,
children = _this$props.children,
level = _this$props.level;
return createElement(Heading, Object.assign({
level: level
}, attributes), children);
};
return DefaultEditorComponent;
}(Component);
var DefaultRenderComponent =
/*#__PURE__*/
function (_React$Component2) {
_inheritsLoose(DefaultRenderComponent, _React$Component2);
function DefaultRenderComponent() {
return _React$Component2.apply(this, arguments) || this;
}
var _proto2 = DefaultRenderComponent.prototype;
_proto2.render = function render() {
var _this$props2 = this.props,
children = _this$props2.children,
level = _this$props2.level;
return createElement(Heading, {
level: level
}, children);
};
return DefaultRenderComponent;
}(Component);
var createIsHeading = function createIsHeading(level) {
return function (editor) {
var type = createHeadingNode(level);
return editor.value.blocks.some(function (block) {
return block ? block.type === type : false;
});
};
};
var createSetHeading = function createSetHeading(level) {
return function (editor) {
var type = createHeadingNode(level);
return editor.setBlocks(type);
};
};
var getHeadingLevel = function getHeadingLevel(editor) {
return find(function (level) {
return createIsHeading(level)(editor);
}, [1, 2, 3, 4, 5, 6]);
};
var createHeadingsPlugin = function createHeadingsPlugin(_temp) {
var _ref2 = _temp === void 0 ? {} : _temp,
_ref2$EditorComponent = _ref2.EditorComponent,
EditorComponent = _ref2$EditorComponent === void 0 ? DefaultEditorComponent$1 : _ref2$EditorComponent,
_ref2$RenderComponent = _ref2.RenderComponent,
RenderComponent = _ref2$RenderComponent === void 0 ? DefaultRenderComponent : _ref2$RenderComponent;
return function () {
return {
deserialize: function deserialize(el, next) {
var match = el.tagName.toLowerCase().match(/h([1-6])/);
if (match) {
var level = parseInt(match[1], 10);
return {
object: 'block',
type: createHeadingNode(level),
nodes: next(el.childNodes)
};
}
},
serialize: function serialize(obj, children) {
var block = obj;
if (block.object === 'block') {
var match = block.type.match(/@splish-me\/h([1-6])/);
if (match) {
var level = parseInt(match[1], 10);
return createElement(RenderComponent, {
level: level,
node: obj
}, children);
}
}
},
renderNode: function renderNode(props, _editor, next) {
var block = props.node;
if (block.object === 'block') {
var match = block.type.match(/@splish-me\/h([1-6])/);
if (match) {
var level = parseInt(match[1], 10);
return createElement(EditorComponent, Object.assign({
level: level
}, props));
}
}
return next();
}
};
};
};
var Dropdown =
/*#__PURE__*/
styled.select(function (props) {
var theme = createTextPluginTheme(props.name, props.theme);
return {
backgroundColor: theme.backgroundColor,
cursor: 'pointer',
color: theme.color,
outline: 'none',
height: '25px',
border: 'none',
borderRadius: '4px',
margin: '5px',
'&:hover': {
color: theme.hoverColor
}
};
});
var Option =
/*#__PURE__*/
styled.option(function (props) {
var theme = createTextPluginTheme(props.name, props.theme);
return {
backgroundColor: props.active ? theme.active.backgroundColor : theme.dropDown.backgroundColor,
color: props.active ? theme.active.color : theme.color,
cursor: 'pointer',
'&:hover': {
color: theme.hoverColor
}
};
});
var linkNode = '@splish-me/a';
var OpenInNewTab =
/*#__PURE__*/
styled$1.span({
margin: '0 0 0 10px'
});
var isLink = function isLink(editor) {
return editor.value.inlines.some(function (inline) {
return inline ? inline.type === linkNode : false;
});
};
var unwrapLink = function unwrapLink(editor) {
return editor.unwrapInline(linkNode);
};
var wrapLink = function wrapLink(data) {
if (data === void 0) {
data = {
href: ''
};
}
return function (editor) {
if (editor.value.selection.isExpanded) {
trimSelection(editor);
return editor.wrapInline({
type: linkNode,
data: data
}).moveToEnd().focus().moveBackward(1);
}
return editor.insertText(' ').focus().moveFocusBackward(1).wrapInline({
type: linkNode,
data: data
}).moveToStart();
};
};
var DefaultEditorComponent$2 = function DefaultEditorComponent(props) {
var attributes = props.attributes,
children = props.children,
node = props.node,
isSelected = props.isSelected;
var inline = node;
var href = inline.data.get('href');
return createElement("a", Object.assign({}, attributes, {
href: href,
style: isSelected ? {
textDecoration: 'underline'
} : undefined
}), children);
};
var DefaultControlsComponent = function DefaultControlsComponent(props) {
var editor = props.editor;
var inline = editor.value.inlines.find(nodeIsLink);
var lastInline = useRef(inline);
var _React$useState = useState(inline ? inline.data.get('href') : undefined),
value = _React$useState[0],
setValue = _React$useState[1];
var edit = !props.readOnly && isLink(editor) && editor.value.selection.isCollapsed;
var lastEdit = useRef(edit);
useEffect(function () {
if (lastEdit.current !== edit) {
if (inline && value !== inline.data.get('href')) {
handleHrefChange(value, inline, editor);
}
lastEdit.current = edit;
}
}, [edit, inline, value, editor]);
if (!inline) return createElement(Fragment, null, props.children);
if (value === undefined || lastInline.current.key !== inline.key) {
var href = inline.data.get('href');
setValue(href);
lastInline.current = inline;
}
function handleHrefChange(href, inline, editor) {
editor.setNodeByKey(inline.key, {
type: inline.type,
data: {
href: href
}
});
}
function nodeIsLink(inline) {
return inline ? inline.type === linkNode : false;
}
return createElement(Fragment, null, props.children, !props.readOnly && isLink(editor) && editor.value.selection.isCollapsed ? createElement(InlineSettings, {
key: "inlineoverlay" + inline.key,
onDelete: function onDelete() {
return unwrapLink(editor).focus();
},
position: "below"
}, createElement(InlineInput, {
value: value,
placeholder: "Hier Link einf\xFCgen",
onChange: function onChange(e) {
var newValue = e.target.value;
setValue(newValue);
handleHrefChange(newValue, inline, editor);
},
onKeyDown: function onKeyDown(event) {
if (event.key === 'Enter') {
event.preventDefault();
handleHrefChange(value, inline, editor);
editor.focus();
}
},
//@ts-ignore FIXME
ref: function ref(_ref) {
if (!_ref) return;
if (!lastEdit.current && !value) {
setTimeout(function () {
editor.blur();
setTimeout(function () {
_ref.focus();
});
});
}
}
}), createElement("a", {
target: "_blank",
href: value,
rel: "noopener noreferrer"
}, createElement(OpenInNewTab, {
title: "\xD6ffne in neuem Tab"
}, createElement(Icon, {
icon: faExternalLinkAlt
})))) : null);
};
var DefaultRendererComponent$1 =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(DefaultRendererComponent, _React$Component);
function DefaultRendererComponent() {
return _React$Component.apply(this, arguments) || this;
}
var _proto = DefaultRendererComponent.prototype;
_proto.render = function render() {
var _this$props = this.props,
children = _this$props.children,
node = _this$props.node;
var data = node.data;
if (!data) {
return null;
}
return createElement("a", {
href: data.href
}, children);
};
return DefaultRendererComponent;
}(Component);
var createLinkPlugin = function createLinkPlugin(_temp) {
var _ref2 = _temp === void 0 ? {} : _temp,
_ref2$EditorComponent = _ref2.EditorComponent,
EditorComponent = _ref2$EditorComponent === void 0 ? DefaultEditorComponent$2 : _ref2$EditorComponent,
_ref2$RenderComponent = _ref2.RenderComponent,
RenderComponent = _ref2$RenderComponent === void 0 ? DefaultRendererComponent$1 : _ref2$RenderComponent,
_ref2$ControlsCompone = _ref2.ControlsComponent,
ControlsComponent = _ref2$ControlsCompone === void 0 ? DefaultControlsComponent : _ref2$ControlsCompone;
return function () {
return {
onKeyDown: function onKeyDown(event, editor, next) {
var e = event;
if (isHotkey('mod+k', e)) {
e.preventDefault();
return isLink(editor) ? unwrapLink(editor) : wrapLink()(editor);
}
return next();
},
deserialize: function deserialize(el, next) {
if (el.tagName.toLowerCase() === 'a') {
// @ts-ignore FIXME
var attr = el.attrs.find(function (_ref3) {
var name = _ref3.name;
return name === 'href';
});
return {
object: 'inline',
type: linkNode,
nodes: next(el.childNodes),
data: Data.create({
href: attr ? attr.value : ''
})
};
}
},
serialize: function serialize(obj, children) {
var block = obj;
if (block.object === 'inline' && block.type === linkNode) {
return createElement(RenderComponent, {
node: obj
}, children);
}
},
renderNode: function renderNode(props, _editor, next) {
var block = props.node;
if (block.object === 'inline' && block.type === linkNode) {
return createElement(EditorComponent, Object.assign({}, props));
}
return next();
},
renderEditor: function renderEditor(props, editor, next) {
var children = next();
if (props.readOnly) return children;
return createElement(ControlsComponent, Object.assign({}, props, {
editor: editor
}), children);
}
};
};
};
var createTextEditor = function createTextEditor(options) {
var _inlines, _blocks;
var schema = {
inlines: (_inlines = {}, _inlines[katexInlineNode] = {
isVoid: true
}, _inlines[linkNode] = {
text: /.+/
}, _inlines),
blocks: (_blocks = {}, _blocks[katexBlockNode] = {
isVoid: true
}, _blocks)
};
return function SlateEditor(props) {
var _useEditorFocus = useEditorFocus(),
focusPrevious = _useEditorFocus.focusPrevious,
focusNext = _useEditorFocus.focusNext;
var editor = useRef();
var store = useStore();
var overlayContext = useContext(OverlayContext);
var plugins = selectors.getPlugins(store.getState());
var _React$useState = useState(Value.fromJSON(props.state.value)),
rawState = _React$useState[0],
setRawState = _React$useState[1];
var lastValue = useRef(props.state.value);
useEffect(function () {
if (lastValue.current !== props.state.value) {
setRawState(Value.fromJSON(props.state.value));
lastValue.current = props.state.value;
setTimeout(function () {
if (!editor.current) return;
if (props.focused) {
editor.current.focus();
}
});
}
}, [lastValue, props.focused, props.state.value]); // PLEASE DONT FIX THIS! Closure needed because on* isn't recreated so doesnt use current props
var slateClosure = useRef({
name: props.name,
plugins: plugins,
insert: props.insert,
replace: props.replace,
remove: props.remove,
parent: props.parent,
focusPrevious: focusPrevious,
focusNext: focusNext,
mergeWithNext: props.mergeWithNext,
mergeWithPrevious: props.mergeWithPrevious
});
slateClosure.current = {
name: props.name,
plugins: plugins,
insert: props.insert,
replace: props.replace,
remove: props.remove,
parent: props.parent,
focusPrevious: focusPrevious,
focusNext: focusNext,
mergeWithNext: props.mergeWithNext,
mergeWithPrevious: props.mergeWithPrevious
};
useEffect(function () {
if (!editor.current) return;
if (props.focused) {
setTimeout(editor.current.focus);
} else {
editor.current.blur();
}
}, [props.focused]);
var pluginClosure = useRef({
overlayContext: overlayContext,
name: props.name,
parent: props.parent,
replace: props.replace,
plugins: plugins
});
pluginClosure.current = {
overlayContext: overlayContext,
name: props.name,
parent: props.parent,
replace: props.replace,
plugins: plugins
};
var slatePlugins = useRef();
if (slatePlugins.current === undefined) {
slatePlugins.current = [].concat(options.plugins.map(function (slatePluginFactory) {
return slatePluginFactory(pluginClosure);
}), [newSlateOnEnter(slateClosure), focusNextDocumentOnArrowDown(slateClosure)]);
}
var onPaste = useMemo(function () {
return createOnPaste(slateClosure);
}, [slateClosure]);
var onKeyDown = useMemo(function () {
return createOnKeyDown(slateClosure);
}, [slateClosure]);
var onClick = useCallback(function (e, editor, next) {
if (e.target) {
// @ts-ignore
var node = findNode(e.target, editor);
if (!node) {
return editor;
}
}
next();
}, []);
var onChange = useCallback(function (change) {
var nextValue = change.value.toJSON();
setRawState(change.value);
var withoutSelections = change.operations.filter(function (operation) {
return typeof operation !== 'undefined' && operation.type !== 'set_selection';
});
if (!withoutSelections.isEmpty()) {
lastValue.current = nextValue;
props.state.set(nextValue);
}
}, [props.state]);
return createElement(Editor, {
ref: function ref(slateReact) {
if (slateReact && !editor.current) {
editor.current = slateReact;
patchSlateInsertFragment(slateReact);
}
},
// ref={editor as React.RefObject<Editor>}
onPaste: onPaste,
onKeyDown: onKeyDown,
onClick: onClick,
onChange: onChange,
placeholder: props.editable ? options.placeholder : '',
plugins: slatePlugins.current,
readOnly: !props.focused,
value: rawState,
schema: schema
});
};
}; // PLEASE DONT FIX THIS! Closure needed because onPaste isn't recreated so doesnt use props
function createOnPaste(slateClosure) {
return function (e, editor, next) {
if (!slateClosure.current) {
next();
return;
}
var _slateClosure$current = slateClosure.current,
plugins = _slateClosure$current.plugins,
name = _slateClosure$current.name,
insert = _slateClosure$current.insert,
replace = _slateClosure$current.replace;
if (typeof insert !== 'function') {
next();
return;
}
var clipboardData = e.clipboardData;
var _loop = function _loop(key) {
var onPaste = plugins[key].onPaste;
if (clipboardData && typeof onPaste === 'function') {
var result = onPaste(clipboardData);
if (result !== undefined) {
if (typeof replace === 'function' && isValueEmpty(editor.value)) {
// replace with pasted plugin
setTimeout(function () {
replace({
plugin: key,
state: result.state
});
});
} else {
var nextSlateState = splitBlockAtSelection(editor);
setTimeout(function () {
// insert new text-plugin with the parts after the current cursor position if any
if (nextSlateState) {
insert({
plugin: name,
state: nextSlateState
});
} // insert the plugin that handled the pasted data.
// It's inserted at the same index, so will be between the text plugins
insert({
plugin: key,
state: result.state
});
});
}
return {
v: void 0
};
}
}
};
for (var key in plugins) {
var _ret = _loop(key);
if (typeof _ret === "object") return _ret.v;
}
var currentBlock = editor.value.blocks.first(); // no plugin handled the pasted data
// continue normal then split the plugin if multiple blocks were inserted
next();
var blocks = editor.value.document.getBlocks(); //blocks must be inserted in reversed order
var afterSelected = blocks.reverse().takeUntil(function (block) {
if (!block) {
return false;
}
return currentBlock.key === block.key;
});
if (afterSelected.size) {
afterSelected.forEach(function (block) {
if (!block) return;
editor.removeNodeByKey(block.key);
});
setTimeout(function () {
afterSelected.forEach(function (block) {
var isOnlyWhitespace = function isOnlyWhitespace(text) {
return /^\s*$/.test(text);
};
if (!block || isOnlyWhitespace(block.text)) return;
insert({
plugin: name,
state: createDocumentFromBlocks([block])
});
});
});
}
};
} // PLEASE DONT FIX THIS! Closure needed because onKeyDown isn't recreated so doesnt use props
function createOnKeyDown(slateClosure) {
return function (e, editor, next) {
var key = e.key;
if (isHotkey('mod+z', e) || isHotkey('mod+y', e) || isHotkey('mod+shift+z', e)) {
e.preventDefault();
return;
}
if (key === 'Backspace' && isSelectionAtStart(editor) || key === 'Delete' && isSelectionAtEnd(editor)) {
if (!slateClosure.current) return;
var previous = key === 'Backspace';
if (isValueEmpty(editor.value)) {
// Focus previous resp. next document and remove self
var remove = slateClosure.current.remove;
var focus = slateClosure.current[previous ? 'focusPrevious' : 'focusNext'];
if (typeof remove === 'function') {
if (typeof focus === 'function') focus();
remove();
}
} else {
// Merge with previous resp. next document
var merge = slateClosure.current[previous ? 'mergeWithPrevious' : 'mergeWithNext'];
if (typeof merge !== 'function') return;
merge(function (other) {
var value = Value.fromJSON(other);
var selection = Range.create(editor.value.selection);
editor.insertFragmentAtRange(selection, value.document);
if (!previous) editor.select(selection);
return editor.value.toJSON();
});
}
return;
}
return next();
};
}
function isSelectionAtStart(editor) {
var selection = editor.value.selection;
var startNode = editor.value.document.getFirstText();
return selection.isCollapsed && startNode && editor.value.startText.key === startNode.key && selection.start.offset === 0;
}
function isSelectionAtEnd(editor) {
var selection = editor.value.selection;
var endNode = editor.value.document.getLastText();
return selection.isCollapsed && endNode && editor.value.endText.key === endNode.key && selection.end.offset === editor.value.endText.text.length;
}
function newSlateOnEnter(slateClosure) {
return {
commands: {
replaceWithPlugin: function replaceWithPlugin(editor, options) {
if (!slateClosure.current) return editor;
var replace = slateClosure.current.replace;
if (typeof replace !== 'function') return editor;
replace(options);
return editor;
},
unwrapParent: function unwrapParent(editor) {
if (!slateClosure.current) return editor;
var parentWithReplace = findParentWith('replace', slateClosure.current);
if (parentWithReplace && typeof parentWithReplace.replace === 'function') {
parentWithReplace.replace({
plugin: slateClosure.current.name,
state: editor.value.toJSON()
});
}
return editor;
}
},
onKeyDown: function onKeyDown(e, editor, next) {
if (isHotkey('enter', e) && !editor.value.selection.isExpanded) {
// remove text plugin and insert on parent if plugin is empty
if (isValueEmpty(editor.value) && slateClosure.current) {
var parentWithInsert = findParentWith('insert', slateClosure.current);
if (parentWithInsert) {
e.preventDefault();
setTimeout(function () {
if (!slateClosure.current) return next();
var remove = slateClosure.current.remove;
if (typeof remove === 'function' && typeof parentWithInsert.insert === 'function') {
parentWithInsert.insert({
plugin: slateClosure.current.name
});
remove();
}
});
return;
}
} // remove block and insert plugin on parent, if block is empty
if (editor.value.startText.text === '' && editor.value.startBlock.nodes.size === 1 && slateClosure.current) {
var _parentWithInsert = findParentWith('insert', slateClosure.current);
if (_parentWithInsert) {
e.preventDefault();
if (!slateClosure.current) return next();
if (typeof _parentWithInsert.insert === 'function') {
editor["delete"]();
_parentWithInsert.insert({
plugin: slateClosure.current.name
});
}
return;
}
}
if (slateClosure.current && typeof slateClosure.current.insert === 'function') {
e.preventDefault();
var nextSlateState = splitBlockAtSelection(editor);
setTimeout(function () {
if (!slateClosure.current) return next();
var insert = slateClosure.current.insert;
if (typeof insert !== 'function') return;
insert({
plugin: slateClosure.current.name,
state: nextSlateState
});
});
return;
}
}
return next();
}
};
}
function focusNextDocumentOnArrowDown(slateClosure) {
return {
onKeyDown: function onKeyDown(e, editor, next) {
var key = e.key;
if (key === 'ArrowDown' || key === 'ArrowUp') {
var lastRange = getRange();
if (lastRange) {
var lastY = lastRange.getBoundingClientRect().top;
setTimeout(function () {
if (!slateClosure.current) {
return;
}
var currentRange = getRange();
if (!currentRange) {
return;
}
var currentY = currentRange.getBoundingClientRect().top;
if (lastY === currentY) {
if (key === 'ArrowDown') {
slateClosure.current.focusNext();
} else {
slateClosure.current.focusPrevious();
}
}
});
}
}
return next();
function getRange() {
var selection = window.getSelection();
if (selection && selection.rangeCount > 0) {
return selection.getRangeAt(0);
}
return null;
}
}
};
} // search recursively for a parent with the required function
function findParentWith(funcQuery, closure) {
if (!closure.parent) return;
if (typeof closure.parent[funcQuery] === 'function') return closure.parent;
return findParentWith(funcQuery, closure.parent);
}
function splitBlockAtSelection(editor) {
if (isSelectionAtEnd(editor)) {
return;
}
if (editor.value.focusBlock.type == katexBlockNode) {
// If katex block node is focused, don't attempt to split it, insert empty paragraph instead
editor.moveToEndOfBlock();
editor.insertBlock('paragraph');
} else {
editor.splitBlock(1);
}
var blocks = editor.value.document.getBlocks();
var afterSelected = blocks.skipUntil(function (block) {
if (!block) {
return false;
}
return editor.value.blocks.first().key === block.key;
});
afterSelected.forEach(function (block) {
if (!block) return;
editor.removeNodeByKey(block.key);
});
return createDocumentFromBlocks(afterSelected.toArray());
}
function createDocumentFromBlocks(blocks) {
return {
document: {
nodes: [].concat(blocks.map(function (block) {
return block.toJSON();
}))
}
};
} // TEMPORARY
// Testbed for integration of slate fix
// polyfilling slate editor
function patchSlateInsertFragment(reacteditor) {
// @ts-ignore
var editor = reacteditor; // @ts-ignore
editor.insertFragment = function (fragment) {
if (!fragment.nodes.size) return editor;
if (editor.value.selection.isExpanded) {
editor["delete"]();
}
var value = editor.value;
var _value = value,
document = _value.document,
selection = _value.selection;
var start = selection.start,
end = selection.end;
var _value2 = value,
startText = _value2.startText,
endText = _value2.endText,
startInline = _value2.startInline;
var lastText = fragment.getLastText(); // @ts-ignore
var lastInline = fragment.getClosestInline(lastText.key); // @ts-ignore
var lastBlock = fragment.getClosestBlock(lastText.key);
var firstChild = fragment.nodes.first();
var lastChild = fragment.nodes.last(); // @ts-ignore
var keys = document.getTexts().map(function (text) {
return text.key;
});
var isAppending = !startInline || start.isAtStartOfNode(startText) || end.isAtStartOfNode(startText) || start.isAtEndOfNode(endText) || end.isAtEndOfNode(endText);
var isInserting = firstChild.hasBlockChildren() || lastChild.hasBlockChildren(); // @ts-ignore
editor.insertFragmentAtRange(selection, fragment);
value = editor.value;
document = value.document; // @ts-ignore
var newTexts = document.getTexts().filter(function (n) {
return !keys.includes(n.key);
});
var newText = isAppending ? newTexts.last() : newTexts.takeLast(2).first();
if (newText && (lastInline || isInserting)) {
editor.moveToEndOfNode(newText);
} else if (newText && lastBlock) {
// Changed code
var lastInlineIndex = lastBlock.nodes.findLastIndex(function (node) {
if (!node) return false;
return node.object == 'inline';
});
var skipLength = lastBlock.nodes.takeLast(lastBlock.nodes.size - lastInlineIndex - 1).reduce(function (num, v) {
if (!num) num = 0;
if (v) return num + v.text.length;
return num;
}, 0);
editor.moveToStartOfNode(newText).moveForward(skipLength);
}
return editor;
};
}
var defaultNode = 'paragraph';
var textState =
/*#__PURE__*/
StateType.scalar({
document: {
nodes: [{
object: 'block',
type: defaultNode,
nodes: [{
object: 'text',
leaves: [{
object: 'leaf',
text: ''
}]
}]
}]
}
});
var createTextPlugin = function createTextPlugin(options) {
return {
Component: createTextEditor(options),
state: textState,
icon: createIcon(faParagraph),
title: 'Text',
description: 'Schreibe Text und Matheformeln und formatiere sie.',
onKeyDown: function onKeyDown() {
return false;
},
isEmpty: function isEmpty(state) {
var value = Value.fromJSON(state);
return isValueEmpty(value);
}
};
};
function isValueEmpty(value) {
return value.document.text === '' && value.document.nodes.size === 1 && value.document.nodes.get(0).type === defaultNode && value.document.getTexts().size === 1;
}
//@ts-ignore FIXME
var orderedListNode = 'ordered-list';
var unorderedListNode = 'unordered-list';
var listItemNode = 'list-item';
var listItemChildNode = 'list-item-child';
var isList = function isList(type) {
return function (editor) {
var _editor$value = editor.value,
document = _editor$value.document,
startBlock = _editor$value.startBlock;
if (!startBlock || startBlock.type !== listItemChildNode) return false;
var listItem = document.getParent(startBlock.key);
var list = document.getParent(listItem.key);
return list.type === type;
};
};
var toggleList = function toggleList(type) {
if (type === void 0) {
type = unorderedListNode;
}
return function (editor) {
return editor.command('toggleList', {
type: type
});
};
};
/* eslint-disable @typescript-eslint/camelcase */
var createListPlugin = function createListPlugin() {
return function () {
return List({
blocks: {
ordered_list: orderedListNode,
unordered_list: unorderedListNode,
list_item: listItemNode,
list_item_child: listItemChildNode,
"default": defaultNode
}
});
};
};
/* eslint-enable @typescript-eslint/camelcase */
var createMathComponent = function createMathComponent(Component$1, _ref) {
var displayMode = _ref.displayMode;
var MathComponent =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(MathComponent, _React$Component);
function MathComponent() {
var _this;
_this = _React$Component.apply(this, arguments) || this;
_this.state = {
html: ''
};
return _this;
}
var _proto = MathComponent.prototype;
_proto.render = function render() {
var _this$state = this.state,
error = _this$state.error,
html = _this$state.html;
var renderError = this.props.renderError;
if (error) {
return renderError ? renderError(error) : createElement(Component$1, {
html: "" + error.message
});
}
return createElement(Component$1, {
html: html
});
};
_proto.shouldComponentUpdate = function shouldComponentUpdate(nextProps) {
return nextProps.math !== this.props.math;
};
MathComponent.getDerivedStateFromProps = function getDerivedStateFromProps(props) {
try {
var errorColor = props.errorColor,
renderError = props.renderError;
var html = KaTeX.renderToString(props.math, {
displayMode: displayMode,
errorColor: errorColor,
throwOnError: !!renderError
});
return {
html: html,
error: undefined
};
} catch (error) {
if (error instanceof KaTeX.ParseError) {
return {
error: error
};
}
throw error;
}
};
return MathComponent;
}(Component);
return MathComponent;
};
var IBlockMath = function IBlockMath(_ref2) {
var html = _ref2.html;
return createElement("span", {
style: {
display: 'block'
},
dangerouslySetInnerHTML: {
__html: html
}
});
};
var BlockMath =
/*#__PURE__*/
createMathComponent(IBlockMath, {
displayMode: true
});
var IInlineMath = function IInlineMath(_ref3) {
var html = _ref3.html;
return createElement("span", {
dangerouslySetInnerHTML: {
__html: html
}
});
};
var InlineMath =
/*#__PURE__*/
createMathComponent(IInlineMath, {
displayMode: false
});
var handleError = function handleError(formula, error, inline, oldErrorPosition) {
var errorStyle = {
color: '#CC0000'
};
if (error.position === oldErrorPosition) {
return createElement("span", {
style: errorStyle
}, formula);
}
var beforeError = formula.substring(0, error.position);
var afterError = formula.substring(error.position);
return createElement("span", {
style: {
display: 'inline-block'
}
}, createElement(Math, {
formula: beforeError,
inline: inline,
oldErrorPosition: error.position
}), createElement("span", {
style: errorStyle
}, afterError), createElement("span", {
style: _extends({}, errorStyle, {
display: 'block'
})
}, createElement("b", null, error.name, ": ", error.message)));
};
var Math =
/*#__PURE__*/
function (_React$Component2) {
_inheritsLoose(Math, _React$Component2);
function Math() {
return _React$Component2.apply(this, arguments) || this;
}
var _proto2 = Math.prototype;
_proto2.render = function render() {
var _this$props = this.props,
inline = _this$props.inline,
formula = _this$props.formula,
oldErrorPosition = _this$props.oldErrorPosition;
if (!formula) {
return null;
}
var Component = inline ? InlineMath : BlockMath;
return createElement(Component, {
math: formula,
renderError: function renderError(error) {
return handleError(formula, error, !inline, oldErrorPosition);
}
});
};
return Math;
}(Component);
var MathQuill = canUseDOM ?
/*#__PURE__*/
require('react-mathquill')["default"] : function () {
return null;
};
var EditorWrapper =
/*#__PURE__*/
styled$1.div(function (props) {
return _extends({
whiteSpace: undefined,
overflowWrap: undefined
}, props.inline ? {
display: 'inline-block'
} : {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginTop: '15px'
});
});
var KeySpan =
/*#__PURE__*/
styled$1.span({
background: '#ddd',
padding: '2px 4px',
borderRadius: 5,
color: '#1d1c1d',
textAlign: 'center',
minWidth: 20
});
var HelpText =
/*#__PURE__*/
createElement(Fragment, null, "Tastenk\xFCrzel:",
/*#__PURE__*/
createElement("br", null),
/*#__PURE__*/
createElement("br", null),
/*#__PURE__*/
createElement("p", null, "Bruch: ",
/*#__PURE__*/
createElement(KeySpan, null, "/")),
/*#__PURE__*/
createElement("p", null, "Hochgestellt: ",
/*#__PURE__*/
createElement(KeySpan, null, "\u2191"), " oder ",
/*#__PURE__*/
createElement(KeySpan, null, "^")),
/*#__PURE__*/
createElement("p", null, "Tiefgestellt: ",
/*#__PURE__*/
createElement(KeySpan, null, "\u2193"), " oder ",
/*#__PURE__*/
createElement(KeySpan, null, "_")),
/*#__PURE__*/
createElement("p", null, "\u03C0, \u03B1, \u03B2, \u03B3: ",
/*#__PURE__*/
createElement(KeySpan, null, "pi"), ", ",
/*#__PURE__*/
createElement(KeySpan, null, "alpha"), ",", ' ',
/*#__PURE__*/
createElement(KeySpan, null, "beta"), ",",
/*#__PURE__*/
createElement(KeySpan, null, "gamma")),
/*#__PURE__*/
createElement("p", null, "\u2264, \u2265: ",
/*#__PURE__*/
createElement(KeySpan, null, '<='), ", ",
/*#__PURE__*/
createElement(KeySpan, null, '>=')),
/*#__PURE__*/
createElement("p", null, "Wurzeln: ",
/*#__PURE__*/
createElement(KeySpan, null, "\\sqrt"), ", ",
/*#__PURE__*/
createElement(KeySpan, null, "\\nthroot")),
/*#__PURE__*/
createElement("p", null, "Mathematische Symbole: ",
/*#__PURE__*/
createElement(KeySpan, null, '\\<NAME>'), ", z.B.", ' ',
/*#__PURE__*/
createElement(KeySpan, null, "\\neq"), " (\u2260), ",
/*#__PURE__*/
createElement(KeySpan, null, "\\pm"), " (\xB1), ..."),
/*#__PURE__*/
createElement("p", null, "Funktionen: ",
/*#__PURE__*/
createElement(KeySpan, null, "sin"), ", ",
/*#__PURE__*/
createElement(KeySpan, null, "cos"), ",", ' ',
/*#__PURE__*/
createElement(KeySpan, null, "ln"), ", ..."));
function isAndroid() {
return isTouchDevice() && navigator && /(android)/i.test(navigator.userAgent);
}
var DefaultEditorComponent$3 = function DefaultEditorComponent(props) {
var attributes = props.attributes,
node = props.node,
editor = props.editor,
readOnly = props.readOnly,
name = props.name;
var data = node.data;
var inline = data.get('inline');
var formula = data.get('formula');
function setFormula(value) {
editor.setNodeByKey(node.key, {
type: node.type,
data: {
formula: value,
inline: node.data.get('inline')
}
});
}
var _React$useState = useState(true),
useVisual = _React$useState[0],
setUseVisual = _React$useState[1]; //Refs for positioning of hovering menu
var mathQuillRef = createRef();
var latexInputRef = useRef();
var wrappedMathquillRef = Object.defineProperty({}, 'current', {
// wrapper around Mathquill component
get: function get() {
return mathQuillRef.current ? mathQuillRef.current.element : null;
}
}); // state for math formula, because focus jumps to end of input field if updated directly
var _React$useState2 = useState(formula),
formulaState = _React$useState2[0],
setFormulaState = _React$useState2[1];
var overlayContext = useContext(OverlayContext); // if math formula gets selected or leaves edit, update formula from state
var edit = props.isSelected && editor.value.selection.isCollapsed && !readOnly;
var lastEdit = useRef(edit);
if (lastEdit.current !== edit) {
if (formula !== formulaState) {
setFormula(formulaState);
}
lastEdit.current = edit;
}
function checkLeaveLatexInput(e) {
if (!latexInputRef.current) return;
var _latexInputRef$curren = latexInputRef.current,
selectionEnd = _latexInputRef$curren.selectionEnd,
value = _latexInputRef$curren.value;
if (e.key === 'ArrowLeft' && selectionEnd === 0) {
// leave left
editor.moveToStart().moveBackward(1).focus();
} else if (e.key === 'ArrowRight' && selectionEnd === value.length) {
// leave right
editor.moveToEnd().moveForward(1).focus();
}
}
function handleInlineToggle(checked) {
var newData = {
formula: formulaState,
inline: !checked
}; // remove old node, merge blocks if necessary
if (node.isLeafBlock()) {
var n = editor.value.document.getNextBlock(node.key);
editor.removeNodeByKey(node.key);
if (n) {
editor.mergeNodeByKey(n.key);
}
} else {
editor.removeNodeByKey(node.key);
}
if (checked) {
editor.insertBlock({
type: katexBlockNode,
data: newData
});
} else {
editor.insertInline({
type: katexInlineNode,
data: newData
});
}
}
function checkLatexError(mathquill) {
if (mathquill) {
if (mathquill.latex() == '' && formula != '') {
// Error occured
alert('Error while parsing LaTeX.');
setUseVisual(false);
}
setTimeout(function () {
editor.blur();
setTimeout(function () {
mathquill.focus();
});
});
}
}
function updateLatex(val) {
//cant set formula directly, because otherwise focus jumps to end of input field
setFormulaState(val); // but android is different ...
if (isAndroid()) {
setFormula(val);
}
}
if (edit) {
var mathquillConfig = _extends({
supSubsRequireOperand: true,
autoComman