UNPKG

sdk-datagrid

Version:

Customizable (Angular) datagrid with data options for manipulation, and charts for visualization.

1,152 lines 291 kB
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { Sorts } from './enums/sorts'; import { Clone } from './utils/clone'; import { SDKDataGridColumn } from './models/datagrid-column'; import { SDKDataGridOptions, StorageType } from './models/datagrid-options'; import { SDKDataGridCustomFilter } from './models/datagrid-custom-filter'; import { SDKDataGridSettings } from './models/datagrid-settings'; import { SDKDataGridMessage } from './models/datagrid-message'; import * as i0 from "@angular/core"; import * as i1 from "@angular/router"; import * as i2 from "@angular/common"; import * as i3 from "ng-dynamic-component"; import * as i4 from "sdk-select"; import * as i5 from "sdk-tabs"; import * as i6 from "sdk-textbox"; import * as i7 from "./options/settings/settings-option.component"; import * as i8 from "./options/columns/columns-option.component"; import * as i9 from "./options/sort/sort-option.component"; import * as i10 from "./options/filter/filter-option.component"; import * as i11 from "./options/formula/formula-option.component"; import * as i12 from "./options/export/export-option.component"; import * as i13 from "./options/chart/chart-option.component"; export class SDKDatagridComponent { /************************************************************************** * Private Variables **************************************************************************/ /************************************************************************** * Component Lifecycle Methods **************************************************************************/ constructor(route) { this.route = route; /************************************************************************** * Input/Output Parameters for updating the Grid **************************************************************************/ this.updateGrid = false; // Applies changes/data to the grid. this.updateGridChange = new EventEmitter(); // Callback to set updateGrid variable. this.editRecordIndexChange = new EventEmitter(); // Callback to set editRecordIndex variable. /************************************************************************** * Input/Output Parameters for dataset change **************************************************************************/ this.datasets = []; // List of datasets. this.activeDataset = ""; // The current selected dataset. this.activeDatasetChange = new EventEmitter(); // Callback to set activeDataset variable. /************************************************************************** * Input/Output Parameters for updating columns **************************************************************************/ this.columns = []; // The columns to be used for the grid. this.columnHeaderStyle = ""; // Style used for column headers. this.columnsChange = new EventEmitter(); // Callback to set columns variable. /************************************************************************** * Input/Output Parameters for updating custom filters **************************************************************************/ this.customFilters = []; // Custom filters added to standard filter provided in loaddata callback (filters). this.customFiltersChange = new EventEmitter(); // Callback to set custom filters variable. /************************************************************************** * Input/Output Parameters for exporting data **************************************************************************/ this.dataExport = ""; // The data to export. this.dataExportChange = new EventEmitter(); // Callback to set dataExport variable. /************************************************************************** * Input/Output Parameters for settings data **************************************************************************/ this.settings = []; // Options that are saved outside the grid. This value should be set to the value retieved from the datagrid @Output method "settingsSavedEvent". Consider it a passthrough value. this.clearSettingsCache = false; // Clears any grid storage of settings. this.savedSettingsEvent = new EventEmitter(); // Used as a callback to save settings. this.clearSettingsCacheChange = new EventEmitter(); // Callback to set clearSettingsCache variable. /************************************************************************** * Required Output Parameters **************************************************************************/ this.loadDataEvent = new EventEmitter(); // Callback with grid changes. /************************************************************************** * Optional Input/Output Parameters **************************************************************************/ this.name = ""; // Unique name of the grid (per page). this.title = ""; // Title of the grid. this.titleStyle = ""; // Style used for title. this.fontFamily = ""; // font-family used for grid. this.data = ""; // The data to display in the grid. this.rowValues = [10, 25, 50, 100, 500, 1000]; // Sets the values used for records per page. this.defaultRows = 100; // Default value for records per page. this.saveGridDataEvent = new EventEmitter(); // Callback for saving changes to data. this.deleteGridDataEvent = new EventEmitter(); // Callback for deleting data. this.selectedRowsChangedEvent = new EventEmitter(); // Callback for row changes. this.selectedRowActionEvent = new EventEmitter(); // // Callback for row actions. NOTE: The return object is { record: [the record], index: [the record index] } /************************************************************************** * Addon Parameters (Option Icon/Modal Window) **************************************************************************/ this.optionAddons = []; this.windowAddons = []; /************************************************************************** * Processing Parameters **************************************************************************/ this.error = ""; // Any errors that occur during processing. this.isDebug = false; // Turns on debugging. this.showGrid = false; this.editGrid = false; this.totalPages = new Array(0); this.deleteRecords = new Array(0); this.editRecords = false; /************************************************************************** * Component Variables based on Input parameters **************************************************************************/ this._editRecordIndex = undefined; this._datasets = []; this._activeDataset = ""; this._selectedDataset = ""; this._name = ""; this._options = new SDKDataGridOptions(); this._menuOptions = false; this._data = ""; this._columns = []; this._customFilters = []; this._settings = []; this._rows = 100; this._page = 1; this._selectedPage = ""; this._total = 0; this._isExporting = false; /************************************************************************** * Options Variables **************************************************************************/ this.optionTitle = ""; this.dataMode = "data"; this.dataClass = ""; this.columnBadge = "0"; this.filterBadge = "0"; this.sortBadge = "0"; this.formulaBadge = "0"; this.chartBadge = "0"; this.showReset = false; this.showNote = false; this.lockGrid = false; /************************************************************************** * Tooltip Variables **************************************************************************/ this.tooltipTimer = null; this.tooltipAutoCloseTimer = null; this.tooltipVisible = false; this.tooltipStyles = {}; this.recordIndex = null; this.recordData = null; /************************************************************************** * Addon Variables * - option(Inputs/Outputs): Used with the Addon icons. * - window(Inputs/Outputs): Used with the Addon modal. **************************************************************************/ this.optionInputs = {}; this.windowInputs = {}; this.optionOutputs = { showDataOptionsEvent: (event) => this.showDataOptions(event), }; this.windowOutputs = { closeEvent: () => this.closeDataOptions(), applyEvent: (event) => this.applyDataOptions(event), }; /************************************************************************** * Message Variable **************************************************************************/ this.showMessage = false; this.message = new SDKDataGridMessage(); } ngOnChanges(_args) { this.showLog("START Method: ngOnChanges >>>"); // this.showLog(["[Input]", Clone.deepCopy(_args)]); // Handles update grid event if (_args.updateGrid !== undefined) { this.showLog(["updateGrid", _args.updateGrid.currentValue]); if (_args.updateGrid.currentValue) { if (_args.updateGrid.previousValue === undefined) { this.setDatasets(); this.setActiveDataset(); // MUST be called before setName() - active dataset is used in setting the name. this.setName(); this.setOptions(); this.setColumns(); this.setCustomFilters(); this.setSettings(); // MUST be called after setColumns() and setCustomFilters() in case a setting is set to active. setTimeout(() => { this.loadData("Initial Load", this._rows, 1); }, 10); } else { this.showLog("!!! Updating Grid !!!"); this.showInputValues(); this.setGrid(); this.setDatasets(); this.setActiveDataset(); // MUST be called before setName() - active dataset is used in setting the name. this.setName(); this.setOptions(); this.setColumns(); this.setCustomFilters(); this.setSettings(); // MUST be called after setColumns() and setCustomFilters() in case a setting is set to active. this.setBadges(); this.setData(); this.setRows(); this.setPage(); this.setTotal(); this.setSelectedPage(); this.showLog("!!! Grid Updated !!!"); this.showGrid = true; setTimeout(() => { this.editRecordIndexChange.emit(undefined); }, 10); } setTimeout(() => { this.updateGridChange.emit(false); }, 10); } } // Handles edit mode event if (_args.editRecordIndex) { this.setEditRecordIndex(); } // Handles "all data" export if (_args.dataExport !== undefined && _args.dataExport.currentValue !== undefined && _args.dataExport.currentValue !== "") { this.exportData(true, _args.dataExport.currentValue); } this.showLog(">>> END Method: ngOnChanges"); } /************************************************************************** * Change Methods **************************************************************************/ showInputValues() { this.showLog("*** START: Input Values ***"); this.showLog(["editRecordIndex:", this.editRecordIndex]); this.showLog(["datasets:", this.datasets]); this.showLog(["activeDataset:", this.activeDataset]); this.showLog(["name:", this.name]); this.showLog(["title:", this.title]); this.showLog(["titleStyle:", this.titleStyle]); this.showLog(["settings:", Clone.deepCopy(this.settings)]); this.showLog(["options:", Clone.deepCopy(this.options)]); this.showLog(["data:", Clone.deepCopy(this.data)]); this.showLog(["columns:", Clone.deepCopy(this.columns)]); this.showLog(["customFilters:", Clone.deepCopy(this.customFilters)]); this.showLog(["rowValues:", this.rowValues]); this.showLog(["defaultRows:", this.defaultRows]); this.showLog(["rows:", this.rows]); this.showLog(["page:", this.page]); this.showLog(["total:", this.total]); this.showLog("*** END: Input Values ***"); } setGrid() { this.showLog("START Method: setGrid >>>"); this.showLog(["Set Styles", this.fontFamily]); if (this.sdkdatagrid) { if (this.fontFamily !== "") { this.sdkdatagrid.nativeElement.style.setProperty("--font-family", this.fontFamily); } } if (this.sdkdatagridcontent) { this.sdkdatagridcontent.nativeElement.scrollTop = 0; } this.showLog(">>> END Method: setGrid"); } setDatasets() { this.showLog("START Method: setDatasets >>>"); this.showLog(["[Input]", this.datasets]); this._datasets = this.datasets ?? []; this.datasetTabs = []; this._datasets.forEach((dataset) => { this.datasetTabs.push({ title: dataset, type: null, inputs: {}, outputs: {} }); }); this.showLog(["[Output]", this._datasets]); this.showLog(">>> END Method: setDatasets"); } setActiveDataset() { this.showLog("START Method: setActiveDataset >>>"); this.showLog(["[Input]", this.activeDataset]); this._activeDataset = this.activeDataset !== "" ? this.activeDataset : this._datasets.length > 0 ? this._datasets[0] : ""; // Initialize then let sdk-select handle value moving forward. if (this._selectedDataset === "") { this._selectedDataset = this._activeDataset; } this.showLog(["[Output]", this._activeDataset]); this.showLog(">>> END Method: setActiveDataset"); } setName() { this.showLog("START Method: setName >>>"); this.showLog(["[Input]", this.name]); if (this.route.url) { this.route.url.subscribe(([url]) => { if (url) { const { path } = url; if (url.path) this._name = `${url.path}_${this.route.component?.name}_${this.name}_${this._activeDataset}`; } }); } if (this._name === "") { this._name = `${this.route.component?.name}_${this.name}_${this._activeDataset}`; } this._name = this._name.replaceAll(/[^a-zA-Z0-9]/g, "_"); this.showLog(["[Output]", this._name]); this.showLog(">>> END Method: setName"); } setOptions() { this.showLog("START Method: setOptions >>>"); this.showLog(["[Input]", Clone.deepCopy(this.options)]); this._options = new SDKDataGridOptions(); if (this.options) { Object.getOwnPropertyNames(this._options).forEach((prop) => { const descriptor = Object.getOwnPropertyDescriptor(this.options, prop); if (descriptor) { this._options[prop] = this.options[prop]; } }); } if (this._options.minimizeOptions !== undefined) { this._menuOptions = false; } this.showLog(["[Output]", Clone.deepCopy(this._options)]); this.showLog(">>> END Method: setOptions"); } setColumns() { this.showLog("START Method: setColumns >>>"); this.showLog(["[Input]", Clone.deepCopy(this.columns)]); this._columns = []; let _tmp = Clone.deepCopy(this.columns); if (!_tmp || _tmp.length === 0) { _tmp = []; if (this.data && this.data.length > 0) { let data = Clone.deepCopy(this.data[0]); for (let key in data) { _tmp.push({ Name: key.toString() }); } } } _tmp.forEach((column, index) => { this.initializeColumn(new SDKDataGridColumn(), column, index); }); // Load all left-side action columns. _tmp.filter((column) => column.isAction && column.actionSide === "left").forEach((column) => { this._columns.push({ ...column }); }); // Load all non-action columns. _tmp.filter((column) => !column.isAction).forEach((column) => { this._columns.push({ ...column }); }); // Load all right-side action columns. _tmp.filter((column) => column.isAction && column.actionSide === "right").forEach((column) => { this._columns.push({ ...column }); }); this.showLog(["[Output]", Clone.deepCopy(this._columns)]); this.showLog(">>> END Method: setColumns"); } setCustomFilters() { this.showLog("START Method: setCustomFilters >>>"); this.showLog(["[Input]", Clone.deepCopy(this.customFilters)]); this._customFilters = []; let _tmp = Clone.deepCopy(this.customFilters); if (_tmp && _tmp.length > 0) { _tmp.forEach((filter, index) => { this.initializeCustomFilter(new SDKDataGridCustomFilter(), filter, index); this._customFilters.push({ ...filter }); }); } this.showLog(["[Output]", Clone.deepCopy(this._customFilters)]); this.showLog(">>> END Method: setCustomFilters"); } setSettings() { this.showLog("START Method: setSettings >>>"); if (!this._options.settings) { this.settings = []; // REMOVES SETTINGS FROM "GRID" STORAGE IF FORCED if (this.clearSettingsCache) { this.showLog("!!! Clear Settings Cache !!!"); let storageItemName = `SDK-DATAGRID_${this._name}`; localStorage.removeItem(storageItemName); sessionStorage.removeItem(storageItemName); setTimeout(() => { this.clearSettingsCacheChange.emit(false); }, 10); } else { // LOAD SETTINGS FROM "GRID" STORAGE let storageItemName = `SDK-DATAGRID_${this._name}`; let storage = this._options.settingsStorage === StorageType.Local ? localStorage.getItem(storageItemName) : sessionStorage.getItem(storageItemName); if (storage) { this.settings = JSON.parse(storage); } } } else { let storageItemName = `SDK-DATAGRID_${this._name}`; localStorage.removeItem(storageItemName); sessionStorage.removeItem(storageItemName); } this.showLog(["[Input]", Clone.deepCopy(this.settings)]); this._settings = []; let _tmp = Clone.deepCopy(this.settings); _tmp.forEach((setting) => { let _setting = new SDKDataGridSettings(); Object.getOwnPropertyNames(_setting).forEach((prop) => { const descriptor = Object.getOwnPropertyDescriptor(setting, prop); if (!descriptor) { setting[prop] = _setting[prop]; } }); this._settings.push({ ...setting }); }); let ndx = this._settings.findIndex((setting) => setting.Active); if (ndx > -1) { this._columns = Clone.deepCopy(this._settings[ndx].Columns); this._columns.forEach((column, index) => { let columnNdx = this.columns.findIndex((c) => c.Name === column.Name); if (columnNdx > -1) { this.initializeColumn(this.columns[columnNdx], column, index); } }); this._customFilters = Clone.deepCopy(this._settings[ndx].CustomFilters); this._customFilters.forEach((filter, index) => { let filternNdx = this.customFilters.findIndex((f) => f.Name === filter.Name); if (filternNdx > -1) { this.initializeCustomFilter(this.customFilters[filternNdx], filter, index); } }); } this.showLog(["[Output]", Clone.deepCopy(this._settings)]); this.showLog(">>> END Method: setSettings"); } setBadges() { this.showLog("START Method: setBadges >>>"); this.showNote = this._columns.filter((column) => column.FriendlyName !== "").length > 0 ? true : false; let columnCount = this._columns.filter((column, index) => index !== column._original.index).length > 0 ? 1 : 0; columnCount += this._columns.filter((column) => column.isVisible !== column._original.isVisible || column.FriendlyName !== column._original.FriendlyName).length; this.columnBadge = columnCount.toString(); let filterCount = this._columns.filter((column) => column.Filter).length; filterCount += this._customFilters.filter((filter) => filter.Filter).length; this.filterBadge = filterCount.toString(); this.sortBadge = this._columns.filter((column) => column.Sort).length.toString(); let formulaCount = 0; this._columns.filter((column) => column.Formulas).forEach((column) => { formulaCount += column.Formulas.length; }); this.formulaBadge = formulaCount.toString(); if (this.columnBadge !== "0" || this.filterBadge !== "0" || this.sortBadge !== "0" || this.formulaBadge !== "0") { this.showReset = true; } else { this.showReset = false; } this.showLog(">>> END Method: setBadges"); } setData() { this.showLog("START Method: setData >>>"); this.showLog(["[Input]", Clone.deepCopy(this.data)]); this._data = Clone.deepCopy(this.data); this.showLog(["[Output]", Clone.deepCopy(this._data)]); this.showLog(">>> END Method: setData"); } setRows() { this.showLog("START Method: setRows >>>"); this.showLog(["[Input]", this.rows, "(Default)", this.defaultRows]); this._rows = this.rows ?? this.defaultRows; this.showLog(["[Output]", this._rows]); this.showLog(">>> END Method: setRows"); } setPage() { this.showLog("START Method: setPage >>>"); this.showLog(["[Input]", this.page]); this._page = this.page ?? 1; this.showLog(["[Output]", this._page]); this.showLog(">>> END Method: setPage"); } setTotal() { this.showLog("START Method: setTotal >>>"); this.showLog(["[Input]", this.total]); if (this.total === undefined) { this._total = this._data.length; } else { this._total = this.total; } this.showLog(["[Output]", this._total]); this.showLog(">>> END Method: setTotal"); } setSelectedPage() { this.showLog("START Method: setSelectedPage >>>"); if (this.totalPages.length === 0 || this.totalPages.length !== Math.ceil(this._total / this._rows)) { this.showLog("Updating pages..."); this.totalPages = new Array(0); for (let p = 0; p < Math.ceil(this._total / this._rows); p++) { let start = (p * this._rows) + 1; let end = (start + this._rows) - 1; if (end > this._total) end = this._total; this.totalPages.push(start.toString() + " - " + end.toString()); } } let rowStart = ((this._page - 1) * this._rows) + 1; let rowEnd = (rowStart + this._rows) - 1; if (rowEnd > this._total) rowEnd = this._total; this._selectedPage = rowStart.toString() + " - " + rowEnd.toString(); this.showLog(["[Output]", this._selectedPage]); this.showLog(">>> END Method: setSelectedPage"); } setEditRecordIndex() { this.showLog("START Method: setEditRecordIndex >>>"); this.showLog(["[Input]", this.editRecordIndex]); this._editRecordIndex = this.editRecordIndex; this.showLog(["[Output]", this._editRecordIndex]); this.showLog(">>> END Method: setEditRecordIndex"); } /************************************************************************** * Data Methods **************************************************************************/ loadData(action, rows, page) { this.showLog("START Method: loadData >>>"); this.showLog(["action", action, "rows", rows, "page", page]); this.editRecordIndexChange.emit(undefined); this.columnsChange.emit(this._columns); this.customFiltersChange.emit(this._customFilters); this.setAddonInputs(); this.loadDataEvent.emit({ action: action, rows: rows, page: page }); this.showLog(">>> END Method: loadData"); } selectDataset(event) { this.showLog("START Method: selectDataset (Dropdown) >>>"); this.showLog(["[Input]", event[0]]); this.activeDatasetChange.emit(event[0]); setTimeout(() => { this.setActiveDataset(); // MUST be called before setName() - active dataset is used in setting the name. this.setName(); this.setSettings(); // MUST be called in case columns are saved. this.loadData(`Dataset Change`, this._rows, 1); }, 10); this.showLog(">>> END Method: selectDataset (Dropdown)"); } datasetChanged(event) { this.showLog("START Method: datasetChanged (Tab) >>>"); this.showLog(["[Input]", event]); if (event.from) { this.activeDatasetChange.emit(event.to.title); setTimeout(() => { this.setActiveDataset(); // MUST be called before setName() - active dataset is used in setting the name. this.setName(); this.setSettings(); // MUST be called in case columns are saved. this.loadData(`Dataset Change`, this._rows, 1); }, 10); } this.showLog(">>> END Method: datasetChanged (Tab)"); } saveData() { this.showLog("START Method: saveData >>>"); if (this.missingData()) { this.message = { Title: "Missing Data", Text: "You are missing some required data.", OKText: "OK", OK: () => { this.showMessage = false; } }; } else { this.message = { Title: "Save Changes", Text: "Are you sure?", OKText: "YES", CancelText: "NO", OK: () => { this.showMessage = false; this.editGrid = false; this.editRecords = false; if (this.saveGridDataEvent.observed) { this.saveGridDataEvent.emit(this._data); } this.editRecordIndexChange.emit(undefined); this.updateGridChange.emit(false); }, Cancel: () => { this.showMessage = false; } }; } this.showMessage = true; this.showLog(">>> END Method: saveData"); } deleteData() { this.showLog("START Method: deleteData >>>"); this.showLog(["records", this.deleteRecords]); if (this.deleteRecords.length > 0) { this.editRecords = this.hasDataChanged(); if (this.editRecords) { this.message = { Title: "Save Changes", Text: "You've made changes to the data.<br /><br />Would you like to save them before continuing?", OKText: "YES", CancelText: "NO", OK: () => { this.editRecords = false; if (this.saveGridDataEvent.observed) { this.saveGridDataEvent.emit(this._data); } this.editRecordIndexChange.emit(undefined); this.updateGridChange.emit(false); this.deleteData(); }, Cancel: () => { this.editRecords = false; this._data = Clone.deepCopy(this.data); this.deleteData(); } }; } else { this.message = { Title: "Delete record(s)", Text: "Are you sure?", OKText: "YES", CancelText: "NO", OK: () => { this.showMessage = false; if (this.deleteGridDataEvent.observed) { this.deleteGridDataEvent.emit(this.deleteRecords); } this.deleteRecords = []; }, Cancel: () => { this.showMessage = false; } }; } this.showMessage = true; } this.showLog(">>> END Method: deleteData"); } selectRecord(index) { let ndx = this.deleteRecords.findIndex((record) => record === index); if (ndx > -1) { this.deleteRecords.splice(ndx, 1); } else { this.deleteRecords.push(index); } } isRecordSelected(index) { let ndx = this.deleteRecords.findIndex((record) => record === index); if (ndx > -1) { return true; } else { return false; } } cancelEdit() { this.showLog("START Method: cancelEdit >>>"); this.editRecords = this.hasDataChanged(); if (this.editRecords) { this.message = { Title: "Cancel Changes", Text: "You've made changes to the data.<br /><br />Are you sure you want to cancel without saving them?", OKText: "YES", CancelText: "NO", OK: () => { this.showMessage = false; this.editGrid = false; this.editRecords = false; this.deleteRecords = []; this.loadData("Cancel Changes", this._rows, this._page); }, Cancel: () => { this.showMessage = false; } }; this.showMessage = true; } else { this.editGrid = false; this.editRecords = false; this.deleteRecords = []; } this.showLog(">>> END Method: cancelEdit"); } hasDataChanged() { return !(this.data.length === this._data.length && this.data.every((element_1) => this._data.some((element_2) => Object.keys(element_1).every((key) => element_1[key] === element_2[key])))); } missingData() { return this._data.some((record) => { return this._columns.filter((column) => column.required) .some((column) => record[column.Name] === ""); }); } /************************************************************************** * Options Methods **************************************************************************/ showDataOptions(type) { this.showLog("START Method: showDataOptions >>>"); this.showLog(["type", type]); this.lockGrid = true; this.dataClass = this._options.panelOverlay ? "overlay" : "expand"; this.dataMode = "data"; this.optionTitle = type; if (type === "export") { this.dataExportChange.emit(""); // Clears out value } this.setAddonInputs(); this.showLog(">>> END Method: showDataOptions"); } toggleDataMode(type, autoClose = false) { // this.dataMode = type; // if (this.dataMode === "data" && this.optionTitle === "Chart") { // this.dataClass = ""; // this.optionTitle = ""; // } // if (this.dataMode === "chart") { // if (this.chart && this.chart !== "") { // this.applyChartOptions(this.chart, autoClose); // } // } } closeDataOptions() { this.showLog("START Method: closeDataOptions >>>"); this.optionTitle = ""; this.dataClass = ""; this.lockGrid = false; this.setAddonInputs(); this.showLog(">>> END Method: closeDataOptions"); } resetAllOptions() { this.showLog("START Method: resetAllOptions >>>"); this.message = { Title: "Reset ALL Options", Text: "Are you sure?", OKText: "YES", CancelText: "NO", OK: () => { this.showMessage = false; this.dataMode = "data"; this.optionTitle = ""; this.dataClass = ""; this._columns.sort((a, b) => (a._original.index > b._original.index) ? 1 : -1); this._columns.forEach((column) => { column.FriendlyName = column._original.FriendlyName; column.isVisible = column._original.isVisible; column.Sort = column._original.Sort; column.Filter = column._original.Filter; column.Formulas = column._original.Formulas; }); this._customFilters.forEach((filter) => { filter.Filter = filter._original.Filter; }); if (this._options.settings) { this.saveSettings(this._settings, true); } else { // REMOVE SETTINGS FROM "GRID" STORAGE let storageItemName = `SDK-DATAGRID_${this._name}`; if (this._options.settingsStorage === StorageType.Local) { localStorage.removeItem(storageItemName); } else { sessionStorage.removeItem(storageItemName); } this.settings = []; } this.showReset = false; this.loadData("Reset ALL Options", this._rows, 1); }, Cancel: () => { this.showMessage = false; } }; this.showMessage = true; this.showLog(["columns", this._columns, "customFilters", this._customFilters]); this.showLog(">>> END Method: resetAllOptions"); } applyOptions(event) { this.showLog("START Method: applyOptions >>>"); this.showLog(["[Input]", event]); let action = "Options"; if (event?.option) action = event.option; if (event?.columns) this._columns = event.columns; if (event?.customFilters) this._customFilters = event.customFilters; if (this._options.settings) { this.saveSettings(this._settings, true); } else { // ADD SETTINGS TO "GRID" STORAGE let storageItemName = `SDK-DATAGRID_${this._name}`; let settings = [{ Name: "[sdk-datagrid Settings]", Columns: this._columns, CustomFilters: this._customFilters, Active: true }]; if (this._options.settingsStorage === StorageType.Local) { localStorage.setItem(storageItemName, JSON.stringify(settings)); } else { sessionStorage.setItem(storageItemName, JSON.stringify(settings)); } } let rows = this._rows; let page = this._page; if (this.optionTitle !== "columns") { page = 1; } if (this._options.autoClosePanel) { this.closeDataOptions(); } this.loadData(`Apply ${action}`, rows, page); this.showLog(">>> END Method: applyOptions"); } /************************************************************************** * Addon Methods **************************************************************************/ applyDataOptions(event) { this.showLog("START Method: applyDataOptions >>>"); this.showLog(["option", this.optionTitle]); if (event?.columns) this._columns = event.columns; if (event?.customFilters) this._customFilters = event.customFilters; this.editRecordIndexChange.emit(undefined); this.columnsChange.emit(this._columns); this.customFiltersChange.emit(this._customFilters); this.loadDataEvent.emit({ action: this.optionTitle, rows: this._rows, page: 1, addon: event }); this.setAddonInputs(); this.showLog(">>> END Method: applyDataOptions"); } setAddonInputs() { this.optionInputs = { optionTitle: this.optionTitle, }; this.windowInputs = { dataClass: this.dataClass, optionTitle: this.optionTitle, columns: this._columns ?? [], customFilters: this._customFilters ?? [] }; } /************************************************************************** * Settings Methods **************************************************************************/ applySettingsOptions(event) { this.showLog("START Method: applySettingsOptions >>>"); this.showLog(["[Input]", event]); if (event?.columns) { this._columns = Clone.deepCopy(event.columns); this._columns.forEach((column, index) => { let columnNdx = this.columns.findIndex((c) => c.Name === column.Name); if (columnNdx > -1) { this.initializeColumn(this.columns[columnNdx], column, index); } }); } if (event?.customFilters) { this._customFilters = Clone.deepCopy(event.customFilters); this._customFilters.forEach((filter, index) => { let filternNdx = this.customFilters.findIndex((f) => f.Name === filter.Name); if (filternNdx > -1) { this.initializeCustomFilter(this.customFilters[filternNdx], filter, index); } }); } let rows = this._rows; let page = 1; if (this._options.autoClosePanel) { this.closeDataOptions(); } this.loadData("Apply Settings", rows, page); this.showLog(">>> END Method: applySettingsOptions"); } saveSettings(event, resetActive = false) { this.showLog("START Method: saveSettings >>>"); this.showLog(["[Input]", event]); let settings = event.filter((setting) => setting.Name !== "[Current Settings]"); if (resetActive) { settings.forEach((setting) => { setting.Active = false; }); } this._settings = settings; this.savedSettingsEvent.emit(this._settings); this.showLog(">>> END Method: saveSettings"); } /************************************************************************** * Export Methods **************************************************************************/ applyExportOptions(event) { this.showLog("START Method: applyExportOptions >>>"); this.showLog(["[Input]", event]); this._isExporting = true; setTimeout(() => { let includeAllData = event; if (includeAllData) { this.loadDataEvent.emit({ action: "Export Data" }); } else { this.exportData(false, Clone.deepCopy(this._data)); } }, 100); this.showLog(">>> END Method: applyExportOptions"); } exportData(includeAllData, data) { this.showLog("START Method: exportData >>>"); this.showLog(["includeAllData", includeAllData]); if (!includeAllData) { data = this.adjustExportColumns(data); } let dataArray = JSON.parse(JSON.stringify(data)); let link = document.createElement("a"); link.href = 'data:text/csv;charset=utf-8,' + this.convertToCSV(dataArray); link.download = "export.csv"; link.click(); this._isExporting = false; if (this._options.autoClosePanel) { this.closeDataOptions(); } this.showLog(">>> END Method: exportData"); } adjustExportColumns(tmp) { let rows = []; tmp.forEach((row) => { let columns = {}; this._columns.forEach((column) => { if (column.isVisible && !column.isAction) { if (column.FriendlyName !== "") { columns[column.FriendlyName] = row[column.Name]; } else if (column.DisplayName !== "") { columns[column.DisplayName] = row[column.Name]; } else { columns[column.Name] = row[column.Name]; } } }); rows.push(columns); }); return rows; } convertToCSV(objArray) { let csv = ""; for (let row = 0; row < objArray.length; row++) { let output = ""; // Add column names as first row. if (csv === "") { for (let column in objArray[row]) { if (output !== "") { output += ","; } output += column; } csv += `${output}\r\n`; output = ""; } for (let column in objArray[row]) { if (output !== "") { output += ","; } if (objArray[row][column]) { let data = `"${objArray[row][column].toString().replace(/"/g, '""')}"`; output += data; } else { output += ""; } } csv += `${encodeURIComponent(output)}\r\n`; } return csv; } /************************************************************************** * Row Methods **************************************************************************/ takeAction(args) { this.showLog("START Method: takeAction >>>"); this.showLog(["[Input]", args]); if (this.selectedRowActionEvent.observed) { this.selectedRowActionEvent.emit(args); } this.showLog(">>> END Method: takeAction"); } setTooltip(event, index, record) { if (!this.tooltipTemplate) return; if (index !== this.recordIndex) { this.tooltipVisible = false; this.tooltipStyles = { position: 'fixed', zIndex: '9999', top: `0px`, left: `0px`, visibility: 'hidden' }; this.recordIndex = index; this.recordData = record; } } showTooltip(event, index, record) { if (!this.tooltipTemplate) return; clearTimeout(this.tooltipTimer); clearTimeout(this.tooltipAutoCloseTimer); if (!this.tooltipVisible) { this.tooltipTimer = setTimeout(() => { this.tooltipVisible = true; this.setTooltipPosition(event); }, 1000); } this.tooltipAutoCloseTimer = setTimeout(() => { this.hideTooltip(event, -1, -1); }, 5000); } keepTooltipAlive() { if (!this.tooltipTemplate) return; clearTimeout(this.tooltipTimer); clearTimeout(this.tooltipAutoCloseTimer); this.tooltipVisible = true; } hideTooltip(event, index, record) { if (!this.tooltipTemplate) return; clearTimeout(this.tooltipTimer); clearTimeout(this.tooltipAutoCloseTimer); if (this.tooltipVisible && index !== this.recordIndex) { this.tooltipVisible = false; this.recordIndex = null; this.recordData = null; this.tooltipStyles = { position: 'fixed', zIndex: '9999', top: `0px`, left: `0px`, visibility: 'hidden' }; } } setTooltipPosition(event) { if (!this.tooltipTemplate) return; let offsetY = 5; let offsetX = 5; setTimeout(() => { let tooltip = this.sdktooltip.nativeElement; if (tooltip && (event.clientY + tooltip.offsetHeight) > (window.innerHeight - 50)) { offsetY = (tooltip.offsetHeight * -1) - 5; } if (tooltip && (event.clientX + tooltip.offsetWidth) > (window.innerWidth - 100)) { offsetX = (tooltip.offsetWidth * -1) - 5; } this.tooltipStyles = { position: 'fixed', zIndex: '9999', top: `${event.clientY + offsetY}px`, left: `${event.clientX + offsetX}px`, visibility: 'visible' }; }, 1); } // Used to remember where the scrollbar was on click-throughs (drill-downs). // protected onScroll(args: any) { // // if (this.selectedRowActionEvent.observed) { // // this._scrollTop = args.target.scrollTop; // // } // } /************************************************************************** * Expand/Collapse Rows **************************************************************************/ expandAll() { this.setExpandedForAll(true); } collapseAll() { this.setExpandedForAll(false); } toggleExpanded(rowItem) { rowItem["Expanded"] = !rowItem["Expanded"]; } isFirstColumn(column) { let visibleColumns = this._columns.filter(c => c.isVisible); return visibleColumns.length > 0 && column.Name === visibleColumns[0].Name; } setExpandedForAll(isExpanded) { let dataItems = this._data; dataItems.forEach((item) => { item["Expanded"] = isExpanded; }); } /************************************************************************** * Column Methods **************************************************************************/ getColumnName(column) { let originalName = column.Name; if (column.FriendlyName !== "") { originalName = `${column.FriendlyName} *`; } else if (column.DisplayName !== "") { originalName = column.DisplayName; } return originalName; } getOriginalColumnName(column) { let originalName = column.Name; if (column.DisplayName && column.DisplayName !== "") { originalName = column.DisplayName; } return `Orig. Name:\n${originalName}`; } hasVisibleColumns() { return this._columns.filter((column) => column.isVisible); } hasFormulas() { return this._columns.filter((column) => column.Formulas); } isColumnObject(value) { if (typeof (value) === "object") { return true; } else { return false; } } sortColumn(column) { let ndx = this._columns.findIndex((c) => c.Name === column.Name); let sort = null; if (ndx > -1 && !this._columns[ndx].isAction && this._columns[ndx].showSort) { sort = this._columns[ndx].Sort; if (sort) { this._columns[ndx].Sort.direction = (this._columns[ndx].Sort?.direction === Sorts.ASC) ? Sorts.DESC : Sorts.ASC; } else { this._columns[ndx].Sort = { direction: Sorts.ASC, position: 0 }; } this.applyOptions({ columns: this._columns }); } } /************************************************************************** * Page Methods **************************************************************************/ getRows(event) { this.showLog("START Method: getRows >>>"); this.showLog(["rows", event[0]]); this.loadData("Rows/Page Change", parseInt(event[0]), 1); this.showLog(">>> END Method: getRows"); } getPage(event) { this.showLog("START Method: getPage >>>"); this.showLog(["page", Array.isArray(event) ? Math.ceil(parseInt(event[0].split(" - ")[1]) / this._rows) : event]); if (A