react-text-holder-insertable
Version:
insert text holder block to content editable area
134 lines (106 loc) • 3.95 kB
JavaScript
import $ from 'jquery';
import { get } from 'lodash';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import ContentEditable from 'react-contenteditable';
var style = {"text-holder":"_text-holder-module__text-holder__YG9kW","text-holder-label":"_text-holder-module__text-holder-label__2FfSn","text-holder-cancel":"_text-holder-module__text-holder-cancel__3Sv40"};
class TextHolder {
constructor(label, options = {}) {
this.getJq = () => {
const holderLabel = $("<span/>").addClass(style["text-holder-label"]).css(this.options.textHolderLabelStyle || {}).html(this.label);
const jq = $("<span/>").addClass(style["text-holder"]).css(this.options.textHolderStyle || {}).attr("contenteditable", "false").append(holderLabel);
if (this.options.withCancel) {
const holderCancel = $("<span/>").addClass(style["text-holder-cancel"]).css(this.options.textHolderCancelStyle || {}).html("x");
jq.append(holderCancel);
}
return jq;
};
this.getEl = () => this.getJq()[0];
this.getHolderOuterHTML = () => this.getEl().outerHTML;
this.insert = (anchorOffset, setHtml, boxRef) => {
const inputBox = get(boxRef, "current.el.current");
if (anchorOffset >= 0 && anchorOffset !== null && inputBox) {
let __html = $(inputBox).html();
const front = __html.substring(0, anchorOffset);
const latter = __html.substring(anchorOffset);
const inserted = this.getEl().outerHTML;
__html = `${front}${inserted}${latter}`;
setHtml(__html);
}
};
this.label = label;
this.options = options;
}
}
function TextHolderInsertable({
html,
onChange,
initialAnchorOffset,
onAnchorOffsetChange,
onTextChange,
boxRef
}) {
const [__anchorOffset, __setAnchorOffset] = useState(initialAnchorOffset || 0);
const [__html, __setHtml] = useState(html || "");
const handleChange = e => {
onChange(e);
__setHtml(e.target.value);
};
useEffect(() => {
onAnchorOffsetChange(__anchorOffset);
}, [__anchorOffset, onAnchorOffsetChange]);
useEffect(() => {
__setHtml(html);
}, [html]);
useEffect(() => {
onTextChange && onTextChange(getText());
}, [__html]);
function getText() {
const jq = $("<div/>").html(__html);
jq.find(`.${style["text-holder-cancel"]}`).each((_, ele) => $(ele).remove());
return jq.text();
}
function getTextActualStartOffset(startContainer) {
const prevSib = startContainer.previousSibling;
if (!prevSib) return 0;
const prevSibContentLen = prevSib.nodeType === 1 ? prevSib.outerHTML.length : prevSib.textContent.length;
return prevSibContentLen + getTextActualStartOffset(prevSib);
}
const updateAnchorOffset = e => {
const sel = window.getSelection();
const jq = $(e.target);
if ([style["text-holder"], style["text-holder-label"], style["text-holder-cancel"]].every(className => !jq.hasClass(className))) {
__setAnchorOffset(getTextActualStartOffset(sel.getRangeAt(0).startContainer) + sel.anchorOffset);
}
};
const handleClick = e => {
if ($(e.target).hasClass(style["text-holder-cancel"])) {
const box = e.target.parentNode.parentNode;
$(e.target.parentNode).remove();
__setHtml($(box).html());
}
updateAnchorOffset(e);
};
const handleKeyUp = e => {
updateAnchorOffset(e);
};
return /*#__PURE__*/React.createElement(ContentEditable, {
ref: boxRef,
html: __html,
disabled: false,
onChange: handleChange,
onClick: handleClick,
onKeyUp: handleKeyUp
});
}
TextHolderInsertable.propTypes = {
ref: PropTypes.object,
html: PropTypes.string,
onChange: PropTypes.func,
onTextChange: PropTypes.func,
initialAnchorOffset: PropTypes.number,
onAnchorOffsetChange: PropTypes.func
};
export default TextHolderInsertable;
export { TextHolder };
//# sourceMappingURL=index.modern.js.map