slate-react
Version:
Tools for building completely customizable richtext editors with React.
1,233 lines (1,194 loc) • 148 kB
JavaScript
'use strict';
var getDirection = require('direction');
var debounce = require('lodash/debounce');
var throttle = require('lodash/throttle');
var React = require('react');
var scrollIntoView = require('scroll-into-view-if-needed');
var slate = require('slate');
var slateDom = require('slate-dom');
var resizeObserver = require('@juggle/resize-observer');
var ReactDOM = require('react-dom');
function unwrapExports (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var arrayWithHoles = createCommonjsModule(function (module) {
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
module.exports = _arrayWithHoles, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(arrayWithHoles);
var iterableToArrayLimit = createCommonjsModule(function (module) {
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (i = (t = t.call(r)).next, 0 === l) {
if (Object(t) !== t) return;
f = !1;
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = !0, n = r;
} finally {
try {
if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
module.exports = _iterableToArrayLimit, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(iterableToArrayLimit);
var arrayLikeToArray = createCommonjsModule(function (module) {
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
module.exports = _arrayLikeToArray, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(arrayLikeToArray);
var unsupportedIterableToArray = createCommonjsModule(function (module) {
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return arrayLikeToArray(o, minLen);
}
module.exports = _unsupportedIterableToArray, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(unsupportedIterableToArray);
var nonIterableRest = createCommonjsModule(function (module) {
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
module.exports = _nonIterableRest, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(nonIterableRest);
var slicedToArray = createCommonjsModule(function (module) {
function _slicedToArray(arr, i) {
return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || unsupportedIterableToArray(arr, i) || nonIterableRest();
}
module.exports = _slicedToArray, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
var _slicedToArray = unwrapExports(slicedToArray);
var objectWithoutPropertiesLoose = createCommonjsModule(function (module) {
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;
}
module.exports = _objectWithoutPropertiesLoose, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(objectWithoutPropertiesLoose);
var objectWithoutProperties = createCommonjsModule(function (module) {
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
module.exports = _objectWithoutProperties, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
var _objectWithoutProperties = unwrapExports(objectWithoutProperties);
var _typeof_1 = createCommonjsModule(function (module) {
function _typeof(o) {
"@babel/helpers - typeof";
return (module.exports = _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, module.exports.__esModule = true, module.exports["default"] = module.exports), _typeof(o);
}
module.exports = _typeof, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(_typeof_1);
var toPrimitive = createCommonjsModule(function (module) {
var _typeof = _typeof_1["default"];
function _toPrimitive(input, hint) {
if (_typeof(input) !== "object" || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || "default");
if (_typeof(res) !== "object") return res;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return (hint === "string" ? String : Number)(input);
}
module.exports = _toPrimitive, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(toPrimitive);
var toPropertyKey = createCommonjsModule(function (module) {
var _typeof = _typeof_1["default"];
function _toPropertyKey(arg) {
var key = toPrimitive(arg, "string");
return _typeof(key) === "symbol" ? key : String(key);
}
module.exports = _toPropertyKey, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
unwrapExports(toPropertyKey);
var defineProperty = createCommonjsModule(function (module) {
function _defineProperty(obj, key, value) {
key = toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
module.exports = _defineProperty, module.exports.__esModule = true, module.exports["default"] = module.exports;
});
var _defineProperty = unwrapExports(defineProperty);
/**
* A React context for sharing the editor object.
*/
var EditorContext = /*#__PURE__*/React.createContext(null);
/**
* Get the current editor object from the React context.
*/
var useSlateStatic = function useSlateStatic() {
var editor = React.useContext(EditorContext);
if (!editor) {
throw new Error("The `useSlateStatic` hook must be used inside the <Slate> component's context.");
}
return editor;
};
// eslint-disable-next-line no-redeclare
var ReactEditor = slateDom.DOMEditor;
function ownKeys$5(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread$5(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$5(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$5(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
// https://github.com/facebook/draft-js/blob/main/src/component/handlers/composition/DraftEditorCompositionHandler.js#L41
// When using keyboard English association function, conpositionEnd triggered too fast, resulting in after `insertText` still maintain association state.
var RESOLVE_DELAY = 25;
// Time with no user interaction before the current user action is considered as done.
var FLUSH_DELAY = 200;
// Replace with `const debug = console.log` to debug
var debug = function debug() {};
// Type guard to check if a value is a DataTransfer
var isDataTransfer = function isDataTransfer(value) {
return (value === null || value === void 0 ? void 0 : value.constructor.name) === 'DataTransfer';
};
function createAndroidInputManager(_ref) {
var editor = _ref.editor,
scheduleOnDOMSelectionChange = _ref.scheduleOnDOMSelectionChange,
onDOMSelectionChange = _ref.onDOMSelectionChange;
var flushing = false;
var compositionEndTimeoutId = null;
var flushTimeoutId = null;
var actionTimeoutId = null;
var idCounter = 0;
var insertPositionHint = false;
var applyPendingSelection = function applyPendingSelection() {
var pendingSelection = slateDom.EDITOR_TO_PENDING_SELECTION.get(editor);
slateDom.EDITOR_TO_PENDING_SELECTION["delete"](editor);
if (pendingSelection) {
var selection = editor.selection;
var normalized = slateDom.normalizeRange(editor, pendingSelection);
if (normalized && (!selection || !slate.Range.equals(normalized, selection))) {
slate.Transforms.select(editor, normalized);
}
}
};
var performAction = function performAction() {
var action = slateDom.EDITOR_TO_PENDING_ACTION.get(editor);
slateDom.EDITOR_TO_PENDING_ACTION["delete"](editor);
if (!action) {
return;
}
if (action.at) {
var target = slate.Point.isPoint(action.at) ? slateDom.normalizePoint(editor, action.at) : slateDom.normalizeRange(editor, action.at);
if (!target) {
return;
}
var _targetRange = slate.Editor.range(editor, target);
if (!editor.selection || !slate.Range.equals(editor.selection, _targetRange)) {
slate.Transforms.select(editor, target);
}
}
action.run();
};
var flush = function flush() {
if (flushTimeoutId) {
clearTimeout(flushTimeoutId);
flushTimeoutId = null;
}
if (actionTimeoutId) {
clearTimeout(actionTimeoutId);
actionTimeoutId = null;
}
if (!hasPendingDiffs() && !hasPendingAction()) {
applyPendingSelection();
return;
}
if (!flushing) {
flushing = true;
setTimeout(function () {
return flushing = false;
});
}
if (hasPendingAction()) {
flushing = 'action';
}
var selectionRef = editor.selection && slate.Editor.rangeRef(editor, editor.selection, {
affinity: 'forward'
});
slateDom.EDITOR_TO_USER_MARKS.set(editor, editor.marks);
debug('flush', slateDom.EDITOR_TO_PENDING_ACTION.get(editor), slateDom.EDITOR_TO_PENDING_DIFFS.get(editor));
var scheduleSelectionChange = hasPendingDiffs();
var diff;
while (diff = (_EDITOR_TO_PENDING_DI = slateDom.EDITOR_TO_PENDING_DIFFS.get(editor)) === null || _EDITOR_TO_PENDING_DI === void 0 ? void 0 : _EDITOR_TO_PENDING_DI[0]) {
var _EDITOR_TO_PENDING_DI, _EDITOR_TO_PENDING_DI2;
var pendingMarks = slateDom.EDITOR_TO_PENDING_INSERTION_MARKS.get(editor);
if (pendingMarks !== undefined) {
slateDom.EDITOR_TO_PENDING_INSERTION_MARKS["delete"](editor);
editor.marks = pendingMarks;
}
if (pendingMarks && insertPositionHint === false) {
insertPositionHint = null;
}
var range = slateDom.targetRange(diff);
if (!editor.selection || !slate.Range.equals(editor.selection, range)) {
slate.Transforms.select(editor, range);
}
if (diff.diff.text) {
slate.Editor.insertText(editor, diff.diff.text);
} else {
slate.Editor.deleteFragment(editor);
}
// Remove diff only after we have applied it to account for it when transforming
// pending ranges.
slateDom.EDITOR_TO_PENDING_DIFFS.set(editor, (_EDITOR_TO_PENDING_DI2 = slateDom.EDITOR_TO_PENDING_DIFFS.get(editor)) === null || _EDITOR_TO_PENDING_DI2 === void 0 ? void 0 : _EDITOR_TO_PENDING_DI2.filter(function (_ref2) {
var id = _ref2.id;
return id !== diff.id;
}));
if (!slateDom.verifyDiffState(editor, diff)) {
scheduleSelectionChange = false;
slateDom.EDITOR_TO_PENDING_ACTION["delete"](editor);
slateDom.EDITOR_TO_USER_MARKS["delete"](editor);
flushing = 'action';
// Ensure we don't restore the pending user (dom) selection
// since the document and dom state do not match.
slateDom.EDITOR_TO_PENDING_SELECTION["delete"](editor);
scheduleOnDOMSelectionChange.cancel();
onDOMSelectionChange.cancel();
selectionRef === null || selectionRef === void 0 || selectionRef.unref();
}
}
var selection = selectionRef === null || selectionRef === void 0 ? void 0 : selectionRef.unref();
if (selection && !slateDom.EDITOR_TO_PENDING_SELECTION.get(editor) && (!editor.selection || !slate.Range.equals(selection, editor.selection))) {
slate.Transforms.select(editor, selection);
}
if (hasPendingAction()) {
performAction();
return;
}
// COMPAT: The selectionChange event is fired after the action is performed,
// so we have to manually schedule it to ensure we don't 'throw away' the selection
// while rendering if we have pending changes.
if (scheduleSelectionChange) {
scheduleOnDOMSelectionChange();
}
scheduleOnDOMSelectionChange.flush();
onDOMSelectionChange.flush();
applyPendingSelection();
var userMarks = slateDom.EDITOR_TO_USER_MARKS.get(editor);
slateDom.EDITOR_TO_USER_MARKS["delete"](editor);
if (userMarks !== undefined) {
editor.marks = userMarks;
editor.onChange();
}
};
var handleCompositionEnd = function handleCompositionEnd(_event) {
if (compositionEndTimeoutId) {
clearTimeout(compositionEndTimeoutId);
}
compositionEndTimeoutId = setTimeout(function () {
slateDom.IS_COMPOSING.set(editor, false);
flush();
}, RESOLVE_DELAY);
};
var handleCompositionStart = function handleCompositionStart(_event) {
slateDom.IS_COMPOSING.set(editor, true);
if (compositionEndTimeoutId) {
clearTimeout(compositionEndTimeoutId);
compositionEndTimeoutId = null;
}
};
var updatePlaceholderVisibility = function updatePlaceholderVisibility() {
var forceHide = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
var placeholderElement = slateDom.EDITOR_TO_PLACEHOLDER_ELEMENT.get(editor);
if (!placeholderElement) {
return;
}
if (hasPendingDiffs() || forceHide) {
placeholderElement.style.display = 'none';
return;
}
placeholderElement.style.removeProperty('display');
};
var storeDiff = function storeDiff(path, diff) {
var _EDITOR_TO_PENDING_DI3;
var pendingDiffs = (_EDITOR_TO_PENDING_DI3 = slateDom.EDITOR_TO_PENDING_DIFFS.get(editor)) !== null && _EDITOR_TO_PENDING_DI3 !== void 0 ? _EDITOR_TO_PENDING_DI3 : [];
slateDom.EDITOR_TO_PENDING_DIFFS.set(editor, pendingDiffs);
var target = slate.Node.leaf(editor, path);
var idx = pendingDiffs.findIndex(function (change) {
return slate.Path.equals(change.path, path);
});
if (idx < 0) {
var normalized = slateDom.normalizeStringDiff(target.text, diff);
if (normalized) {
pendingDiffs.push({
path: path,
diff: diff,
id: idCounter++
});
}
updatePlaceholderVisibility();
return;
}
var merged = slateDom.mergeStringDiffs(target.text, pendingDiffs[idx].diff, diff);
if (!merged) {
pendingDiffs.splice(idx, 1);
updatePlaceholderVisibility();
return;
}
pendingDiffs[idx] = _objectSpread$5(_objectSpread$5({}, pendingDiffs[idx]), {}, {
diff: merged
});
};
var scheduleAction = function scheduleAction(run) {
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
at = _ref3.at;
insertPositionHint = false;
slateDom.EDITOR_TO_PENDING_SELECTION["delete"](editor);
scheduleOnDOMSelectionChange.cancel();
onDOMSelectionChange.cancel();
if (hasPendingAction()) {
flush();
}
slateDom.EDITOR_TO_PENDING_ACTION.set(editor, {
at: at,
run: run
});
// COMPAT: When deleting before a non-contenteditable element chrome only fires a beforeinput,
// (no input) and doesn't perform any dom mutations. Without a flush timeout we would never flush
// in this case and thus never actually perform the action.
actionTimeoutId = setTimeout(flush);
};
var handleDOMBeforeInput = function handleDOMBeforeInput(event) {
var _targetRange2;
if (flushTimeoutId) {
clearTimeout(flushTimeoutId);
flushTimeoutId = null;
}
if (slateDom.IS_NODE_MAP_DIRTY.get(editor)) {
return;
}
var type = event.inputType;
var targetRange = null;
var data = event.dataTransfer || event.data || undefined;
if (insertPositionHint !== false && type !== 'insertText' && type !== 'insertCompositionText') {
insertPositionHint = false;
}
var _event$getTargetRange = event.getTargetRanges(),
_event$getTargetRange2 = _slicedToArray(_event$getTargetRange, 1),
nativeTargetRange = _event$getTargetRange2[0];
if (nativeTargetRange) {
targetRange = ReactEditor.toSlateRange(editor, nativeTargetRange, {
exactMatch: false,
suppressThrow: true
});
}
// COMPAT: SelectionChange event is fired after the action is performed, so we
// have to manually get the selection here to ensure it's up-to-date.
var window = ReactEditor.getWindow(editor);
var domSelection = window.getSelection();
if (!targetRange && domSelection) {
nativeTargetRange = domSelection;
targetRange = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
suppressThrow: true
});
}
targetRange = (_targetRange2 = targetRange) !== null && _targetRange2 !== void 0 ? _targetRange2 : editor.selection;
if (!targetRange) {
return;
}
// By default, the input manager tries to store text diffs so that we can
// defer flushing them at a later point in time. We don't want to flush
// for every input event as this can be expensive. However, there are some
// scenarios where we cannot safely store the text diff and must instead
// schedule an action to let Slate normalize the editor state.
var canStoreDiff = true;
if (type.startsWith('delete')) {
if (slate.Range.isExpanded(targetRange)) {
var _Range$edges = slate.Range.edges(targetRange),
_Range$edges2 = _slicedToArray(_Range$edges, 2),
_start = _Range$edges2[0],
_end = _Range$edges2[1];
var _leaf = slate.Node.leaf(editor, _start.path);
if (_leaf.text.length === _start.offset && _end.offset === 0) {
var next = slate.Editor.next(editor, {
at: _start.path,
match: slate.Text.isText
});
if (next && slate.Path.equals(next[1], _end.path)) {
targetRange = {
anchor: _end,
focus: _end
};
}
}
}
var direction = type.endsWith('Backward') ? 'backward' : 'forward';
var _Range$edges3 = slate.Range.edges(targetRange),
_Range$edges4 = _slicedToArray(_Range$edges3, 2),
start = _Range$edges4[0],
end = _Range$edges4[1];
var _Editor$leaf = slate.Editor.leaf(editor, start.path),
_Editor$leaf2 = _slicedToArray(_Editor$leaf, 2),
leaf = _Editor$leaf2[0],
path = _Editor$leaf2[1];
var diff = {
text: '',
start: start.offset,
end: end.offset
};
var pendingDiffs = slateDom.EDITOR_TO_PENDING_DIFFS.get(editor);
var relevantPendingDiffs = pendingDiffs === null || pendingDiffs === void 0 ? void 0 : pendingDiffs.find(function (change) {
return slate.Path.equals(change.path, path);
});
var diffs = relevantPendingDiffs ? [relevantPendingDiffs.diff, diff] : [diff];
var text = slateDom.applyStringDiff.apply(void 0, [leaf.text].concat(diffs));
if (text.length === 0) {
// Text leaf will be removed, so we need to schedule an
// action to remove it so that Slate can normalize instead
// of storing as a diff
canStoreDiff = false;
}
if (slate.Range.isExpanded(targetRange)) {
if (canStoreDiff && slate.Path.equals(targetRange.anchor.path, targetRange.focus.path)) {
var point = {
path: targetRange.anchor.path,
offset: start.offset
};
var range = slate.Editor.range(editor, point, point);
handleUserSelect(range);
return storeDiff(targetRange.anchor.path, {
text: '',
end: end.offset,
start: start.offset
});
}
return scheduleAction(function () {
return slate.Editor.deleteFragment(editor, {
direction: direction
});
}, {
at: targetRange
});
}
}
switch (type) {
case 'deleteByComposition':
case 'deleteByCut':
case 'deleteByDrag':
{
return scheduleAction(function () {
return slate.Editor.deleteFragment(editor);
}, {
at: targetRange
});
}
case 'deleteContent':
case 'deleteContentForward':
{
var _targetRange3 = targetRange,
anchor = _targetRange3.anchor;
if (canStoreDiff && slate.Range.isCollapsed(targetRange)) {
var targetNode = slate.Node.leaf(editor, anchor.path);
if (anchor.offset < targetNode.text.length) {
return storeDiff(anchor.path, {
text: '',
start: anchor.offset,
end: anchor.offset + 1
});
}
}
return scheduleAction(function () {
return slate.Editor.deleteForward(editor);
}, {
at: targetRange
});
}
case 'deleteContentBackward':
{
var _nativeTargetRange;
var _targetRange4 = targetRange,
_anchor = _targetRange4.anchor;
// If we have a mismatch between the native and slate selection being collapsed
// we are most likely deleting a zero-width placeholder and thus should perform it
// as an action to ensure correct behavior (mostly happens with mark placeholders)
var nativeCollapsed = slateDom.isDOMSelection(nativeTargetRange) ? nativeTargetRange.isCollapsed : !!((_nativeTargetRange = nativeTargetRange) !== null && _nativeTargetRange !== void 0 && _nativeTargetRange.collapsed);
if (canStoreDiff && nativeCollapsed && slate.Range.isCollapsed(targetRange) && _anchor.offset > 0) {
return storeDiff(_anchor.path, {
text: '',
start: _anchor.offset - 1,
end: _anchor.offset
});
}
return scheduleAction(function () {
return slate.Editor.deleteBackward(editor);
}, {
at: targetRange
});
}
case 'deleteEntireSoftLine':
{
return scheduleAction(function () {
slate.Editor.deleteBackward(editor, {
unit: 'line'
});
slate.Editor.deleteForward(editor, {
unit: 'line'
});
}, {
at: targetRange
});
}
case 'deleteHardLineBackward':
{
return scheduleAction(function () {
return slate.Editor.deleteBackward(editor, {
unit: 'block'
});
}, {
at: targetRange
});
}
case 'deleteSoftLineBackward':
{
return scheduleAction(function () {
return slate.Editor.deleteBackward(editor, {
unit: 'line'
});
}, {
at: targetRange
});
}
case 'deleteHardLineForward':
{
return scheduleAction(function () {
return slate.Editor.deleteForward(editor, {
unit: 'block'
});
}, {
at: targetRange
});
}
case 'deleteSoftLineForward':
{
return scheduleAction(function () {
return slate.Editor.deleteForward(editor, {
unit: 'line'
});
}, {
at: targetRange
});
}
case 'deleteWordBackward':
{
return scheduleAction(function () {
return slate.Editor.deleteBackward(editor, {
unit: 'word'
});
}, {
at: targetRange
});
}
case 'deleteWordForward':
{
return scheduleAction(function () {
return slate.Editor.deleteForward(editor, {
unit: 'word'
});
}, {
at: targetRange
});
}
case 'insertLineBreak':
{
return scheduleAction(function () {
return slate.Editor.insertSoftBreak(editor);
}, {
at: targetRange
});
}
case 'insertParagraph':
{
return scheduleAction(function () {
return slate.Editor.insertBreak(editor);
}, {
at: targetRange
});
}
case 'insertCompositionText':
case 'deleteCompositionText':
case 'insertFromComposition':
case 'insertFromDrop':
case 'insertFromPaste':
case 'insertFromYank':
case 'insertReplacementText':
case 'insertText':
{
if (isDataTransfer(data)) {
return scheduleAction(function () {
return ReactEditor.insertData(editor, data);
}, {
at: targetRange
});
}
var _text = data !== null && data !== void 0 ? data : '';
// COMPAT: If we are writing inside a placeholder, the ime inserts the text inside
// the placeholder itself and thus includes the zero-width space inside edit events.
if (slateDom.EDITOR_TO_PENDING_INSERTION_MARKS.get(editor)) {
_text = _text.replace("\uFEFF", '');
}
// Pastes from the Android clipboard will generate `insertText` events.
// If the copied text contains any newlines, Android will append an
// extra newline to the end of the copied text.
if (type === 'insertText' && /.*\n.*\n$/.test(_text)) {
_text = _text.slice(0, -1);
}
// If the text includes a newline, split it at newlines and paste each component
// string, with soft breaks in between each.
if (_text.includes('\n')) {
return scheduleAction(function () {
var parts = _text.split('\n');
parts.forEach(function (line, i) {
if (line) {
slate.Editor.insertText(editor, line);
}
if (i !== parts.length - 1) {
slate.Editor.insertSoftBreak(editor);
}
});
}, {
at: targetRange
});
}
if (slate.Path.equals(targetRange.anchor.path, targetRange.focus.path)) {
var _Range$edges5 = slate.Range.edges(targetRange),
_Range$edges6 = _slicedToArray(_Range$edges5, 2),
_start2 = _Range$edges6[0],
_end2 = _Range$edges6[1];
var _diff = {
start: _start2.offset,
end: _end2.offset,
text: _text
};
// COMPAT: Swiftkey has a weird bug where the target range of the 2nd word
// inserted after a mark placeholder is inserted with an anchor offset off by 1.
// So writing 'some text' will result in 'some ttext'. Luckily all 'normal' insert
// text events are fired with the correct target ranges, only the final 'insertComposition'
// isn't, so we can adjust the target range start offset if we are confident this is the
// swiftkey insert causing the issue.
if (_text && insertPositionHint && type === 'insertCompositionText') {
var hintPosition = insertPositionHint.start + insertPositionHint.text.search(/\S|$/);
var diffPosition = _diff.start + _diff.text.search(/\S|$/);
if (diffPosition === hintPosition + 1 && _diff.end === insertPositionHint.start + insertPositionHint.text.length) {
_diff.start -= 1;
insertPositionHint = null;
scheduleFlush();
} else {
insertPositionHint = false;
}
} else if (type === 'insertText') {
if (insertPositionHint === null) {
insertPositionHint = _diff;
} else if (insertPositionHint && slate.Range.isCollapsed(targetRange) && insertPositionHint.end + insertPositionHint.text.length === _start2.offset) {
insertPositionHint = _objectSpread$5(_objectSpread$5({}, insertPositionHint), {}, {
text: insertPositionHint.text + _text
});
} else {
insertPositionHint = false;
}
} else {
insertPositionHint = false;
}
if (canStoreDiff) {
storeDiff(_start2.path, _diff);
return;
}
}
return scheduleAction(function () {
return slate.Editor.insertText(editor, _text);
}, {
at: targetRange
});
}
}
};
var hasPendingAction = function hasPendingAction() {
return !!slateDom.EDITOR_TO_PENDING_ACTION.get(editor);
};
var hasPendingDiffs = function hasPendingDiffs() {
var _EDITOR_TO_PENDING_DI4;
return !!((_EDITOR_TO_PENDING_DI4 = slateDom.EDITOR_TO_PENDING_DIFFS.get(editor)) !== null && _EDITOR_TO_PENDING_DI4 !== void 0 && _EDITOR_TO_PENDING_DI4.length);
};
var hasPendingChanges = function hasPendingChanges() {
return hasPendingAction() || hasPendingDiffs();
};
var isFlushing = function isFlushing() {
return flushing;
};
var handleUserSelect = function handleUserSelect(range) {
slateDom.EDITOR_TO_PENDING_SELECTION.set(editor, range);
if (flushTimeoutId) {
clearTimeout(flushTimeoutId);
flushTimeoutId = null;
}
var selection = editor.selection;
if (!range) {
return;
}
var pathChanged = !selection || !slate.Path.equals(selection.anchor.path, range.anchor.path);
var parentPathChanged = !selection || !slate.Path.equals(selection.anchor.path.slice(0, -1), range.anchor.path.slice(0, -1));
if (pathChanged && insertPositionHint || parentPathChanged) {
insertPositionHint = false;
}
if (pathChanged || hasPendingDiffs()) {
flushTimeoutId = setTimeout(flush, FLUSH_DELAY);
}
};
var handleInput = function handleInput() {
if (hasPendingAction() || !hasPendingDiffs()) {
flush();
}
};
var handleKeyDown = function handleKeyDown(_) {
// COMPAT: Swiftkey closes the keyboard when typing inside a empty node
// directly next to a non-contenteditable element (= the placeholder).
// The only event fired soon enough for us to allow hiding the placeholder
// without swiftkey picking it up is the keydown event, so we have to hide it
// here. See https://github.com/ianstormtaylor/slate/pull/4988#issuecomment-1201050535
if (!hasPendingDiffs()) {
updatePlaceholderVisibility(true);
setTimeout(updatePlaceholderVisibility);
}
};
var scheduleFlush = function scheduleFlush() {
if (!hasPendingAction()) {
actionTimeoutId = setTimeout(flush);
}
};
var handleDomMutations = function handleDomMutations(mutations) {
if (hasPendingDiffs() || hasPendingAction()) {
return;
}
if (mutations.some(function (mutation) {
return slateDom.isTrackedMutation(editor, mutation, mutations);
})) {
var _EDITOR_TO_FORCE_REND;
// Cause a re-render to restore the dom state if we encounter tracked mutations without
// a corresponding pending action.
(_EDITOR_TO_FORCE_REND = slateDom.EDITOR_TO_FORCE_RENDER.get(editor)) === null || _EDITOR_TO_FORCE_REND === void 0 || _EDITOR_TO_FORCE_REND();
}
};
return {
flush: flush,
scheduleFlush: scheduleFlush,
hasPendingDiffs: hasPendingDiffs,
hasPendingAction: hasPendingAction,
hasPendingChanges: hasPendingChanges,
isFlushing: isFlushing,
handleUserSelect: handleUserSelect,
handleCompositionEnd: handleCompositionEnd,
handleCompositionStart: handleCompositionStart,
handleDOMBeforeInput: handleDOMBeforeInput,
handleKeyDown: handleKeyDown,
handleDomMutations: handleDomMutations,
handleInput: handleInput
};
}
function useIsMounted() {
var isMountedRef = React.useRef(false);
React.useEffect(function () {
isMountedRef.current = true;
return function () {
isMountedRef.current = false;
};
}, []);
return isMountedRef.current;
}
/**
* Prevent warning on SSR by falling back to useEffect when DOM isn't available
*/
var useIsomorphicLayoutEffect = slateDom.CAN_USE_DOM ? React.useLayoutEffect : React.useEffect;
function useMutationObserver(node, callback, options) {
var _useState = React.useState(function () {
return new MutationObserver(callback);
}),
_useState2 = _slicedToArray(_useState, 1),
mutationObserver = _useState2[0];
useIsomorphicLayoutEffect(function () {
// Discard mutations caused during render phase. This works due to react calling
// useLayoutEffect synchronously after the render phase before the next tick.
mutationObserver.takeRecords();
});
React.useEffect(function () {
if (!node.current) {
throw new Error('Failed to attach MutationObserver, `node` is undefined');
}
mutationObserver.observe(node.current, options);
return function () {
return mutationObserver.disconnect();
};
}, [mutationObserver, node, options]);
}
var _excluded$2 = ["node"];
function ownKeys$4(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread$4(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$4(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$4(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var MUTATION_OBSERVER_CONFIG$1 = {
subtree: true,
childList: true,
characterData: true
};
var useAndroidInputManager = !slateDom.IS_ANDROID ? function () {
return null;
} : function (_ref) {
var node = _ref.node,
options = _objectWithoutProperties(_ref, _excluded$2);
if (!slateDom.IS_ANDROID) {
return null;
}
var editor = useSlateStatic();
var isMounted = useIsMounted();
var _useState = React.useState(function () {
return createAndroidInputManager(_objectSpread$4({
editor: editor
}, options));
}),
_useState2 = _slicedToArray(_useState, 1),
inputManager = _useState2[0];
useMutationObserver(node, inputManager.handleDomMutations, MUTATION_OBSERVER_CONFIG$1);
slateDom.EDITOR_TO_SCHEDULE_FLUSH.set(editor, inputManager.scheduleFlush);
if (isMounted) {
inputManager.flush();
}
return inputManager;
};
function ownKeys$3(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread$3(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$3(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$3(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
/**
* Leaf content strings.
*/
var String$1 = function String(props) {
var isLast = props.isLast,
leaf = props.leaf,
parent = props.parent,
text = props.text;
var editor = useSlateStatic();
var path = ReactEditor.findPath(editor, text);
var parentPath = slate.Path.parent(path);
var isMarkPlaceholder = Boolean(leaf[slateDom.MARK_PLACEHOLDER_SYMBOL]);
// COMPAT: Render text inside void nodes with a zero-width space.
// So the node can contain selection but the text is not visible.
if (editor.isVoid(parent)) {
return /*#__PURE__*/React.createElement(ZeroWidthString, {
length: slate.Node.string(parent).length
});
}
// COMPAT: If this is the last text node in an empty block, render a zero-
// width space that will convert into a line break when copying and pasting
// to support expected plain text.
if (leaf.text === '' && parent.children[parent.children.length - 1] === text && !editor.isInline(parent) && slate.Editor.string(editor, parentPath) === '') {
return /*#__PURE__*/React.createElement(ZeroWidthString, {
isLineBreak: true,
isMarkPlaceholder: isMarkPlaceholder
});
}
// COMPAT: If the text is empty, it's because it's on the edge of an inline
// node, so we render a zero-width space so that the selection can be
// inserted next to it still.
if (leaf.text === '') {
return /*#__PURE__*/React.createElement(ZeroWidthString, {
isMarkPlaceholder: isMarkPlaceholder
});
}
// COMPAT: Browsers will collapse trailing new lines at the end of blocks,
// so we need to add an extra trailing new lines to prevent that.
if (isLast && leaf.text.slice(-1) === '\n') {
return /*#__PURE__*/React.createElement(TextString, {
isTrailing: true,
text: leaf.text
});
}
return /*#__PURE__*/React.createElement(TextString, {
text: leaf.text
});
};
/**
* Leaf strings with text in them.
*/
var TextString = function TextString(props) {
var text = props.text,
_props$isTrailing = props.isTrailing,
isTrailing = _props$isTrailing === void 0 ? false : _props$isTrailing;
var ref = React.useRef(null);
var getTextContent = function getTextContent() {
return "".concat(text !== null && text !== void 0 ? text : '').concat(isTrailing ? '\n' : '');
};
var _useState = React.useState(getTextContent),
_useState2 = _slicedToArray(_useState, 1),
initialText = _useState2[0];
// This is the actual text rendering boundary where we interface with the DOM
// The text is not rendered as part of the virtual DOM, as since we handle basic character insertions natively,
// updating the DOM is not a one way dataflow anymore. What we need here is not reconciliation and diffing
// with previous version of the virtual DOM, but rather diffing with the actual DOM element, and replace the DOM <span> content
// exactly if and only if its current content does not match our current virtual DOM.
// Otherwise the DOM TextNode would always be replaced by React as the user types, which interferes with native text features,
// eg makes native spellcheck opt out from checking the text node.
// useLayoutEffect: updating our span before browser paint
useIsomorphicLayoutEffect(function () {
// null coalescing text to make sure we're not outputing "null" as a string in the extreme case it is nullish at runtime
var textWithTrailing = getTextContent();
if (ref.current && ref.current.textContent !== textWithTrailing) {
ref.current.textContent = textWithTrailing;
}
// intentionally not specifying dependencies, so that this effect runs on every render
// as this effectively replaces "specifying the text in the virtual DOM under the <span> below" on each render
});
// We intentionally render a memoized <span> that only receives the initial text content when the component is mounted.
// We defer to the layout effect above to update the `textContent` of the span element when needed.
return /*#__PURE__*/React.createElement(MemoizedText$1, {
ref: ref
}, initialText);
};
var MemoizedText$1 = /*#__PURE__*/React.memo( /*#__PURE__*/React.forwardRef(function (props, ref) {
return /*#__PURE__*/React.createElement("span", {
"data-slate-string": true,
ref: ref
}, props.children);
}));
/**
* Leaf strings without text, render as zero-width strings.
*/
var ZeroWidthString = function ZeroWidthString(props) {
var _props$length = props.length,
length = _props$length === void 0 ? 0 : _props$length,
_props$isLineBreak = props.isLineBreak,
isLineBreak = _props$isLineBreak === void 0 ? false : _props$isLineBreak,
_props$isMarkPlacehol = props.isMarkPlaceholder,
isMarkPlaceholder = _props$isMarkPlacehol === void 0 ? false : _props$isMarkPlacehol;
var attributes = {
'data-slate-zero-width': isLineBreak ? 'n' : 'z',
'data-slate-length': length
};
if (isMarkPlaceholder) {
attributes['data-slate-mark-placeholder'] = true;
}
return /*#__PURE__*/React.createElement("span", _objectSpread$3({}, attributes), !(slateDom.IS_ANDROID || slateDom.IS_IOS) || !isLineBreak ? "\uFEFF" : null, isLineBreak ? /*#__PURE__*/React.createElement("br", null) : null);
};
function ownKeys$2(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread$2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$2(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$2(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
// Delay the placeholder on Android to prevent the keyboard from closing.
// (https://github.com/ianstormtaylor/slate/pull/5368)
var PLACEHOLDER_DELAY = slateDom.IS_ANDROID ? 300 : 0;
function disconnectPlaceholderResizeObserver(placeholderResizeObserver, releaseObserver) {
if (placeholderResizeObserver.current) {
placeholderResizeObserver.current.disconnect();
if (releaseObserver) {
placeholderResizeObserver.current = null;
}
}
}
function clearTimeoutRef(timeoutRef) {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
}
/**
* Individual leaves in a text node with unique formatting.
*/
var Leaf = function Leaf(props) {
var leaf = props.leaf,
isLast = props.isLast,
text = props.text,
parent = props.parent,
renderPlaceholder = props.renderPlaceholder,
_props$renderLeaf = props.renderLeaf,
renderLeaf = _props$renderLeaf === void 0 ? function (props) {
return /*#__PURE__*/React.createElement(DefaultLeaf, _objectSpread$2({}, props));
} : _props$renderLeaf;
var editor = useSlateStatic();
var placeholderResizeObserver = React.useRef(null);
var placeholderRef = React.useRef(null);
var _useState = React.useState(false),
_useState2 = _slicedToArray(_useState, 2),
showPlaceholder = _useState2[0],
setShowPlaceholder = _useState2[1];
var showPlaceholderTimeoutRef = React.useRef(null);
var callbackPlaceholderRef = React.useCallback(function (placeholderEl) {
disconnectPlaceholderResizeObserver(placeholderResizeObserver, placeholderEl == null);
if (placeholderEl == null) {
var _leaf$onPlaceholderRe;
slateDom.EDITOR_TO_PLACEHOLDER_ELEMENT["delete"](editor);
(_leaf$onPlaceholderRe = leaf.onPlaceholderResize) === null || _leaf$onPlaceholderRe === void 0 || _leaf$onPlaceholderRe.call(leaf, null);
} else {
slateDom.EDITOR_TO_PLACEHOLDER_ELEMENT.set(editor, placeholderEl);
if (!placeholderResizeObserver.current) {
// Create a new observer and observe the placeholder element.
var ResizeObserver = window.ResizeObserver || resizeObserver.ResizeObserver;
placeholderResizeObserver.current = new ResizeObserver(function () {
var _leaf$onPlaceholderRe2;
(_leaf$onPlaceholderRe2 = leaf.onPlaceholderResize) === null || _leaf$onPlaceholderRe2 === void 0 || _leaf$onPlaceholderRe2.call(leaf, placeholderEl);
});
}
placeholderResizeObserver.current.observe(placeholderEl);
placeholderRef.current = placeholderEl;
}
}, [placeholderRef, leaf, editor]);
var children = /*#__PURE__*/React.createElement(String$1, {
isLast: isLast,
leaf: leaf,
parent: parent,
text: text
});
var leafIsPlaceholder = Boolean(leaf[slateDom.PLACEHOLDER_SYMBOL]);
React.useEffect(function () {
if (leafIsPlaceholder) {
if (!showPlaceholderTimeoutRef.current) {
// Delay the placeholder, so it will not render in a selection
showPlaceholderTimeoutRef.current = setTimeout(function () {
setShowPlaceholder(true);
showPlaceholderTimeoutRef.current = null;
}, PLACEHOLDER_DELAY);
}
} else {
clearTimeoutRef(showPlaceholderTimeoutRef);
setShowPlaceholder(false);
}
return function () {
return clearTimeoutRef(showPlaceholderTimeoutRef);
};
}, [leafIsPlaceholder, setShowPlaceholder]);
if (leafIsPlaceholder && showPlaceholder) {
var placeholderProps = {
children: leaf.placeholder,
attributes: {
'data-slate-placeholder': true,
style: {
position: 'absolute',
top: 0,
pointerEvents: 'none',
width: '100%',
maxWidth: '100%',
display: 'block',
opacity: '0.333',
userSelect: 'none',
textDecoration: 'none',
// Fixes https://github.com/udecode/plate/issues/2315
WebkitUserModify: slateDom.IS_WEBKIT ? 'inherit' : undefined
},
contentEditable: false,
ref: callbackPlaceholderRef
}
};
children = /*#__PURE__*/React.createElement(React.Fragment, null, renderPlaceholder(placeholderProps), children);
}
// COMPAT: Having the `data-` attributes on these leaf elements ensures that
// in certain misbehaving browsers they aren't weirdly cloned/destroyed by
// contenteditable behaviors. (2019/05/08)
var attributes = {
'data-slate-leaf': true
};
return renderLeaf({
attributes: attributes,
children: children,
leaf: leaf,
text: text
});
};
var MemoizedLeaf = /*#__PURE__*/React.memo(Leaf, function (prev, next) {
return next.parent === prev.parent && next.isLast === prev.isLast && next.renderLeaf === prev.renderLeaf && next.renderPlaceholder === prev.renderPlaceholder && next.text === prev.text && slate.Text.equals(next.leaf, prev.leaf) && next.leaf[slateDom.PLACEHOLDER_SYMBOL] === prev.leaf[slateDom.PLACEHOLDER_SYMBOL];
});
var DefaultLeaf = function DefaultLeaf(props) {
var attributes = props.attributes,
children = props.children;
return /*#__PURE__*/React.createElement("span", _objectSpread$2({}, attributes), children);
};
/**
* Text.
*/
var Text = function Text(props) {
var decorations = props.decorations,
isLast = props.isLast,
parent = props.parent,
renderPlaceholder = props.renderPlaceholder,
renderLeaf = props.renderLeaf,
text = props.text;
var editor = useSlateStatic();
var ref = React.useRef(null);
var leaves = slate.Text.decorations(text, decorations);
var key = ReactEditor.findKey(editor, text);
var children = [];
for (var i = 0; i < leaves.length; i++) {
var leaf = leaves[i];
children.push( /*#__PURE__*/React.createElement(MemoizedLeaf, {
isLast: isLast && i === leaves.length - 1,
key: "".concat(key.id, "-").concat(i),
renderPlaceholder: renderPlaceholder,
leaf: leaf,
text: text,
parent: parent,
renderLeaf: renderLeaf
}));
}
// Update element-related weak maps with the DOM element ref.
var callbackRef = React.useCallback(function (span) {
var KEY_TO_ELEMENT = slateDom.EDITOR_TO_KEY_TO_ELEMENT.get(editor);
if (span) {
KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 || KEY_TO_ELEMENT.set(key, span);
slateDom.NODE_TO_ELEMENT.set(text, span);
slateDom.ELEMENT_TO_NODE.set(span, text);
} else {
KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 || KEY_TO_ELEMENT["delete"]