usxeditor
Version:
USX editor react component.
219 lines (218 loc) • 12 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importDefault(require("react"));
var react_autobind_1 = __importDefault(require("react-autobind"));
var DomUtils = __importStar(require("../DomUtils"));
var EditableText = /** @class */ (function (_super) {
__extends(EditableText, _super);
function EditableText(props) {
var _this = _super.call(this, props) || this;
(0, react_autobind_1.default)(_this);
_this.containerRef = react_1.default.createRef();
return _this;
}
EditableText.prototype.shouldComponentUpdate = function (nextProps) {
if (nextProps.value !== this.props.value)
return true;
if (nextProps.allowFocus !== this.props.allowFocus)
return true;
if (nextProps.selectionStart !== this.props.selectionStart)
return this.isSelectionInComponent(nextProps.selectionStart, nextProps.startOffset, nextProps.value);
if (nextProps.selectionEnd !== this.props.selectionEnd)
return this.isSelectionInComponent(nextProps.selectionEnd, nextProps.startOffset, nextProps.value);
return false;
};
EditableText.prototype.componentDidMount = function () {
this.restoreSelection();
};
EditableText.prototype.componentDidUpdate = function () {
this.restoreSelection();
};
EditableText.prototype.isSelectionInComponent = function (selection, startOffset, content) {
if (selection >= startOffset && selection <= startOffset + (content === null || content === void 0 ? void 0 : content.length))
return true;
return false;
};
EditableText.prototype.restoreSelection = function () {
var _a, _b;
if (!this.props.allowFocus)
return;
if (this.props.selectionStart < this.props.startOffset)
return;
if (this.props.selectionStart > this.props.startOffset + ((_a = this.props.value) === null || _a === void 0 ? void 0 : _a.length))
return;
var firstNode = (_b = this.containerRef.current) === null || _b === void 0 ? void 0 : _b.firstChild;
if (!firstNode)
return;
DomUtils.setSelectionStart(firstNode, this.props.selectionStart - this.props.startOffset);
};
EditableText.prototype.handleKeyDown = function (e) {
var _a, _b, _c, _d;
var currentSelection = ((_a = document === null || document === void 0 ? void 0 : document.getSelection()) === null || _a === void 0 ? void 0 : _a.anchorOffset) || 0;
var inputEvent = e.nativeEvent;
var key = inputEvent.key;
if (key !== 'Backspace')
return;
var currentNode = (_b = document === null || document === void 0 ? void 0 : document.getSelection()) === null || _b === void 0 ? void 0 : _b.anchorNode;
var text = currentNode === null || currentNode === void 0 ? void 0 : currentNode.textContent;
var allPrecedingWhitespace = !(text === null || text === void 0 ? void 0 : text.substring(0, currentSelection).split('').some(function (x) { return x !== ' '; }));
var atStartOfBlock = currentSelection === 0 || !text || allPrecedingWhitespace;
if (atStartOfBlock) {
e.preventDefault();
e.stopPropagation();
(_d = (_c = this.props).onDeleteBackward) === null || _d === void 0 ? void 0 : _d.call(_c);
}
};
EditableText.prototype.handleClick = function (_) {
var _a, _b;
var selectionStartIndex = ((_a = document === null || document === void 0 ? void 0 : document.getSelection()) === null || _a === void 0 ? void 0 : _a.anchorOffset) || 0;
var selectionEndIndex = ((_b = document === null || document === void 0 ? void 0 : document.getSelection()) === null || _b === void 0 ? void 0 : _b.focusOffset) || 0;
this.props.onSelectionChanged(this.props.startOffset + selectionStartIndex, this.props.startOffset + selectionEndIndex);
};
// TODO: inputType value we need to support.
// insertText
// deleteContentBackward
// deleteContentForward
// insertCompositionText (eg. android, does this most of the time)
// insertParagraph (ie. press enter)
// insertFromPaste
// deleteByCut
// Maybe:
// historyUndo
// historyRedo
EditableText.prototype.handleInput = function (e) {
var _this = this;
var inputEvent = e.nativeEvent;
var data = inputEvent.data;
var inputType = inputEvent.inputType;
var text = e.currentTarget.textContent || '';
var selectionStartIndex = -1;
var selectionEndIndex = -1;
if (inputType === 'insertText') {
clearTimeout(this.inputTimer);
this.inputTimer = setTimeout(function () {
var _a;
_a = _this.insertText(document === null || document === void 0 ? void 0 : document.getSelection(), text, data), selectionStartIndex = _a[0], selectionEndIndex = _a[1];
_this.props.onSelectionChanged(_this.props.startOffset + selectionStartIndex, _this.props.startOffset + selectionEndIndex);
});
return;
}
else if (inputType === 'insertParagraph') {
if (this.props.onInsertMarker)
selectionStartIndex = this.insertNewParagraph(document === null || document === void 0 ? void 0 : document.getSelection(), text);
e.preventDefault();
}
else if (inputType === 'deleteContentBackward') {
clearTimeout(this.inputTimer);
this.inputTimer = setTimeout(function () {
selectionStartIndex = _this.deleteContentBackward(document === null || document === void 0 ? void 0 : document.getSelection(), text);
_this.props.onSelectionChanged(_this.props.startOffset + selectionStartIndex, _this.props.startOffset + selectionEndIndex);
});
return;
}
this.props.onSelectionChanged(this.props.startOffset + selectionStartIndex, this.props.startOffset + selectionEndIndex);
};
// returns true if marker found otherwise false.
EditableText.prototype.checkForMarker = function (marker, text) {
var toMatch = "\\\\".concat(marker, "(\\s|\\*)");
var re = new RegExp(toMatch);
return text.match(re);
};
EditableText.prototype.markerLength = function (marker) {
// backslash + length of marker. (not including the space)
return 1 + marker.length;
};
// Returns new selection index.
EditableText.prototype.insertText = function (selection, text, data) {
if (!selection)
return [0, 0];
var currentStartSelection = selection.anchorOffset || 0;
var currentEndSelection = selection.focusOffset || 0;
var endMarker = data === '*';
if (this.props.onInsertMarker && this.props.paraMap && (text === null || text === void 0 ? void 0 : text.includes('\\')) && (data === ' ' || endMarker)) {
for (var _i = 0, _a = this.props.paraMap; _i < _a.length; _i++) {
var paraMarker = _a[_i];
if (!this.checkForMarker(paraMarker, text))
continue;
this.props.onInsertMarker(true, paraMarker);
return [currentStartSelection - this.markerLength(paraMarker), currentEndSelection - this.markerLength(paraMarker)];
}
for (var _b = 0, _c = Object.keys(this.props.charMap || {}); _b < _c.length; _b++) {
var charMarker = _c[_b];
if (!this.checkForMarker(charMarker, text))
continue;
this.props.onInsertMarker(false, charMarker + (endMarker ? '*' : ''));
return [currentStartSelection - this.markerLength(charMarker), currentEndSelection - this.markerLength(charMarker)];
}
}
this.props.onChanged(text);
return [currentStartSelection, currentEndSelection];
};
// Returns new selection index.
EditableText.prototype.deleteContentBackward = function (selection, text) {
var currentSelection = (selection === null || selection === void 0 ? void 0 : selection.anchorOffset) || 0;
this.props.onChanged(text);
return currentSelection;
};
// Returns new selection index.
// Should never be called in this.props.onInsertMarker is undefined.
EditableText.prototype.insertNewParagraph = function (selection, text) {
var _a, _b;
var newParagraphMarker = ' \\p ';
var currentSelection = DomUtils.toTextNodeOffset(selection === null || selection === void 0 ? void 0 : selection.anchorNode, (selection === null || selection === void 0 ? void 0 : selection.anchorOffset) || 0);
text = (text === null || text === void 0 ? void 0 : text.substring(0, currentSelection)) + newParagraphMarker + (text === null || text === void 0 ? void 0 : text.substring(currentSelection));
(_b = (_a = this.props).onInsertMarker) === null || _b === void 0 ? void 0 : _b.call(_a, true, 'p', text);
var extraSpaceLength = 2;
return currentSelection + extraSpaceLength;
};
EditableText.prototype.render = function () {
var value = this.props.value;
// TODO: REVIEW should this be instead of ' '?
if ((value === null || value === void 0 ? void 0 : value.length) === 0)
value = ' ';
return react_1.default.createElement("span", { ref: this.containerRef, style: { paddingLeft: 4, paddingRight: 4 }, className: 'EditableText', contentEditable: true, onInput: this.handleInput, onKeyDown: this.handleKeyDown, onClick: this.handleClick }, value);
};
return EditableText;
}(react_1.default.Component));
exports.default = EditableText;