slickgrid
Version:
A lightning fast JavaScript grid/spreadsheet
1,057 lines (1,054 loc) • 673 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key != "symbol" ? key + "" : key, value);
// src/slick.core.ts
var SlickEventData = class {
constructor(event2, args) {
this.event = event2;
this.args = args;
__publicField(this, "_isPropagationStopped", !1);
__publicField(this, "_isImmediatePropagationStopped", !1);
__publicField(this, "_isDefaultPrevented", !1);
__publicField(this, "returnValues", []);
__publicField(this, "returnValue");
__publicField(this, "_eventTarget");
__publicField(this, "nativeEvent");
__publicField(this, "arguments_");
// public props that can be optionally pulled from the provided Event in constructor
// they are all optional props because it really depends on the type of Event provided (KeyboardEvent, MouseEvent, ...)
__publicField(this, "altKey");
__publicField(this, "ctrlKey");
__publicField(this, "metaKey");
__publicField(this, "shiftKey");
__publicField(this, "key");
__publicField(this, "keyCode");
__publicField(this, "clientX");
__publicField(this, "clientY");
__publicField(this, "offsetX");
__publicField(this, "offsetY");
__publicField(this, "pageX");
__publicField(this, "pageY");
__publicField(this, "bubbles");
__publicField(this, "target");
__publicField(this, "type");
__publicField(this, "which");
__publicField(this, "x");
__publicField(this, "y");
this.nativeEvent = event2, this.arguments_ = args, event2 && [
"altKey",
"ctrlKey",
"metaKey",
"shiftKey",
"key",
"keyCode",
"clientX",
"clientY",
"offsetX",
"offsetY",
"pageX",
"pageY",
"bubbles",
"target",
"type",
"which",
"x",
"y"
].forEach((key) => this[key] = event2[key]), this._eventTarget = this.nativeEvent ? this.nativeEvent.target : void 0;
}
get defaultPrevented() {
return this._isDefaultPrevented;
}
/**
* Stops event from propagating up the DOM tree.
* @method stopPropagation
*/
stopPropagation() {
this._isPropagationStopped = !0, this.nativeEvent?.stopPropagation();
}
/**
* Returns whether stopPropagation was called on this event object.
* @method isPropagationStopped
* @return {Boolean}
*/
isPropagationStopped() {
return this._isPropagationStopped;
}
/**
* Prevents the rest of the handlers from being executed.
* @method stopImmediatePropagation
*/
stopImmediatePropagation() {
this._isImmediatePropagationStopped = !0, this.nativeEvent && this.nativeEvent.stopImmediatePropagation();
}
/**
* Returns whether stopImmediatePropagation was called on this event object.\
* @method isImmediatePropagationStopped
* @return {Boolean}
*/
isImmediatePropagationStopped() {
return this._isImmediatePropagationStopped;
}
getNativeEvent() {
return this.nativeEvent;
}
preventDefault() {
this.nativeEvent && this.nativeEvent.preventDefault(), this._isDefaultPrevented = !0;
}
isDefaultPrevented() {
return this.nativeEvent ? this.nativeEvent.defaultPrevented : this._isDefaultPrevented;
}
addReturnValue(value) {
this.returnValues.push(value), this.returnValue === void 0 && value !== void 0 && (this.returnValue = value);
}
getReturnValue() {
return this.returnValue;
}
getArguments() {
return this.arguments_;
}
}, SlickEvent = class {
/**
* Constructor
* @param {String} [eventName] - event name that could be used for dispatching CustomEvent (when enabled)
* @param {BasePubSub} [pubSubService] - event name that could be used for dispatching CustomEvent (when enabled)
*/
constructor(eventName, pubSub) {
this.eventName = eventName;
this.pubSub = pubSub;
__publicField(this, "_handlers", []);
__publicField(this, "_pubSubService");
this._pubSubService = pubSub;
}
get subscriberCount() {
return this._handlers.length;
}
/**
* Adds an event handler to be called when the event is fired.
* <p>Event handler will receive two arguments - an <code>EventData</code> and the <code>data</code>
* object the event was fired with.<p>
* @method subscribe
* @param {Function} fn - Event handler.
*/
subscribe(fn) {
this._handlers.push(fn);
}
/**
* Removes an event handler added with <code>subscribe(fn)</code>.
* @method unsubscribe
* @param {Function} [fn] - Event handler to be removed.
*/
unsubscribe(fn) {
for (let i = this._handlers.length - 1; i >= 0; i--)
this._handlers[i] === fn && this._handlers.splice(i, 1);
}
/**
* Fires an event notifying all subscribers.
* @method notify
* @param {Object} args Additional data object to be passed to all handlers.
* @param {EventData} [event] - An <code>EventData</code> object to be passed to all handlers.
* For DOM events, an existing W3C event object can be passed in.
* @param {Object} [scope] - The scope ("this") within which the handler will be executed.
* If not specified, the scope will be set to the <code>Event</code> instance.
*/
notify(args, evt, scope) {
let sed = evt instanceof SlickEventData ? evt : new SlickEventData(evt, args);
scope = scope || this;
for (let i = 0; i < this._handlers.length && !(sed.isPropagationStopped() || sed.isImmediatePropagationStopped()); i++) {
let returnValue = this._handlers[i].call(scope, sed, args);
sed.addReturnValue(returnValue);
}
if (typeof this._pubSubService?.publish == "function" && this.eventName) {
let ret = this._pubSubService.publish(this.eventName, { args, eventData: sed });
sed.addReturnValue(ret);
}
return sed;
}
setPubSubService(pubSub) {
this._pubSubService = pubSub;
}
}, SlickEventHandler = class {
constructor() {
__publicField(this, "handlers", []);
}
subscribe(event2, handler) {
return this.handlers.push({ event: event2, handler }), event2.subscribe(handler), this;
}
unsubscribe(event2, handler) {
let i = this.handlers.length;
for (; i--; )
if (this.handlers[i].event === event2 && this.handlers[i].handler === handler) {
this.handlers.splice(i, 1), event2.unsubscribe(handler);
return;
}
return this;
}
unsubscribeAll() {
let i = this.handlers.length;
for (; i--; )
this.handlers[i].event.unsubscribe(this.handlers[i].handler);
return this.handlers = [], this;
}
}, SlickRange = class {
constructor(fromRow, fromCell, toRow, toCell) {
__publicField(this, "fromRow");
__publicField(this, "fromCell");
__publicField(this, "toCell");
__publicField(this, "toRow");
toRow === void 0 && toCell === void 0 && (toRow = fromRow, toCell = fromCell), this.fromRow = Math.min(fromRow, toRow), this.fromCell = Math.min(fromCell, toCell), this.toCell = Math.max(fromCell, toCell), this.toRow = Math.max(fromRow, toRow);
}
/**
* Returns whether a range represents a single row.
* @method isSingleRow
* @return {Boolean}
*/
isSingleRow() {
return this.fromRow === this.toRow;
}
/**
* Returns whether a range represents a single cell.
* @method isSingleCell
* @return {Boolean}
*/
isSingleCell() {
return this.fromRow === this.toRow && this.fromCell === this.toCell;
}
/**
* Returns whether a range contains a given cell.
* @method contains
* @param row {Integer}
* @param cell {Integer}
* @return {Boolean}
*/
contains(row, cell) {
return row >= this.fromRow && row <= this.toRow && cell >= this.fromCell && cell <= this.toCell;
}
/**
* Returns a readable representation of a range.
* @method toString
* @return {String}
*/
toString() {
return this.isSingleCell() ? `(${this.fromRow}:${this.fromCell})` : `(${this.fromRow}:${this.fromCell} - ${this.toRow}:${this.toCell})`;
}
}, SlickNonDataItem = class {
constructor() {
__publicField(this, "__nonDataRow", !0);
}
}, SlickGroup = class extends SlickNonDataItem {
constructor() {
super();
__publicField(this, "__group", !0);
/**
* Grouping level, starting with 0.
* @property level
* @type {Number}
*/
__publicField(this, "level", 0);
/**
* Number of rows in the group.
* @property count
* @type {Integer}
*/
__publicField(this, "count", 0);
/**
* Grouping value.
* @property value
* @type {Object}
*/
__publicField(this, "value", null);
/**
* Formatted display value of the group.
* @property title
* @type {String}
*/
__publicField(this, "title", null);
/**
* Whether a group is collapsed.
* @property collapsed
* @type {Boolean}
*/
__publicField(this, "collapsed", !1);
/**
* Whether a group selection checkbox is checked.
* @property selectChecked
* @type {Boolean}
*/
__publicField(this, "selectChecked", !1);
/**
* GroupTotals, if any.
* @property totals
* @type {GroupTotals}
*/
__publicField(this, "totals", null);
/**
* Rows that are part of the group.
* @property rows
* @type {Array}
*/
__publicField(this, "rows", []);
/**
* Sub-groups that are part of the group.
* @property groups
* @type {Array}
*/
__publicField(this, "groups", null);
/**
* A unique key used to identify the group. This key can be used in calls to DataView
* collapseGroup() or expandGroup().
* @property groupingKey
* @type {Object}
*/
__publicField(this, "groupingKey", null);
}
/**
* Compares two Group instances.
* @method equals
* @return {Boolean}
* @param group {Group} Group instance to compare to.
*/
equals(group) {
return this.value === group.value && this.count === group.count && this.collapsed === group.collapsed && this.title === group.title;
}
}, SlickGroupTotals = class extends SlickNonDataItem {
constructor() {
super();
__publicField(this, "__groupTotals", !0);
/**
* Parent Group.
* @param group
* @type {Group}
*/
__publicField(this, "group", null);
/**
* Whether the totals have been fully initialized / calculated.
* Will be set to false for lazy-calculated group totals.
* @param initialized
* @type {Boolean}
*/
__publicField(this, "initialized", !1);
}
}, SlickEditorLock = class {
constructor() {
__publicField(this, "activeEditController", null);
}
/**
* Returns true if a specified edit controller is active (has the edit lock).
* If the parameter is not specified, returns true if any edit controller is active.
* @method isActive
* @param editController {EditController}
* @return {Boolean}
*/
isActive(editController) {
return editController ? this.activeEditController === editController : this.activeEditController !== null;
}
/**
* Sets the specified edit controller as the active edit controller (acquire edit lock).
* If another edit controller is already active, and exception will be throw new Error(.
* @method activate
* @param editController {EditController} edit controller acquiring the lock
*/
activate(editController) {
if (editController !== this.activeEditController) {
if (this.activeEditController !== null)
throw new Error("Slick.EditorLock.activate: an editController is still active, can't activate another editController");
if (!editController.commitCurrentEdit)
throw new Error("Slick.EditorLock.activate: editController must implement .commitCurrentEdit()");
if (!editController.cancelCurrentEdit)
throw new Error("Slick.EditorLock.activate: editController must implement .cancelCurrentEdit()");
this.activeEditController = editController;
}
}
/**
* Unsets the specified edit controller as the active edit controller (release edit lock).
* If the specified edit controller is not the active one, an exception will be throw new Error(.
* @method deactivate
* @param editController {EditController} edit controller releasing the lock
*/
deactivate(editController) {
if (this.activeEditController) {
if (this.activeEditController !== editController)
throw new Error("Slick.EditorLock.deactivate: specified editController is not the currently active one");
this.activeEditController = null;
}
}
/**
* Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit
* controller and returns whether the commit attempt was successful (commit may fail due to validation
* errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded
* and false otherwise. If no edit controller is active, returns true.
* @method commitCurrentEdit
* @return {Boolean}
*/
commitCurrentEdit() {
return this.activeEditController ? this.activeEditController.commitCurrentEdit() : !0;
}
/**
* Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit
* controller and returns whether the edit was successfully cancelled. If no edit controller is
* active, returns true.
* @method cancelCurrentEdit
* @return {Boolean}
*/
cancelCurrentEdit() {
return this.activeEditController ? this.activeEditController.cancelCurrentEdit() : !0;
}
};
function regexSanitizer(dirtyHtml) {
return dirtyHtml.replace(/(\b)(on[a-z]+)(\s*)=|javascript:([^>]*)[^>]*|(<\s*)(\/*)script([<>]*).*(<\s*)(\/*)script(>*)|(<)(\/*)(script|script defer)(.*)(>|>">)/gi, "");
}
var BindingEventService = class {
constructor() {
__publicField(this, "_boundedEvents", []);
}
getBoundedEvents() {
return this._boundedEvents;
}
destroy() {
this.unbindAll();
}
/** Bind an event listener to any element */
bind(element, eventName, listener, options, groupName = "") {
element && (element.addEventListener(eventName, listener, options), this._boundedEvents.push({ element, eventName, listener, groupName }));
}
/** Unbind all will remove every every event handlers that were bounded earlier */
unbind(element, eventName, listener) {
element?.removeEventListener && element.removeEventListener(eventName, listener);
}
unbindByEventName(element, eventName) {
let boundedEvent = this._boundedEvents.find((e) => e.element === element && e.eventName === eventName);
boundedEvent && this.unbind(boundedEvent.element, boundedEvent.eventName, boundedEvent.listener);
}
/**
* Unbind all event listeners that were bounded, optionally provide a group name to unbind all listeners assigned to that specific group only.
*/
unbindAll(groupName) {
if (groupName) {
let groupNames = Array.isArray(groupName) ? groupName : [groupName];
for (let i = this._boundedEvents.length - 1; i >= 0; --i) {
let boundedEvent = this._boundedEvents[i];
if (groupNames.some((g) => g === boundedEvent.groupName)) {
let { element, eventName, listener } = boundedEvent;
this.unbind(element, eventName, listener), this._boundedEvents.splice(i, 1);
}
}
} else
for (; this._boundedEvents.length > 0; ) {
let boundedEvent = this._boundedEvents.pop(), { element, eventName, listener } = boundedEvent;
this.unbind(element, eventName, listener);
}
}
}, _Utils = class _Utils {
static isFunction(obj) {
return typeof obj == "function" && typeof obj.nodeType != "number" && typeof obj.item != "function";
}
static isPlainObject(obj) {
if (!obj || _Utils.toString.call(obj) !== "[object Object]")
return !1;
let proto = _Utils.getProto(obj);
if (!proto)
return !0;
let Ctor = _Utils.hasOwn.call(proto, "constructor") && proto.constructor;
return typeof Ctor == "function" && _Utils.fnToString.call(Ctor) === _Utils.ObjectFunctionString;
}
static calculateAvailableSpace(element) {
let bottom = 0, top = 0, left = 0, right = 0, windowHeight = window.innerHeight || 0, windowWidth = window.innerWidth || 0, scrollPosition = _Utils.windowScrollPosition(), pageScrollTop = scrollPosition.top, pageScrollLeft = scrollPosition.left, elmOffset = _Utils.offset(element);
if (elmOffset) {
let elementOffsetTop = elmOffset.top || 0, elementOffsetLeft = elmOffset.left || 0;
top = elementOffsetTop - pageScrollTop, bottom = windowHeight - (elementOffsetTop - pageScrollTop), left = elementOffsetLeft - pageScrollLeft, right = windowWidth - (elementOffsetLeft - pageScrollLeft);
}
return { top, bottom, left, right };
}
static extend(...args) {
let options, name, src, copy, copyIsArray, clone, target = args[0], i = 1, deep = !1, length = args.length;
for (typeof target == "boolean" ? (deep = target, target = args[i] || {}, i++) : target = target || {}, typeof target != "object" && !_Utils.isFunction(target) && (target = {}), i === length && (target = this, i--); i < length; i++)
if (_Utils.isDefined(options = args[i]))
for (name in options)
copy = options[name], !(name === "__proto__" || target === copy) && (deep && copy && (_Utils.isPlainObject(copy) || (copyIsArray = Array.isArray(copy))) ? (src = target[name], copyIsArray && !Array.isArray(src) ? clone = [] : !copyIsArray && !_Utils.isPlainObject(src) ? clone = {} : clone = src, copyIsArray = !1, target[name] = _Utils.extend(deep, clone, copy)) : copy !== void 0 && (target[name] = copy));
return target;
}
/**
* Create a DOM Element with any optional attributes or properties.
* It will only accept valid DOM element properties that `createElement` would accept.
* For example: `createDomElement('div', { className: 'my-css-class' })`,
* for style or dataset you need to use nested object `{ style: { display: 'none' }}
* The last argument is to optionally append the created element to a parent container element.
* @param {String} tagName - html tag
* @param {Object} options - element properties
* @param {[HTMLElement]} appendToParent - parent element to append to
*/
static createDomElement(tagName, elementOptions, appendToParent) {
let elm = document.createElement(tagName);
return elementOptions && Object.keys(elementOptions).forEach((elmOptionKey) => {
elmOptionKey === "innerHTML" && console.warn(`[SlickGrid] For better CSP (Content Security Policy) support, do not use "innerHTML" directly in "createDomElement('${tagName}', { innerHTML: 'some html'})", it is better as separate assignment: "const elm = createDomElement('span'); elm.innerHTML = 'some html';"`);
let elmValue = elementOptions[elmOptionKey];
typeof elmValue == "object" ? Object.assign(elm[elmOptionKey], elmValue) : elm[elmOptionKey] = elementOptions[elmOptionKey];
}), appendToParent?.appendChild && appendToParent.appendChild(elm), elm;
}
/**
* From any input provided, return the HTML string (when a string is provided, it will be returned "as is" but when it's a number it will be converted to string)
* When detecting HTMLElement/DocumentFragment, we can also specify which HTML type to retrieve innerHTML or outerHTML.
* We can get the HTML by looping through all fragment `childNodes`
* @param {DocumentFragment | HTMLElement | string | number} input
* @param {'innerHTML' | 'outerHTML'} [type] - when the input is a DocumentFragment or HTMLElement, which type of HTML do you want to return? 'innerHTML' or 'outerHTML'
* @returns {String}
*/
static getHtmlStringOutput(input, type = "innerHTML") {
return input instanceof DocumentFragment ? [].map.call(input.childNodes, (x) => x[type]).join("") || input.textContent || "" : input instanceof HTMLElement ? input[type] : String(input);
}
static emptyElement(element) {
for (; element?.firstChild; )
element.removeChild(element.firstChild);
return element;
}
/**
* Accepts string containing the class or space-separated list of classes, and
* returns list of individual classes.
* Method properly takes into account extra whitespaces in the `className`
* e.g.: " class1 class2 " => will result in `['class1', 'class2']`.
* @param {String} className - space separated list of class names
*/
static classNameToList(className = "") {
return className.split(" ").filter((cls) => cls);
}
static innerSize(elm, type) {
let size = 0;
if (elm) {
let clientSize = type === "height" ? "clientHeight" : "clientWidth", sides = type === "height" ? ["top", "bottom"] : ["left", "right"];
size = elm[clientSize];
for (let side of sides) {
let sideSize = parseFloat(_Utils.getElementProp(elm, `padding-${side}`) || "") || 0;
size -= sideSize;
}
}
return size;
}
static isDefined(value) {
return value != null && value !== "";
}
static getElementProp(elm, property) {
return elm?.getComputedStyle ? window.getComputedStyle(elm, null).getPropertyValue(property) : null;
}
/**
* Get the function details (param & body) of a function.
* It supports regular function and also ES6 arrow functions
* @param {Function} fn - function to analyze
* @param {Boolean} [addReturn] - when using ES6 function as single liner, we could add the missing `return ...`
* @returns
*/
static getFunctionDetails(fn, addReturn = !0) {
let isAsyncFn = !1, getFunctionBody = (func) => {
let fnStr = func.toString();
if (isAsyncFn = fnStr.includes("async "), fnStr.replaceAll(" ", "").includes("=>({")) {
let matches = fnStr.match(/(({.*}))/g) || [];
return matches.length >= 1 ? `return ${matches[0].trimStart()}` : fnStr;
}
let isOneLinerArrowFn = !fnStr.includes("{") && fnStr.includes("=>"), body = fnStr.substring(
fnStr.indexOf("{") + 1 || fnStr.indexOf("=>") + 2,
fnStr.includes("}") ? fnStr.lastIndexOf("}") : fnStr.length
);
return addReturn && isOneLinerArrowFn && !body.startsWith("return") ? "return " + body.trimStart() : body;
};
return {
params: ((func) => {
let STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,)]*))/mg, ARG_NAMES = /([^\s,]+)/g, fnStr = func.toString().replace(STRIP_COMMENTS, "");
return fnStr.slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")")).match(ARG_NAMES) ?? [];
})(fn),
body: getFunctionBody(fn),
isAsync: isAsyncFn
};
}
static insertAfterElement(referenceNode, newNode) {
referenceNode.parentNode?.insertBefore(newNode, referenceNode.nextSibling);
}
static isEmptyObject(obj) {
return obj == null ? !0 : Object.entries(obj).length === 0;
}
static noop() {
}
static offset(el) {
if (!el || !el.getBoundingClientRect)
return;
let box = el.getBoundingClientRect(), docElem = document.documentElement;
return {
top: box.top + window.pageYOffset - docElem.clientTop,
left: box.left + window.pageXOffset - docElem.clientLeft
};
}
static windowScrollPosition() {
return {
left: window.pageXOffset || document.documentElement.scrollLeft || 0,
top: window.pageYOffset || document.documentElement.scrollTop || 0
};
}
static width(el, value) {
if (!(!el || !el.getBoundingClientRect)) {
if (value === void 0)
return el.getBoundingClientRect().width;
_Utils.setStyleSize(el, "width", value);
}
}
static height(el, value) {
if (el) {
if (value === void 0)
return el.getBoundingClientRect().height;
_Utils.setStyleSize(el, "height", value);
}
}
static setStyleSize(el, style, val) {
typeof val == "function" ? val = val() : typeof val == "string" ? el.style[style] = val : el.style[style] = val + "px";
}
static contains(parent, child) {
return !parent || !child ? !1 : !_Utils.parents(child).every((p) => parent !== p);
}
static isHidden(el) {
return el.offsetWidth === 0 && el.offsetHeight === 0;
}
static parents(el, selector) {
let parents = [], visible = selector === ":visible", hidden = selector === ":hidden";
for (; (el = el.parentNode) && el !== document && !(!el || !el.parentNode); )
hidden ? _Utils.isHidden(el) && parents.push(el) : visible ? _Utils.isHidden(el) || parents.push(el) : (!selector || el.matches(selector)) && parents.push(el);
return parents;
}
static toFloat(value) {
let x = parseFloat(value);
return isNaN(x) ? 0 : x;
}
static show(el, type = "") {
Array.isArray(el) ? el.forEach((e) => e.style.display = type) : el.style.display = type;
}
static hide(el) {
Array.isArray(el) ? el.forEach((e) => e.style.display = "none") : el.style.display = "none";
}
static slideUp(el, callback) {
return _Utils.slideAnimation(el, "slideUp", callback);
}
static slideDown(el, callback) {
return _Utils.slideAnimation(el, "slideDown", callback);
}
static slideAnimation(el, slideDirection, callback) {
if (window.jQuery !== void 0) {
window.jQuery(el)[slideDirection]("fast", callback);
return;
}
slideDirection === "slideUp" ? _Utils.hide(el) : _Utils.show(el), callback();
}
static applyDefaults(targetObj, srcObj) {
typeof srcObj == "object" && Object.keys(srcObj).forEach((key) => {
srcObj.hasOwnProperty(key) && !targetObj.hasOwnProperty(key) && (targetObj[key] = srcObj[key]);
});
}
/**
* User could optionally add PubSub Service to SlickEvent
* When it is defined then a SlickEvent `notify()` call will also dispatch it by using the PubSub publish() method
* @param {BasePubSub} [pubSubService]
* @param {*} scope
*/
static addSlickEventPubSubWhenDefined(pubSub, scope) {
if (pubSub)
for (let prop in scope)
scope[prop] instanceof SlickEvent && typeof scope[prop].setPubSubService == "function" && scope[prop].setPubSubService(pubSub);
}
};
// jQuery's extend
__publicField(_Utils, "getProto", Object.getPrototypeOf), __publicField(_Utils, "class2type", {}), __publicField(_Utils, "toString", _Utils.class2type.toString), __publicField(_Utils, "hasOwn", _Utils.class2type.hasOwnProperty), __publicField(_Utils, "fnToString", _Utils.hasOwn.toString), __publicField(_Utils, "ObjectFunctionString", _Utils.fnToString.call(Object)), __publicField(_Utils, "storage", {
// https://stackoverflow.com/questions/29222027/vanilla-alternative-to-jquery-data-function-any-native-javascript-alternati
_storage: /* @__PURE__ */ new WeakMap(),
// eslint-disable-next-line object-shorthand
put: function(element, key, obj) {
this._storage.has(element) || this._storage.set(element, /* @__PURE__ */ new Map()), this._storage.get(element).set(key, obj);
},
// eslint-disable-next-line object-shorthand
get: function(element, key) {
let el = this._storage.get(element);
return el ? el.get(key) : null;
},
// eslint-disable-next-line object-shorthand
remove: function(element, key) {
let ret = this._storage.get(element).delete(key);
return this._storage.get(element).size !== 0 && this._storage.delete(element), ret;
}
});
var Utils = _Utils, SlickGlobalEditorLock = new SlickEditorLock(), SlickCore = {
Event: SlickEvent,
EventData: SlickEventData,
EventHandler: SlickEventHandler,
Range: SlickRange,
NonDataRow: SlickNonDataItem,
Group: SlickGroup,
GroupTotals: SlickGroupTotals,
EditorLock: SlickEditorLock,
RegexSanitizer: regexSanitizer,
/**
* A global singleton editor lock.
* @class GlobalEditorLock
* @static
* @constructor
*/
GlobalEditorLock: SlickGlobalEditorLock,
keyCode: {
SPACE: 8,
BACKSPACE: 8,
DELETE: 46,
DOWN: 40,
END: 35,
ENTER: 13,
ESCAPE: 27,
HOME: 36,
INSERT: 45,
LEFT: 37,
PAGE_DOWN: 34,
PAGE_UP: 33,
RIGHT: 39,
TAB: 9,
UP: 38,
A: 65
},
preClickClassName: "slick-edit-preclick",
GridAutosizeColsMode: {
None: "NOA",
LegacyOff: "LOF",
LegacyForceFit: "LFF",
IgnoreViewport: "IGV",
FitColsToViewport: "FCV",
FitViewportToCols: "FVC"
},
ColAutosizeMode: {
Locked: "LCK",
Guide: "GUI",
Content: "CON",
ContentExpandOnly: "CXO",
ContentIntelligent: "CTI"
},
RowSelectionMode: {
FirstRow: "FS1",
FirstNRows: "FSN",
AllRows: "ALL",
LastRow: "LS1"
},
ValueFilterMode: {
None: "NONE",
DeDuplicate: "DEDP",
GetGreatestAndSub: "GR8T",
GetLongestTextAndSub: "LNSB",
GetLongestText: "LNSC"
},
WidthEvalMode: {
Auto: "AUTO",
TextOnly: "CANV",
HTML: "HTML"
}
}, {
EditorLock,
Event,
EventData,
EventHandler,
Group,
GroupTotals,
NonDataRow,
Range,
RegexSanitizer,
GlobalEditorLock,
keyCode,
preClickClassName,
GridAutosizeColsMode,
ColAutosizeMode,
RowSelectionMode,
ValueFilterMode,
WidthEvalMode
} = SlickCore;
// src/controls/slick.columnmenu.ts
var BindingEventService2 = BindingEventService, SlickEvent2 = Event, Utils2 = Utils, SlickColumnMenu = class {
constructor(columns, grid, options) {
this.columns = columns;
this.grid = grid;
// --
// public API
__publicField(this, "onColumnsChanged", new SlickEvent2("onColumnsChanged"));
// --
// protected props
__publicField(this, "_gridUid");
__publicField(this, "_columnTitleElm");
__publicField(this, "_listElm");
__publicField(this, "_menuElm");
__publicField(this, "_columnCheckboxes", []);
__publicField(this, "_bindingEventService", new BindingEventService2());
__publicField(this, "_options");
__publicField(this, "_defaults", {
fadeSpeed: 250,
// the last 2 checkboxes titles
hideForceFitButton: !1,
hideSyncResizeButton: !1,
forceFitTitle: "Force fit columns",
syncResizeTitle: "Synchronous resize",
headerColumnValueExtractor: (columnDef) => Utils2.getHtmlStringOutput(columnDef.name || "", "innerHTML")
});
this._gridUid = grid.getUID(), this._options = Utils2.extend({}, this._defaults, options), this.init(this.grid);
}
init(grid) {
Utils2.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this), grid.onHeaderContextMenu.subscribe(this.handleHeaderContextMenu.bind(this)), grid.onColumnsReordered.subscribe(this.updateColumnOrder.bind(this)), this._menuElm = document.createElement("div"), this._menuElm.className = `slick-columnpicker ${this._gridUid}`, this._menuElm.style.display = "none", document.body.appendChild(this._menuElm);
let buttonElm = document.createElement("button");
buttonElm.type = "button", buttonElm.className = "close", buttonElm.dataset.dismiss = "slick-columnpicker", buttonElm.ariaLabel = "Close";
let spanCloseElm = document.createElement("span");
if (spanCloseElm.className = "close", spanCloseElm.ariaHidden = "true", spanCloseElm.textContent = "\xD7", buttonElm.appendChild(spanCloseElm), this._menuElm.appendChild(buttonElm), this._options.columnPickerTitle || this._options.columnPicker?.columnTitle) {
let columnTitle = this._options.columnPickerTitle || this._options.columnPicker?.columnTitle;
this._columnTitleElm = document.createElement("div"), this._columnTitleElm.className = "slick-gridmenu-custom", this._columnTitleElm.textContent = columnTitle || "", this._menuElm.appendChild(this._columnTitleElm);
}
this._bindingEventService.bind(this._menuElm, "click", this.updateColumn.bind(this)), this._listElm = document.createElement("span"), this._listElm.className = "slick-columnpicker-list", this._bindingEventService.bind(document.body, "mousedown", this.handleBodyMouseDown.bind(this)), this._bindingEventService.bind(document.body, "beforeunload", this.destroy.bind(this));
}
destroy() {
this.grid.onHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this)), this.grid.onColumnsReordered.unsubscribe(this.updateColumnOrder.bind(this)), this._bindingEventService.unbindAll(), this._listElm?.remove(), this._menuElm?.remove();
}
handleBodyMouseDown(e) {
(this._menuElm !== e.target && !(this._menuElm && this._menuElm.contains(e.target)) || e.target.className === "close") && (this._menuElm.setAttribute("aria-expanded", "false"), this._menuElm.style.display = "none");
}
handleHeaderContextMenu(e) {
e.preventDefault(), Utils2.emptyElement(this._listElm), this.updateColumnOrder(), this._columnCheckboxes = [];
let columnId, columnLabel, excludeCssClass;
for (let i = 0; i < this.columns.length; i++) {
columnId = this.columns[i].id;
let colName = this.columns[i].name instanceof HTMLElement ? this.columns[i].name.innerHTML : this.columns[i].name || "";
excludeCssClass = this.columns[i].excludeFromColumnPicker ? "hidden" : "";
let liElm = document.createElement("li");
liElm.className = excludeCssClass, liElm.ariaLabel = colName;
let checkboxElm = document.createElement("input");
checkboxElm.type = "checkbox", checkboxElm.id = `${this._gridUid}colpicker-${columnId}`, checkboxElm.dataset.columnid = String(this.columns[i].id), liElm.appendChild(checkboxElm), this._columnCheckboxes.push(checkboxElm), Utils2.isDefined(this.grid.getColumnIndex(columnId)) && !this.columns[i].hidden && (checkboxElm.checked = !0), columnLabel = this._options?.columnPicker?.headerColumnValueExtractor ? this._options.columnPicker.headerColumnValueExtractor(this.columns[i], this._options) : this._defaults.headerColumnValueExtractor(this.columns[i], this._options);
let labelElm = document.createElement("label");
labelElm.htmlFor = `${this._gridUid}colpicker-${columnId}`, this.grid.applyHtmlCode(labelElm, columnLabel), liElm.appendChild(labelElm), this._listElm.appendChild(liElm);
}
if (this._options.columnPicker && (!this._options.columnPicker.hideForceFitButton || !this._options.columnPicker.hideSyncResizeButton) && this._listElm.appendChild(document.createElement("hr")), !this._options.columnPicker?.hideForceFitButton) {
let forceFitTitle = this._options.columnPicker?.forceFitTitle || this._options.forceFitTitle, liElm = document.createElement("li");
liElm.ariaLabel = forceFitTitle || "", this._listElm.appendChild(liElm);
let forceFitCheckboxElm = document.createElement("input");
forceFitCheckboxElm.type = "checkbox", forceFitCheckboxElm.id = `${this._gridUid}colpicker-forcefit`, forceFitCheckboxElm.dataset.option = "autoresize", liElm.appendChild(forceFitCheckboxElm);
let labelElm = document.createElement("label");
labelElm.htmlFor = `${this._gridUid}colpicker-forcefit`, labelElm.textContent = forceFitTitle || "", liElm.appendChild(labelElm), this.grid.getOptions().forceFitColumns && (forceFitCheckboxElm.checked = !0);
}
if (!this._options.columnPicker?.hideSyncResizeButton) {
let syncResizeTitle = this._options.columnPicker?.syncResizeTitle || this._options.syncResizeTitle, liElm = document.createElement("li");
liElm.ariaLabel = syncResizeTitle || "", this._listElm.appendChild(liElm);
let syncResizeCheckboxElm = document.createElement("input");
syncResizeCheckboxElm.type = "checkbox", syncResizeCheckboxElm.id = `${this._gridUid}colpicker-syncresize`, syncResizeCheckboxElm.dataset.option = "syncresize", liElm.appendChild(syncResizeCheckboxElm);
let labelElm = document.createElement("label");
labelElm.htmlFor = `${this._gridUid}colpicker-syncresize`, labelElm.textContent = syncResizeTitle || "", liElm.appendChild(labelElm), this.grid.getOptions().syncColumnCellResize && (syncResizeCheckboxElm.checked = !0);
}
this.repositionMenu(e);
}
repositionMenu(event2) {
let targetEvent = event2?.touches?.[0] || event2;
this._menuElm.style.top = `${targetEvent.pageY - 10}px`, this._menuElm.style.left = `${targetEvent.pageX - 10}px`, this._menuElm.style.maxHeight = `${window.innerHeight - targetEvent.clientY}px`, this._menuElm.style.display = "block", this._menuElm.setAttribute("aria-expanded", "true"), this._menuElm.appendChild(this._listElm);
}
updateColumnOrder() {
let current = this.grid.getColumns().slice(0), ordered = new Array(this.columns.length);
for (let i = 0; i < ordered.length; i++)
this.grid.getColumnIndex(this.columns[i].id) === void 0 ? ordered[i] = this.columns[i] : ordered[i] = current.shift();
this.columns = ordered;
}
/** Update the Titles of each sections (command, customTitle, ...) */
updateAllTitles(pickerOptions) {
this.grid.applyHtmlCode(this._columnTitleElm, pickerOptions.columnTitle);
}
updateColumn(e) {
if (e.target.dataset.option === "autoresize") {
let previousVisibleColumns = this.getVisibleColumns(), isChecked = e.target.checked;
this.grid.setOptions({ forceFitColumns: isChecked }), this.grid.setColumns(previousVisibleColumns);
return;
}
if (e.target.dataset.option === "syncresize") {
e.target.checked ? this.grid.setOptions({ syncColumnCellResize: !0 }) : this.grid.setOptions({ syncColumnCellResize: !1 });
return;
}
if (e.target.type === "checkbox") {
let isChecked = e.target.checked, columnId = e.target.dataset.columnid || "", visibleColumns = [];
if (this._columnCheckboxes.forEach((columnCheckbox, idx) => {
this.columns[idx].hidden !== void 0 && (this.columns[idx].hidden = !columnCheckbox.checked), columnCheckbox.checked && visibleColumns.push(this.columns[idx]);
}), !visibleColumns.length) {
e.target.checked = !0;
return;
}
this.grid.setColumns(visibleColumns), this.onColumnsChanged.notify({ columnId, showing: isChecked, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });
}
}
/** @deprecated because of a typo @use `setColumnVisibility()` instead */
setColumnVisibiliy(idxOrId, show) {
this.setColumnVisibility(idxOrId, show);
}
setColumnVisibility(idxOrId, show) {
let idx = typeof idxOrId == "number" ? idxOrId : this.getColumnIndexbyId(idxOrId), visibleColumns = this.getVisibleColumns(), col = this.columns[idx];
if (show)
col.hidden = !1, visibleColumns.splice(idx, 0, col);
else {
let newVisibleColumns = [];
for (let i = 0; i < visibleColumns.length; i++)
visibleColumns[i].id !== col.id && newVisibleColumns.push(visibleColumns[i]);
visibleColumns = newVisibleColumns;
}
this.grid.setColumns(visibleColumns), this.onColumnsChanged.notify({ columnId: col.id, showing: show, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });
}
getAllColumns() {
return this.columns;
}
getColumnbyId(id) {
for (let i = 0; i < this.columns.length; i++)
if (this.columns[i].id === id)
return this.columns[i];
return null;
}
getColumnIndexbyId(id) {
for (let i = 0; i < this.columns.length; i++)
if (this.columns[i].id === id)
return i;
return -1;
}
/** visible columns, we can simply get them directly from the grid */
getVisibleColumns() {
return this.grid.getColumns();
}
};
// src/controls/slick.columnpicker.ts
var BindingEventService3 = BindingEventService, SlickEvent3 = Event, Utils3 = Utils, SlickColumnPicker = class {
constructor(columns, grid, gridOptions) {
this.columns = columns;
this.grid = grid;
// --
// public API
__publicField(this, "onColumnsChanged", new SlickEvent3("onColumnsChanged"));
// --
// protected props
__publicField(this, "_gridUid");
__publicField(this, "_columnTitleElm");
__publicField(this, "_listElm");
__publicField(this, "_menuElm");
__publicField(this, "_columnCheckboxes", []);
__publicField(this, "_bindingEventService", new BindingEventService3());
__publicField(this, "_gridOptions");
__publicField(this, "_defaults", {
fadeSpeed: 250,
// the last 2 checkboxes titles
hideForceFitButton: !1,
hideSyncResizeButton: !1,
forceFitTitle: "Force fit columns",
syncResizeTitle: "Synchronous resize",
headerColumnValueExtractor: (columnDef) => Utils3.getHtmlStringOutput(columnDef.name || "", "innerHTML")
});
this._gridUid = grid.getUID(), this._gridOptions = Utils3.extend({}, this._defaults, gridOptions), this.init(this.grid);
}
init(grid) {
Utils3.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this), grid.onColumnsReordered.subscribe(this.updateColumnOrder.bind(this)), grid.onHeaderContextMenu.subscribe(this.handleHeaderContextMenu.bind(this)), grid.onPreHeaderContextMenu.subscribe((e) => {
["slick-column-name", "slick-header-column"].some((className) => e.target?.classList.contains(className)) && this.handleHeaderContextMenu(e);
}), this._menuElm = document.createElement("div"), this._menuElm.className = `slick-columnpicker ${this._gridUid}`, this._menuElm.style.display = "none", document.body.appendChild(this._menuElm);
let buttonElm = document.createElement("button");
buttonElm.type = "button", buttonElm.className = "close", buttonElm.dataset.dismiss = "slick-columnpicker", buttonElm.ariaLabel = "Close";
let spanCloseElm = document.createElement("span");
if (spanCloseElm.className = "close", spanCloseElm.ariaHidden = "true", spanCloseElm.textContent = "\xD7", buttonElm.appendChild(spanCloseElm), this._menuElm.appendChild(buttonElm), this._gridOptions.columnPickerTitle || this._gridOptions.columnPicker?.columnTitle) {
let columnTitle = this._gridOptions.columnPickerTitle || this._gridOptions.columnPicker?.columnTitle;
this._columnTitleElm = document.createElement("div"), this._columnTitleElm.className = "slick-gridmenu-custom", this._columnTitleElm.textContent = columnTitle || "", this._menuElm.appendChild(this._columnTitleElm);
}
this._bindingEventService.bind(this._menuElm, "click", this.updateColumn.bind(this)), this._listElm = document.createElement("span"), this._listElm.className = "slick-columnpicker-list", this._bindingEventService.bind(document.body, "mousedown", this.handleBodyMouseDown.bind(this)), this._bindingEventService.bind(document.body, "beforeunload", this.destroy.bind(this));
}
destroy() {
this.grid.onPreHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this)), this.grid.onHeaderContextMenu.unsubscribe(this.handleHeaderContextMenu.bind(this)), this.grid.onColumnsReordered.unsubscribe(this.updateColumnOrder.bind(this)), this._bindingEventService.unbindAll(), this._listElm?.remove(), this._menuElm?.remove();
}
handleBodyMouseDown(e) {
(this._menuElm !== e.target && !this._menuElm?.contains(e.target) || e.target.className === "close") && (this._menuElm.setAttribute("aria-expanded", "false"), this._menuElm.style.display = "none");
}
handleHeaderContextMenu(e) {
e.preventDefault(), Utils3.emptyElement(this._listElm), this.updateColumnOrder(), this._columnCheckboxes = [];
let columnId, columnLabel, excludeCssClass;
for (let i = 0; i < this.columns.length; i++) {
columnId = this.columns[i].id;
let colName = this.columns[i].name instanceof HTMLElement ? this.columns[i].name.innerHTML : this.columns[i].name || "";
excludeCssClass = this.columns[i].excludeFromColumnPicker ? "hidden" : "";
let liElm = document.createElement("li");
liElm.className = excludeCssClass, liElm.ariaLabel = colName;
let checkboxElm = document.createElement("input");
checkboxElm.type = "checkbox", checkboxElm.id = `${this._gridUid}colpicker-${columnId}`, checkboxElm.dataset.columnid = String(this.columns[i].id), liElm.appendChild(checkboxElm), this._columnCheckboxes.push(checkboxElm), Utils3.isDefined(this.grid.getColumnIndex(columnId)) && !this.columns[i].hidden && (checkboxElm.checked = !0), columnLabel = this._gridOptions?.columnPicker?.headerColumnValueExtractor ? this._gridOptions.columnPicker.headerColumnValueExtractor(this.columns[i], this._gridOptions) : this._defaults.headerColumnValueExtractor(this.columns[i], this._gridOptions);
let labelElm = document.createElement("label");
labelElm.htmlFor = `${this._gridUid}colpicker-${columnId}`, this.grid.applyHtmlCode(labelElm, columnLabel), liElm.appendChild(labelElm), this._listElm.appendChild(liElm);
}
if (this._gridOptions.columnPicker && (!this._gridOptions.columnPicker.hideForceFitButton || !this._gridOptions.columnPicker.hideSyncResizeButton) && this._listElm.appendChild(document.createElement("hr")), !this._gridOptions.columnPicker?.hideForceFitButton) {
let forceFitTitle = this._gridOptions.columnPicker?.forceFitTitle || this._gridOptions.forceFitTitle, liElm = document.createElement("li");
liElm.ariaLabel = forceFitTitle || "", this._listElm.appendChild(liElm);
let forceFitCheckboxElm = document.createElement("input");
forceFitCheckboxElm.type = "checkbox", forceFitCheckboxElm.id = `${this._gridUid}colpicker-forcefit`, forceFitCheckboxElm.dataset.option = "autoresize", liElm.appendChild(forceFitCheckboxElm);
let labelElm = document.createElement("label");
labelElm.htmlFor = `${this._gridUid}colpicker-forcefit`, labelElm.textContent = forceFitTitle || "", liElm.appendChild(labelElm), this.grid.getOptions().forceFitColumns && (forceFitCheckboxElm.checked = !0);
}
if (!this._gridOptions.columnPicker?.hideSyncResizeButton) {
let syncResizeTitle = this._gridOptions.columnPicker?.syncResizeTitle || this._gridOptions.syncResizeTitle, liElm = document.createElement("li");
liElm.ariaLabel = syncResizeTitle || "", this._listElm.appendChild(liElm);
let syncResizeCheckboxElm = document.createElement("input");
syncResizeCheckboxElm.type = "checkbox", syncResizeCheckboxElm.id = `${this._gridUid}colpicker-syncresize`, syncResizeCheckboxElm.dataset.option = "syncresize", liElm.appendChild(syncResizeCheckboxElm);
let labelElm = document.createElement("label");
labelElm.htmlFor = `${this._gridUid}colpicker-syncresize`, labelElm.textContent = syncResizeTitle || "", liElm.appendChild(labelElm), this.grid.getOptions().syncColumnCellResize && (syncResizeCheckboxElm.checked = !0);
}
this.repositionMenu(e);
}
repositionMenu(event2) {
let targetEvent = event2?.touches?.[0] ?? event2;
if (this._menuElm) {
this._menuElm.style.display = "block";
let gridPos = this.grid.getGridPosition(), menuWidth = this._menuElm.clientWidth || 0, menuOffsetLeft = targetEvent.pageX || 0;
gridPos?.width && menuOffsetLeft + menuWidth >= gridPos.width && (menuOffsetLeft = menuOffsetLeft - menuWidth), this._menuElm.style.top = `${targetEvent.pageY - 10}px`, this._menuElm.style.left = `${menuOffsetLeft}px`, this._menuElm.style.maxHeight = `${window.innerHeight - targetEvent.clientY}px`, this._menuElm.setAttribute("aria-expanded", "true"), this._menuElm.appendChild(this._listElm);
}
}
updateColumnOrder() {
let current = this.grid.getColumns().slice(0), ordered = new Array(this.columns.length);
for (let i = 0; i < ordered.length; i++)
this.grid.getColumnIndex(this.columns[i].id) === void 0 ? ordered[i] = this.columns[i] : ordered[i] = current.shift();
this.columns = ordered;
}
/** Update the Titles of each sections (command, customTitle, ...) */
updateAllTitles(pickerOptions) {
this.grid.applyHtmlCode(this._columnTitleElm, pickerOptions.columnTitle);
}
updateColumn(e) {
if (e.target.dataset.option === "autoresize") {
let previousVisibleColumns = this.getVisibleColumns(), isChecked = e.target.checked || !1;
this.grid.setOptions({ forceFitColumns: isChecked }), this.grid.setColumns(previousVisibleColumns);
return;
}
if (e.target.dataset.option === "syncresize") {
e.target.checked ? this.grid.setOptions({ syncColumnCellResize: !0 }) : this.grid.setOptions({ syncColumnCellResize: !1 });
return;
}
if (e.target.type === "checkbox") {
let isChecked = e.target.checked, columnId = e.target.dataset.columnid || "", visibleColumns = [];
if (this._columnCheckboxes.forEach((columnCheckbox, idx) => {
this.columns[idx].hidden !== void 0 && (this.columns[idx].hidden = !columnCheckbox.checked), columnCheckbox.checked && visibleColumns.push(this.columns[idx]);
}), !visibleColumns.length) {
e.target.checked = !0;
return;
}
this.grid.setColumns(visibleColumns), this.onColumnsChanged.notify({ columnId, showing: isChecked, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });
}
}
/** @deprecated because of a typo @use `setColumnVisibility()` instead */
setColumnVisibiliy(idxOrId, show) {
this.setColumnVisibility(idxOrId, show);
}
setColumnVisibility(idxOrId, show) {
let idx = typeof idxOrId == "number" ? idxOrId : this.getColumnIndexbyId(idxOrId), visibleColumns = this.getVisibleColumns(), col = this.columns[idx];
if (show)
col.hidden = !1, visibleColumns.splice(idx, 0, col);
else {
let newVisibleColumns = [];
for (let i = 0; i < visibleColumns.length; i++)
visibleColumns[i].id !== col.id && newVisibleColumns.push(visibleColumns[i]);
visibleColumns = newVisibleColumns;
}
this.grid.setColumns(visibleColumns), this.onColumnsChanged.notify({ columnId: col.id, showing: show, allColumns: this.columns, columns: this.columns, visibleColumns, grid: this.grid });
}
getAllColumns() {
return this.columns;
}
getColumnbyId(id) {
for (let i = 0; i < this.columns.length; i++)
if (this.columns[i].id === id)
return this.columns[i];
return null;
}
getColumnIndexbyId(id) {
for (let i = 0; i < this.columns.length; i++)
if (this.columns[i].id === id)
return i;
r