UNPKG

slickgrid

Version:

A lightning fast JavaScript grid/spreadsheet

1,057 lines (1,054 loc) 673 kB
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(>*)|(&lt;)(\/*)(script|script defer)(.*)(&gt;|&gt;">)/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