UNPKG

ms-data-grid

Version:

A powerful, customizable Angular data grid component with advanced features like sorting, filtering, pagination, column pinning, and taskbar actions. Perfect for enterprise applications.

1,187 lines (1,179 loc) 1.03 MB
import * as i0 from '@angular/core'; import { Injectable, Pipe, EventEmitter, Component, Input, Output, ViewChild, HostListener, Directive, NgModule } from '@angular/core'; import * as i7 from '@angular/cdk/drag-drop'; import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop'; import { trigger, state, style, transition, animate } from '@angular/animations'; import * as i5 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i6 from '@angular/forms'; import { FormsModule } from '@angular/forms'; import * as i8 from 'ng-inline-svg'; import { InlineSVGModule } from 'ng-inline-svg'; import { ScrollingModule } from '@angular/cdk/scrolling'; class DataGridService { constructor() { } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataGridService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataGridService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DataGridService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return []; } }); const STATUSES_BADGE_MAP = { // Success – Green 'active': 'badge badge-success', 'approved': 'badge badge-success', 'accepted': 'badge badge-success', 'completed': 'badge badge-success', 'evaluated': 'badge badge-success', 'assigned': 'badge badge-success', 'scrap': 'badge badge-success', 'move-available': 'badge badge-success', 'move-assigned': 'badge badge-success', // Warning – Yellow/Amber 'contract': 'badge badge-warning', 'warranty': 'badge badge-warning', 'scheduled': 'badge badge-warning', 'leased': 'badge badge-warning', 'disposed': 'badge badge-warning', 'maintenance': 'badge badge-warning', 'assigning start': 'badge badge-warning', 'evaluation start': 'badge badge-warning', 'to be start assigning': 'badge badge-warning', 'pending': 'badge badge-warning', 'leave': 'badge badge-warning', // Danger – Red 'inactive': 'badge badge-danger', 'rejected': 'badge badge-danger', 'unassigned': 'badge badge-danger', 'trashed': 'badge badge-danger', 'onhold': 'badge badge-danger', 'assigning stop': 'badge badge-danger', 'evaluation stop': 'badge badge-danger', 'unavailable': 'badge badge-danger', 'move-error': 'badge badge-danger', 'failed': 'badge badge-danger', 'absent': 'badge badge-danger', // Info – Blue 'insurance': 'badge badge-info', 'pastdue': 'badge badge-info', // Dark – Neutral/Other 'expired': 'badge badge-dark', 'draft': 'badge badge-dark', 'present': 'badge badge-success' }; class SplitColumnsService { prepareColumns(columns, containerWidth) { const left = []; const center = []; const right = []; for (const col of columns) { if (col.children?.length) { const leftChildren = []; const centerChildren = []; const rightChildren = []; for (const child of col.children) { if (child.is_visible === false) continue; const pinned = child.pinned ?? col.pinned ?? null; const childWithPinned = { ...child, pinned }; if (pinned === 'left') leftChildren.push(childWithPinned); else if (pinned === 'right') rightChildren.push(childWithPinned); else centerChildren.push(childWithPinned); } if (leftChildren.length) { left.push({ header: col.header, children: leftChildren, id: col?.id || col?._id }); } if (centerChildren.length) { center.push({ header: col.header, children: centerChildren, id: col.id || col._id, }); } if (rightChildren.length) { right.push({ header: col.header, children: rightChildren, id: col.id || col._id, }); } } else if (col.is_visible !== false) { const pinned = col.pinned ?? null; if (pinned === 'left') left.push(col); else if (pinned === 'right') right.push(col); else center.push(col); } } return { left, center, right }; } setColumnsQuery(columns) { for (const col of columns) { // if (col.children?.length) { // for (const child of col.children) { // if (!child?.query?.firt_value && !child?.query?._ids?.length) { // child['query'] = { // first_condition: 'contain', // first_value: null, // condition: 'none', // second_condition: 'contain', // second_value: null, // _ids: [], // }; // } // } // } // if (!col?.query?.firt_value && !col?.query?._ids?.length) { // col.query = { // first_condition: 'contain', // first_value: null, // condition: 'none', // second_condition: 'contain', // second_value: null, // _ids: [], // }; // } } console.log('Updated Columns: ', columns); return columns; } assignDefaultWidths(columns, containerWidth) { const visibleLeafCols = this.getVisibleLeafColumns(columns); if (!visibleLeafCols.length) return columns; let defaultWidth = Math.floor(containerWidth / visibleLeafCols.length); if (defaultWidth < 80) defaultWidth = 80; const cloneColumns = (cols) => cols.map((col) => { if (col.children?.length) { const newChildren = col.children.map((child) => { // If visible → dynamic default width // If invisible → fixed 150px if (!child.width) { if (child.is_visible === false) { return { ...child, width: 150 }; } else { return { ...child, width: defaultWidth }; } } return { ...child }; }); return { ...col, children: newChildren }; } if (!col.width) { if (col.is_visible === false) { return { ...col, width: 150 }; } else { return { ...col, width: defaultWidth }; } } return { ...col }; }); return cloneColumns(columns); } getVisibleLeafColumns(columns) { const result = []; for (const col of columns) { if (col.children?.length) { const visibleChildren = col.children.filter((c) => c.is_visible !== false); result.push(...visibleChildren); } else if (col.is_visible !== false) { result.push(col); } } return result; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitColumnsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitColumnsService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SplitColumnsService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); class CommonService { constructor() { this.activeFilteredColumns = []; } gethasVisibleColumns(columns) { const checkVisible = (columns) => { return columns.some((col) => { if (col?.is_visible) return true; if (col?.children?.length) { return checkVisible(col.children); } return false; }); }; return checkVisible(columns); } gethasInVisibleColumns(columns) { const checkVisible = (columns) => { return columns.some((col) => { if (!col?.is_visible) return true; if (col?.children?.length) { return checkVisible(col.children); } return false; }); }; return checkVisible(columns); } getTotalColumnsLength(columns) { let count = 0; columns.forEach(col => { if (col.children && Array.isArray(col.children) && col.children.length) { count += col.children.length; // count children instead of parent } else { count += 1; // count parent directly } }); return count; } gethasRightPinnedColumns(columns) { const checkPinnedRight = (columns) => { return columns.some((col) => { if (col?.pinned === 'right' && col?.is_visible) return true; if (col?.children?.length) { return checkPinnedRight(col.children); } return false; }); }; return checkPinnedRight(columns); } gethasLeftPinnedColumns(columns) { const checkPinnedRight = (columns) => { return columns.some((col) => { if (col?.pinned === 'left' && col?.is_visible) return true; if (col?.children?.length) { return checkPinnedRight(col.children); } return false; }); }; return checkPinnedRight(columns); } getExpandedRowCount(data) { let groupCount = 0; let rowCount = 0; data.forEach(group => { if (group?.isGroup) { groupCount++; if (group?.isExpand && Array.isArray(group?.children)) { group.children.forEach((child) => { if (child.isGroup) { if (child.isExpand && Array.isArray(child.children)) { rowCount += child.children.length; } } else { rowCount++; } }); } } }); if (groupCount === 0) { return data.length; } return groupCount + rowCount; } getFiltersFromColumns(columns, filtersConfig) { const result = []; const checkColumn = (col) => { const hasFirstValue = col.query?.first_value != null && col.query?.first_value !== "" && filtersConfig.some((ele) => ele.field == col.field); const hasIds = Array.isArray(col.query?._ids) && col.query._ids.length > 0 && filtersConfig.some((ele) => ele.field == col.field); if (hasFirstValue || hasIds) { result.push({ search: col.search ?? "", field: col.field, type: col.type, _ids: col.type == 'dropdown' ? col.query?._ids : [], query: col.type == 'dropdown' ? null : (col?.query?.first_value ? col?.query : null) }); } if (Array.isArray(col.children) && col.children.length > 0) { col.children.forEach(checkColumn); } }; columns.forEach(checkColumn); return result; } async applyFiltersToColumns(columns, filters) { debugger; for (const col of columns) { if (!col.query) { col.query = { first_value: null, second_value: null, first_condition: col.type !== 'date' ? 'contain' : 'equal', second_condition: col.type !== 'date' ? 'contain' : 'equal', condition: 'none', _ids: [] }; } const filter = filters.find(f => f.field === col.field); if (filter) { if (col.type === 'dropdown') { col.filterValue = filter._ids ?? []; col.search = filter.search ?? ''; col.query._ids = filter._ids ?? []; } else { col.filterValue = filter.search ?? null; col.search = filter.search ?? ''; col.query.first_value = filter.query?.first_value ?? null; col.query.second_value = filter.query?.second_value ?? null; col.query.first_condition = filter.query?.first_condition ?? filter.type !== 'date' ? 'contain' : 'equal'; col.query.second_condition = filter.query?.second_condition ?? filter.type !== 'date' ? 'contain' : 'equal'; col.query.condition = filter.query?.condition ?? 'none'; } } if (Array.isArray(col.children) && col.children.length > 0) { col.children = await this.applyFiltersToColumns(col.children, filters); } } return columns; } updateActiveFilteredColumns(columns) { const collectFiltered = (cols) => { let result = []; for (let i = 0; i < cols.length; i++) { const col = cols[i]; if (col.children && col.children.length > 0) { result = result.concat(collectFiltered(col.children)); } if (col.query) { const hasDropdownFilter = Array.isArray(col.query._ids) && col.query._ids.length > 0; const hasValueFilter = (col.query.first_value && col.query.first_value !== '') || (col.query.second_value && col.query.second_value !== ''); if (hasDropdownFilter || hasValueFilter) { result.push(col); } } } return result; }; this.activeFilteredColumns = [...collectFiltered(columns)]; return this.activeFilteredColumns; } hasFieldChanged(current, original, type) { switch (type) { case 'number': return Number(current) !== Number(original); case 'string': return String(current || '') !== String(original || ''); case 'dropdown': if (typeof current === 'object' && typeof original === 'object') { return current?.id !== original?.id || current?.value !== original?.value; } return current !== original; case 'boolean': return Boolean(current) !== Boolean(original); case 'date': const currentDate = new Date(current).getTime(); const originalDate = new Date(original).getTime(); return isNaN(currentDate) || isNaN(originalDate) ? current !== original : currentDate !== originalDate; default: return JSON.stringify(current) !== JSON.stringify(original); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CommonService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CommonService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CommonService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return []; } }); class SwapColumnsService { constructor() { } deepClone(obj) { return JSON.parse(JSON.stringify(obj)); } flattenColumnsWithPaths(columns) { const result = []; const recurse = (cols, currentPath = []) => { cols.forEach((col, index) => { const path = [...currentPath, index]; result.push({ col, path }); if (Array.isArray(col.children)) { recurse(col.children, path); } }); }; recurse(columns); return result; } findColumnByIndex(columns, index, flat, _path = [], currentPath = []) { for (let i = 0; i < columns.length; i++) { const col = columns[i]; const nextPath = [...currentPath, i]; if (flat[index] === col) { return { column: col, parent: columns, path: nextPath }; } if (Array.isArray(col?.children)) { const result = this.findColumnByIndex(col?.children, index, flat, _path, nextPath); if (result) return result; } } return null; } insertAtPath(columns, path, newColumn) { const cloned = this.deepClone(columns); let current = cloned; for (let i = 0; i < path.length - 1; i++) { if (!current[path[i]]?.children) { current[path[i]].children = []; } current = current[path[i]]?.children; } const insertIndex = path[path.length - 1]; current.splice(insertIndex, 0, newColumn); return cloned; } removeAtPath(columns, path) { const cloned = this.deepClone(columns); let current = cloned; for (let i = 0; i < path.length - 1; i++) { if (!current[path[i]]?.children) { return cloned; // Safety: path is invalid } current = current[path[i]]?.children; } const removeIndex = path[path.length - 1]; if (Array.isArray(current) && current.length > removeIndex) { current.splice(removeIndex, 1); } return cloned; } swapColumnsInTree(currentColumn, columns, fromIndex, toIndex) { const flat = this.flattenColumnsWithPaths(columns); if (fromIndex < 0 || toIndex < 0 || fromIndex >= flat.length || toIndex >= flat.length) { return columns; // invalid indices } const fromInfo = this.findColumnByIndex(columns, fromIndex, flat); const toInfo = this.findColumnByIndex(columns, toIndex, flat); if (!fromInfo || !toInfo) return columns; const fromCol = fromInfo.column; const toCol = toInfo.column; let updatedCols = this.removeAtPath(columns, fromInfo.path); // Case 1: Same parent if (JSON.stringify(fromInfo.path.slice(0, -1)) === JSON.stringify(toInfo.path.slice(0, -1))) { return this.insertAtPath(updatedCols, toInfo.path, fromCol); } // Case 2: Drop target is a group header if (Array.isArray(toCol?.children)) { const cloned = this.deepClone(updatedCols); let parent = cloned; for (let i = 0; i < toInfo.path.length - 1; i++) { parent = parent[toInfo.path[i]].children; } const target = parent[toInfo.path[toInfo.path.length - 1]]; if (!target.children) { target.children = []; } target.children.push(fromCol); return cloned; } // Case 3: Drop target is a regular column → wrap both into a group const groupHeader = { headerName: toCol.headerName || 'Group', children: [toCol, fromCol], }; // Remove target from its original location updatedCols = this.removeAtPath(updatedCols, toInfo.path); // Insert group at target index return this.insertAtPath(updatedCols, toInfo.path, groupHeader); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SwapColumnsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SwapColumnsService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SwapColumnsService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: function () { return []; } }); class CopyServiceService { constructor() { this.updateRows = []; } getSelectedDataForCopy(dataSet, columns, rowSelectedIndexes, selectedCells, getNestedValue) { const copiedRows = []; const findRowByVirtualIndex = (vIndex) => dataSet.find((r) => r.__virtualIndex === vIndex); if (rowSelectedIndexes && rowSelectedIndexes.size > 0) { const sortedIndexes = [...rowSelectedIndexes].sort((a, b) => a - b); for (const vIndex of sortedIndexes) { const row = findRowByVirtualIndex(vIndex); if (!row) continue; const extractValue = (field) => { const nested = getNestedValue(row, field); const value = nested?.value ?? nested?.name ?? nested ?? '-'; if (Array.isArray(value)) { return value .map(v => typeof v === 'object' ? (v.departmentName ?? v.roleName ?? '') : (v?.toString() ?? '')) .join(', '); } return value ?? ''; }; const mapped = columns.map(col => { if (col.children && Array.isArray(col.children)) { return col.children.map((c) => extractValue(c.field)); } return [extractValue(col.field)]; }); const rowValues = mapped.reduce((acc, curr) => acc.concat(curr), []); copiedRows.push(rowValues); } } if (selectedCells && selectedCells.length > 0) { const rowsMap = new Map(); for (const cell of selectedCells) { const row = findRowByVirtualIndex(cell.rowIndex); if (!row) continue; const col = columns[cell.colIndex]; let fieldName = col?.field; if (col?.children && col.children[cell.subColIndex]) { fieldName = col.children[cell.subColIndex].field; } let value = getNestedValue(row, fieldName) || getNestedValue(row, fieldName)?.name || getNestedValue(row, fieldName) || '-'; if (Array.isArray(value)) { value = value .map(v => typeof v === 'object' ? v.departmentName ?? v.roleName ?? '' : v?.toString() ?? '') .join(', '); } else { value = value ?? ''; } if (!rowsMap.has(cell.rowIndex)) rowsMap.set(cell.rowIndex, []); rowsMap.get(cell.rowIndex).push(value); } const sortedCells = [...rowsMap.entries()] .sort(([a], [b]) => a - b) .map(([_, v]) => v); copiedRows.push(...sortedCells); } if (copiedRows.length === 0) { const activeCell = document.querySelector('.active-cell'); if (activeCell) return [[activeCell.textContent?.trim() || '']]; } const maxCols = copiedRows.reduce((max, row) => Math.max(max, row.length), 0); return copiedRows.map(row => { const newRow = [...row]; while (newRow.length < maxCols) newRow.push(''); return newRow; }); } copyToClipboard(selectedData, selector = '.selected-cell, .active-cell, .row-selected') { if (!selectedData || selectedData.length === 0) return; const textToCopy = selectedData .map(row => row.map(cell => (cell ?? '').toString()).join('\t')) .join('\n'); navigator.clipboard.writeText(textToCopy).catch(err => console.error(err)); const selectedEls = document.querySelectorAll(selector); selectedEls.forEach(el => el.classList.add('flash-bg')); setTimeout(() => { selectedEls.forEach(el => el.classList.remove('flash-bg')); }, 1000); } async pasteFromClipboardText(text, dataSet, columns, startRowIndex, startColIndex, startSubColIndex = 0) { if (!text) return []; this.updateRows = []; const rows = text.split(/\r?\n/).map(r => r.split('\t')); const flattenedColumns = []; columns.forEach(col => { if (col.children?.length) { col.children.forEach((child) => flattenedColumns.push({ parent: col, ...child })); } else { flattenedColumns.push(col); } }); let startFlattenedIndex = 0; for (let i = 0; i < startColIndex; i++) { const col = columns[i]; startFlattenedIndex += col.children?.length || 1; } const startCol = columns[startColIndex]; if (startCol?.children?.length) { startFlattenedIndex += Math.min(startSubColIndex, startCol.children.length - 1); } rows.forEach((rowValues, rOffset) => { const targetRowIndex = startRowIndex + rOffset; const row = dataSet.find(r => r?.__virtualIndex === targetRowIndex); if (!row) return; rowValues.forEach((value, cOffset) => { const targetIndex = startFlattenedIndex + cOffset; if (targetIndex < 0 || targetIndex >= flattenedColumns.length) return; const targetColumn = flattenedColumns[targetIndex]; this.setNestedValue(row, targetColumn, value); }); }); return this.updateRows; } setNestedValue(obj, column, option, calledFromInput = false) { debugger; if (column.type === 'date' || column.type === 'image') return; const path = column.field; if (!path) return; const keys = path.split('.'); const lastKey = keys.pop(); const parent = keys.reduce((acc, key) => (acc[key] ??= {}), obj); if (parent && lastKey) { if (typeof option === 'object' && option !== null) { parent[lastKey] = { id: option.id ?? 1, value: option.value ?? option, }; } else { parent[lastKey] = option; } } const sendObj = { data: { ...obj }, eventType: 'onCellEdit', }; this.updateRows.push(obj); } cutSelectedSelectedData(dataSet, rowSelectedIndexes, selectedCells, columns, setNestedValue) { let updatedData = [...dataSet]; if (rowSelectedIndexes?.size) { const indexes = [...rowSelectedIndexes]; updatedData = updatedData.filter((row) => !indexes.includes(row.__virtualIndex)); // rowSelectedIndexes.clear(); } else if (selectedCells?.length) { this.updateRows = []; for (const cell of selectedCells) { const row = updatedData.find(r => r.__virtualIndex === cell.rowIndex); if (!row) continue; let col = columns[cell.colIndex]; if (col?.children && col.children[cell.subColIndex]) { col = col.children[cell.subColIndex]; } this.setNestedValue(row, col, ''); } // selectedCells.length = 0; } return { updatedData: updatedData, updatedRows: this.updateRows }; } cutWithAnimation(selectedData, selector = '.selected-cell, .active-cell, .row-selected') { if (!selectedData || selectedData.length === 0) return; const textToCopy = selectedData .map(row => row.map(cell => (cell ?? '').toString()).join('\t')) .join('\n'); navigator.clipboard.writeText(textToCopy).catch(err => console.error(err)); // Apply red cut animation const selectedEls = document.querySelectorAll(selector); selectedEls.forEach(el => el.classList.add('cut-flash-bg')); setTimeout(() => { selectedEls.forEach(el => el.classList.remove('cut-flash-bg')); }, 1000); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CopyServiceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CopyServiceService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CopyServiceService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return []; } }); class FilterPipe { transform(items, searchText, key) { if (!items || !searchText) return items; searchText = searchText.toLowerCase(); return items.filter(item => { const value = key ? item?.[key] : item; return value?.toString().toLowerCase().includes(searchText); }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: FilterPipe, name: "filter" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FilterPipe, decorators: [{ type: Pipe, args: [{ name: 'filter', }] }] }); class DataGridComponent { constructor(columnService, cdr, commonSevice, swapColumnService, elementRef, ngZone, copyService) { this.columnService = columnService; this.cdr = cdr; this.commonSevice = commonSevice; this.swapColumnService = swapColumnService; this.elementRef = elementRef; this.ngZone = ngZone; this.copyService = copyService; // **************************************// // **************************************// // ********** Input Goes Here *********** // // **************************************// // **************************************// // Pagination Config Store Here this.paginationConfig = {}; // The dataset store here; this.dataSet = []; // The columns Store Here this.columns = []; // Row Height; this.rowHeight = 44; // Header Row Height; this.headerRowHeight = 44; // Show Vertical Borders; this.showVerticalBorder = true; // Even Rows Background Color; this.evenRowsBackgroundColor = undefined; // Even Rows Background Color; this.oddRowsBackgroundColor = undefined; // Header Rows Background Color; this.headerBackgroundColor = '#eaeaea'; // Header Rows Background Color; this.checkboxesBackgroundColor = '#eaeaea'; // Show Columns Grouping; this.showColumnsGrouping = false; // Row Hovered Background color; this.rowHoverColor = 'rgba(0, 123, 255, 0.1)'; // Left PinnedBackground color; this.leftPinnedBackgroundColor = '#000'; // Body Background color; this.bodyBackgroundColor = '#fff'; // Right Pinned Background color; this.rightPinnedBackgroundColor = '#000'; // Side Menu Background color; this.sidemenuBackgroundColor = '#000'; // Body text color; this.bodyTextColor = '#000'; // Header text color; this.headerTextColor = '#000'; // Header text size; this.headerTextFontsSize = 16; // Body text color; this.bodyTextFontsSize = 16; // Header font weight; this.headerFontWeight = 500; // Body Font Weight; this.bodyFontWeight = 400; // Checked Row Background Color; this.checkedRowBackgroundColor = ''; // dropdowns Background Color; this.dropdownsBackgroundColor = ''; // Footer row Height; this.footerRowHeight = 46; // Footer row Height; this.topGroupedBadgesBackgroundColor = ''; // Show Row wise grouping; this.showRowsGrouping = false; // Show Row wise grouping; this.showFilterRow = false; // Show Row wise grouping; this.fontFaimly = 'sans-serif'; // Show SideColumn; this.showSideMenu = false; // Footer Padding this.footerPadding = 3; // Footer Padding this.topFilterRowHeight = 50; // Show Rows shading this.rowShadingEnabled = false; // Show Rows shading this.showSerialNumber = false; // Single Spa Url Attach to icons this.singleSpaAssetsPath = 'assets/'; // Applied Filters this.filtersConfig = []; // Applied Filters this.loading = false; //Vertical Scrollbar style this.verticalScrollbarWidth = 'auto'; //Horizintal Scrollbar style this.horizintalScrollbarWidth = 'auto'; // Show Cell details box this.showCellDetailsBox = false; //Date format this.dateFormat = 'dd/MM/yyyy'; //Date format this.tableSearch = ''; //Date format this.actions = ['edit', 'delete']; // Selection task bar this.showTaskbar = false; // Table Name for state manage this.tableName = true; // Listing type this.listingType = ''; // Listing type this.checkboxState = { reset: true }; // Taskbar actions this.taskbarActions = []; // Sorting Config to show sort icons this.sortingConfig = null; this.tableFilterViewId = ''; this.selectedTableLayout = 'medium'; this.closeDropdown = { preset: { closed: false, loading: false } }; // Table View // GlobalSearch this.globalSearchText = ''; // Nested Table Row Fontsize this.nestedTablerowFontsize = 14; // Nested table row Header row Height this.nestedTableHeaderRowHeight = 40; // Nested Table row height this.nestedTablerowHeight = 36; this.gridType = 'Assets'; this.leftPinnedBoxshadow = '#4b4b4b 1px 1px 5px 0px'; this.rightPinnedBoxshadow = '#4b4b4b 4px 1px 6px 0px'; // GlobalSearch this.selectedRowsBackgroundColor = '#8ac5ff'; // GlobalSearch this.nestedTableHeaderBAckgroundColor = '#eaeaea'; this.tableView = []; this.columnThreedotsMunuConfig = { showPinleft: true, showPinright: true, showAscending: true, showDescending: true, showFilter: true, showRowsGrouping: this.showRowsGrouping, showAutosizeAllColumns: false, showAutosizeThisColumn: false, showChoseColumns: false, showResetColumns: false, }; // /////////////////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////////////////// // Out Put Events // /////////////////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////////////////// //Change Table Layout this.changeLayout = new EventEmitter(); // Filter Apply event; this.filterOptions = new EventEmitter(); this.genericEvent = new EventEmitter(); this.tablePresetConfig = new EventEmitter(); this.sortingOrderOptions = new EventEmitter(); this.createUpdateConfigListing = new EventEmitter(); this.groupedColumns = []; this.activeCol = null; this.activeFilterCell = null; this.showActionsDropDown = false; this.sideMenuVisible = false; this.pivotMode = false; this.columnSearch = ''; this.expandAllAccordians = true; this.currentOpenedSideMenue = null; this.originalColumns = []; this.originalDataSet = []; this.activeTopButton = ''; this.filterColumnsList = []; this.groupBoxPadding = 200; this.presetName = ''; this.presetFilter = ''; this.searchTextPresetTable = ''; this.addFilterColumnInput = ''; this.searchInDropdown = ''; this.addFilterDropdownSearch = ''; this.topShowHideColumns = ''; this.choseColumnsSearch = ''; this.editinDropdownSearch = ''; this.isThreeDotsFilterOpen = false; this.confirmDelete = false; this.fontFamilies = [ // Common Excel fonts 'Arial', 'Arial Black', 'Bahnschrift', 'Calibri', 'Cambria', 'Cambria Math', 'Candara', 'Comic Sans MS', 'Consolas', 'Constantia', 'Corbel', 'Courier New', 'Ebrima', 'Franklin Gothic Medium', 'Gabriola', 'Gadugi', 'Georgia', 'Impact', 'Ink Free', 'Javanese Text', 'Leelawadee UI', 'Lucida Console', 'Lucida Sans Unicode', 'Malgun Gothic', 'Microsoft Himalaya', 'Microsoft JhengHei', 'Microsoft New Tai Lue', 'Microsoft PhagsPa', 'Microsoft Sans Serif', 'Microsoft Tai Le', 'Microsoft YaHei', 'Microsoft Yi Baiti', 'Mongolian Baiti', 'MS Gothic', 'MS PGothic', 'MS UI Gothic', 'MV Boli', 'Nirmala UI', 'Palatino Linotype', 'Segoe Print', 'Segoe Script', 'Segoe UI', 'Segoe UI Historic', 'Segoe UI Emoji', 'Segoe UI Symbol', 'SimSun', 'Sitka Small', 'Sylfaen', 'Symbol', 'Tahoma', 'Times New Roman', 'Trebuchet MS', 'Verdana', 'Webdings', 'Wingdings', 'Yu Gothic', 'sans-serif', 'serif', 'monospace' ]; this.fontSizes = [ '8', '9', '10', '11', '12', '14', '16', '18', '20', '24', '28', '32' ]; this.leftPinnedColumns = []; this.centerColumns = []; this.rightPinnedColumns = []; this.previewLeftPinnedColumns = []; this.previewCenterColumns = []; this.previewRightPinnedColumns = []; this.fakeScrollbarScrollLeft = 0; // Row Hover Logic Here this.hoveredRowId = null; this.isMenueHidden = false; // Rows Selection Logic Goes Here this.selectedRows = new Set(); this.showColumnPanel = false; this.draggingInGroupArea = false; this.visibleRowCount = 25; this.startIndex = 0; this.visibleRows = []; this.trackByRenderedIndex = (i, _row) => this.renderStart + i; this.isSyncingFromMain = false; this.isSyncingFromFake = false; this.mainScrollRaf = null; this.flattenedData = []; // requestAnimationFrame IDs this.fakeScrollRaf = null; // onMainScroll(event: Event) { // this.expandedCells.clear(); // const scrollTop = (event.target as HTMLElement).scrollTop; // if (this.groupedColumns?.length > 0) { // this.startIndex = 0; // this.cdr.detectChanges(); // return; // } // const overscan = this.overscan * 2; // this.startIndex = Math.floor(scrollTop / this.rowHeight); // const start = Math.max(0, this.startIndex - overscan); // const end = Math.min(this.flattenedData.length, this.startIndex + this.viewportRows + overscan); // this.visibleRows = this.flattenedData.slice(start, end); // this.cdr.detectChanges(); // } this.translateY = 0; this.viewportRows = 0; // how many rows fit in viewport (computed) this.firstIndex = 0; // index of the first visible row (clamped) this.renderStart = 0; // where the slice actually starts (firstIndex - overscan, clamped) this.scrollRaf = null; this.pendingScrollTop = 0; this.overscan = 10; // buffer rows above and below this.dragStartIndex = 0; // onDragStart(data: any, index: number) { // this.draggingColumn = data.column; // this.dragStartIndex = index; // } // onDragMove(data: any) { // const { clientX, clientY } = data.event; // const headers = Array.from( // document.querySelectorAll('.one-row-header-cells') // ) as HTMLElement[]; // headers.forEach((headerEl, idx) => { // const rect = headerEl.getBoundingClientRect(); // if ( // idx !== this.dragStartIndex && // clientX > rect.left && // clientX < rect.right && // clientY > rect.top && // clientY < rect.bottom // ) { // const otherIndex = idx; // console.log(`${this.dragStartIndex} --> ${otherIndex}`); // this.swapColumn(this.dragStartIndex, otherIndex); // console.log('Updated Columns: ', this.columns); // this.dragStartIndex = otherIndex; // update index // } // }); // } // swapColumn(previusIndex: number, currentIndex: number) { // const columns = structuredClone(this.columns); // const flattenColumns = this.flattenColumns(columns); // const visibleFlattenColumns = flattenColumns.filter( // (col) => col.is_visible // ); // const previusColumn = visibleFlattenColumns[previusIndex]; // const currentColumn = visibleFlattenColumns[currentIndex]; // console.log('Previus Column: ', previusColumn); // console.log('current clumn: ', currentColumn); // } // onDragEnd(data: any) { // this.draggingColumn = null; // this.dragStartIndex = null; // } // CDK DRAG DROP LOGIC GOES HERE this.canEnterToRowsGrouping = (drag, drop) => { // Example: Block if already pinned left const data = drag.data; if (data?.children && data?.children.length) { return data.children.some((col) => col.is_visible && col.isGroupable); } return data.is_visible && data.isGroupable; }; this.shouldDisableDroplistSorting = false; this.isDisableColumnGrouping = false; this.currentDraggingColumn = null; this.onSortGroup = async (event, section) => { const columns = this[section]; if (event.previousIndex === event.currentIndex) return; const visibleColumns = columns.filter((col) => { if (col?.children && Array.isArray(col.children)) { return col.children.some((child) => child.is_visible); } return col.is_visible; }); const previousHeader = visibleColumns[event.previousIndex].header; const currentHeader = visibleColumns[event.currentIndex].header; const visiblePrevIndex = visibleColumns.findIndex((col) => col.header === previousHeader); const visibleCurrIndex = visibleColumns.findIndex((col) => col.header === currentHeader); const getFields = (item) => { if (item?.children && Array.isArray(item.children)) { return item.children.map((child) => child.field); } return [item.field]; }; const prevFields = getFields(visibleColumns[visiblePrevIndex]); const currFields = getFields(visibleColumns[visibleCurrIndex]); const allFields = [...prevFields, ...currFields]; const cells = allFields.length ? [].concat(...allFields.map((field) => Array.from(document.querySelectorAll(`[field="${field}"]`)))) : []; const firstPositions = new Map(); cells.forEach((el) => firstPositions.set(el, el.getBoundingClientRect().left)); console.log("this .coluns", this.columns); moveItemInArray(this[section], visiblePrevIndex, visibleCurrIndex); const prevColIndexInColumns = this.columns.findIndex(item => item.header === previousHeader); const currColIndexInColumns = this.columns.findIndex(item => item.header === currentHeader); moveItemInArray(this.columns, prevColIndexInColumns, currColIndexInColumns); // await this.refreshPreviewColumns(); this.cdr.detectChanges(); allFields.forEach((field) => { const updatedCells = Array.from(document.querySelectorAll(`[field="${field}"]`)); updatedCells.forEach((el) => { const newLeft = el.getBoundingClientRect().left; const oldLeft = firstPositions.get(el); const deltaX = oldLeft - newLeft; if (deltaX !== 0) { el.style.willChange = 'transform'; el.style.transition = 'none'; el.style.transform = `translateX(${deltaX}px)`; void el.offsetWidth; el.style.transition = 'transform 400ms cubic-bezier(0.4, 0, 0.2, 1)'; el.style.transform = 'translateX(0)'; const handle = () => { el.style.transition = ''; el.style.transform = ''; el.style.willChange = ''; el.removeEventListener('transitionend', handle); }; el.addEventListener('transitionend', handle); } }); }); }; this.dropListIds = []; this.onChildDroplistSorted = async (event, section) => { if (event.previousIndex === event.currentIndex) return; const pinned = section == 'previewLeftPinnedColumns' ? 'left' : section == 'previewRightPinnedColumns' ? 'right' : ""; const column = event.item.data; let group = this.columns.find((col) => Array.isArray(col.children) && col.children.some((child) => child?.field === column?.field)); const groupIndex = this.columns.findIndex((col) => col.header === group.header); const filteredGroup = group?.children.filter((col) => { const isPinnedMatch = (pinned === "" && (!col?.pinned || col?.pinned === "")) || col?.pinned === pinned; return isPinnedMatch && col?.is_visible; }); const previousField = filteredGroup[event.previousIndex].field; const currentField = filteredGroup[event.currentIndex].field; const visiblePrevIndex = group.children.findIndex((col) => col.field == previousField); const visibleCur