chayns-components
Version:
A set of beautiful React components for developing chayns® applications.
386 lines (380 loc) • 14.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = void 0;
var _clsx = _interopRequireDefault(require("clsx"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _react = _interopRequireWildcard(require("react"));
var _loadOptionalDependency = _interopRequireDefault(require("../../utils/loadOptionalDependency"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
/**
* @component
*/
function requireEmojione(returnPromise) {
return (0, _loadOptionalDependency.default)('emojione', 'emojione', ['https://cdn.jsdelivr.net/npm/emojione@3.1.7/lib/js/emojione.min.js'], ['https://cdn.jsdelivr.net/npm/emojione@3.1.7/extras/css/emojione.min.css'], returnPromise);
}
/**
* A text input that allows emojis to be put in.
*/
class EmojiInput extends _react.Component {
constructor(props) {
super(props);
this.lastKeyPressed = null;
this.firstRender = true;
this.activeNode = 0;
this.cursorPos = 0;
this.getActiveChildNode = () => {
const inputDiv = this.input;
const selection = window.getSelection();
const {
anchorNode
} = selection;
const {
childNodes
} = inputDiv;
let activeChildNode = -1;
if (anchorNode && anchorNode !== inputDiv) {
for (let i = 0; i < childNodes.length; i += 1) {
let curNode = childNodes[i];
if (chayns.env.isIOS && curNode.nodeName.toUpperCase() === 'I') {
// eslint-disable-next-line prefer-destructuring
curNode = curNode.childNodes[0];
}
if (curNode === anchorNode || curNode.wholeText === anchorNode.wholeText && curNode.nextElementSibling === anchorNode.nextElementSibling && curNode.previousElementSibling === anchorNode.previousElementSibling) {
activeChildNode = curNode.nodeType === 1 ? i + 1 : i;
break;
}
}
} else {
activeChildNode = this.activeNode;
}
return activeChildNode;
};
this.setCursorPos = () => {
const inputDiv = this.input;
const {
activeNode,
cursorPos
} = this;
if (cursorPos > -1) {
inputDiv.focus();
const inputChildNodes = inputDiv.childNodes;
const range = document.createRange();
if (activeNode > -1) {
const cursorNode = inputChildNodes[activeNode];
if (cursorNode) {
if (cursorNode.nodeType === 1 || cursorNode.length < cursorPos) {
const nextSibling = cursorNode.nextSibling ? cursorNode.nextSibling.nextSibling : undefined;
if (nextSibling && nextSibling.nodeType === 3) {
range.setStart(nextSibling, 0);
range.setEnd(nextSibling, 0);
} else {
const newTextNode = document.createTextNode('');
inputDiv.insertBefore(newTextNode, nextSibling);
range.setStart(newTextNode, 0);
range.setEnd(newTextNode, 0);
}
} else {
range.setStart(cursorNode, cursorPos);
range.setEnd(cursorNode, cursorPos);
}
}
} else {
const newTextNode = document.createTextNode('');
inputDiv.appendChild(newTextNode);
range.setStart(newTextNode, 0);
range.setEnd(newTextNode, 0);
}
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
};
this.getPureInnerText = elem => {
const emojione = requireEmojione(false);
const textLines = [''];
let lineIndex = 0;
let curChild = elem.firstChild;
const isInDavid = navigator.userAgent.toLowerCase().indexOf('david client') >= 0;
while (curChild !== null) {
if (curChild.nodeType === 1) {
switch (curChild.tagName) {
case 'IMG':
textLines[lineIndex] += curChild.getAttribute('alt');
break;
case 'DIV':
textLines.push('');
lineIndex += 1;
break;
case 'BR':
if (chayns.env.browser.name.toLowerCase() !== 'chrome' || isInDavid) {
textLines[lineIndex] += '\n';
}
break;
default:
break;
}
} else if (curChild.nodeType === 3) {
textLines[lineIndex] += curChild.nodeValue;
}
if (curChild.hasChildNodes()) {
curChild = curChild.firstChild;
} else {
while (curChild.nextSibling === null) {
curChild = curChild.parentNode;
if (curChild === elem) {
return textLines.join('\n');
}
}
curChild = curChild.nextSibling;
}
}
if (!emojione) {
return emojione.shortnameToUnicode(textLines.join('\n'));
}
return textLines.join('\n');
};
this.handleInput = event => {
const {
onInput
} = this.props;
// eslint-disable-next-line no-param-reassign
event.target.pureInnerText = this.getPureInnerText(event.target);
onInput(event);
};
this.handleKeyDown = event => {
const {
onKeyDown
} = this.props;
this.lastKeyPressed = event.keyCode;
if (onKeyDown) {
onKeyDown(event);
}
};
this.handleKeyUp = event => {
if (chayns.env.browser.name.toLowerCase() === 'ie' && event.keyCode !== 16) {
this.handleInput(event);
}
if (chayns.env.browser.name.toLowerCase() === 'edge' && event.keyCode === 13 && event.shiftKey) {
this.handleInput(event);
}
};
this.handleFocus = event => {
const {
onFocus
} = this.props;
if (onFocus) {
onFocus(event);
}
};
this.handleBlur = event => {
const {
onBlur
} = this.props;
if (onBlur) {
onBlur(event);
}
};
this.scrollToCursor = (scrollTop, scrollHeight) => {
const inputDiv = this.input;
const elemScrollHeight = inputDiv.scrollHeight;
const elemClientHeight = inputDiv.clientHeight;
if (!(elemScrollHeight <= elemClientHeight)) {
const diff = elemScrollHeight - scrollHeight;
this.input.scrollTop = (scrollTop || 0) + diff;
}
};
this.formatText = text => {
const emojione = requireEmojione(false);
let result = '';
let newText = text.replace(/</g, '<').replace(/>/g, '>').replace(/™/g, '™').replace(/©/g, '©').replace(/®/g, '®').replace(/\(y\)/g, '👍').replace(/\(n\)/g, '👎');
if (emojione) {
newText = emojione.toImage(newText);
}
newText = newText.replace(/(<img[^<]*)\/>/g, '$1>').replace(/™/g, '™').replace(/©/g, '©').replace(/®/g, '®');
const lines = newText.split('\n');
const isInDavid = navigator.userAgent.toLowerCase().indexOf('david client') >= 0;
if (chayns.env.browser.name.toLowerCase() === 'chrome' && !isInDavid) {
if (lines[lines.length - 1] === '' && this.lastKeyPressed === 8 && lines.length > 1) {
lines[lines.length - 1] = '<br>';
}
result = lines.join('\n');
} else {
if (lines.length === 1 && chayns.env.browser.name.toLowerCase() === 'edge' && lines[0] === '') {
lines[0] = '<br>';
}
result = lines.join('<br>');
}
return result.replace(String.fromCharCode(160), String.fromCharCode(32)).replace(/ /gm, String.fromCharCode(32)).replace(/&/gm, String.fromCharCode(38));
};
this.updateDOM = newProps => {
const inputDiv = this.input;
const newHtml = this.formatText(newProps.value);
const oldHtml = inputDiv.innerHTML.replace(/ /gm, String.fromCharCode(32)).replace(/&/gm, String.fromCharCode(38)).replace(String.fromCharCode(160), String.fromCharCode(32));
if (newHtml !== oldHtml) {
this.activeNode = this.getActiveChildNode();
const activeElem = inputDiv.childNodes[this.activeNode];
if (activeElem) {
this.cursorPos = EmojiInput.getCaretCharacterOffsetWithin(activeElem);
const {
scrollTop,
scrollHeight
} = inputDiv.scrollTop;
inputDiv.innerHTML = newHtml;
this.scrollToCursor(scrollTop, scrollHeight);
this.setCursorPos();
} else {
inputDiv.innerHTML = newHtml;
}
}
};
requireEmojione().then(emojione => {
/* eslint-disable no-param-reassign */
emojione.ascii = true;
emojione.imageTitleTag = false;
emojione.blacklistChars = '*,#';
emojione.imagePathPNG = 'https://sub54.tobit.com/frontend/assets/emojione/3.1/png/64/';
/* eslint-enable no-param-reassign */
});
}
static shouldComponentUpdate() {
return false;
}
componentDidUpdate(prevProps) {
const {
placeholder,
disabled,
value
} = this.props;
if (value.trim() === '') {
this.placeholder.classList.remove('emoji-input__placeholder--hidden');
} else {
this.placeholder.classList.add('emoji-input__placeholder--hidden');
}
if (prevProps.placeholder !== placeholder) {
this.placeholder.innerText = placeholder;
}
if (prevProps.disabled !== disabled) {
this.input.contentEditable = !disabled;
}
if (prevProps.value !== value || this.firstRender) {
this.updateDOM(this.props);
this.firstRender = false;
}
}
static getCaretCharacterOffsetWithin(element) {
let caretOffset = -1;
if (typeof window.getSelection !== 'undefined') {
const sel = window.getSelection();
if (sel.anchorNode && (sel.anchorNode.nodeType === 3 || !sel.anchorNode.classList.contains('icon-smile-o'))) {
const range = sel.getRangeAt(0);
const preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
} else if (typeof document.selection !== 'undefined' && document.selection.type !== 'Control') {
const sel = document.selection;
if (sel.anchorNode && (sel.anchorNode.nodeType === 3 || !sel.anchorNode.classList.contains('icon-smile-o'))) {
const textRange = document.selection.createRange();
const preCaretTextRange = document.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint('EndToEnd', textRange);
caretOffset = preCaretTextRange.text.length;
}
}
return caretOffset;
}
render() {
const {
hideBorder,
disabled,
style,
id
} = this.props;
const messageInputClasses = (0, _clsx.default)('emoji-input__message-input', disabled ? 'emoji-input__message-input--disabled' : "input", hideBorder && 'emoji-input__message-input--hide-border');
return /*#__PURE__*/_react.default.createElement("div", {
className: "emoji-input notranslate"
}, /*#__PURE__*/_react.default.createElement("div", {
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML: {
__html: '<br />'
},
ref: ref => {
this.input = ref;
},
className: messageInputClasses,
onKeyDown: this.handleKeyDown,
contentEditable: !disabled,
onKeyUp: this.handleKeyUp,
onInput: this.handleInput,
onFocus: this.handleFocus,
onBlur: this.handleBlur,
style: style,
dir: "auto",
id: id
}), /*#__PURE__*/_react.default.createElement("div", {
className: "emoji-input__placeholder",
ref: ref => {
this.placeholder = ref;
}
}));
}
}
exports.default = EmojiInput;
EmojiInput.propTypes = {
/**
* Text that will be shown as a placeholder when the input is empty.
*/
placeholder: _propTypes.default.string.isRequired,
/**
* This is called when the text changes. There is an additional key on the
* `event.target` property called `pureInnerText` which contains the full
* text without any of the emoji elements. This is the text you should store
* in your local state and pass to this input as the `value`-prop.
*/
onInput: _propTypes.default.func.isRequired,
/**
* The value of the input.
*/
value: _propTypes.default.string.isRequired,
/**
* The HTML id to give to the input element.
*/
id: _propTypes.default.string.isRequired,
/**
* Hides the bottom border of the input.
*/
hideBorder: _propTypes.default.bool,
/**
* This will be called on the `keydown`-event of the input element.
*/
onKeyDown: _propTypes.default.func,
/**
* Disables any interaction with the input and changes to a disabled style.
*/
disabled: _propTypes.default.bool,
/**
* A React style object that will be passed to the input element.
*/
style: _propTypes.default.objectOf(_propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number])),
/**
* This function will be called when the input element receives focus.
*/
onFocus: _propTypes.default.func,
/**
* This function will be called when the input element loses focus.
*/
onBlur: _propTypes.default.func
};
EmojiInput.defaultProps = {
hideBorder: false,
onKeyDown: null,
disabled: false,
onFocus: null,
onBlur: null,
style: null
};
EmojiInput.displayName = 'EmojiInput';
//# sourceMappingURL=EmojiInput.js.map