canvas-datagrid
Version:
Canvas based data grid web component. Capable of displaying millions of contiguous hierarchical rows and columns without paging or loading, on a single canvas element.
1,484 lines (1,244 loc) • 538 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["canvasDatagrid"] = factory();
else
root["canvasDatagrid"] = factory();
})(self, function() {
return /******/ (function() { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./lib/button.js":
/*!***********************!*\
!*** ./lib/button.js ***!
\***********************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* export default binding */ __WEBPACK_DEFAULT_EXPORT__; }
/* harmony export */ });
/*jslint browser: true, unparam: true, todo: true*/
/*globals define: true, MutationObserver: false, requestAnimationFrame: false, performance: false, btoa: false, Event: false*/
function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
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); }
/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(self) {
var zIndexTop;
function applyButtonMenuItemStyle(buttonMenuItemContainer) {
self.createInlineStyle(buttonMenuItemContainer, 'canvas-datagrid-button-menu-item' + (self.mobile ? '-mobile' : ''));
buttonMenuItemContainer.addEventListener('mouseover', function () {
self.createInlineStyle(buttonMenuItemContainer, 'canvas-datagrid-button-menu-item:hover');
});
buttonMenuItemContainer.addEventListener('mouseout', function () {
self.createInlineStyle(buttonMenuItemContainer, 'canvas-datagrid-button-menu-item');
});
}
function applyButtonStyle(button) {
self.createInlineStyle(button, 'canvas-datagrid-button-wrapper');
button.addEventListener('mouseover', function () {
if (!self.buttonMenu) {
self.createInlineStyle(button, 'canvas-datagrid-button-wrapper:hover');
}
});
button.addEventListener('mouseout', function () {
if (!self.buttonMenu) {
self.createInlineStyle(button, 'canvas-datagrid-button-wrapper');
}
});
}
function createButton(pos, items, imgSrc) {
var wrapper = document.createElement('div'),
buttonArrow = document.createElement('div'),
buttonIcon = document.createElement('div'),
intf = {};
if (!Array.isArray(items)) {
throw new Error('createButton expects an array.');
}
function init() {
var loc = {},
s = self.scrollOffset(self.canvas);
if (zIndexTop === undefined) {
zIndexTop = self.style.buttonZIndex;
}
applyButtonStyle(wrapper);
self.createInlineStyle(buttonIcon, 'canvas-datagrid-button-icon');
self.createInlineStyle(buttonArrow, 'canvas-datagrid-button-arrow');
loc.x = pos.left - s.left;
loc.y = pos.top - s.top;
loc.height = 0;
zIndexTop += 1;
wrapper.style.position = 'absolute';
wrapper.style.zIndex = zIndexTop;
wrapper.style.left = loc.x + 'px';
wrapper.style.top = loc.y + 'px';
wrapper.left = pos.left + self.scrollBox.scrollLeft;
wrapper.top = pos.top + self.scrollBox.scrollTop;
buttonArrow.innerHTML = self.style.buttonArrowDownHTML;
if (imgSrc) {
var img = document.createElement('img');
img.setAttribute('src', imgSrc);
img.style.maxWidth = '100%';
img.style.height = '100%';
buttonIcon.appendChild(img);
}
wrapper.appendChild(buttonIcon);
wrapper.appendChild(buttonArrow);
document.body.appendChild(wrapper);
wrapper.addEventListener('click', toggleButtonMenu);
}
intf.wrapper = wrapper;
intf.items = items;
init();
intf.dispose = function () {
if (wrapper.parentNode) {
wrapper.parentNode.removeChild(wrapper);
}
};
return intf;
}
function toggleButtonMenu() {
function createDisposeEvent() {
requestAnimationFrame(function () {
document.addEventListener('click', self.disposeButtonMenu);
});
}
if (self.buttonMenu) {
self.disposeButtonMenu();
} else {
var pos = {
left: self.button.wrapper.left - self.scrollBox.scrollLeft,
top: self.button.wrapper.top + self.button.wrapper.offsetHeight - self.scrollBox.scrollTop
};
self.buttonMenu = createButtonMenu(pos, self.button.items);
self.createInlineStyle(self.button.wrapper, 'canvas-datagrid-button-wrapper:active');
createDisposeEvent();
}
}
function createButtonMenu(pos, items) {
var buttonMenu = document.createElement('div'),
selectedIndex = -1,
intf = {},
rect;
function createItems() {
function addItem(item, menuItemContainer) {
function addContent(content) {
if (content === null) {
return;
}
if (_typeof(content) === 'object') {
menuItemContainer.appendChild(content);
return;
}
applyButtonMenuItemStyle(menuItemContainer);
menuItemContainer.innerHTML = content;
return;
}
addContent(item.title);
item.buttonMenuItemContainer = menuItemContainer;
if (item.click) {
menuItemContainer.addEventListener('click', function (ev) {
item.click.apply(self, [ev]);
self.disposeButton();
});
}
}
var _iterator = _createForOfIteratorHelper(items),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var item = _step.value;
var buttonMenuItemContainer = document.createElement('div');
addItem(item, buttonMenuItemContainer);
buttonMenu.appendChild(buttonMenuItemContainer);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
function clickIndex(idx) {
items[idx].buttonMenuItemContainer.dispatchEvent(new Event('click'));
}
function init() {
var loc = {},
s = self.scrollOffset(self.canvas);
if (zIndexTop === undefined) {
zIndexTop = self.style.buttonZIndex;
}
createItems();
self.createInlineStyle(buttonMenu, 'canvas-datagrid-button-menu' + (self.mobile ? '-mobile' : ''));
loc.x = pos.left - s.left;
loc.y = pos.top - s.top;
loc.height = 0;
zIndexTop += 1;
buttonMenu.style.position = 'absolute';
buttonMenu.style.zIndex = zIndexTop;
buttonMenu.style.left = loc.x + 'px';
buttonMenu.style.top = loc.y + 'px';
document.body.appendChild(buttonMenu);
rect = buttonMenu.getBoundingClientRect();
if (rect.bottom > window.innerHeight) {
loc.y = self.button.wrapper.top - buttonMenu.offsetHeight - self.scrollBox.scrollTop;
if (loc.y < 0) {
loc.y = self.style.buttonMenuWindowMargin;
}
if (buttonMenu.offsetHeight > window.innerHeight - self.style.buttonMenuWindowMargin) {
buttonMenu.style.height = window.innerHeight - self.style.buttonMenuWindowMargin * 2 + 'px';
}
}
if (rect.right > window.innerWidth) {
loc.x -= rect.right - window.innerWidth + self.style.buttonMenuWindowMargin;
}
if (loc.x < 0) {
loc.x = self.style.buttonMenuWindowMargin;
}
if (loc.y < 0) {
loc.y = self.style.buttonMenuWindowMargin;
}
buttonMenu.style.left = loc.x + 'px';
buttonMenu.style.top = loc.y + 'px';
}
intf.buttonMenu = buttonMenu;
init();
intf.clickIndex = clickIndex;
intf.rect = rect;
intf.items = items;
intf.dispose = function () {
if (buttonMenu.parentNode) {
buttonMenu.parentNode.removeChild(buttonMenu);
}
};
Object.defineProperty(intf, 'selectedIndex', {
get: function get() {
return selectedIndex;
},
set: function set(value) {
if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
throw new Error('Button menu selected index must be a sane number.');
}
selectedIndex = value;
if (selectedIndex > items.length - 1) {
selectedIndex = items.length - 1;
}
if (selectedIndex < 0) {
selectedIndex = 0;
}
items.forEach(function (item, index) {
if (index === selectedIndex) {
return self.createInlineStyle(item.buttonMenuItemContainer, 'canvas-datagrid-button-menu-item:hover');
}
self.createInlineStyle(item.buttonMenuItemContainer, 'canvas-datagrid-button-menu-item');
});
}
});
return intf;
}
self.disposeButtonMenu = function () {
if (self.buttonMenu) {
document.removeEventListener('click', self.disposeButtonMenu);
self.buttonMenu.dispose();
self.buttonMenu = undefined;
self.createInlineStyle(self.button.wrapper, 'canvas-datagrid-button-wrapper:hover');
}
};
self.disposeButton = function (e) {
if (e && e.keyCode !== 27) return;
document.removeEventListener('keydown', self.disposeButton);
zIndexTop = self.style.buttonZIndex;
self.disposeButtonMenu();
if (self.button) {
self.button.dispose();
}
self.button = undefined;
};
self.moveButtonPos = function () {
self.button.wrapper.style.left = self.button.wrapper.left - self.scrollBox.scrollLeft + 'px';
self.button.wrapper.style.top = self.button.wrapper.top - self.scrollBox.scrollTop + 'px';
self.disposeButtonMenu();
};
self.attachButton = function (pos, items, imgSrc) {
function createDisposeEvent() {
requestAnimationFrame(function () {
document.addEventListener('keydown', self.disposeButton);
});
}
if (self.button) {
self.disposeButton();
}
self.button = createButton(pos, items, imgSrc);
createDisposeEvent();
};
return;
}
/***/ }),
/***/ "./lib/component.js":
/*!**************************!*\
!*** ./lib/component.js ***!
\**************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* export default binding */ __WEBPACK_DEFAULT_EXPORT__; }
/* harmony export */ });
/* harmony import */ var _defaults__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaults */ "./lib/defaults.js");
/*jslint browser: true, unparam: true, todo: true*/
/*globals define: true, MutationObserver: false, requestAnimationFrame: false, performance: false, btoa: false*/
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); }
/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__() {
var typeMap,
component = {};
component.dehyphenateProperty = function hyphenateProperty(prop) {
prop = prop.replace('--cdg-', '');
var p = '',
nextLetterCap;
Array.prototype.forEach.call(prop, function (_char) {
if (nextLetterCap) {
nextLetterCap = false;
p += _char.toUpperCase();
return;
}
if (_char === '-') {
nextLetterCap = true;
return;
}
p += _char;
});
return p;
};
component.hyphenateProperty = function hyphenateProperty(prop, cust) {
var p = '';
Array.prototype.forEach.call(prop, function (_char2) {
if (_char2 === _char2.toUpperCase()) {
p += '-' + _char2.toLowerCase();
return;
}
p += _char2;
});
return (cust ? '--cdg-' : '') + p;
};
function getDefaultItem(base, item) {
var i = {},
r;
(0,_defaults__WEBPACK_IMPORTED_MODULE_0__["default"])(i);
r = i.defaults[base].filter(function (i) {
return i[0].toLowerCase() === item.toLowerCase() || component.hyphenateProperty(i[0]) === item.toLowerCase() || component.hyphenateProperty(i[0], true) === item.toLowerCase();
})[0];
return r;
}
component.applyComponentStyle = function (supressChangeAndDrawEvents, intf) {
if (!intf.isComponent) {
return;
}
var cStyle = window.getComputedStyle(intf.tagName === 'CANVAS-DATAGRID' ? intf : intf.canvas, null),
defs = {};
intf.computedStyle = cStyle;
(0,_defaults__WEBPACK_IMPORTED_MODULE_0__["default"])(defs);
defs.defaults.styles.forEach(function (def) {
var val;
val = cStyle.getPropertyValue(component.hyphenateProperty(def[0], true));
if (val === '') {
val = cStyle.getPropertyValue(component.hyphenateProperty(def[0], false));
}
if (val !== '' && typeof val === 'string') {
intf.setStyleProperty(def[0], typeMap[_typeof(def[1])](val.replace(/^\s+/, '').replace(/\s+$/, ''), def[1]), true);
}
});
if (!supressChangeAndDrawEvents && intf.dispatchEvent) {
requestAnimationFrame(function () {
intf.resize(true);
});
intf.dispatchEvent('stylechanged', intf.style);
}
};
typeMap = {
data: function data(strData) {
try {
return JSON.parse(strData);
} catch (e) {
throw new Error('Cannot read JSON data in canvas-datagrid data.');
}
},
schema: function schema(strSchema) {
try {
return JSON.parse(strSchema);
} catch (e) {
throw new Error('Cannot read JSON data in canvas-datagrid schema attribute.');
}
},
number: function number(strNum, def) {
var n = parseInt(strNum, 10);
return isNaN(n) ? def : n;
},
"boolean": function boolean(strBool) {
return /true/i.test(strBool);
},
string: function string(str) {
return str;
}
};
component.getObservableAttributes = function () {
var i = {},
attrs = ['data', 'schema', 'style', 'className', 'name'];
(0,_defaults__WEBPACK_IMPORTED_MODULE_0__["default"])(i);
i.defaults.attributes.forEach(function (attr) {
attrs.push(attr[0].toLowerCase());
});
return attrs;
};
component.disconnectedCallback = function () {
this.connected = false;
};
component.connectedCallback = function () {
var intf = this;
intf.parentDOMNode.innerHTML = '';
intf.parentDOMNode.appendChild(intf.canvas);
intf.connected = true;
component.observe(intf);
component.applyComponentStyle(true, intf);
intf.resize(true);
};
component.adoptedCallback = function () {
this.resize();
};
component.attributeChangedCallback = function (attrName, oldVal, newVal) {
var tfn,
intf = this,
def;
if (attrName === 'style') {
component.applyComponentStyle(false, intf);
return;
}
if (attrName === 'data') {
if (intf.dataType === 'application/x-canvas-datagrid') {
intf.dataType = 'application/json+x-canvas-datagrid';
}
intf.data = newVal;
return;
}
if (attrName === 'schema') {
intf.schema = typeMap.schema(newVal);
return;
}
if (attrName === 'name') {
intf.name = newVal;
return;
}
if (attrName === 'class' || attrName === 'className') {
return;
}
def = getDefaultItem('attributes', attrName);
if (def) {
tfn = typeMap[_typeof(def[1])];
intf.attributes[def[0]] = tfn(newVal);
return;
}
if (/^on/.test(attrName)) {
intf.addEventListener('on' + attrName, Function('e', newVal));
}
return;
};
component.observe = function (intf) {
var observer;
if (!window.MutationObserver) {
return;
}
intf.applyComponentStyle = function () {
component.applyComponentStyle(false, intf);
intf.resize();
};
/**
* Applies the computed css styles to the grid. In some browsers, changing directives in attached style sheets does not automatically update the styles in this component. It is necessary to call this method to update in these cases.
* @memberof canvasDatagrid
* @name applyComponentStyle
* @method
*/
observer = new window.MutationObserver(function (mutations) {
var checkInnerHTML, checkStyle;
Array.prototype.forEach.call(mutations, function (mutation) {
if (mutation.attributeName === 'class' || mutation.attributeName === 'style') {
checkStyle = true;
return;
}
if (mutation.target.nodeName === 'STYLE') {
checkStyle = true;
return;
}
if (mutation.target.parentNode && mutation.target.parentNode.nodeName === 'STYLE') {
checkStyle = true;
return;
}
if (mutation.target === intf && (mutation.addedNodes.length > 0 || mutation.type === 'characterData')) {
checkInnerHTML = true;
}
});
if (checkStyle) {
intf.applyComponentStyle(false, intf);
}
if (checkInnerHTML) {
if (intf.dataType === 'application/x-canvas-datagrid') {
intf.dataType = 'application/json+x-canvas-datagrid';
}
intf.data = intf.innerHTML;
}
});
observer.observe(intf, {
characterData: true,
childList: true,
attributes: true,
subtree: true
});
Array.prototype.forEach.call(document.querySelectorAll('style'), function (el) {
observer.observe(el, {
characterData: true,
childList: true,
attributes: true,
subtree: true
});
});
};
return component;
}
/***/ }),
/***/ "./lib/contextMenu.js":
/*!****************************!*\
!*** ./lib/contextMenu.js ***!
\****************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* export default binding */ __WEBPACK_DEFAULT_EXPORT__; }
/* harmony export */ });
/*jslint browser: true, unparam: true, todo: true*/
/*globals define: true, MutationObserver: false, requestAnimationFrame: false, performance: false, btoa: false, Event: false*/
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); }
/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(self) {
var zIndexTop, hoverScrollTimeout, autoCompleteContext;
function applyContextItemStyle(contextItemContainer) {
self.createInlineStyle(contextItemContainer, 'canvas-datagrid-context-menu-item' + (self.mobile ? '-mobile' : ''));
contextItemContainer.addEventListener('mouseover', function () {
self.createInlineStyle(contextItemContainer, 'canvas-datagrid-context-menu-item:hover');
});
contextItemContainer.addEventListener('mouseout', function () {
self.createInlineStyle(contextItemContainer, 'canvas-datagrid-context-menu-item');
});
}
function createContextMenu(ev, pos, items, parentContextMenu) {
var container = document.createElement('div'),
upArrow = document.createElement('div'),
downArrow = document.createElement('div'),
children = [],
selectedIndex = -1,
intf = {},
rect;
if (!Array.isArray(items)) {
throw new Error('createContextMenu expects an array.');
}
function createItems() {
items.forEach(function (item) {
var contextItemContainer = document.createElement('div'),
childMenuArrow;
function removeChildContext(e) {
if (e.relatedTarget === container || item.contextMenu.container === e.relatedTarget || childMenuArrow === e.relatedTarget || contextItemContainer === e.relatedTarget || item.contextMenu.container.contains(e.relatedTarget)) {
return;
}
item.contextMenu.dispose();
children.splice(children.indexOf(item.contextMenu), 1);
item.contextMenu = undefined;
contextItemContainer.removeEventListener('mouseout', removeChildContext);
container.removeEventListener('mouseout', removeChildContext);
contextItemContainer.setAttribute('contextOpen', '0');
contextItemContainer.setAttribute('opening', '0');
}
function contextAddCallback(items) {
// check yet again if the user hasn't moved off
if (contextItemContainer.getAttribute('opening') !== '1' || contextItemContainer.getAttribute('contextOpen') === '1') {
return;
}
var cPos = contextItemContainer.getBoundingClientRect();
cPos = {
left: cPos.left + self.style.childContextMenuMarginLeft + container.offsetWidth,
top: cPos.top + self.style.childContextMenuMarginTop,
bottom: cPos.bottom,
right: cPos.right
};
item.contextMenu = createContextMenu(ev, cPos, items, intf);
contextItemContainer.setAttribute('contextOpen', '1');
contextItemContainer.addEventListener('mouseout', removeChildContext);
container.addEventListener('mouseout', removeChildContext);
children.push(item.contextMenu);
}
function createChildContext() {
var i;
if (contextItemContainer.getAttribute('contextOpen') === '1') {
return;
}
contextItemContainer.setAttribute('opening', '1');
if (typeof item.items === 'function') {
i = item.items.apply(intf, [function (items) {
contextAddCallback(items);
}]);
if (i !== undefined && Array.isArray(i)) {
contextAddCallback(i);
}
return;
}
contextAddCallback(item.items);
}
function addItem(item) {
function addContent(content) {
if (content === null) {
return;
}
if (typeof content === 'function') {
return addContent(content(ev));
}
if (_typeof(content) === 'object') {
contextItemContainer.appendChild(content);
return;
}
applyContextItemStyle(contextItemContainer);
contextItemContainer.innerHTML = content;
return;
}
addContent(item.title);
item.contextItemContainer = contextItemContainer;
if (item.items && item.items.length > 0 || typeof item.items === 'function') {
childMenuArrow = document.createElement('div');
self.createInlineStyle(childMenuArrow, 'canvas-datagrid-context-child-arrow');
childMenuArrow.innerHTML = self.style.childContextMenuArrowHTML;
contextItemContainer.appendChild(childMenuArrow);
contextItemContainer.addEventListener('mouseover', createChildContext);
contextItemContainer.addEventListener('mouseout', function () {
contextItemContainer.setAttribute('opening', '0');
});
}
if (item.click) {
contextItemContainer.addEventListener('click', function (ev) {
item.click.apply(self, [ev]);
});
}
}
addItem(item);
container.appendChild(contextItemContainer);
});
}
function clickIndex(idx) {
items[idx].contextItemContainer.dispatchEvent(new Event('click'));
}
function checkArrowVisibility() {
if (container.scrollTop > 0) {
self.parentDOMNode.appendChild(upArrow);
} else if (upArrow.parentNode) {
upArrow.parentNode.removeChild(upArrow);
}
if (container.scrollTop >= container.scrollHeight - container.offsetHeight && downArrow.parentNode) {
downArrow.parentNode.removeChild(downArrow);
} else if (container.scrollHeight - container.offsetHeight > 0 && !(container.scrollTop >= container.scrollHeight - container.offsetHeight)) {
self.parentDOMNode.appendChild(downArrow);
}
}
function fade(element) {
var opacity = 1;
var timer = setInterval(function () {
if (opacity <= 0.1) {
clearInterval(timer);
element.style.display = 'none';
if (element.parentNode) {
element.parentNode.removeChild(element);
}
}
element.style.opacity = opacity;
element.style.filter = 'alpha(opacity=' + opacity * 100 + ')';
opacity -= opacity * 0.1;
}, self.attributes.animationDurationHideContextMenu * 0.1);
}
function unfade(element) {
var opacity = 0.1;
element.style.display = 'block';
var timer = setInterval(function () {
if (opacity >= 1) {
clearInterval(timer);
}
element.style.opacity = opacity;
element.style.filter = 'alpha(opacity=' + opacity * 100 + ')';
opacity += opacity * 0.1;
}, self.attributes.animationDurationShowContextMenu * 0.1);
}
function startHoverScroll(type) {
return function t() {
var a = self.attributes.contextHoverScrollAmount;
if (type === 'up' && container.scrollTop === 0) {
return;
}
if (type === 'down' && container.scrollTop === container.scrollHeight) {
return;
}
container.scrollTop += type === 'up' ? -a : a;
hoverScrollTimeout = setTimeout(t, self.attributes.contextHoverScrollRateMs, type);
};
}
function endHoverScroll(type) {
return function () {
clearTimeout(hoverScrollTimeout);
};
}
function init() {
var loc = {},
s = self.scrollOffset(self.canvas);
if (zIndexTop === undefined) {
zIndexTop = self.style.contextMenuZIndex;
}
createItems();
self.createInlineStyle(container, 'canvas-datagrid-context-menu' + (self.mobile ? '-mobile' : ''));
loc.x = pos.left - s.left;
loc.y = pos.top - s.top;
loc.height = 0;
zIndexTop += 1;
container.style.opacity = 0;
container.style.position = 'absolute';
upArrow.style.color = self.style.contextMenuArrowColor;
downArrow.style.color = self.style.contextMenuArrowColor;
[upArrow, downArrow].forEach(function (el) {
el.style.textAlign = 'center';
el.style.position = 'absolute';
el.style.zIndex = zIndexTop + 1;
});
container.style.zIndex = zIndexTop;
if (parentContextMenu && parentContextMenu.inputDropdown) {
container.style.maxHeight = window.innerHeight - loc.y - self.style.autocompleteBottomMargin + 'px';
container.style.minWidth = pos.width + 'px';
loc.y += pos.height;
}
if (self.mobile) {
container.style.width = pos.width + 'px';
}
container.style.left = loc.x + 'px';
container.style.top = loc.y + 'px';
container.addEventListener('scroll', checkArrowVisibility);
container.addEventListener('wheel', function (e) {
if (self.hasFocus) {
container.scrollTop += e.deltaY;
container.scrollLeft += e.deltaX;
}
checkArrowVisibility();
});
upArrow.innerHTML = self.style.contextMenuArrowUpHTML;
downArrow.innerHTML = self.style.contextMenuArrowDownHTML;
container.appendChild(upArrow);
document.body.appendChild(downArrow);
document.body.appendChild(container);
unfade(container);
rect = container.getBoundingClientRect(); // TODO: fix !(parentContextMenu && parentContextMenu.inputDropdown) state (autocomplete)
if (rect.bottom > window.innerHeight) {
if (!(parentContextMenu && parentContextMenu.inputDropdown)) {
loc.y -= rect.bottom + self.style.contextMenuWindowMargin - window.innerHeight;
}
if (loc.y < 0) {
loc.y = self.style.contextMenuWindowMargin;
}
if (container.offsetHeight > window.innerHeight - self.style.contextMenuWindowMargin) {
container.style.height = window.innerHeight - self.style.contextMenuWindowMargin * 2 + 'px';
}
}
if (rect.right > window.innerWidth) {
loc.x -= rect.right - window.innerWidth + self.style.contextMenuWindowMargin;
}
if (loc.x < 0) {
loc.x = self.style.contextMenuWindowMargin;
}
if (loc.y < 0) {
loc.y = self.style.contextMenuWindowMargin;
}
container.style.left = loc.x + 'px';
container.style.top = loc.y + 'px';
rect = container.getBoundingClientRect();
upArrow.style.top = rect.top + 'px';
downArrow.style.top = rect.top + rect.height - downArrow.offsetHeight + 'px';
upArrow.style.left = rect.left + 'px';
downArrow.style.left = rect.left + 'px';
downArrow.style.width = container.offsetWidth + 'px';
upArrow.style.width = container.offsetWidth + 'px';
downArrow.addEventListener('mouseover', startHoverScroll('down'));
downArrow.addEventListener('mouseout', endHoverScroll('down'));
upArrow.addEventListener('mouseover', startHoverScroll('up'));
upArrow.addEventListener('mouseout', endHoverScroll('up'));
checkArrowVisibility();
}
intf.parentGrid = self.intf;
intf.parentContextMenu = parentContextMenu;
intf.container = container;
init();
intf.clickIndex = clickIndex;
intf.rect = rect;
intf.items = items;
intf.upArrow = upArrow;
intf.downArrow = downArrow;
intf.dispose = function () {
clearTimeout(hoverScrollTimeout);
children.forEach(function (c) {
c.dispose();
});
[downArrow, upArrow, container].forEach(function (el) {
fade(el);
});
};
Object.defineProperty(intf, 'selectedIndex', {
get: function get() {
return selectedIndex;
},
set: function set(value) {
if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
throw new Error('Context menu selected index must be a sane number.');
}
selectedIndex = value;
if (selectedIndex > items.length - 1) {
selectedIndex = items.length - 1;
}
if (selectedIndex < 0) {
selectedIndex = 0;
}
items.forEach(function (item, index) {
if (index === selectedIndex) {
return self.createInlineStyle(item.contextItemContainer, 'canvas-datagrid-context-menu-item:hover');
}
self.createInlineStyle(item.contextItemContainer, 'canvas-datagrid-context-menu-item');
});
}
});
return intf;
}
function createFilterContextMenuItems(e) {
var filterContainer = document.createElement('div'),
filterLabel = document.createElement('div'),
filterAutoCompleteButton = document.createElement('button'),
filterInput = document.createElement('input'),
n = e.cell && e.cell.header ? e.cell.header.title || e.cell.header.name : '',
iRect;
function checkRegExpErrorState() {
filterInput.style.background = self.style.contextFilterInputBackground;
filterInput.style.color = self.style.contextFilterInputColor;
if (self.invalidFilterRegEx) {
filterInput.style.background = self.style.contextFilterInvalidRegExpBackground;
filterInput.style.color = self.style.contextFilterInvalidRegExpColor;
}
}
function fillAutoComplete() {
var count = 0;
var items = {};
var blanksItem = [];
self.viewData.forEach(function (row) {
var cellValue = row[e.cell.header.name] == null ? row[e.cell.header.name] : String(row[e.cell.header.name]).trim();
var value = self.blankValues.includes(cellValue) ? self.attributes.blanksText : cellValue;
if (items[value] || count > self.attributes.maxAutoCompleteItems) {
return;
}
count += 1;
items[value] = {
title: self.formatters[e.cell.header.type || 'string']({
cell: {
value: value
}
}),
click: function click(e) {
filterInput.value = value;
e.stopPropagation();
filterInput.dispatchEvent(new Event('keyup'));
self.disposeAutocomplete();
return;
}
};
});
if (Object.keys(items).indexOf(self.attributes.blanksText) !== -1) {
blanksItem.push(items[self.attributes.blanksText]);
delete items[self.attributes.blanksText];
}
return blanksItem.concat(Object.keys(items).map(function (key) {
return items[key];
}));
}
function createAutoCompleteContext(ev) {
if (ev && ['ArrowDown', 'ArrowUp', 'Enter', 'Tab'].includes(ev.key)) {
return;
}
var autoCompleteItems = fillAutoComplete();
iRect = filterInput.getBoundingClientRect();
if (autoCompleteContext) {
autoCompleteContext.dispose();
autoCompleteContext = undefined;
}
autoCompleteContext = createContextMenu(e, {
left: iRect.left,
top: iRect.top,
right: iRect.right,
bottom: iRect.bottom,
height: iRect.height,
width: iRect.width
}, autoCompleteItems, {
inputDropdown: true
});
autoCompleteContext.selectedIndex = 0;
}
self.createInlineStyle(filterLabel, 'canvas-datagrid-context-menu-label');
self.createInlineStyle(filterAutoCompleteButton, 'canvas-datagrid-context-menu-filter-button');
self.createInlineStyle(filterInput, 'canvas-datagrid-context-menu-filter-input');
checkRegExpErrorState();
filterInput.onclick = self.disposeAutocomplete;
filterInput.addEventListener('keydown', function (e) {
if (e.key === 'ArrowDown') {
autoCompleteContext.selectedIndex += 1;
}
if (e.key === 'ArrowUp') {
autoCompleteContext.selectedIndex -= 1;
}
if (e.key === 'Enter') {
autoCompleteContext.clickIndex(autoCompleteContext.selectedIndex);
self.disposeContextMenu();
}
if (e.key === 'Tab') {
autoCompleteContext.clickIndex(autoCompleteContext.selectedIndex);
e.preventDefault();
}
if (e.key === 'Escape') {
self.disposeContextMenu();
}
});
filterInput.addEventListener('keyup', function () {
self.setFilter(e.cell.header.name, filterInput.value);
});
filterInput.addEventListener('keyup', createAutoCompleteContext);
['focus', 'blur', 'keydown', 'keyup', 'change'].forEach(function (en) {
filterInput.addEventListener(en, checkRegExpErrorState);
});
filterInput.value = e.cell.header ? self.columnFilters[e.cell.header.name] || '' : '';
filterLabel.innerHTML = self.attributes.filterOptionText.replace(/%s/g, n);
filterAutoCompleteButton.onclick = function () {
if (autoCompleteContext) {
return self.disposeAutocomplete();
}
createAutoCompleteContext();
};
filterAutoCompleteButton.innerHTML = self.style.contextFilterButtonHTML;
filterContainer.addEventListener('click', function (e) {
return e.stopPropagation();
});
filterContainer.appendChild(filterLabel);
filterContainer.appendChild(filterInput);
filterContainer.appendChild(filterAutoCompleteButton);
e.items.push({
title: filterContainer
});
if (Object.keys(self.columnFilters).length) {
Object.keys(self.columnFilters).forEach(function (cf) {
var h = self.getHeaderByName(cf);
e.items.push({
title: self.attributes.removeFilterOptionText.replace(/%s/g, h.title || h.name),
click: function removeFilterClick(e) {
e.preventDefault();
self.setFilter(cf, '');
self.controlInput.focus();
}
});
});
}
}
function addDefaultContextMenuItem(e) {
var schema = self.getSchema();
/**
* A map between columnIndex and column data
* @type {Map<string,any>}
*/
var columns;
var getColumnsMap = function getColumnsMap() {
if (!columns) columns = new Map(schema.map(function (_col) {
return [_col.columnIndex, _col];
}));
return columns;
};
var isSorting = self.orderings.columns && self.orderings.columns.length > 0;
var isNormalCell = !(e.cell.isBackground || e.cell.isColumnHeaderCellCap || e.cell.isScrollBar || e.cell.isCorner || e.cell.isRowHeader) && e.cell.header;
if (self.attributes.showFilter && isNormalCell) {
createFilterContextMenuItems(e);
}
if (self.attributes.showCopy && self.canSelectionsBeCopied()) {
e.items.push({
title: self.attributes.copyText,
click: function click() {
document.execCommand('copy');
self.disposeContextMenu();
self.controlInput.focus();
}
});
}
if (self.attributes.showPaste && self.clipBoardData) {
e.items.push({
title: self.attributes.pasteText,
click: function click() {
// Original function call:
// self.paste(self.clipBoardData, e.cell.columnIndex, e.cell.rowIndex);
// We can remove the last two parameters follow
// because this function only need one parameter by following the function declaration
self.paste(self.clipBoardData);
self.draw();
}
});
}
if (self.attributes.showColumnSelector) {
e.items.push({
title: self.attributes.columnSelectorText,
items: function items() {
var d = [];
self.getSchema().forEach(function (column) {
function toggleColumnVisibility(e) {
column.hidden = !column.hidden;
self.dispatchEvent('togglecolumn', {
column: column,
hidden: column.hidden
});
e.preventDefault();
self.stopPropagation(e);
self.disposeContextMenu();
self.resize(true);
self.setStorageData();
}
var el = document.createElement('div');
applyContextItemStyle(el);
el.addEventListener('touchstart', toggleColumnVisibility);
el.addEventListener('click', toggleColumnVisibility);
el.innerHTML = (column.hidden ? self.attributes.columnSelectorHiddenText : self.attributes.columnSelectorVisibleText) + (column.title || column.name);
d.push({
title: el
});
});
return d;
}
});
if (e.cell && e.cell.header && e.cell.columnIndex > -1) {
// This variable represents the order index because of the following codes from `draw.js`:
// columnIndex: columnOrderIndex,
var columnOrderIndex = e.cell.columnIndex;
var columnIndex = self.orders.columns[columnOrderIndex];
var contiguousColumns = self.getSelectedContiguousColumns(schema);
var title = '';
if (contiguousColumns) {
title = contiguousColumns.map(function (col) {
return col.title || col.name;
}).join('-');
} else {
var column = schema[columnIndex];
if (column) title = column.title || column.name;
}
e.items.push({
title: self.attributes.hideColumnText.replace(/%s/gi, title),
click: function click(ev) {
ev.preventDefault();
self.stopPropagation(ev);
self.disposeContextMenu();
if (contiguousColumns) {
self.hideColumns(contiguousColumns[0].orderIndex, contiguousColumns[1].orderIndex);
} else {
self.hideColumns(columnOrderIndex);
}
}
});
}
}
if (self.attributes.saveAppearance && self.attributes.showClearSettingsOption && (Object.keys(self.sizes.rows).length > 0 || Object.keys(self.sizes.columns).length > 0)) {
e.items.push({
title: self.attributes.clearSettingsOptionText,
click: function click(e) {
e.preventDefault();
self.sizes.rows = {};
self.sizes.columns = {};
self.createRowOrders();
self.createColumnOrders();
self.storedSettings = undefined;
self.dispatchEvent('resizecolumn', {
columnWidth: self.style.cellWidth
});
self.dispatchEvent('resizerow', {
cellHeight: self.style.cellHeight
});
self.setStorageData();
self.resize(true);
self.disposeContextMenu();
self.controlInput.focus();
}
});
}
if (self.attributes.allowSorting && self.attributes.showOrderByOption && isNormalCell) {
e.items.push({
title: self.attributes.showOrderByOptionTextAsc.replace('%s', e.cell.header.title || e.cell.header.name),
click: function click(ev) {
ev.preventDefault();
self.order(e.cell.header.name, 'asc');
self.controlInput.focus();
}
});
e.items.push({
title: self.attributes.showOrderByOptionTextDesc.replace('%s', e.cell.header.title || e.cell.header.name),
click: function click(ev) {
ev.preventDefault();
self.order(e.cell.header.name, 'desc');
self.disposeContextMenu();
self.controlInput.focus();
}
});
} //#region hide rows
var canHideRows = !isSorting && e.cell.isRowHeader && e.cell.header;
if (canHideRows) {
var range = self.getSelectedContiguousRows(true);
if (range) {
var boundRowIndexes = range.map(function (viewRowIndex) {
return self.getBoundRowIndexFromViewRowIndex(viewRowIndex);
});
var _title;
if (boundRowIndexes.length === 1) {
if (typeof boundRowIndexes[0] === 'number') _title = boundRowIndexes[0] + 1;else _title = range[0] + 1;
_title = self.attributes.showHideRow.replace('%s', _title); // hide one row
e.items.push({
title: _title,
click: function click(ev) {
ev.preventDefault();
self.hideRows(boundRowIndexes[0], boundRowIndexes[0]);
}
});
} else if (boundRowIndexes[0] <= boundRowIndexes[1]) {
_title = boundRowIndexes.map(function (it, index) {
if (typeof it === 'number') return it + 1;
return range[index] + 1;
}).join('-');
_title = self.attributes.showHideRows.replace('%s', _title); // hide rows
e.items.push({
title: _title,
click: function click(ev) {
ev.preventDefault();
self.hideRows(boundRowIndexes[0], boundRowIndexes[1]);
}
});
}
}
} //#endregion hide rows
//#region group/ungroup columns
var groupAreaHeight = self.getColumnGroupAreaHeight();
var groupAreaWidth = self.getRowGroupAreaWidth();
var setCollapseStateForAllGroups = function setCollapseStateForAllGroups(allGroups, collapsed) {
if (allGroups.length === 0) return;
for (var i = 0; i < allGroups.length; i++) {
var groups = allGroups[i];
for (var j = 0; j < groups.length; j++) {
var group = groups[j];
group.collapsed = collapsed;
}
}
self.refresh();
};
if (e.pos && e.pos.y < groupAreaHeight) {
e.items.push({
title: self.attributes.showRemoveAllGroupColumns,
click: function click(ev) {
ev.preventDefault();
self.groupedColumns = [];
self.refresh();
}
});
e.items.push({
title: self.attributes.showExpandAllGroupColumns,
click: function click(ev) {
ev.preventDefault();
setCollapseStateForAllGroups(self.groupedColumns, false);
}
});
e.items.push({
title: self.attributes.showCollapseAllGroupColumns,
click: function click(ev) {
ev.preventDefault();
setCollapseStateForAllGroups(self.groupedColumns, true);
}
});
}
if (e.pos && e.pos.x < groupAreaWidth) {
e.items.push({
title: self.attributes.showRemoveAllGroupRows,
click: function click(ev) {
ev.preventDefault();
self.groupedRows = [];
self.refresh();
}
});
e.items.push({
title: self.attributes.showExpandAllGroupRows,
click: function click(ev) {
ev.preventDefault();
setCollapseStateForAllGroups(self.groupedRows, false);
}
});
e.items.push({
title: self.attributes.showCollapseAllGroupRows,
click: function click(ev) {
ev.preventDefault();
setCollapseStateForAllGroups(self.groupedRows, true);
}
});
}
var canGroupByColumns = self.attributes.allowGroupingColumns && e.cell.isColumnHeader && e.cell.header && e.cell.header.index > 0;
var canUngroupColumns = self.attributes.allowGroupingColumns && e.cell.isColumnHeader;
var canGroupByRows = !isSorting && self.attributes.allowGroupingRows && e.cell.isRowHeader && e.cell.header;
var canUngroupRows = self.attributes.allowGroupingRows && e.cell.isRowHeader;
if (canGroupByColumns) {
/** @type {number[]} */
var groupIndexes = [];
/** @type {number} */
var headerIndex = e.cell.header.index;
var col = headerIndex;
for (; col >= 0; col--) {
if (!self.isColumnSelected(col)) break;
groupIndexes[0] = col;
}
for (col = headerIndex;; col++) {
if (!self.isColumnSelected(col)) break;
groupIndexes[1] = col;
}
if (col !== headerIndex && groupIndexes.length === 2 && groupIndexes[1] > groupIndexes[0] && self.isNewGroupRangeValid(self.groupedColumns, groupIndexes[0], groupIndexes[1])) {
var _columns = getColumnsMap();
var groupTitles = [];
var groupNames = [];
for (var i = 0; i < groupIndexes.length; i++) {
var _columnIndex = groupIndexes[i];
var _column = _columns.get(_columnIndex);
if (_column) {
groupNames.push(_column.name);
groupTitles.push(_column.title || _column.name || _column.index);
}
}
if (groupNames[0] && groupNames[1]) {
// show group options
e.items.push({
title: self.attributes.showGroupColumns.replace('%s', groupTitles[0] + '-' + groupTitles[1]),
click: function click(ev) {
ev.preventDefault();
self.groupColumns(groupNames[0], groupNames[1]);
self.controlInput.focus();
}
});
}
}
}
if (canUngroupColumns) {
var _columnIndex2 = e.cell.columnIndex;
var groups = self.getGroupsColumnBelongsTo(_columnIndex2);
var _columns2 = getColumnsMap();
var _loop = function _loop(_i) {
var _groups$_i = groups[_i],
from = _groups$_i.from,
to = _groups$_i.to;
var cell0 = _columns2.get(from);
var cell1 = _columns2.get(to);
if (cell0 && cell1) {
var formatArgs = (cell0.title || cell0.name || cell0.in