<!--
tabindex=1 so the grid body can capture keyboard events.
-->
-<div class="eg-grid-body" tabindex="1" (keydown)="onGridKeyDown($event)">
- <div role="row" class="eg-grid-row eg-grid-body-row {{context.rowClassCallback(row)}}"
+<div class="eg-grid-row eg-grid-body-row {{context.rowClassCallback(row)}}"
[ngClass]="{'selected': context.rowSelector.contains(context.getRowIndex(row))}"
- *ngFor="let row of context.dataSource.getPageOfRows(context.pager); let idx = index">
-
+ *ngFor="let row of context.dataSource.getPageOfRows(context.pager); let idx = index"(keydown)="onGridKeyDown($event)" >
<ng-container *ngIf="!context.disableSelect">
- <div class="eg-grid-cell eg-grid-checkbox-cell eg-grid-cell-skinny">
+ <td class="eg-grid-cell eg-grid-checkbox-cell eg-grid-cell-skinny">
<input type='checkbox' [(ngModel)]="context.rowSelector.indexes[context.getRowIndex(row)]"
i18n-aria-label="e.g. Row 13" attr.aria-label="Row {{context.pager.rowNumber(idx)}}"
#rowContextMenu="ngbPopover"
[ngbPopover]="contextMenu"
placement="right"
triggers="manual">
- </div>
+ </td>
</ng-container>
- <div class="eg-grid-cell eg-grid-number-cell eg-grid-cell-skinny-2">
+ <td class="eg-grid-cell eg-grid-number-cell eg-grid-cell-skinny-2">
{{context.pager.rowNumber(idx)}}
- </div>
- <div *ngIf="context.rowFlairIsEnabled" class="eg-grid-cell eg-grid-flair-cell">
+ </td>
+ <td *ngIf="context.rowFlairIsEnabled" class="eg-grid-cell eg-grid-flair-cell">
<!-- using *ngIf allows us to assign the flair callback to a value,
obviating the need for multiple calls of the same function -->
<ng-container *ngIf="context.rowFlairCallback(row); let flair">
</span>
</ng-container>
</ng-container>
- </div>
+ </td>
<!-- contextMenu applied to cells instead of rows so the position
of the popover is close to the mouse. As of writing, no way
to position the popover at the mouse -->
- <div role="gridcell" class="eg-grid-cell eg-grid-body-cell"
+ <td role="gridcell" class="eg-grid-cell eg-grid-body-cell"
[ngStyle]="{flex:col.flex}"
[ngClass]="{'eg-grid-cell-overflow': context.overflowCells}"
(dblclick)="onRowDblClick(row)"
<eg-grid-body-cell [context]="context" [row]="row" [column]="col">
</eg-grid-body-cell>
- </div>
- </div>
-</div>
-
+ </td>
+ </div>
\ No newline at end of file
import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
@Component({
- selector: 'eg-grid-body',
+ selector: 'eg-grid-body, [eg-grid-body]',
templateUrl: './grid-body.component.html'
})
-<div *ngIf="isVisible" class="eg-grid-column-width-config">
- <div class="eg-grid-row">
- <div class="eg-grid-column-width-header" i18n>Expand</div>
- <div *ngFor="let col of columnSet.displayColumns()"
+
+ <tr class="eg-grid-row" *ngIf="isVisible" class="eg-grid-column-width-config">
+ <td class="eg-grid-column-width-header" i18n>Expand</td>
+ <td *ngFor="let col of columnSet.displayColumns()"
class="eg-grid-cell text-center" [ngStyle]="{flex:col.flex}">
<a (click)="expandColumn(col)" title="Expand Column" i18n-title>
<span class="material-icons eg-grid-column-width-icon">call_made</span>
</a>
- </div>
- </div>
- <div class="eg-grid-row">
- <div class="eg-grid-column-width-header" i18n>Shrink</div>
- <div *ngFor="let col of columnSet.displayColumns()"
+ </td>
+ </tr>
+ <tr class="eg-grid-row" *ngIf="isVisible" class="eg-grid-column-width-config">
+ <td class="eg-grid-column-width-header" i18n>Shrink</td>
+ <td *ngFor="let col of columnSet.displayColumns()"
class="eg-grid-cell text-center" [ngStyle]="{flex:col.flex}">
<a (click)="shrinkColumn(col)" title="Shrink Column" i18n-title>
<span class="material-icons eg-grid-column-width-icon">call_received</span>
</a>
- </div>
- </div>
-</div>
+ </td>
+ </tr>
import {GridContext, GridColumn, GridColumnSet} from './grid';
@Component({
- selector: 'eg-grid-column-width',
+ selector: 'eg-grid-column-width, [eg-grid-column-width]',
templateUrl: './grid-column-width.component.html'
})
-<div row="row" class="eg-grid-row eg-grid-header-row">
+
<ng-container *ngIf="!context.disableSelect">
- <div role="columnheader"
+ <th resizable role="columnheader" scope="col"
class="eg-grid-cell eg-grid-header-cell eg-grid-checkbox-cell eg-grid-cell-skinny">
<input type='checkbox' (click)="handleBatchSelect($event)"
i18n-aria-label aria-label="All rows"
[(ngModel)]="batchRowCheckbox">
- </div>
+ </th>
</ng-container>
- <div role="columnheader"
+ <th resizable role="columnheader" scope="col"
class="eg-grid-cell eg-grid-header-cell eg-grid-number-cell eg-grid-cell-skinny">
<span i18n="number|Row Number Header">#</span>
- </div>
- <div *ngIf="context.rowFlairIsEnabled"
+ </th>
+ <th resizable *ngIf="context.rowFlairIsEnabled" scope="col"
role="columnheader"
class="eg-grid-cell eg-grid-header-cell eg-grid-flair-cell">
<span class="material-icons">notifications</span>
- </div>
- <div role="columnheader"
+ </th>
+ <th resizable role="columnheader" scope="col"
*ngFor="let col of context.columnSet.displayColumns()"
draggable="true"
(dragstart)="dragColumn = col"
*ngIf="isColumnSorting(col, 'DESC')">arrow_downwards</span>
</a>
<span *ngIf="!col.isSortable">{{col.label}}</span>
- </div>
-</div>
-<div *ngIf="context.isFilterable"
- class="eg-grid-row eg-grid-filter-controls-row">
- <ng-container *ngIf="!context.disableSelect">
- <div class="eg-grid-cell eg-grid-header-cell eg-grid-cell-skinny"></div>
- </ng-container>
- <div class="eg-grid-cell eg-grid-header-cell eg-grid-cell-skinny"></div>
- <div *ngIf="context.rowFlairIsEnabled"
- class="eg-grid-cell eg-grid-header-cell"></div>
-
- <div *ngFor="let col of context.columnSet.displayColumns()"
- class="eg-grid-cell eg-grid-filter-control-cell" [ngStyle]="{flex:col.flex}">
- <eg-grid-filter-control [context]="context" [col]="col"></eg-grid-filter-control>
- </div>
-</div>
+ </th>
import {GridContext} from '@eg/share/grid/grid';
@Component({
- selector: 'eg-grid-print',
+ selector: 'eg-grid-print, [eg-grid-print]',
templateUrl: './grid-print.component.html'
})
.eg-grid {
- width: 100%;
- color: rgba(0,0,0,.87);
+ color: rgba(0,0,0,.87);
}
.eg-grid-row {
- display: flex;
border-bottom: 1px solid rgba(0,0,0,.12);
padding-left: 10px;
padding-right: 10px;
white-space: normal;
}
+.eg-grid-resizable th {
+ resize: horizontal;
+ overflow: auto;
+}
+
.eg-grid-body-cell {
}
-
-<div class="eg-grid" role="grid">
-
- <eg-grid-toolbar #toolbar
+<eg-grid-toolbar #toolbar
[gridContext]="context"
[gridPrinter]="gridPrinter"
[colWidthConfig]="colWidthConfig"
[disableSaveSettings]="!persistKey || ('disabled' === persistKey)">
- </eg-grid-toolbar>
+</eg-grid-toolbar>
+<table class="eg-grid" role="grid">
- <div #egGridStickyHeader [ngClass]="{'eg-grid-sticky-header' : context.stickyGridHeader}">
- <eg-grid-header [context]="context"></eg-grid-header>
- </div>
+ <thead #egGridStickyHeader class="eg-grid-resizable" [ngClass]="{'eg-grid-sticky-header' : context.stickyGridHeader}">
+ <tr row="row" class="eg-grid-row eg-grid-header-row"><eg-grid-header [context]="context"></eg-grid-header></tr>
+ </thead>
- <eg-grid-column-width #colWidthConfig [gridContext]="context">
+
+
+<eg-grid-column-width #colWidthConfig [gridContext]="context">
</eg-grid-column-width>
<eg-grid-print #gridPrinter [gridContext]="context">
</ng-container>
</div>
</ng-container>
-
- <eg-grid-body [context]="context"></eg-grid-body>
-</div>
+ <tbody class="eg-grid-body" tabindex="1">
+ <tr role="row" eg-grid-body [context]="context"></tr>
+ </tbody>
+</table>
import {GridPrintComponent} from './grid-print.component';
import {GridFilterControlComponent} from './grid-filter-control.component';
import {GridToolbarActionsEditorComponent} from './grid-toolbar-actions-editor.component';
+import {ResizableComponent} from './resizable.component';
+import {ResizableDirective} from './resizable.directive';
@NgModule({
GridColumnWidthComponent,
GridPrintComponent,
GridFilterControlComponent,
- GridToolbarActionsEditorComponent
+ GridToolbarActionsEditorComponent,
+ ResizableComponent,
+ ResizableDirective
],
imports: [
EgCommonModule,
GridColumnComponent,
GridToolbarButtonComponent,
GridToolbarCheckboxComponent,
- GridToolbarActionComponent
+ GridToolbarActionComponent,
+ ResizableComponent
],
providers: [
]
--- /dev/null
+<div class="resizable-wrapper">\r
+ <div class="resizable-content">\r
+ <ng-content></ng-content>\r
+ </div>\r
+ <div class="resizable-bar" (resizable)="onResize($event)"></div>\r
+</div>
\ No newline at end of file
--- /dev/null
+import { Component, ElementRef, HostBinding } from "@angular/core";\r
+\r
+@Component({\r
+ selector: "th[resizable]",\r
+ templateUrl: "./resizable.component.html",\r
+ styleUrls: ["./resizable.style.less"]\r
+})\r
+export class ResizableComponent {\r
+ @HostBinding("style.width.px")\r
+ width: number | null = null;\r
+\r
+ onResize(width: number) {\r
+ this.width = width;\r
+ }\r
+}\r
--- /dev/null
+:host {\r
+ &:last-child .bar {\r
+ display: none;\r
+ }\r
+}\r
+\r
+.resizable-wrapper {\r
+ display: flex;\r
+ justify-content: flex-end;\r
+}\r
+\r
+.resizable-content {\r
+ flex: 2;\r
+}\r
+\r
+.resizable-bar {\r
+ position: absolute;\r
+ top: 0;\r
+ bottom: 0;\r
+ width: 2px;\r
+ margin: 0 -16px 0 16px;\r
+ justify-self: flex-end;\r
+ border-left: 2px solid transparent;\r
+ border-right: 2px solid transparent;\r
+ background: blueviolet;\r
+ background-clip: content-box;\r
+ cursor: ew-resize;\r
+ opacity: 0;\r
+ transition: opacity .3s;\r
+\r
+ &:hover,\r
+ &:active {\r
+ opacity: 1;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+import { DOCUMENT } from "@angular/common";\r
+import { Directive, ElementRef, Inject, Output } from "@angular/core";\r
+import {\r
+ distinctUntilChanged, map, switchMap, takeUntil, tap\r
+} from "rxjs/operators";\r
+import { fromEvent } from "rxjs";\r
+\r
+@Directive({\r
+ selector: "[resizable]"\r
+})\r
+export class ResizableDirective {\r
+ @Output()\r
+ readonly resizable = fromEvent<MouseEvent>(\r
+ this.elementRef.nativeElement,\r
+ "mousedown"\r
+ ).pipe(\r
+ tap(e => e.preventDefault()),\r
+ switchMap(() => {\r
+ const { width, right } = this.elementRef.nativeElement\r
+ .closest("th")\r
+ .getBoundingClientRect();\r
+\r
+ return fromEvent<MouseEvent>(this.documentRef, "mousemove").pipe(\r
+ map(({ clientX }) => width + clientX - right),\r
+ distinctUntilChanged(),\r
+ takeUntil(fromEvent(this.documentRef, "mouseup"))\r
+ );\r
+ })\r
+ );\r
+\r
+ constructor(\r
+ @Inject(DOCUMENT) private readonly documentRef: Document,\r
+ @Inject(ElementRef)\r
+ private readonly elementRef: ElementRef<HTMLElement>\r
+ ) {}\r
+}\r
--- /dev/null
+:host {\r
+ &:last-child .bar {\r
+ display: none;\r
+ }\r
+}\r
+\r
+.resizable-wrapper {\r
+ display: flex;\r
+ justify-content: flex-end;\r
+}\r
+\r
+.resizable-content {\r
+ flex: 1;\r
+}\r
+\r
+.resizable-bar {\r
+ position: absolute;\r
+ top: 0;\r
+ bottom: 0;\r
+ width: 2px;\r
+ margin: 0 -16px 0 16px;\r
+ justify-self: flex-end;\r
+ border-left: 2px solid transparent;\r
+ border-right: 2px solid transparent;\r
+ background: blueviolet;\r
+ background-clip: content-box;\r
+ cursor: ew-resize;\r
+ opacity: 0;\r
+ transition: opacity .3s;\r
+\r
+ &:hover,\r
+ &:active {\r
+ opacity: 1;\r
+ }\r
+}
\ No newline at end of file