From 82e65ed36ce76272df750f9cf0430ba3a816284c Mon Sep 17 00:00:00 2001 From: Stephanie Leary Date: Mon, 22 May 2023 15:26:34 +0000 Subject: [PATCH] table grid column resize buttons Signed-off-by: Stephanie Leary --- .../src/app/share/grid/grid-header.component.html | 4 + .../src/eg2/src/app/share/grid/grid.component.css | 1 + .../src/eg2/src/app/share/grid/grid.component.ts | 8 +- Open-ILS/src/eg2/src/app/share/grid/grid.ts | 102 ++++++++++++++++++++- 4 files changed, 109 insertions(+), 6 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-header.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-header.component.html index c60336705c..c160891786 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid-header.component.html +++ b/Open-ILS/src/eg2/src/app/share/grid/grid-header.component.html @@ -39,5 +39,9 @@ + + \ No newline at end of file diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.css b/Open-ILS/src/eg2/src/app/share/grid/grid.component.css index efd2ac9ac6..b34481688f 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.css +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.css @@ -75,6 +75,7 @@ table.table.eg-grid { .eg-grid-header-cell { font-weight: 600; + position: relative; white-space: normal; } diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts index 0d8fcc1c1a..95a9bfaa25 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.component.ts @@ -1,5 +1,5 @@ import {Component, Input, Output, OnInit, AfterViewInit, EventEmitter, - OnDestroy, ViewChild, ViewEncapsulation} from '@angular/core'; + OnDestroy, ViewChild, ViewEncapsulation, Renderer2, ElementRef} from '@angular/core'; import {IdlService} from '@eg/core/idl.service'; import {OrgService} from '@eg/core/org.service'; import {ServerStoreService} from '@eg/core/server-store.service'; @@ -145,10 +145,12 @@ export class GridComponent implements OnInit, AfterViewInit, OnDestroy { private idl: IdlService, private org: OrgService, private store: ServerStoreService, - private format: FormatService + private format: FormatService, + private renderer: Renderer2, + private gridTable: ElementRef ) { this.context = - new GridContext(this.idl, this.org, this.store, this.format); + new GridContext(this.idl, this.org, this.store, this.format, this.renderer, this.gridTable); this.onRowActivate = new EventEmitter(); this.onRowClick = new EventEmitter(); this.rowSelectionChange = new EventEmitter(); diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid.ts b/Open-ILS/src/eg2/src/app/share/grid/grid.ts index 04d19c4816..2fb783fec4 100644 --- a/Open-ILS/src/eg2/src/app/share/grid/grid.ts +++ b/Open-ILS/src/eg2/src/app/share/grid/grid.ts @@ -1,7 +1,7 @@ /** * Collection of grid related classses and interfaces. */ -import {TemplateRef, EventEmitter, QueryList} from '@angular/core'; +import {TemplateRef, EventEmitter, AfterViewInit, QueryList, Renderer2, ElementRef} from '@angular/core'; import {Observable, Subscription, empty} from 'rxjs'; import {IdlService, IdlObject} from '@eg/core/idl.service'; import {OrgService} from '@eg/core/org.service'; @@ -663,6 +663,9 @@ export class GridContext { showDeclaredFieldsOnly: boolean; cellTextGenerator: GridCellTextGenerator; reloadOnColumnChange: boolean; + ColX: number; + ColW: number; + charWidth: number; // Allow calling code to know when the select-all-rows-in-page // action has occurred. @@ -680,7 +683,9 @@ export class GridContext { idl: IdlService, org: OrgService, store: ServerStoreService, - format: FormatService) { + format: FormatService, + private renderer: Renderer2, + private gridTable: ElementRef) { this.idl = idl; this.org = org; @@ -705,6 +710,7 @@ export class GridContext { this.pager.limit = this.disablePaging ? MAX_ALL_ROW_COUNT : 10; } this.generateColumns(); + this.generateColumnResizers(); } // Load initial settings and data. @@ -1022,7 +1028,16 @@ export class GridContext { if (col.cellTemplate) { return ''; // avoid 'undefined' values } else { - return this.getRowColumnValue(row, col); + let str = this.getRowColumnValue(row, col); + switch (col.name) { + case 'name': + case 'url': + case 'email': + //TODO: insert around punctuation + break; + default: break; + } + return str; } } } @@ -1423,6 +1438,87 @@ export class GridContext { // smush into string and replace dots in name and path return classes.join(' ').replaceAll('.', ''); } + + generateColumnResizers() { + if (!this.gridTable) { return; } + + const cols = this.gridTable.nativeElement.querySelectorAll('th'); + cols.forEach((col) => { + // Find resizer element + const resizer = col.nativeElement.querySelector('button.col-resize'); + if (resizer) { + this.createResizableColumn(col, resizer); + } + }); + } + + createResizableColumn(col, resizer) { + // Track the current position of mouse + let x = 0; + let w = 0; + + const mouseDownHandler = function ($event) { + // Get the current mouse position + x = $event.clientX; + + // Calculate the current width of column + const styles = window.getComputedStyle(col); + w = parseInt(styles.width); + + // Attach listeners for document's events + document.addEventListener('pointermove', mouseMoveHandler); + document.addEventListener('pointerup', mouseUpHandler); + }; + + const mouseMoveHandler = function ($event) { + // Determine how far the mouse has been moved + const dx = $event.clientX - x; + + // Update the width of column + col.style.width = `${w + dx}px`; + }; + + // When user releases the mouse, remove the existing event listeners + // also recalculate grabber height + // also save column width to user prefs + const mouseUpHandler = function ($event) { + document.removeEventListener('pointermove', mouseMoveHandler); + document.removeEventListener('pointerup', mouseUpHandler); + + /* Recalculate grabber height in case cells reflowed */ + this.setColumnHandleHeight(this.gridTable); + + // TODO: save column width in ch + }; + + resizer.addEventListener('pointerdown', mouseDownHandler); + + // Handle keyboard events + + resizer.addEventListener("keydown", ($event) => { + const th = $event.currentTarget.closest("th"); + + // TODO: find out if screen reader users would prefer we use a combo (probably) + if ($event.code == "ArrowLeft") { + th.style.width = (th.offsetWidth - this.charWidth) + 'px'; + } + if ($event.code == "ArrowRight") { + th.style.width = (th.offsetWidth + this.charWidth) + 'px'; + } + + /* Recalculate grabber height in case cells reflowed */ + this.setColumnHandleHeight(this.gridTable); + }); + } + + setColumnHandleHeight($event) { + /* Recalculate all handle heights in case cells reflowed */ + const tableHeight = this.gridTable.nativeElement.offsetHeight + 'px'; + console.log("table height is " + tableHeight); + this.gridTable.nativeElement.querySelectorAll('.col-resize').forEach((btn) => { + this.renderer.setStyle(btn, 'height', tableHeight); + }); + } } -- 2.11.0