From: Stephanie Leary Date: Wed, 29 Mar 2023 16:08:37 +0000 (+0000) Subject: HELP Column resize buttons X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=refs%2Fheads%2Fuser%2Fsleary%2Fgrid-classes-wip;p=working%2FEvergreen.git HELP Column resize buttons Signed-off-by: Stephanie Leary --- 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 47e4d7f428..581ac750b4 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; } @@ -97,6 +98,27 @@ table.table.eg-grid { font-size: 14px; } +.col-resize { + display: block; + background: transparent; + border: 0; + position: absolute; + top: 0; + right: 0; + width: 5px; + height: 100%; + cursor: col-resize; +} + +.col-resize:hover, +.col-resize:active, +.col-resize:focus, +.resizing { + background-color: rgba(0,0,225,0.05); + border-right: 2px solid blue; + cursor: col-resize; +} + .eg-grid-toolbar { display: flex; } 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..d930ed7e74 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,11 +1028,35 @@ 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': + //str = this.insert_wbr(str); + break; + default: break; + } + return str; } } } + insert_wbr(txt: string): string { + const doubleSlash = txt.split('//'); + const formatted = doubleSlash.map(str => + // Insert a word break opportunity after a colon + str.replace(/(?:)/giu, '$1') + // Before a single slash, tilde, at symbol, comma, hyphen, underline, question mark, number sign, or percent symbol + .replace(/(?[/~@,\-_?#%])/giu, '$1') + // Before and after an equals sign, period, or ampersand + .replace(/(?[=.&])/giu, '$1') + // Reconnect the strings with word break opportunities after double slashes + ).join('//'); + + return formatted; + } + selectOneRow(index: any) { this.rowSelector.clear(); this.rowSelector.select(index); @@ -1423,6 +1453,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); + }); + } }