@gpa-gemstone/react-forms
Version:
React Form modules for gpa webapps
169 lines (168 loc) • 8.7 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = AutoCompleteTextArea;
var React = require("react");
var TextArea_1 = require("./TextArea");
var react_portal_1 = require("react-portal");
var _ = require("lodash");
var AutoCompleteInput_1 = require("./AutoCompleteInput");
function AutoCompleteTextArea(props) {
var autoCompleteTextArea = React.useRef(null);
var tableContainer = React.useRef(null);
var selectTable = React.useRef(null);
var textAreaElement = React.useRef(null);
var _a = React.useState([]), suggestions = _a[0], setSuggestions = _a[1];
var _b = React.useState(null), position = _b[0], setPosition = _b[1];
var _c = React.useState({ Start: 0, End: 0, Variable: "" }), variable = _c[0], setVariable = _c[1];
var _d = React.useState(true), show = _d[0], setShow = _d[1];
// Handle showing and hiding of the dropdown.
var HandleShow = React.useCallback(function (evt) {
var _a;
// Ignore if disabled or not a mousedown event
if ((props.Disabled === undefined ? false : props.Disabled)
|| evt.type !== 'mousedown') {
return;
}
//ignore the click if it was inside the table or table container
if ((selectTable.current != null && selectTable.current.contains(evt.target)) || (tableContainer.current != null && tableContainer.current.contains(evt.target))) {
return;
}
if (textAreaElement.current !== null && !((_a = textAreaElement.current) === null || _a === void 0 ? void 0 : _a.contains(evt.target))) {
setShow(false);
}
else {
setShow(true);
}
}, [props.Disabled]);
// add listeners to follow caret
React.useEffect(function () {
var autoComplete = textAreaElement.current;
if (autoComplete == null)
return;
autoComplete.addEventListener("keyup", handleCaretPosition);
autoComplete.addEventListener("click", handleCaretPosition);
return function () {
autoComplete.removeEventListener("keyup", handleCaretPosition);
autoComplete.removeEventListener("click", handleCaretPosition);
};
}, []);
// set position of the suggestion dropdown
React.useLayoutEffect(function () {
if ((suggestions === null || suggestions === void 0 ? void 0 : suggestions.length) == 0) {
setPosition(null);
return;
}
var updatePosition = _.debounce(function () {
if (textAreaElement.current == null) {
return;
}
var rect = textAreaElement.current.getBoundingClientRect();
var _a = getTextDimensions(textAreaElement, variable.Start - 1, "\n"), caret_X = _a[0], caret_Y = _a[1];
setPosition({
Top: rect.top + caret_Y,
Left: rect.left + caret_X,
Width: rect.width - caret_X,
Height: rect.height
});
}, 200);
var handleScroll = function () {
if (tableContainer.current == null)
return;
updatePosition();
};
updatePosition();
window.addEventListener('scroll', handleScroll, true);
window.addEventListener('resize', updatePosition);
window.addEventListener('mousedown', HandleShow, false);
return function () {
window.removeEventListener('scroll', handleScroll, true);
window.removeEventListener('resize', updatePosition);
window.removeEventListener('mousedown', HandleShow, false);
updatePosition.cancel();
};
}, [suggestions, HandleShow]);
// update variable and suggestions when caret position changes
var handleCaretPosition = function () {
var _a, _b, _c, _d;
if (textAreaElement.current !== null) {
var selection = textAreaElement.current.selectionStart;
var variable_1 = (0, AutoCompleteInput_1.getCurrentVariable)((_b = (_a = textAreaElement.current) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "", selection);
setVariable(variable_1);
var suggests = (0, AutoCompleteInput_1.getSuggestions)(variable_1, (_d = (_c = textAreaElement.current) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : "", props.Options);
setSuggestions(suggests);
}
};
// edit text area contents with selected suggestion
var handleOptionClick = function (option) {
var _a;
var _b, _c, _d, _e;
var currentPos = textAreaElement.current !== null ? textAreaElement.current.selectionStart : 0;
var optionLength = option.Value.length;
props.Setter(__assign(__assign({}, props.Record), (_a = {}, _a[props.Field] = option.Value, _a)));
var textLength = textAreaElement.current !== null ? (_c = (_b = textAreaElement.current.textContent) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0 : 0;
var newCaretPos = (optionLength > textLength ? textLength - 1 : optionLength + currentPos);
(_d = textAreaElement.current) === null || _d === void 0 ? void 0 : _d.focus();
(_e = textAreaElement.current) === null || _e === void 0 ? void 0 : _e.setSelectionRange(newCaretPos, newCaretPos);
setSuggestions([]);
};
return (React.createElement("div", { ref: autoCompleteTextArea },
React.createElement(TextArea_1.default, __assign({}, props, { TextAreaRef: textAreaElement, SpellCheck: false })),
position == null || !show ? React.createElement(React.Fragment, null) :
React.createElement(react_portal_1.Portal, null,
React.createElement("div", { ref: tableContainer, className: 'popover', style: {
maxHeight: window.innerHeight - position.Top,
overflowY: 'auto',
padding: '10 5',
display: 'block',
position: 'absolute',
zIndex: 9999,
maxWidth: '100%',
top: "".concat(position.Top, "px"),
left: "".concat(position.Left, "px"),
minWidth: "".concat(Math.min(position.Width, window.innerWidth - position.Left), "px"),
overflowWrap: 'break-word',
} },
React.createElement("table", { className: "table table-hover", style: { margin: 0 }, ref: selectTable },
React.createElement("tbody", null, suggestions.map(function (f, i) { return (f.Value === props.Record[props.Field] ? null :
React.createElement("tr", { key: i, onMouseDown: function (_) { return handleOptionClick(f); } },
React.createElement("td", null, f.Label))); })))))));
}
var getTextDimensions = function (textArea, selection, prefix) {
var _a;
if (textArea.current == null)
return [0, 0];
var textarea = textArea.current;
if (textarea.parentNode == null)
return [0, 0];
var hiddenDiv = document.createElement('div');
var style = getComputedStyle(textarea);
Array.from(style).forEach(function (propertyName) {
var value = style.getPropertyValue(propertyName);
hiddenDiv.style.setProperty(propertyName, value);
});
// Set text content up to caret
var beforeSelection = textarea.value.substring(0, selection);
var afterSelection = (_a = textarea.value.substring(selection)) !== null && _a !== void 0 ? _a : '.';
hiddenDiv.textContent = (prefix !== null && prefix !== void 0 ? prefix : "") + beforeSelection;
// Create a span to mark caret position
var span = document.createElement('span');
span.textContent = afterSelection[0];
hiddenDiv.appendChild(span);
textarea.parentNode.appendChild(hiddenDiv);
// Get caret's vertical position relative to textarea
var caretX = span.offsetLeft - hiddenDiv.offsetLeft - hiddenDiv.scrollLeft;
var caretY = span.offsetTop - hiddenDiv.offsetTop - textarea.scrollTop;
textarea.parentNode.removeChild(hiddenDiv);
return [caretX, caretY];
};