UNPKG

usxeditor

Version:

USX editor react component.

219 lines (218 loc) 12 kB
"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 &nbsp; 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;