Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(material-experimental/column-resize): Add support for "lazy" rat… #30120

Merged
merged 1 commit into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,7 @@
"matchPackageNames": ["*"]
},
{
"matchPackageNames": [
"@angular/ng-dev",
"@angular/build-tooling",
"angular/dev-infra"
],
"matchPackageNames": ["@angular/ng-dev", "@angular/build-tooling", "angular/dev-infra"],
"groupName": "angular shared dev-infra code",
"enabled": true
},
Expand Down
27 changes: 26 additions & 1 deletion src/cdk-experimental/column-resize/column-resize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {AfterViewInit, Directive, ElementRef, inject, NgZone, OnDestroy} from '@angular/core';
import {
AfterViewInit,
Directive,
ElementRef,
inject,
InjectionToken,
Input,
NgZone,
OnDestroy,
} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {fromEvent, merge, Subject} from 'rxjs';
import {filter, map, mapTo, pairwise, startWith, take, takeUntil} from 'rxjs/operators';
Expand All @@ -20,6 +29,15 @@ import {HeaderRowEventDispatcher} from './event-dispatcher';
const HOVER_OR_ACTIVE_CLASS = 'cdk-column-resize-hover-or-active';
const WITH_RESIZED_COLUMN_CLASS = 'cdk-column-resize-with-resized-column';

/** Configurable options for column resize. */
export interface ColumnResizeOptions {
liveResizeUpdates?: boolean; // Defaults to true.
}

export const COLUMN_RESIZE_OPTIONS = new InjectionToken<ColumnResizeOptions>(
'CdkColumnResizeOptions',
);

/**
* Base class for ColumnResize directives which attach to mat-table elements to
* provide common events and services for column resizing.
Expand All @@ -45,6 +63,13 @@ export abstract class ColumnResize implements AfterViewInit, OnDestroy {
/** The id attribute of the table, if specified. */
id?: string;

/**
* Whether to update the column's width continuously as the mouse position
* changes, or to wait until mouseup to apply the new size.
*/
@Input() liveResizeUpdates =
inject(COLUMN_RESIZE_OPTIONS, {optional: true})?.liveResizeUpdates ?? true;

ngAfterViewInit() {
this.elementRef.nativeElement!.classList.add(this.getUniqueCssClass());

Expand Down
46 changes: 34 additions & 12 deletions src/cdk-experimental/column-resize/overlay-handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
protected abstract readonly resizeRef: ResizeRef;
protected abstract readonly styleScheduler: _CoalescedStyleScheduler;

private _cumulativeDeltaX = 0;

ngAfterViewInit() {
this._listenForMouseEvents();
}
Expand Down Expand Up @@ -101,6 +103,7 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
let originOffset = this._getOriginOffset();
let size = initialSize;
let overshot = 0;
this._cumulativeDeltaX = 0;

this.updateResizeActive(true);

Expand All @@ -125,6 +128,14 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
.subscribe(([prevX, currX]) => {
let deltaX = currX - prevX;

if (!this.resizeRef.liveUpdates) {
this._cumulativeDeltaX += deltaX;
const sizeDelta = this._computeNewSize(size, this._cumulativeDeltaX) - size;
this._updateOverlayOffset(sizeDelta);

return;
}

// If the mouse moved further than the resize was able to match, limit the
// movement of the overlay to match the actual size and position of the origin.
if (overshot !== 0) {
Expand All @@ -143,18 +154,7 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
}
}

let computedNewSize: number = size + (this._isLtr() ? deltaX : -deltaX);
computedNewSize = Math.min(
Math.max(computedNewSize, this.resizeRef.minWidthPx, 0),
this.resizeRef.maxWidthPx,
);

this.resizeNotifier.triggerResize.next({
columnId: this.columnDef.name,
size: computedNewSize,
previousSize: size,
isStickyColumn: this.columnDef.sticky || this.columnDef.stickyEnd,
});
this._triggerResize(size, deltaX);

this.styleScheduler.scheduleEnd(() => {
const originNewSize = this._getOriginWidth();
Expand All @@ -178,6 +178,24 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
);
}

private _triggerResize(startSize: number, deltaX: number): void {
this.resizeNotifier.triggerResize.next({
columnId: this.columnDef.name,
size: this._computeNewSize(startSize, deltaX),
previousSize: startSize,
isStickyColumn: this.columnDef.sticky || this.columnDef.stickyEnd,
});
}

private _computeNewSize(startSize: number, deltaX: number): number {
let computedNewSize: number = startSize + (this._isLtr() ? deltaX : -deltaX);
computedNewSize = Math.min(
Math.max(computedNewSize, this.resizeRef.minWidthPx, 0),
this.resizeRef.maxWidthPx,
);
return computedNewSize;
}

private _getOriginWidth(): number {
return this.resizeRef.origin.nativeElement!.offsetWidth;
}
Expand All @@ -202,6 +220,10 @@ export abstract class ResizeOverlayHandle implements AfterViewInit, OnDestroy {
this.ngZone.run(() => {
const sizeMessage = {columnId: this.columnDef.name, size};
if (completedSuccessfully) {
if (!this.resizeRef.liveUpdates) {
this._triggerResize(size, this._cumulativeDeltaX);
}

this.resizeNotifier.resizeCompleted.next(sizeMessage);
} else {
this.resizeNotifier.resizeCanceled.next(sizeMessage);
Expand Down
1 change: 1 addition & 0 deletions src/cdk-experimental/column-resize/resizable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ export abstract class Resizable<HandleComponent extends ResizeOverlayHandle>
this.overlayRef!,
this.minWidthPx,
this.maxWidthPx,
this.columnResize.liveResizeUpdates,
),
},
],
Expand Down
1 change: 1 addition & 0 deletions src/cdk-experimental/column-resize/resize-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ export class ResizeRef {
readonly overlayRef: OverlayRef,
readonly minWidthPx: number,
readonly maxWidthPx: number,
readonly liveUpdates = true,
) {}
}
40 changes: 39 additions & 1 deletion src/material-experimental/column-resize/column-resize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ describe('Material Popover Edit', () => {
expect(component.getOverlayThumbElement(0)).toBeUndefined();
}));

it('resizes the target column via mouse input', fakeAsync(() => {
it('resizes the target column via mouse input (live updates)', fakeAsync(() => {
const initialTableWidth = component.getTableWidth();
const initialColumnWidth = component.getColumnWidth(1);
const initialColumnPosition = component.getColumnOriginPosition(1);
Expand Down Expand Up @@ -485,6 +485,44 @@ describe('Material Popover Edit', () => {
fixture.detectChanges();
}));

it('resizes the target column via mouse input (no live update)', fakeAsync(() => {
const initialTableWidth = component.getTableWidth();
const initialColumnWidth = component.getColumnWidth(1);

component.columnResize.liveResizeUpdates = false;

component.triggerHoverState();
fixture.detectChanges();
component.beginColumnResizeWithMouse(1);

const initialThumbPosition = component.getOverlayThumbPosition(1);
component.updateResizeWithMouseInProgress(5);
fixture.detectChanges();
flush();

let thumbPositionDelta = component.getOverlayThumbPosition(1) - initialThumbPosition;
(expect(thumbPositionDelta) as any).isApproximately(5);
(expect(component.getColumnWidth(1)) as any).toBe(initialColumnWidth);

component.updateResizeWithMouseInProgress(1);
fixture.detectChanges();
flush();

thumbPositionDelta = component.getOverlayThumbPosition(1) - initialThumbPosition;

(expect(component.getTableWidth()) as any).toBe(initialTableWidth);
(expect(component.getColumnWidth(1)) as any).toBe(initialColumnWidth);

component.completeResizeWithMouseInProgress(1);
flush();

(expect(component.getTableWidth()) as any).isApproximately(initialTableWidth + 1);
(expect(component.getColumnWidth(1)) as any).isApproximately(initialColumnWidth + 1);

component.endHoverState();
fixture.detectChanges();
}));

it('should not start dragging using the right mouse button', fakeAsync(() => {
const initialColumnWidth = component.getColumnWidth(1);

Expand Down
2 changes: 2 additions & 0 deletions src/material-experimental/column-resize/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ export * from './resizable-directives/default-enabled-resizable';
export * from './resizable-directives/resizable';
export * from './resize-strategy';
export * from './overlay-handle';
export type {ColumnResizeOptions} from '@angular/cdk-experimental/column-resize';
export {COLUMN_RESIZE_OPTIONS} from '@angular/cdk-experimental/column-resize';
Loading