usxeditor
Version:
USX editor react component.
159 lines (158 loc) • 7.44 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultDeleteBackward = exports.backspaceDeletesComponent = exports.allowMerge = exports.postIncrementStartOffset = exports.parentTag = exports.getTagName = exports.readTagFromRaw = exports.toOuterUsx = exports.toAttributesString = exports.serializeMerge = exports.serialize = exports.addHandlersToChildren = void 0;
var react_1 = __importDefault(require("react"));
// Add needed handlers to a react components child components.
// Currently adds:
// onChanged + onInsertMarker.
// The onChanged handler implementation responds to a child components change event and
// calculates how that change should be applied to the current component and invokes
// the current components onChange property, with the resultant value.
// The onInsertMarker handler implementation just passes the event upwards, after updating the events
// newValue argument if needed.
function addHandlersToChildren(parentProps) {
var children = parentProps.children;
if (!children)
return;
var singleChild = false;
if (!("length" in children)) {
singleChild = true;
children = [children];
}
var ret = children.filter(function (x) { return x; }).map(function (child, index) { return react_1.default.cloneElement(child, {
onChanged: function (v) { return parentProps.onChanged(serialize(children, index, v)); },
onSelectionChanged: parentProps.onSelectionChanged,
onInsertMarker: function (isPara, style, newValue) { return parentProps.onInsertMarker(isPara, style, serialize(children, index, newValue)); },
onDeleteBackward: function () { var _a; return (_a = parentProps.onDeleteBackward) === null || _a === void 0 ? void 0 : _a.call(parentProps, index); }
}); });
return singleChild ? ret[0] : ret;
}
exports.addHandlersToChildren = addHandlersToChildren;
function serialize(children, replacementIndex, replacementValue) {
if (replacementIndex === void 0) { replacementIndex = -1; }
if (replacementValue === void 0) { replacementValue = undefined; }
if (!children)
return '';
if (!("length" in children))
return replacementIndex === 0 ? replacementValue || '' : children.props.raw;
return children.filter(function (x) { return x; }).map(function (child, index) { return index === replacementIndex && replacementValue !== undefined ? replacementValue : child.props.raw; }).join('');
}
exports.serialize = serialize;
function serializeMerge(children, from, to) {
if (!children)
return '';
if (!("length" in children))
return children.props.raw;
return children.map(function (child, index) {
var _a, _b;
if (!child)
return null;
// This makes no sense so ignore the merge.
if (from === to)
return child.props.raw;
if (index !== from && index !== to)
return child.props.raw;
if (index === from)
return null;
var fromObject = children[from];
var fromObjectContent = (_a = fromObject.props.value) !== null && _a !== void 0 ? _a : serialize(fromObject.props.children);
var toObjectContent = (_b = child.props.value) !== null && _b !== void 0 ? _b : serialize(child.props.children);
// (from > to) If merging from right into left
// (to > from) If merging from left into right
var newContents = from > to ? toObjectContent + fromObjectContent : fromObjectContent + toObjectContent;
// If EditableText (text node), then just return contents.
if (child.props.value)
return newContents;
return toOuterUsx(readTagFromRaw(child.props.raw), child.props.attrs, newContents);
}).join('');
}
exports.serializeMerge = serializeMerge;
function toAttributesString(attrs) {
if (!attrs)
return '';
return Object.keys(attrs).map(function (key) { return "".concat(key, "=\"").concat(attrs[key], "\""); }).join(' ');
}
exports.toAttributesString = toAttributesString;
function toOuterUsx(tag, attrs, content) {
return "<".concat(tag, " ").concat(toAttributesString(attrs), ">").concat(content, "</").concat(tag, ">");
}
exports.toOuterUsx = toOuterUsx;
function readTagFromRaw(raw) {
if (!raw)
return '';
var index = raw.indexOf(' ');
if (index === -1) {
var endMarker = raw.indexOf('>');
index = raw.indexOf('/>');
if (index !== -1 && endMarker !== -1)
index = Math.min(index, endMarker);
}
if (index === -1)
index = raw.indexOf('>');
return raw.substring(1, index);
}
exports.readTagFromRaw = readTagFromRaw;
function getTagName(child) {
if (child.props.value)
return '';
return readTagFromRaw(child.props.raw);
}
exports.getTagName = getTagName;
function parentTag(tagStack) {
if (!tagStack)
return null;
if (tagStack.length <= 1)
return null;
return tagStack[tagStack.length - 2];
}
exports.parentTag = parentTag;
function postIncrementStartOffset(data, text) {
var ret = data.startOffset;
var implicitOffsetSeparator = 1;
// Having an extra offset between text nodes allows distinguishing between being at the end of text node and the start of the next text node.
data.startOffset += text.length + implicitOffsetSeparator;
return ret;
}
exports.postIncrementStartOffset = postIncrementStartOffset;
function allowMerge(toTag, fromTag) {
// Expand this as needed.
if (toTag === 'note' || toTag === 'verse' || toTag === 'unmatched')
return false;
if (fromTag === 'annot')
return false;
return true;
}
exports.allowMerge = allowMerge;
function backspaceDeletesComponent(toTag) {
if (toTag === 'verse')
return true;
if (toTag === 'unmatched')
return true;
return false;
}
exports.backspaceDeletesComponent = backspaceDeletesComponent;
function defaultDeleteBackward(props, index, componentTagName, attrs) {
var _a;
// From first child in paragraph, just pass up for parent component to handle
if (index === 0) {
(_a = props.onDeleteBackward) === null || _a === void 0 ? void 0 : _a.call(props);
}
// Combine the child component with it's preceding component.
else {
var currentComponentTag = getTagName(props.children[index]);
var previousComponentTag = getTagName(props.children[index - 1]);
if (allowMerge(previousComponentTag, currentComponentTag)) {
props.onChanged(toOuterUsx(componentTagName, attrs, serializeMerge(props.children, index, index - 1)));
var newSelectionOffset = props.children[index].props.startOffset - 1;
props.onSelectionChanged(newSelectionOffset, newSelectionOffset);
}
else if (backspaceDeletesComponent(previousComponentTag)) {
// We could allow just deleting it. (ie. just call serialize(props.children, index - 1, ''))
props.onChanged(toOuterUsx(componentTagName, attrs, serialize(props.children, index - 1, '')));
}
}
}
exports.defaultDeleteBackward = defaultDeleteBackward;