@transunion-ui/tablejs
Version:
Tablejs ========
1,061 lines • 358 kB
JavaScript
import { Directive, ElementRef, EventEmitter, Inject, InjectionToken, Injector, Input, Output } from '@angular/core';
import { DragAndDropGhostComponent } from './../../components/drag-and-drop-ghost/drag-and-drop-ghost.component';
import { DOCUMENT } from '@angular/common';
import { TablejsGridProxy } from './../../shared/classes/tablejs-grid-proxy';
import { ColumnReorderEvent } from './../../shared/classes/events/column-reorder-event';
import { ColumnResizeEvent } from './../../shared/classes/events/column-resize-event';
import { GridEvent } from './../../shared/classes/events/grid-event';
import { ScrollViewportDirective } from './../../directives/scroll-viewport/scroll-viewport.directive';
import { ResizeSensor } from 'css-element-queries';
import { Subject } from 'rxjs';
import { OverlayConfig } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import * as i0 from "@angular/core";
import * as i1 from "./../../services/grid/grid.service";
import * as i2 from "./../../services/directive-registration/directive-registration.service";
import * as i3 from "@angular/cdk/overlay";
import * as i4 from "./../../services/scroll-dispatcher/scroll-dispatcher.service";
import * as i5 from "./../../services/operating-system/operating-system.service";
export class GridDirective extends TablejsGridProxy {
constructor(elementRef, resolver, gridService, directiveRegistrationService, document, overlay, scrollDispatcherService, operatingSystem, rendererFactory) {
super();
this.elementRef = elementRef;
this.resolver = resolver;
this.gridService = gridService;
this.directiveRegistrationService = directiveRegistrationService;
this.document = document;
this.overlay = overlay;
this.scrollDispatcherService = scrollDispatcherService;
this.operatingSystem = operatingSystem;
this.rendererFactory = rendererFactory;
this.dragging = false;
this.reordering = false;
this.startX = 0;
this.startY = 0;
this.stylesByClass = [];
this.id = null;
this.viewport = null;
this.viewportID = null;
this.currentClassesToResize = [];
this.startingWidths = [];
this.minWidths = [];
this.totalComputedMinWidth = 0;
this.totalComputedWidth = 0;
this.defaultTableMinWidth = 25;
this.gridTemplateClasses = [];
this.gridOrder = [];
this.classWidths = [];
this.gridTemplateTypes = [];
this.draggingColumn = null;
this.colRangeGroups = [];
this.lastDraggedOverElement = null;
this.lastDraggedGroupIndex = -1;
this.lastDraggedOverRect = null;
this.lastDraggedGroupBoundingRects = null;
this.lastMoveDirection = -1;
this.resizableColumns = [];
this.resizableGrips = [];
this.reorderGrips = [];
this.reorderableColumns = [];
this.columnsWithDataClasses = [];
this.rows = [];
this.infiniteScrollViewports = [];
this.mutationResizableColumns = [];
this.mutationResizableGrips = [];
this.mutationReorderGrips = [];
this.mutationReorderableColumns = [];
this.mutationColumnsWithDataClasses = [];
this.mutationRows = [];
this.mutationInfiniteScrollViewports = [];
this.headTag = this.document.getElementsByTagName('head')[0];
this.styleContent = '';
this.headStyle = null;
this.styleList = [];
this.initialWidths = [];
this.initialWidthsAreSet = undefined;
this.lastColumns = [];
this.contentResizeSensor = null;
this.observer = null;
this.isCustomElement = false;
this.parentGroups = [];
this.colData = null;
this.colDataGroups = [];
this.elementsWithHighlight = [];
this.dragAndDropGhostComponent = null;
this.dragOffsetX = 0;
this.dragOffsetY = 0;
this.reorderHandleColOffset = 0;
this.scrollbarWidth = 0;
// class used for setting order
this.reorderableClass = 'reorderable-table-row';
// fragments
this.widthStyle = null;
this.widthStyleFragment = null;
this.reorderHighlightStyle = null;
this.reorderHighlightStyleFragment = null;
this.subGroupStyles = [];
this.subGroupFragments = [];
this.gridOrderStyles = [];
this.gridOrderFragments = [];
this.subGroupStyleObjs = {};
this.scrollbarAdjustmentFragment = null;
this.scrollbarAdjustmentStyle = null;
this.resizeMakeUpPercent = 0;
this.resizeMakeUpPerColPercent = 0;
this.scrollViewportDirective = null;
this.hiddenColumnIndices = [];
this.hiddenColumnChanges = new Subject();
this.HIDDEN_COLUMN_CLASS = 'column-is-hidden';
this.DRAG_AND_DROP_GHOST_OVERLAY_DATA = new InjectionToken('DRAG_AND_DROP_GHOST_OVERLAY_DATA');
this.animationFrameIDs = [];
this.linkClass = undefined;
this.resizeColumnWidthByPercent = false;
this.columnResizeStart = new EventEmitter();
this.columnResize = new EventEmitter();
this.columnResizeEnd = new EventEmitter();
this.columnReorder = new EventEmitter();
this.columnReorderStart = new EventEmitter();
this.dragOver = new EventEmitter();
this.columnReorderEnd = new EventEmitter();
this.preGridInitialize = new EventEmitter(true);
this.gridInitialize = new EventEmitter(true);
console.log('TableJS has been moved! Please install the newest versions from https://www.npmjs.com/package/@tablejs/community (npm install @tablejs/community).');
this.registerDirectiveToElement();
this.attachMutationObserver();
}
registerDirectiveToElement() {
this.elementRef.nativeElement.gridDirective = this;
this.elementRef.nativeElement.parentElement.gridDirective = this;
}
attachMutationObserver() {
if (!this.observer) {
const ths = this;
this.observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
ths.updateMutations(mutation);
});
});
this.observer.observe(this.elementRef.nativeElement, {
// configure it to listen to attribute changes
attributes: true,
subtree: true,
childList: true,
characterData: false
});
}
}
updateMutations(mutation) {
if (mutation.type === 'childList') {
const addedNodes = Array.from(mutation.addedNodes);
addedNodes.forEach(node => {
this.directiveRegistrationService.registerNodeAttributes(node);
this.getChildNodes(node);
});
}
}
getChildNodes(node) {
node.childNodes.forEach((childNode) => {
this.directiveRegistrationService.registerNodeAttributes(childNode);
if (childNode.getAttribute) {
this.getChildNodes(childNode);
}
});
}
ngAfterViewInit() {
const viewport = this.elementRef.nativeElement.querySelector('*[tablejsScrollViewport]');
if (viewport !== null && (viewport.scrollViewportDirective === null || viewport.scrollViewportDirective === undefined)) {
// attach directive
const viewportRef = new ElementRef(viewport);
this.scrollViewportDirective = new ScrollViewportDirective(viewportRef, this.gridService, this.document, this.directiveRegistrationService, this.scrollDispatcherService, this.operatingSystem, null, this.rendererFactory);
this.scrollViewportDirective.registerCustomElementsInputs(viewport);
this.scrollViewportDirective.ngOnInit();
this.scrollViewportDirective.ngAfterViewInit();
}
// Close observer if directives are registering
this.elementRef.nativeElement.directive = this;
if (!this.document['hasPointerDownListener']) {
this.pointerListenerFunc = (e) => {
let el = e.target;
if (el) {
while (el !== null && el.getAttribute('tablejsGrid') === null) {
el = el.parentElement;
}
if (el) {
el['directive'].onPointerDown(e);
}
}
};
this.document.addEventListener('pointerdown', this.pointerListenerFunc);
this.document['hasPointerDownListener'] = true;
}
const animationFrameID = window.requestAnimationFrame((timestamp) => {
this.onEnterFrame(this, timestamp);
});
this.animationFrameIDs.push(animationFrameID);
}
onEnterFrame(ths, timestamp) {
if (this.columnsWithDataClasses.length > 0) {
this.observer?.disconnect();
}
if (this.columnsWithDataClasses.length === 0 && this.mutationColumnsWithDataClasses.length === 0) {
const animationFrameID = window.requestAnimationFrame((tmstamp) => {
ths.onEnterFrame(ths, tmstamp);
});
this.animationFrameIDs.push(animationFrameID);
return;
}
if (this.columnsWithDataClasses.length === 0 && this.mutationColumnsWithDataClasses.length !== 0) {
this.isCustomElement = true;
this.resizableColumns = this.mutationResizableColumns.concat();
this.resizableGrips = this.mutationResizableGrips.concat();
this.reorderGrips = this.mutationReorderGrips.concat();
this.reorderableColumns = this.mutationReorderableColumns.concat();
this.columnsWithDataClasses = this.mutationColumnsWithDataClasses.concat();
this.rows = this.mutationRows.concat();
this.infiniteScrollViewports = this.mutationInfiniteScrollViewports.concat();
this.mutationResizableColumns = [];
this.mutationResizableGrips = [];
this.mutationReorderGrips = [];
this.mutationReorderableColumns = [];
this.mutationColumnsWithDataClasses = [];
this.mutationRows = [];
this.mutationInfiniteScrollViewports = [];
}
const allElementsWithDataResizable = this.columnsWithDataClasses;
const el = allElementsWithDataResizable[0];
const resizeClasses = this.getResizableClasses(el);
const resizeCls = resizeClasses[0];
const firstEl = this.elementRef.nativeElement.getElementsByClassName(resizeCls)[0];
if (!this.initialWidthSettingsSubscription$) {
this.initialWidthSettingsSubscription$ = this.gridService.containsInitialWidthSettings.subscribe(hasWidths => {
this.initialWidthsAreSet = hasWidths;
});
}
if (!this.hiddenColumnChangesSubscription$) {
this.hiddenColumnChangesSubscription$ = this.hiddenColumnChanges.subscribe((change) => {
if (change) {
const relatedHeader = this.getRelatedHeader(change.hierarchyColumn.element);
relatedHeader.hideColumn = change.hidden;
if (change.wasTriggeredByThisColumn) {
this.updateHiddenColumnIndices();
const hideColumnIf = change.hierarchyColumn.element.hideColumnIf;
hideColumnIf.updateHeadersThatCanHide();
}
if (!change.hidden) {
if (change.wasTriggeredByThisColumn) {
this.currentClassesToResize = this.getResizableClasses(relatedHeader);
const avgWidthPerColumn = this.getAverageColumnWidth();
this.setMinimumWidths();
const totalTableWidth = this.viewport.clientWidth;
let newWidth = avgWidthPerColumn * this.currentClassesToResize.length;
this.currentClassesToResize.forEach(className => {
const classIndex = this.gridTemplateClasses.indexOf(className);
if (this.resizeColumnWidthByPercent) {
this.classWidths[classIndex] = (avgWidthPerColumn / totalTableWidth * 100).toString() + '%';
// average all percentages
}
else {
this.classWidths[classIndex] = Math.max(avgWidthPerColumn, this.minWidths[classIndex]);
}
});
if (this.resizeColumnWidthByPercent) {
this.fitWidthsToOneHundredPercent();
}
this.updateWidths(newWidth);
}
}
this.setGridOrder();
}
});
}
if (this.parentGroups.length === 0) {
this.setParentGroups(allElementsWithDataResizable);
}
const maxColumnsPerRow = this.parentGroups[this.parentGroups.length - 1].length;
if (firstEl === undefined || firstEl === null) {
const animationFrameID = window.requestAnimationFrame((tmstamp) => {
ths.onEnterFrame(ths, tmstamp);
});
this.animationFrameIDs.push(animationFrameID);
}
else {
const keys = Object.keys(this.initialWidths);
if (this.initialWidthsAreSet === true && keys.length < maxColumnsPerRow) {
const animationFrameID = window.requestAnimationFrame((tmstamp) => {
ths.awaitWidths(ths, tmstamp);
});
this.animationFrameIDs.push(animationFrameID);
}
else {
this.checkForGridInitReady();
}
}
}
canHideColumn(column) {
return column.hideColumnIf.canHide;
}
getFlattenedHierarchy() {
const hierarchy = this.getColumnHierarchy();
return hierarchy.columnGroups.reduce((prev, curr) => {
let arr = [curr];
if (curr.subColumns) {
arr = arr.concat(this.getSubColumns(curr));
}
return prev.concat(arr);
}, []);
}
getSubColumns(item) {
if (item.subColumns.length === 0) {
return [];
}
let arr = [];
for (let i = 0; i < item.subColumns.length; i++) {
const subItem = item.subColumns[i];
arr = arr.concat(subItem);
if (subItem.subColumns.length > 0) {
arr = arr.concat(this.getSubColumns(subItem));
}
}
return arr;
}
getColumnHierarchy() {
const hierarchy = {
columnGroups: []
};
const highestLevelGroup = this.colDataGroups[0];
const hierarchyGroup = highestLevelGroup.map((item) => {
let levelCount = 0;
return {
level: levelCount,
element: item.child,
parent: item.parent,
parentColumn: null,
subColumns: this.getHierarchySubColumns(item, levelCount)
};
});
hierarchy.columnGroups = hierarchyGroup;
return hierarchy;
}
getHierarchySubColumns(item, levelCount) {
levelCount++;
if (!item.subGroups || item.subGroups.length === 0) {
return [];
}
const subColumns = item.subGroups.map((subItem) => {
return {
level: levelCount,
element: subItem.child,
parent: subItem.parent,
parentColumn: item.child,
subColumns: this.getHierarchySubColumns(subItem, levelCount)
};
});
return subColumns;
}
checkForGridInitReady() {
const allElementsWithDataResizable = this.columnsWithDataClasses;
const el = allElementsWithDataResizable[0];
const resizeClasses = this.getResizableClasses(el);
const resizeCls = resizeClasses[0];
const keys = Object.keys(this.initialWidths);
const maxColumnsPerRow = this.parentGroups[this.parentGroups.length - 1].length;
if (this.initialWidthsAreSet === true && (keys.length < maxColumnsPerRow || !this.initialWidths[resizeCls])) {
const animationFrameID = window.requestAnimationFrame((tmstamp) => {
this.awaitWidths(this, tmstamp);
});
this.animationFrameIDs.push(animationFrameID);
}
else if (this.initialWidthsAreSet === undefined) {
const animationFrameID = window.requestAnimationFrame((tmstamp) => {
this.awaitWidths(this, tmstamp);
});
this.animationFrameIDs.push(animationFrameID);
}
else {
if (!this.linkClass) {
this.initGrid();
}
else {
const animationFrameID = window.requestAnimationFrame((tmstamp) => {
this.awaitSingleFrame(this, tmstamp);
});
this.animationFrameIDs.push(animationFrameID);
}
}
}
awaitWidths(ths, timestamp) {
this.checkForGridInitReady();
}
awaitSingleFrame(ths, timestamp) {
this.initGrid();
}
onPointerDown(event) {
this.addPointerListeners();
if (!this.getResizeGripUnderPoint(event)) {
return;
}
// only drag on left mouse button
if (event.button !== 0) {
return;
}
// disables unwanted drag and drop functionality for selected text in browsers
this.clearSelection();
const el = this.elementRef.nativeElement;
let resizeHandles;
if (this.elementRef.nativeElement.reordering) {
return;
}
const reorderHandlesUnderPoint = this.getReorderHandlesUnderPoint(event);
const colsUnderPoint = this.getReorderColsUnderPoint(event);
if (reorderHandlesUnderPoint.length > 0 && colsUnderPoint.length > 0) {
this.elementRef.nativeElement.reordering = true;
this.draggingColumn = colsUnderPoint[0];
this.columnReorderStart.emit({
pointerEvent: event,
columnDragged: this.draggingColumn,
columnHovered: this.draggingColumn
});
const customReorderStartEvent = new CustomEvent(ColumnReorderEvent.ON_REORDER_START, {
detail: {
pointerEvent: event,
columnDragged: this.draggingColumn,
columnHovered: this.draggingColumn
}
});
this.elementRef.nativeElement.parentElement.dispatchEvent(customReorderStartEvent);
const elRect = this.draggingColumn.getBoundingClientRect();
this.dragOffsetX = (event.pageX - elRect.left) - window.scrollX;
this.dragOffsetY = (event.pageY - elRect.top) - window.scrollY;
this.removeDragAndDropComponent();
this.createDragAndDropComponent();
const dragNDropX = event.pageX - this.dragOffsetX;
const dragNDropY = event.pageY - this.dragOffsetY;
this.setDragAndDropPosition(dragNDropX, dragNDropY);
this.attachReorderGhost(this.draggingColumn);
this.setReorderHighlightHeight(this.draggingColumn);
this.lastDraggedOverElement = this.draggingColumn;
this.parentGroups.forEach((arr, index) => {
if (arr.indexOf(this.lastDraggedOverElement) !== -1) {
this.lastDraggedGroupIndex = index;
}
});
this.reorderHandleColOffset = reorderHandlesUnderPoint[0].getBoundingClientRect().left - this.draggingColumn.getBoundingClientRect().left;
this.lastDraggedGroupBoundingRects = this.parentGroups[this.lastDraggedGroupIndex].map(item => {
const boundingRect = item.getBoundingClientRect();
const rect = {
left: item.getBoundingClientRect().left + this.getContainerScrollCount(item),
right: boundingRect.right + window.scrollX,
top: boundingRect.top,
bottom: boundingRect.bottom,
width: boundingRect.width,
height: boundingRect.height
};
rect.x = rect.left;
rect.y = rect.top;
rect.toJSON = {};
return rect;
});
}
resizeHandles = this.resizableGrips;
if (resizeHandles.length === 0) {
return;
}
// if no handle exists, allow whole row to be resizable
if (resizeHandles.length > 0) {
const resizableElements = document.elementsFromPoint(event.clientX, event.clientY);
const els = resizableElements.filter(item => {
let handleItem = null;
resizeHandles.forEach(resizeHandle => {
if (item === resizeHandle) {
handleItem = resizeHandle;
}
});
return handleItem !== null;
});
if (els.length === 0) {
return;
}
}
this.dragging = true;
const elements = this.getResizableElements(event);
if (elements.length === 0) {
return;
}
this.totalComputedMinWidth = 0;
this.totalComputedWidth = 0;
this.minWidths = [];
this.startingWidths = [];
this.currentClassesToResize = this.getResizableClasses(elements[0]);
// disallow resizing the rightmost column with percent sizing
if (this.resizeColumnWidthByPercent) {
const lastColumnClass = this.getLastVisibleColumnClass();
if (this.currentClassesToResize.indexOf(lastColumnClass) !== -1) {
this.dragging = false;
}
}
this.currentClassesToResize.forEach((className) => {
const wdth = this.getClassWidthInPixels(className);
if (!this.columnIsHiddenWithClass(className)) {
this.totalComputedWidth += wdth;
}
this.startingWidths.push(wdth);
});
this.setMinimumWidths();
this.startX = event.clientX;
this.startY = event.clientY;
this.columnResizeStart.emit({
pointerEvent: event,
columnWidth: this.totalComputedWidth,
columnMinWidth: this.totalComputedMinWidth,
classesBeingResized: this.currentClassesToResize
});
const customResizeStartEvent = new CustomEvent(ColumnResizeEvent.ON_RESIZE_START, {
detail: {
pointerEvent: event,
columnWidth: this.totalComputedWidth,
columnMinWidth: this.totalComputedMinWidth,
classesBeingResized: this.currentClassesToResize
}
});
this.elementRef.nativeElement.parentElement.dispatchEvent(customResizeStartEvent);
// stop interference with reordering columns
event.preventDefault();
event.stopImmediatePropagation();
}
getClassWidthInPixels(className) {
const classIndex = this.gridTemplateClasses.indexOf(className);
let wdth = this.classWidths[classIndex];
if (this.resizeColumnWidthByPercent) {
wdth = wdth.replace('%', ''); // remove px
let totalTableWidth = this.viewport.clientWidth;
wdth = (Number(wdth) / 100 * totalTableWidth).toString();
}
return Number(wdth);
}
setMinimumWidths() {
this.gridTemplateClasses.forEach(className => {
const firstEl = this.elementRef.nativeElement.querySelector('.' + className);
const minWidth = window.getComputedStyle(firstEl).getPropertyValue('min-width');
let wdth = Number(minWidth.substring(0, minWidth.length - 2)); // remove px
wdth = Number(wdth) < this.defaultTableMinWidth ? this.defaultTableMinWidth : wdth; // account for minimum TD size in tables
if (this.currentClassesToResize.indexOf(className) !== -1 && !this.columnIsHiddenWithClass(className)) {
this.totalComputedMinWidth += wdth;
}
this.minWidths.push(wdth);
});
}
attachReorderGhost(column) {
this.dragAndDropGhostComponent?.updateView(column.reorderGhost, column.reorderGhostContext);
}
getContainerScrollCount(el) {
if (!el) {
return 0;
}
let scrollXCount = el.scrollLeft;
while (el !== document.body) {
el = el.parentElement;
scrollXCount += el.scrollLeft;
}
// include scrolling on tablejs-grid component
scrollXCount += el.parentElement.scrollLeft;
return scrollXCount;
}
onPointerMove(event) {
const ths = document['currentGridDirective'];
if (ths.elementRef.nativeElement.reordering) {
ths.clearSelection();
const dragNDropX = event.pageX - ths.dragOffsetX;
const dragNDropY = event.pageY - ths.dragOffsetY;
ths.setDragAndDropPosition(dragNDropX, dragNDropY);
const trueMouseX = event.pageX - ths.reorderHandleColOffset + ths.getContainerScrollCount(ths.draggingColumn);
if (!ths.lastDraggedOverElement) {
return;
}
ths.columnReorder.emit({
pointerEvent: event,
columnDragged: ths.draggingColumn,
columnHovered: ths.lastDraggedOverElement
});
const customReorderEvent = new CustomEvent(ColumnReorderEvent.ON_REORDER, {
detail: {
pointerEvent: event,
columnDragged: ths.draggingColumn,
columnHovered: ths.lastDraggedOverElement
}
});
ths.elementRef.nativeElement.parentElement.dispatchEvent(customReorderEvent);
let moveDirection = 0;
let currentRect;
let currentColIndex;
for (const rect of ths.lastDraggedGroupBoundingRects) {
if (trueMouseX > rect.left && trueMouseX < rect.left + rect.width) {
const elX = rect.left;
const elW = rect.width;
if ((trueMouseX - elX) >= elW / 2) {
moveDirection = 1;
}
else {
moveDirection = 0;
}
currentRect = rect;
currentColIndex = ths.lastDraggedGroupBoundingRects.indexOf(rect);
break;
}
}
if (currentColIndex === undefined) {
return;
}
if (ths.lastDraggedOverRect === currentRect && ths.lastMoveDirection === moveDirection) {
return;
}
ths.lastMoveDirection = moveDirection;
ths.lastDraggedOverRect = currentRect;
ths.removeElementHighlight(ths.lastDraggedOverElement);
ths.removeHighlights(ths.lastDraggedOverElement, moveDirection);
const draggableInColumn = ths.parentGroups[ths.lastDraggedGroupIndex][currentColIndex];
ths.lastDraggedOverElement = draggableInColumn;
let colRangeDraggedParentInd = -1;
let colRangeDraggedChildInd = -1;
let colRangeDroppedParentInd = -1;
let colRangeDroppedChildInd = -1;
let draggedInd = -1;
let droppedInd = -1;
let draggedGroup = null;
const pGroup = ths.colDataGroups.forEach((group, groupInd) => group.forEach((columnData, index) => {
const item = columnData.child;
if (item === ths.getRelatedHeader(ths.draggingColumn)) {
colRangeDraggedParentInd = groupInd;
colRangeDraggedChildInd = ths.getRangePosition(columnData); // index;
draggedInd = index;
draggedGroup = group;
}
if (item === ths.getRelatedHeader(ths.lastDraggedOverElement)) {
colRangeDroppedParentInd = groupInd;
colRangeDroppedChildInd = ths.getRangePosition(columnData); // index;
droppedInd = index;
}
}));
if (ths.draggingColumn === ths.lastDraggedOverElement) {
return;
}
let parentRanges = null;
const tempRanges = ths.colRangeGroups.concat();
let parentRangeIndex = -1;
tempRanges.sort((a, b) => b.length - a.length);
tempRanges.forEach((item, index) => {
if (!parentRanges && item.length < draggedGroup.length) {
parentRanges = item;
parentRangeIndex = ths.colRangeGroups.indexOf(item);
}
});
const fromOrder = (colRangeDraggedChildInd + 1);
const toOrder = (colRangeDroppedChildInd + 1);
// if has to stay within ranges, get ranges and swap
if (parentRanges !== null) {
ths.colRangeGroups[parentRangeIndex].forEach(range => {
const lowRange = range[0];
const highRange = range[1];
if (fromOrder >= lowRange && fromOrder < highRange && toOrder >= lowRange && toOrder < highRange) {
if (colRangeDraggedParentInd === colRangeDroppedParentInd) {
if (moveDirection === 1) {
ths.lastDraggedOverElement.classList.add('highlight-right');
}
else {
ths.lastDraggedOverElement.classList.add('highlight-left');
}
ths.elementsWithHighlight.push({ el: ths.lastDraggedOverElement, moveDirection });
}
}
});
}
else {
if (colRangeDraggedParentInd === colRangeDroppedParentInd) {
if (moveDirection === 1) {
ths.lastDraggedOverElement.classList.add('highlight-right');
}
else {
ths.lastDraggedOverElement.classList.add('highlight-left');
}
ths.elementsWithHighlight.push({ el: ths.lastDraggedOverElement, moveDirection });
}
}
}
if (!ths.dragging) {
return;
}
let mouseOffset = Math.round(event.clientX) - Math.round(ths.startX);
const widthsNeedUpdating = Math.round(event.clientX) !== ths.startX;
ths.startX = Math.round(event.clientX); // reset starting X
let newWidth = ths.totalComputedWidth + mouseOffset;
const allowableWidthChange = newWidth - ths.totalComputedMinWidth;
if (allowableWidthChange <= 0) {
return;
}
if (widthsNeedUpdating) {
ths.updateWidths(newWidth);
}
ths.columnResize.emit({
pointerEvent: event,
columnWidth: ths.totalComputedWidth,
columnMinWidth: ths.totalComputedMinWidth
});
const customResizeEvent = new CustomEvent(ColumnResizeEvent.ON_RESIZE, {
detail: {
pointerEvent: event,
columnWidth: ths.totalComputedWidth,
columnMinWidth: ths.totalComputedMinWidth
}
});
ths.elementRef.nativeElement.parentElement.dispatchEvent(customResizeEvent);
}
getLastVisibleColumnClass() {
let highestOrderIndex = 0;
let lastVisibleColumnClass = '';
this.gridTemplateClasses.forEach(className => {
const classNameIndex = this.gridTemplateClasses.indexOf(className);
const gridOrderIndex = this.gridOrder.indexOf(classNameIndex + 1);
if (this.hiddenColumnIndices.indexOf(gridOrderIndex + 1) === -1) {
if (gridOrderIndex > highestOrderIndex) {
highestOrderIndex = gridOrderIndex;
lastVisibleColumnClass = className;
}
}
});
return lastVisibleColumnClass;
}
getRangePosition(columnData) {
let subGroups = columnData.subGroups;
let child = columnData;
while (subGroups.length > 0) {
child = subGroups[0];
subGroups = child.subGroups;
}
return child.nthChild - 1;
}
columnIsHiddenWithClass(className) {
const classNameIndex = this.gridTemplateClasses.indexOf(className);
const gridOrderIndex = this.gridOrder.indexOf(classNameIndex + 1);
return this.hiddenColumnIndices.indexOf(gridOrderIndex + 1) !== -1;
}
getTotalGroupedColumnsVisible(sortableWidths) {
const len = sortableWidths.length;
let totalGroupedColumnsVisible = 0;
for (let i = 0; i < len; i++) {
const item = sortableWidths[i];
if (!this.columnIsHiddenWithClass(item.className)) {
totalGroupedColumnsVisible++;
}
}
return totalGroupedColumnsVisible;
}
getFirstGridOrderIndexAfterColumnGroup(sortableWidthGroup) {
let maxIndex = -1;
sortableWidthGroup.forEach(classItem => {
const columnIndx = this.gridTemplateClasses.indexOf(classItem.className);
const gridOrderIndex = this.gridOrder.indexOf(columnIndx + 1);
if (maxIndex < gridOrderIndex) {
maxIndex = gridOrderIndex;
}
});
return maxIndex + 1;
}
// returns a number in percent moved two decimal places over (10.245 is equal to 10.245%)
getPostColumnWidthTotal(startingIndex) {
let count = 0;
for (let i = startingIndex; i < this.gridOrder.length; i++) {
const clsIndex = this.gridOrder[i] - 1;
let perc = Number(this.classWidths[clsIndex].toString().replace('%', ''));
if (this.hiddenColumnIndices.indexOf(i + 1) === -1) {
count += perc;
}
}
return count;
}
// returns a number in percent moved two decimal places over (10.245 is equal to 10.245%)
getPostColumnMinimumWidthTotal(startingIndex) {
let count = 0;
for (let i = startingIndex; i < this.gridOrder.length; i++) {
const clsIndex = this.gridOrder[i] - 1;
let perc = Number(this.minWidths[clsIndex].toString().replace('%', ''));
if (this.hiddenColumnIndices.indexOf(i + 1) === -1) {
count += perc;
}
}
return count;
}
// returns a number in percent moved two decimal places over (10.245 is equal to 10.245%)
getPreviousColumnWidthTotal(sortableWidthGroup) {
let count = 0;
let minIndex = Infinity;
sortableWidthGroup.forEach(classItem => {
const columnIndx = this.gridTemplateClasses.indexOf(classItem.className);
const gridOrderIndex = this.gridOrder.indexOf(columnIndx + 1);
if (minIndex > gridOrderIndex) {
minIndex = gridOrderIndex;
}
});
for (let i = 0; i < minIndex; i++) {
const classIndx = this.gridOrder[i] - 1;
const wdth = Number(this.classWidths[classIndx].toString().replace('%', ''));
count += wdth;
}
return count;
}
updateWidthsInPercent(newWidth, sortableWidths, totalGroupedColumnsVisible, sortableWidthGroup) {
let totalTableWidth = this.viewport.clientWidth;
let newWidthInPercent = newWidth / totalTableWidth * 100;
const classMinWidths = sortableWidths.map((item) => item.minWidth);
const groupMinWidthCalc = classMinWidths.reduce((prev, curr) => prev + curr);
const firstGridOrderIndexAfterColumnGroup = this.getFirstGridOrderIndexAfterColumnGroup(sortableWidthGroup);
const colsPastMinWidthCalc = this.getPostColumnMinimumWidthTotal(firstGridOrderIndexAfterColumnGroup);
const colsPastMinWidthInPercent = colsPastMinWidthCalc / totalTableWidth * 100;
const colsPastWidthPerc = this.getPostColumnWidthTotal(firstGridOrderIndexAfterColumnGroup);
let prevColPercentTotal = 0;
prevColPercentTotal = this.getPreviousColumnWidthTotal(sortableWidthGroup);
const percentMoved = (prevColPercentTotal + newWidthInPercent + colsPastWidthPerc) - 100;
if (prevColPercentTotal + newWidthInPercent + colsPastMinWidthInPercent > 100) {
const actualPerCanMove = 100 - (prevColPercentTotal + colsPastMinWidthInPercent);
newWidthInPercent = actualPerCanMove;
}
if (newWidth < groupMinWidthCalc) {
newWidthInPercent = groupMinWidthCalc / totalTableWidth * 100;
}
sortableWidths.sort((item1, item2) => {
const wdth1 = item1.width;
const wdth2 = item2.width;
if (wdth1 === wdth2) {
return 0;
}
return wdth1 < wdth2 ? -1 : 1;
});
const mappedGroupWidthsInPixels = sortableWidths.map(item => item.width);
const totalPrevGroupWidths = mappedGroupWidthsInPixels.reduce((prev, curr) => prev + curr);
const dispersedPercs = sortableWidths.map(item => item.width / totalPrevGroupWidths);
const totalPercMoved = newWidthInPercent - (totalPrevGroupWidths / totalTableWidth * 100);
let additionalPercentFromColumnsToSmall = 0;
const sortableWidthsLen = sortableWidths.length;
sortableWidths.forEach((item, index) => {
const classIndex = this.gridTemplateClasses.indexOf(item.className);
const minWidthInPercent = this.minWidths[classIndex] / totalTableWidth * 100;
let calculatedPercent = dispersedPercs[index] * newWidthInPercent;
if (calculatedPercent < minWidthInPercent) {
additionalPercentFromColumnsToSmall += minWidthInPercent - calculatedPercent;
calculatedPercent = minWidthInPercent;
}
else {
const itemsRemaining = sortableWidthsLen - index - 1;
if (itemsRemaining !== 0) {
const extraAmtToRemove = additionalPercentFromColumnsToSmall / itemsRemaining;
calculatedPercent -= extraAmtToRemove;
additionalPercentFromColumnsToSmall -= extraAmtToRemove;
}
}
const colWidthInPercent = calculatedPercent.toString() + '%';
this.classWidths[classIndex] = colWidthInPercent;
});
let remainingPercToDisperse = totalPercMoved + additionalPercentFromColumnsToSmall;
const maxPercsCanMovePerCol = [];
for (let i = firstGridOrderIndexAfterColumnGroup; i < this.gridOrder.length; i++) {
const clsIndex = this.gridOrder[i] - 1;
let perc = Number(this.classWidths[clsIndex].toString().replace('%', ''));
let minWidthPerc = (this.minWidths[clsIndex] / totalTableWidth * 100);
if (this.hiddenColumnIndices.indexOf(i + 1) === -1) {
maxPercsCanMovePerCol.push({
moveAmt: percentMoved > 0 ? perc - minWidthPerc : perc,
classIndex: clsIndex
});
}
}
const totalPercsCanMove = maxPercsCanMovePerCol.reduce((prev, curr) => prev + curr.moveAmt, 0.0000001);
maxPercsCanMovePerCol.forEach((item) => {
const percOfTotalMovementAllowed = item.moveAmt / totalPercsCanMove;
const percOfRemainingDispersement = percOfTotalMovementAllowed * remainingPercToDisperse;
const perc = Number(this.classWidths[item.classIndex].toString().replace('%', ''));
const dispersedWidth = perc - percOfRemainingDispersement;
this.classWidths[item.classIndex] = dispersedWidth + '%';
});
newWidth = newWidthInPercent / 100 * totalTableWidth;
let amountMoved = newWidth - totalPrevGroupWidths;
amountMoved = Math.round(amountMoved * 100) / 100; // round to 2 decimal points
this.totalComputedWidth += amountMoved;
const gridTemplateColumns = this.constructGridTemplateColumns();
this.gridTemplateTypes.forEach(styleObj => {
styleObj.style.innerHTML = this.id + ' .' + this.reorderableClass + ' { display: grid; grid-template-columns:' + gridTemplateColumns + '; }';
this.setStyleContent();
});
}
updateWidthsInPixels(newWidth, sortableWidths, totalGroupedColumnsVisible) {
let remainingWidth = this.totalComputedWidth - newWidth;
sortableWidths.forEach((item) => {
const maxPercOfRemaining = 1 / totalGroupedColumnsVisible;
let amountMoved = 0;
const resizeID = this.id + ' .' + item.className;
if (item.width - item.minWidth < maxPercOfRemaining * remainingWidth) {
amountMoved = item.width - item.minWidth;
}
else {
amountMoved = maxPercOfRemaining * remainingWidth;
}
amountMoved = Math.round(amountMoved * 100) / 100; // round to 2 decimal points
const classIndex = this.gridTemplateClasses.indexOf(item.className);
this.classWidths[classIndex] = (item.width - amountMoved);
const markupItem = this.stylesByClass.filter(style => style.id === resizeID)[0];
let markup = resizeID + ' { width: ' + (item.width - amountMoved) + 'px }';
markupItem.markup = markup;
markupItem.width = (item.width - amountMoved).toString();
this.totalComputedWidth -= amountMoved;
});
const gridTemplateColumns = this.constructGridTemplateColumns();
this.gridTemplateTypes.forEach(styleObj => {
styleObj.style.innerHTML = this.id + ' .' + this.reorderableClass + ' { display: grid; grid-template-columns:' + gridTemplateColumns + '; }';
this.setStyleContent();
});
}
fitWidthsToOneHundredPercent() {
const numericalWidths = this.classWidths.map((wdth, index) => Number(wdth.replace('%', '')));
const widthTotal = numericalWidths.reduce((prev, wdth) => {
return prev + wdth;
}, 0);
const scaledWidths = numericalWidths.map((wdth, index) => {
return {
width: wdth / widthTotal * 100,
index: index
};
});
scaledWidths.forEach((item, index) => {
this.classWidths[item.index] = scaledWidths[item.index].width.toString() + '%';
});
}
updateWidths(newWidth) {
const currentWidths = this.currentClassesToResize.map((resizeClass) => {
return this.getClassWidthInPixels(resizeClass);
});
const sortableWidths = currentWidths.map((w, index) => {
return {
minWidth: this.minWidths[index],
width: w,
className: this.currentClassesToResize[index]
};
});
const visibleSortableWidths = sortableWidths.filter(item => {
return !this.columnIsHiddenWithClass(item.className);
});
const totalGroupedColumnsVisible = this.getTotalGroupedColumnsVisible(visibleSortableWidths);
if (this.resizeColumnWidthByPercent) {
this.updateWidthsInPercent(newWidth, visibleSortableWidths, totalGroupedColumnsVisible, sortableWidths);
}
else {
this.updateWidthsInPixels(newWidth, visibleSortableWidths, totalGroupedColumnsVisible);
}
this.generateWidthStyle();
}
generateWidthStyle() {
let innerHTML = '';
this.stylesByClass.forEach(item => {
innerHTML += item.markup;
});
this.widthStyle.innerHTML = innerHTML;
this.setStyleContent();
}
getResizableClasses(el) {
return el ? el['dataClasses'] : null;
}
setResizableStyles() {
const allElementsWithDataResizable = this.columnsWithDataClasses;
let el;
const classesUsed = [];
let fragment;
let style;
let styleText = '';
if (this.linkClass === undefined || this.gridService.linkedDirectiveObjs[this.linkClass] === undefined) {
fragment = document.createDocumentFragment();
style = document.createElement('style');
style.type = 'text/css';
}
else {
fragment = this.gridService.linkedDirectiveObjs[this.linkClass].widthStyleFragment;
style = this.gridService.linkedDirectiveObjs[this.linkClass].widthStyle;
}
let markup;
if (this.linkClass === undefined || this.gridService.linkedDirectiveObjs[this.linkClass] === undefined) {
for (let i = 0; i < allElementsWithDataResizable.length; i++) {
el = allElementsWithDataResizable[i];
const resizeClasses = this.getResizableClasses(el);
resizeClasses.forEach((resizeCls) => {
if (classesUsed.indexOf(resizeCls) === -1) {
const firstEl = this.elementRef.nativeElement.getElementsByClassName(resizeCls)[0];
const startingWidth = !!this.initialWidths[resizeCls] ? this.initialWidths[resizeCls] : firstEl.offsetWidth;
// Override percentage if we have widthPercent enabled
const startingWidthPercent = this.initialWidths[resizeCls];
const resizeID = this.id + ' .' + resizeCls;
if (this.resizeColumnWidthByPercent || startingWidth.toString().includes('%')) {
markup = resizeID + ' { width: ' + 100 + '%}';
this.resizeColumnWidthByPercent = true;
this.attachContentResizeSensor();
}
else {
markup = resizeID + ' { width: ' + startingWidth + 'px }';
}
styleText += markup;
this.stylesByClass.push({ style, id: resizeID, resizeClass: resizeCls, markup, width: startingWidth });
classesUsed.push(resizeCls);
}
});
}
}
else {
this.stylesByClass = this.gridService.linkedDirectiveObjs[this.linkClass].stylesByClass;
}
if (this.linkClass === undefined || this.gridService.linkedDirectiveObjs[this.linkClass] === undefined) {
style.innerHTML = styleText;
}
fragment.appendChild(style);
this.widthStyle = style;
this.widthStyleFragment = fragment;
this.addStyle(style, false);
if (this.linkClass) {
if (this.gridService.linkedDirectiveObjs[this.linkClass] === undefined) {
this.gridService.linkedDirectiveObjs[this.linkClass] = {};
this.gridService.linkedDirectiveObjs[this.linkClass].gridDirective = this;
this.gridService.linkedDirectiveObjs[this.linkClass].stylesByClass = this.stylesByClass;
}
this.gridService.linkedDirectiveObjs[this.linkClass].widthStyleFragment = fragment;
this.gridService.linkedDirectiveObjs[this.linkClass].widthStyle = style;
}
}
addStyle(style, addToContent = true) {
if (this.styleList.indexOf(style) === -1) {
this.styleList.push(style);
}
if (addToContent) {
this.setStyleContent();
}
}
setStyleContent() {
this.styleContent = '';
this.styleList.forEach(style => {
this.styleContent += style.innerHTML;
});
this.headStyle.innerHTML = this.styleContent;
}
moveStyleContentToProminent() {
this.headTag.appendChild(this.headStyle);
}
setReorderStyles() {
if (this.linkClass === undefined || (this.gridService.linkedDirectiveObjs[this.linkClass] && this.gridService.linkedDirectiveObjs[this.linkClass].reorderHighlightStyle === undefined)) {
const fragment = document.createDocumentFragment();
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = this.id + ' .highlight-left div:after, ' + this.id + ' .highlight-right div:after { height: 200px !important }';
fragment.appendChild(style);
this.reorderHighlightStyle = style;
this.reorderHighlightStyleFragment = fragment;
this.addStyle(style, false);
if (this.linkClass) {
this.gridService.linkedDirectiveObjs[this.linkClass].reorderHighlightStyle = this.reorderHighlightStyle;
this.gridService.linkedDirectiveObjs[this.linkClass].reorderHighlightStyleFragment = this.reorderHighlightStyleFragment;
}
}
else {
this.reorderHighlightStyle = this.gridService.linkedDirectiveObjs[this.linkClass].reorderHighlightStyle;
thi