Skip to content

Commit

Permalink
fix(ngrid/target-events): keyboard focus does not sync when virtual s…
Browse files Browse the repository at this point in the history
…crolling

fixex #117
  • Loading branch information
shlomiassaf committed Dec 3, 2020
1 parent 19bbba6 commit b488d91
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 26 deletions.
2 changes: 1 addition & 1 deletion libs/ngrid/src/lib/grid/context/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ export class ContextApi<T = any> {
}

private getViewRect(): ClientRect | DOMRect {
return this.extApi.grid.viewport.elementRef.nativeElement.getBoundingClientRect();
return this.extApi.grid.viewport.element.getBoundingClientRect();
}

private emitFocusChanged(curr: PblNgridFocusChangedEvent['curr']): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class PblVirtualScrollForOf<T> implements CollectionViewer, NgeVirtualTab

if (FIXED_HEADER_MODE) {
let offset = 0;
const viewPort = this.viewport.elementRef.nativeElement;
const viewPort = this.viewport.element;
const metaRowStickyScroll = new MetaRowStickyScroll(this.viewport, viewPort, { header: this.header, footer: this.footer });
let scrollPosition: number;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<p class="cdk-virtual-scroll-inner-width"></p>
<p #innerBoxHelper class="cdk-virtual-scroll-inner-width"></p>
<ng-content select=".cdk-virtual-scroll-before-content-wrapper"></ng-content>
<!--
Wrap the rendered content in an element that will be used to offset it based on the scroll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Inject,
Input,
ChangeDetectorRef,
ViewChild,
ViewEncapsulation,
NgZone,
Output,
Expand Down Expand Up @@ -60,7 +61,7 @@ function resolveScrollStrategy(config: PblNgridConfigService, scrollStrategy?: V
selector: 'pbl-cdk-virtual-scroll-viewport',
templateUrl: 'virtual-scroll-viewport.component.html',
styleUrls: [ './virtual-scroll-viewport.component.scss' ],
host: { // tslint:disable-line:use-host-property-decorator
host: { // tslint:disable-line: no-host-metadata-property
class: 'cdk-virtual-scroll-viewport',
'[class.cdk-virtual-scroll-disabled]': '!enabled',
'[class.cdk-virtual-scroll-orientation-horizontal]': 'orientation === "horizontal"',
Expand All @@ -74,6 +75,9 @@ export class PblCdkVirtualScrollViewportComponent extends CdkVirtualScrollViewpo
get isScrolling(): boolean { return this._isScrolling; }
readonly enabled: boolean;

/** @internal */
@ViewChild('innerBoxHelper', { static: true }) _innerBoxHelper: ElementRef<HTMLElement>;

/**
* Emits the offset (in pixels) of the rendered content every time it changes.
* The emission is done OUTSIDE of angular (i.e. no change detection cycle is triggered).
Expand Down Expand Up @@ -138,28 +142,38 @@ export class PblCdkVirtualScrollViewportComponent extends CdkVirtualScrollViewpo
return (this.pblScrollStrategy as PblCdkVirtualScrollDirective).wheelMode || this.wheelModeDefault || 'passive';
}

get getBoundingClientRects() {
const innerBox = this._innerBoxHelper.nativeElement.getBoundingClientRect();
const outerBox = this.element.getBoundingClientRect();
return {
innerBox,
outerBox,
scrollBarWidth: outerBox.width - innerBox.width,
scrollBarHeight: outerBox.height - innerBox.height,
}
}

get innerWidth(): number {
const innerWidthHelper = this.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-inner-width') as HTMLElement;
return innerWidthHelper.getBoundingClientRect().width;
return this._innerBoxHelper.nativeElement.getBoundingClientRect().width;
}

get outerWidth(): number {
return this.elementRef.nativeElement.getBoundingClientRect().width;
return this.element.getBoundingClientRect().width;
}

get innerHeight(): number {
const innerWidthHelper = this.elementRef.nativeElement.querySelector('.cdk-virtual-scroll-inner-width') as HTMLElement;
return innerWidthHelper.getBoundingClientRect().height;
return this._innerBoxHelper.nativeElement.getBoundingClientRect().height;
}

get outerHeight(): number {
return this.elementRef.nativeElement.getBoundingClientRect().height;
return this.element.getBoundingClientRect().height;
}

get scrollWidth(): number {
return this.elementRef.nativeElement.scrollWidth;
return this.element.scrollWidth;
}

readonly element: HTMLElement;
readonly _minWidth$: Observable<number>;

private offsetChange$ = new Subject<number>();
Expand All @@ -186,6 +200,7 @@ export class PblCdkVirtualScrollViewportComponent extends CdkVirtualScrollViewpo
dir,
scrollDispatcher,
viewportRuler);
this.element = elementRef.nativeElement;

if (config.has('virtualScroll')) {
this.wheelModeDefault = config.get('virtualScroll').wheelMode;
Expand Down Expand Up @@ -246,7 +261,7 @@ export class PblCdkVirtualScrollViewportComponent extends CdkVirtualScrollViewpo

// TODO(shlomiassaf)[perf, 3]: run this once... (aggregate all calls within the same animation frame)
requestAnimationFrame(() => {
this.scrollHeight = this.elementRef.nativeElement.scrollHeight; //size;
this.scrollHeight = this.element.scrollHeight; //size;
this.updateFiller();
// We must trigger a change detection cycle because the filler div element is updated through bindings
this.cdr.markForCheck();
Expand Down Expand Up @@ -327,6 +342,14 @@ export class PblCdkVirtualScrollViewportComponent extends CdkVirtualScrollViewpo
}
}

getScrollBarThickness(location: 'horizontal' | 'vertical') {
switch (location) {
case 'horizontal':
return this.outerHeight - this.innerHeight;
case 'vertical':
return this.outerWidth - this.innerWidth;
}
}
/**
* Init the scrolling watcher which track scroll events an emits `scrolling` and `scrollFrameRate` events.
*/
Expand Down
18 changes: 15 additions & 3 deletions libs/ngrid/src/lib/grid/ngrid.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ export class PblNgridComponent<T = any> implements AfterContentInit, AfterViewIn
this.listenToResize();

// The following code will catch context focused events, find the HTML element of the cell and focus it.
// TODO: Move to use rowApi with Cells instead of accessing the view
this.contextApi.focusChanged
.subscribe( event => {
if (event.curr) {
Expand All @@ -350,9 +351,20 @@ export class PblNgridComponent<T = any> implements AfterContentInit, AfterViewIn
const view = this._cdkTable._rowOutlet.viewContainer.get(rowContext.index) as EmbeddedViewRef<any>;
if (view) {
const cellViewIndex = this.columnApi.renderIndexOf(this.columnApi.columns[event.curr.colIndex])
const cellElement = view.rootNodes[0].querySelectorAll('pbl-ngrid-cell')[cellViewIndex];
const cellElement: HTMLElement = view.rootNodes[0].querySelectorAll('pbl-ngrid-cell')[cellViewIndex];
if (cellElement) {
cellElement.focus();
cellElement.focus({preventScroll: true});
if (this.viewport.enabled) {
const elBox = cellElement.getBoundingClientRect();
const viewBox = this.viewport.element.getBoundingClientRect();
if (elBox.top < viewBox.top) { // out from top
const offset = elBox.top - viewBox.top;
this.viewport.scrollToOffset(this.viewport.measureScrollOffset() + offset);
} else if (elBox.bottom > viewBox.bottom) { // out from bottom
const offset = elBox.bottom - (viewBox.bottom - this.viewport.getScrollBarThickness('horizontal'));
this.viewport.scrollToOffset(this.viewport.measureScrollOffset() + offset);
}
}
}
}
}
Expand Down Expand Up @@ -561,7 +573,7 @@ export class PblNgridComponent<T = any> implements AfterContentInit, AfterViewIn
unrx(this, value)
)
.subscribe(() => {
const el = this.viewport.elementRef.nativeElement;
const el = this.viewport.element;
if (this.ds.renderLength > 0 && this._fallbackMinHeight > 0) {
const h = Math.min(this._fallbackMinHeight, this.viewport.measureRenderedContentSize());
el.style.minHeight = h + 'px';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,33 +65,39 @@ function createHandlers(targetEvents: PblNgridTargetEventsPlugin) {
const sourceCell = event.cellTarget;

let coeff: 1 | -1 = 1;
let axis: 'h' | 'v';
let isHorizontal = false;

switch (source.keyCode) {
case UP_ARROW:
coeff = -1;
case DOWN_ARROW: // tslint:disable-line: no-switch-case-fall-through
axis = 'v';
break;
case LEFT_ARROW:
coeff = -1;
case RIGHT_ARROW: // tslint:disable-line: no-switch-case-fall-through
axis = 'h';
isHorizontal = true;
break;
default:
return;
}

const cellContext = contextApi.getCell(sourceCell);
const activeFocus = contextApi.focusedCell || {
rowIdent: cellContext.rowContext.identity,
colIndex: cellContext.index,
};
event.source.preventDefault();
event.source.stopPropagation();


let activeFocus: GridDataPoint = contextApi.focusedCell;
if (!activeFocus) {
const cellContext = contextApi.getCell(sourceCell);
activeFocus = {
rowIdent: cellContext.rowContext.identity,
colIndex: cellContext.index,
};
}

if (!!source.shiftKey) {
handleSelectionChangeByArrows(activeFocus, axis === 'h' ? [coeff, 0] : [0, coeff]);
handleSelectionChangeByArrows(activeFocus, isHorizontal ? [coeff, 0] : [0, coeff]);
} else {
handleSingleItemFocus(activeFocus, axis === 'h' ? [coeff, 0] : [0, coeff])
handleSingleItemFocus(activeFocus, isHorizontal ? [coeff, 0] : [0, coeff])
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { fromEvent, timer, Observer, ReplaySubject } from 'rxjs';
import { bufferWhen, debounce, map, filter, takeUntil } from 'rxjs/operators';
import { Directive, EventEmitter, OnDestroy, ChangeDetectorRef, Injector } from '@angular/core';
import { Directive, EventEmitter, OnDestroy, Injector } from '@angular/core';

import { PblNgridComponent, PblNgridPluginController, PblColumn } from '@pebula/ngrid';

Expand Down

0 comments on commit b488d91

Please sign in to comment.