import "core-js/modules/es.symbol";
import "core-js/modules/es.symbol.description";
import "core-js/modules/es.symbol.iterator";
import "core-js/modules/es.array.filter";
import "core-js/modules/es.array.find";
import "core-js/modules/es.array.index-of";
import "core-js/modules/es.array.iterator";
import "core-js/modules/es.array.sort";
import "core-js/modules/es.object.get-own-property-descriptor";
import "core-js/modules/es.object.get-prototype-of";
import "core-js/modules/es.object.set-prototype-of";
import "core-js/modules/es.object.to-string";
import "core-js/modules/es.reflect.get";
import "core-js/modules/es.regexp.exec";
import "core-js/modules/es.string.iterator";
import "core-js/modules/es.string.replace";
import "core-js/modules/es.weak-map";
import "core-js/modules/web.dom-collections.iterator";
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
import { KEY_CODES, isPrintableChar } from './../helpers/unicode';
import { stringify, isDefined } from './../helpers/mixed';
import { stripTags } from './../helpers/string';
import { pivot, arrayMap } from './../helpers/array';
import { addClass, getCaretPosition, getScrollbarWidth, getSelectionEndPosition, outerWidth, outerHeight, offset, getTrimmingContainer, setCaretPosition } from './../helpers/dom/element';
import HandsontableEditor from './handsontableEditor';
var privatePool = new WeakMap();
/**
* @private
* @editor AutocompleteEditor
* @class AutocompleteEditor
* @dependencies HandsontableEditor
*/
var AutocompleteEditor =
/*#__PURE__*/
function (_HandsontableEditor) {
_inherits(AutocompleteEditor, _HandsontableEditor);
function AutocompleteEditor(instance) {
var _this;
_classCallCheck(this, AutocompleteEditor);
_this = _possibleConstructorReturn(this, _getPrototypeOf(AutocompleteEditor).call(this, instance));
/**
* Query string to turn available values over.
*
* @type {String}
*/
_this.query = null;
/**
* Contains stripped choices.
*
* @type {String[]}
*/
_this.strippedChoices = [];
/**
* Contains raw choices.
*
* @type {Array}
*/
_this.rawChoices = [];
privatePool.set(_assertThisInitialized(_this), {
skipOne: false
});
return _this;
}
/**
* Gets current value from editable element.
*
* @returns {String}
*/
_createClass(AutocompleteEditor, [{
key: "getValue",
value: function getValue() {
var _this2 = this;
var selectedValue = this.rawChoices.find(function (value) {
var strippedValue = _this2.stripValueIfNeeded(value);
return strippedValue === _this2.TEXTAREA.value;
});
if (isDefined(selectedValue)) {
return selectedValue;
}
return this.TEXTAREA.value;
}
/**
* Creates an editor's elements and adds necessary CSS classnames.
*/
}, {
key: "createElements",
value: function createElements() {
_get(_getPrototypeOf(AutocompleteEditor.prototype), "createElements", this).call(this);
addClass(this.htContainer, 'autocompleteEditor');
addClass(this.htContainer, this.hot.rootWindow.navigator.platform.indexOf('Mac') === -1 ? '' : 'htMacScroll');
}
/**
* Opens the editor and adjust its size and internal Handsontable's instance.
*/
}, {
key: "open",
value: function open() {
var _this3 = this;
var priv = privatePool.get(this); // this.addHook('beforeKeyDown', event => this.onBeforeKeyDown(event));
// Ugly fix for handsontable which grab window object for autocomplete scroll listener instead table element.
this.TEXTAREA_PARENT.style.overflow = 'auto';
_get(_getPrototypeOf(AutocompleteEditor.prototype), "open", this).call(this);
this.TEXTAREA_PARENT.style.overflow = '';
var choicesListHot = this.htEditor.getInstance();
var trimDropdown = this.cellProperties.trimDropdown === void 0 ? true : this.cellProperties.trimDropdown;
this.showEditableElement();
this.focus();
var scrollbarWidth = getScrollbarWidth(this.hot.rootDocument);
choicesListHot.updateSettings({
colWidths: trimDropdown ? [outerWidth(this.TEXTAREA) - 2] : void 0,
width: trimDropdown ? outerWidth(this.TEXTAREA) + scrollbarWidth + 2 : void 0,
afterRenderer: function afterRenderer(TD, row, col, prop, value) {
var _this3$cellProperties = _this3.cellProperties,
filteringCaseSensitive = _this3$cellProperties.filteringCaseSensitive,
allowHtml = _this3$cellProperties.allowHtml;
var query = _this3.query;
var cellValue = stringify(value);
var indexOfMatch;
var match;
if (cellValue && !allowHtml) {
indexOfMatch = filteringCaseSensitive === true ? cellValue.indexOf(query) : cellValue.toLowerCase().indexOf(query.toLowerCase());
if (indexOfMatch !== -1) {
match = cellValue.substr(indexOfMatch, query.length);
cellValue = cellValue.replace(match, "".concat(match, ""));
}
}
TD.innerHTML = cellValue;
},
autoColumnSize: true,
modifyColWidth: function modifyColWidth(width, col) {
// workaround for text overlapping the dropdown, not really accurate
var autoColumnSize = this.getPlugin('autoColumnSize');
var columnWidth = width;
if (autoColumnSize) {
var autoWidths = autoColumnSize.widths;
if (autoWidths[col]) {
columnWidth = autoWidths[col];
}
}
return trimDropdown ? columnWidth : columnWidth + 15;
}
}); // Add additional space for autocomplete holder
this.htEditor.view.wt.wtTable.holder.parentNode.style['padding-right'] = "".concat(scrollbarWidth + 2, "px");
if (priv.skipOne) {
priv.skipOne = false;
}
this.hot._registerTimeout(function () {
_this3.queryChoices(_this3.TEXTAREA.value);
});
}
/**
* Closes the editor.
*/
}, {
key: "close",
value: function close() {
this.removeHooksByKey('beforeKeyDown');
_get(_getPrototypeOf(AutocompleteEditor.prototype), "close", this).call(this);
}
/**
* Verifies result of validation or closes editor if user's cancelled changes. Re-renders WalkOnTable.
*
* @param {Boolean|undefined} result
*/
}, {
key: "discardEditor",
value: function discardEditor(result) {
_get(_getPrototypeOf(AutocompleteEditor.prototype), "discardEditor", this).call(this, result);
this.hot.view.render();
}
/**
* Prepares choices list based on applied argument.
*
* @private
* @param {String} query
*/
}, {
key: "queryChoices",
value: function queryChoices(query) {
var _this4 = this;
var source = this.cellProperties.source;
this.query = query;
if (typeof source === 'function') {
source.call(this.cellProperties, query, function (choices) {
_this4.rawChoices = choices;
_this4.updateChoicesList(_this4.stripValuesIfNeeded(choices));
});
} else if (Array.isArray(source)) {
this.rawChoices = source;
this.updateChoicesList(this.stripValuesIfNeeded(source));
} else {
this.updateChoicesList([]);
}
}
/**
* Updates list of the possible completions to choose.
*
* @private
* @param {Array} choicesList
*/
}, {
key: "updateChoicesList",
value: function updateChoicesList(choicesList) {
var pos = getCaretPosition(this.TEXTAREA);
var endPos = getSelectionEndPosition(this.TEXTAREA);
var sortByRelevanceSetting = this.cellProperties.sortByRelevance;
var filterSetting = this.cellProperties.filter;
var orderByRelevance = null;
var highlightIndex = null;
var choices = choicesList;
if (sortByRelevanceSetting) {
orderByRelevance = AutocompleteEditor.sortByRelevance(this.stripValueIfNeeded(this.getValue()), choices, this.cellProperties.filteringCaseSensitive);
}
var orderByRelevanceLength = Array.isArray(orderByRelevance) ? orderByRelevance.length : 0;
if (filterSetting === false) {
if (orderByRelevanceLength) {
highlightIndex = orderByRelevance[0];
}
} else {
var sorted = [];
for (var i = 0, choicesCount = choices.length; i < choicesCount; i++) {
if (sortByRelevanceSetting && orderByRelevanceLength <= i) {
break;
}
if (orderByRelevanceLength) {
sorted.push(choices[orderByRelevance[i]]);
} else {
sorted.push(choices[i]);
}
}
highlightIndex = 0;
choices = sorted;
}
this.strippedChoices = choices;
this.htEditor.loadData(pivot([choices]));
this.updateDropdownHeight();
this.flipDropdownIfNeeded();
if (this.cellProperties.strict === true) {
this.highlightBestMatchingChoice(highlightIndex);
}
this.hot.listen(false);
setCaretPosition(this.TEXTAREA, pos, pos === endPos ? void 0 : endPos);
}
/**
* Checks where is enough place to open editor.
*
* @private
* @returns {Boolean}
*/
}, {
key: "flipDropdownIfNeeded",
value: function flipDropdownIfNeeded() {
var textareaOffset = offset(this.TEXTAREA);
var textareaHeight = outerHeight(this.TEXTAREA);
var dropdownHeight = this.getDropdownHeight();
var trimmingContainer = getTrimmingContainer(this.hot.view.wt.wtTable.TABLE);
var trimmingContainerScrollTop = trimmingContainer.scrollTop;
var headersHeight = outerHeight(this.hot.view.wt.wtTable.THEAD);
var containerOffset = {
row: 0,
col: 0
};
if (trimmingContainer !== this.hot.rootWindow) {
containerOffset = offset(trimmingContainer);
}
var spaceAbove = textareaOffset.top - containerOffset.top - headersHeight + trimmingContainerScrollTop;
var spaceBelow = trimmingContainer.scrollHeight - spaceAbove - headersHeight - textareaHeight;
var flipNeeded = dropdownHeight > spaceBelow && spaceAbove > spaceBelow;
if (flipNeeded) {
this.flipDropdown(dropdownHeight);
} else {
this.unflipDropdown();
}
this.limitDropdownIfNeeded(flipNeeded ? spaceAbove : spaceBelow, dropdownHeight);
return flipNeeded;
}
/**
* Checks if the internal table should generate scrollbar or could be rendered without it.
*
* @private
* @param {Number} spaceAvailable
* @param {Number} dropdownHeight
*/
}, {
key: "limitDropdownIfNeeded",
value: function limitDropdownIfNeeded(spaceAvailable, dropdownHeight) {
if (dropdownHeight > spaceAvailable) {
var tempHeight = 0;
var i = 0;
var lastRowHeight = 0;
var height = null;
do {
lastRowHeight = this.htEditor.getRowHeight(i) || this.htEditor.view.wt.wtSettings.settings.defaultRowHeight;
tempHeight += lastRowHeight;
i += 1;
} while (tempHeight < spaceAvailable);
height = tempHeight - lastRowHeight;
if (this.htEditor.flipped) {
this.htEditor.rootElement.style.top = "".concat(parseInt(this.htEditor.rootElement.style.top, 10) + dropdownHeight - height, "px");
}
this.setDropdownHeight(tempHeight - lastRowHeight);
}
}
/**
* Configures editor to open it at the top.
*
* @private
* @param {Number} dropdownHeight
*/
}, {
key: "flipDropdown",
value: function flipDropdown(dropdownHeight) {
var dropdownStyle = this.htEditor.rootElement.style;
dropdownStyle.position = 'absolute';
dropdownStyle.top = "".concat(-dropdownHeight, "px");
this.htEditor.flipped = true;
}
/**
* Configures editor to open it at the bottom.
*
* @private
*/
}, {
key: "unflipDropdown",
value: function unflipDropdown() {
var dropdownStyle = this.htEditor.rootElement.style;
if (dropdownStyle.position === 'absolute') {
dropdownStyle.position = '';
dropdownStyle.top = '';
}
this.htEditor.flipped = void 0;
}
/**
* Updates width and height of the internal Handsontable's instance.
*
* @private
*/
}, {
key: "updateDropdownHeight",
value: function updateDropdownHeight() {
var currentDropdownWidth = this.htEditor.getColWidth(0) + getScrollbarWidth(this.hot.rootDocument) + 2;
var trimDropdown = this.cellProperties.trimDropdown;
this.htEditor.updateSettings({
height: this.getDropdownHeight(),
width: trimDropdown ? void 0 : currentDropdownWidth
});
this.htEditor.view.wt.wtTable.alignOverlaysWithTrimmingContainer();
}
/**
* Sets new height of the internal Handsontable's instance.
*
* @private
* @param {Number} height
*/
}, {
key: "setDropdownHeight",
value: function setDropdownHeight(height) {
this.htEditor.updateSettings({
height: height
});
}
/**
* Creates new selection on specified row index, or deselects selected cells.
*
* @private
* @param {Number|undefined} index
*/
}, {
key: "highlightBestMatchingChoice",
value: function highlightBestMatchingChoice(index) {
if (typeof index === 'number') {
this.htEditor.selectCell(index, 0, void 0, void 0, void 0, false);
} else {
this.htEditor.deselectCell();
}
}
/**
* Calculates and return the internal Handsontable's height.
*
* @private
* @returns {Number}
*/
}, {
key: "getDropdownHeight",
value: function getDropdownHeight() {
var firstRowHeight = this.htEditor.getInstance().getRowHeight(0) || 23;
var visibleRows = this.cellProperties.visibleRows;
return this.strippedChoices.length >= visibleRows ? visibleRows * firstRowHeight : this.strippedChoices.length * firstRowHeight + 8;
}
/**
* Sanitizes value from potential dangerous tags.
*
* @private
* @param {String} value
* @returns {String}
*/
}, {
key: "stripValueIfNeeded",
value: function stripValueIfNeeded(value) {
return this.stripValuesIfNeeded([value])[0];
}
/**
* Sanitizes an array of the values from potential dangerous tags.
*
* @private
* @param {String[]} values
* @returns {String[]}
*/
}, {
key: "stripValuesIfNeeded",
value: function stripValuesIfNeeded(values) {
var allowHtml = this.cellProperties.allowHtml;
var stringifiedValues = arrayMap(values, function (value) {
return stringify(value);
});
var strippedValues = arrayMap(stringifiedValues, function (value) {
return allowHtml ? value : stripTags(value);
});
return strippedValues;
}
/**
* Captures use of arrow down and up to control their behaviour.
*
* @private
* @param {Number} keyCode
* @returns {Boolean}
*/
}, {
key: "allowKeyEventPropagation",
value: function allowKeyEventPropagation(keyCode) {
var selectedRange = this.htEditor.getSelectedRangeLast();
var selected = {
row: selectedRange ? selectedRange.from.row : -1
};
var allowed = false;
if (keyCode === KEY_CODES.ARROW_DOWN && selected.row > 0 && selected.row < this.htEditor.countRows() - 1) {
allowed = true;
}
if (keyCode === KEY_CODES.ARROW_UP && selected.row > -1) {
allowed = true;
}
return allowed;
}
/**
* onBeforeKeyDown callback.
*
* @private
* @param {KeyboardEvent} event
*/
}, {
key: "onBeforeKeyDown",
value: function onBeforeKeyDown(event) {
var _this5 = this;
var priv = privatePool.get(this);
priv.skipOne = false;
if (isPrintableChar(event.keyCode) || event.keyCode === KEY_CODES.BACKSPACE || event.keyCode === KEY_CODES.DELETE || event.keyCode === KEY_CODES.INSERT) {
var timeOffset = 0; // on ctl+c / cmd+c don't update suggestion list
if (event.keyCode === KEY_CODES.C && (event.ctrlKey || event.metaKey)) {
return;
}
if (!this.isOpened()) {
timeOffset += 10;
}
if (this.htEditor) {
this.hot._registerTimeout(function () {
_this5.queryChoices(_this5.TEXTAREA.value);
priv.skipOne = true;
}, timeOffset);
}
}
_get(_getPrototypeOf(AutocompleteEditor.prototype), "onBeforeKeyDown", this).call(this, event);
}
}]);
return AutocompleteEditor;
}(HandsontableEditor);
/**
* Filters and sorts by relevance.
*
* @param value
* @param choices
* @param caseSensitive
* @returns {Number[]} array of indexes in original choices array
*/
AutocompleteEditor.sortByRelevance = function (value, choices, caseSensitive) {
var choicesRelevance = [];
var currentItem;
var valueLength = value.length;
var valueIndex;
var charsLeft;
var result = [];
var i;
var choicesCount = choices.length;
if (valueLength === 0) {
for (i = 0; i < choicesCount; i++) {
result.push(i);
}
return result;
}
for (i = 0; i < choicesCount; i++) {
currentItem = stripTags(stringify(choices[i]));
if (caseSensitive) {
valueIndex = currentItem.indexOf(value);
} else {
valueIndex = currentItem.toLowerCase().indexOf(value.toLowerCase());
}
if (valueIndex !== -1) {
charsLeft = currentItem.length - valueIndex - valueLength;
choicesRelevance.push({
baseIndex: i,
index: valueIndex,
charsLeft: charsLeft,
value: currentItem
});
}
}
choicesRelevance.sort(function (a, b) {
if (b.index === -1) {
return -1;
}
if (a.index === -1) {
return 1;
}
if (a.index < b.index) {
return -1;
} else if (b.index < a.index) {
return 1;
} else if (a.index === b.index) {
if (a.charsLeft < b.charsLeft) {
return -1;
} else if (a.charsLeft > b.charsLeft) {
return 1;
}
}
return 0;
});
for (i = 0, choicesCount = choicesRelevance.length; i < choicesCount; i++) {
result.push(choicesRelevance[i].baseIndex);
}
return result;
};
export default AutocompleteEditor;