Skip to content

Commit 251c1bb

Browse files
jvquarckllorenspujol
authored andcommitted
fix(grid-component): improve end of drag events of the grid elements, animate over other elements
refactor(grid-component): added useful comments on 'addGridItemAnimatingClass'
1 parent bb8598b commit 251c1bb

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

projects/angular-grid-layout/src/lib/grid.component.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ ktd-grid {
4141
}
4242

4343
ktd-grid-item {
44-
&.ktd-grid-item-dragging {
44+
&.ktd-grid-item-dragging, &.ktd-grid-item-animating {
4545
z-index: 1000;
4646
}
4747

projects/angular-grid-layout/src/lib/grid.component.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
} from '@angular/core';
66
import { coerceNumberProperty, NumberInput } from './coercion/number-property';
77
import { KtdGridItemComponent } from './grid-item/grid-item.component';
8-
import { combineLatest, merge, NEVER, Observable, Observer, of, Subscription } from 'rxjs';
8+
import { combineLatest, empty, merge, NEVER, Observable, Observer, of, Subscription } from 'rxjs';
99
import { exhaustMap, map, startWith, switchMap, takeUntil } from 'rxjs/operators';
1010
import { ktdGetGridItemRowHeight, ktdGridItemDragging, ktdGridItemLayoutItemAreEqual, ktdGridItemResizing } from './utils/grid.utils';
1111
import { compact } from './utils/react-grid-layout.utils';
@@ -19,6 +19,7 @@ import { getMutableClientRect, KtdClientRect } from './utils/client-rect';
1919
import { ktdGetScrollTotalRelativeDifference$, ktdScrollIfNearElementClientRect$ } from './utils/scroll';
2020
import { BooleanInput, coerceBooleanProperty } from './coercion/boolean-property';
2121
import { KtdGridItemPlaceholder } from './directives/placeholder';
22+
import { getTransformTransitionDurationInMs } from './utils/transition-duration';
2223

2324
interface KtdDragResizeEvent {
2425
layout: KtdGridLayout;
@@ -272,6 +273,7 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
272273
get backgroundConfig(): KtdGridBackgroundCfg | null {
273274
return this._backgroundConfig;
274275
}
276+
275277
set backgroundConfig(val: KtdGridBackgroundCfg | null) {
276278
this._backgroundConfig = val;
277279

@@ -611,6 +613,8 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
611613
this.renderer.removeClass(gridItem.elementRef.nativeElement, 'no-transitions');
612614
this.renderer.removeClass(gridItem.elementRef.nativeElement, 'ktd-grid-item-dragging');
613615

616+
this.addGridItemAnimatingClass(gridItem).subscribe();
617+
// Consider destroying the placeholder after the animation has finished.
614618
this.destroyPlaceholder();
615619

616620
if (newLayout) {
@@ -645,6 +649,43 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
645649
});
646650
}
647651

652+
653+
/**
654+
* It adds the `ktd-grid-item-animating` class and removes it when the animated transition is complete.
655+
* This function is meant to be executed when the drag has ended.
656+
* @param gridItem that has been dragged
657+
*/
658+
private addGridItemAnimatingClass(gridItem: KtdGridItemComponent): Observable<undefined> {
659+
660+
return new Observable(observer => {
661+
662+
const duration = getTransformTransitionDurationInMs(gridItem.elementRef.nativeElement);
663+
664+
if (duration === 0) {
665+
observer.next();
666+
observer.complete();
667+
return;
668+
}
669+
670+
this.renderer.addClass(gridItem.elementRef.nativeElement, 'ktd-grid-item-animating');
671+
const handler = ((event: TransitionEvent) => {
672+
if (!event || (event.target === gridItem.elementRef.nativeElement && event.propertyName === 'transform')) {
673+
this.renderer.removeClass(gridItem.elementRef.nativeElement, 'ktd-grid-item-animating');
674+
removeEventListener();
675+
clearTimeout(timeout);
676+
observer.next();
677+
observer.complete();
678+
}
679+
}) as EventListener;
680+
681+
// If a transition is short enough, the browser might not fire the `transitionend` event.
682+
// Since we know how long it's supposed to take, add a timeout with a 50% buffer that'll
683+
// fire if the transition hasn't completed when it was supposed to.
684+
const timeout = setTimeout(handler, duration * 1.5);
685+
const removeEventListener = this.renderer.listen(gridItem.elementRef.nativeElement, 'transitionend', handler);
686+
})
687+
}
688+
648689
/** Creates placeholder element */
649690
private createPlaceholderElement(clientRect: KtdClientRect, gridItemPlaceholder?: KtdGridItemPlaceholder) {
650691
this.placeholder = this.renderer.createElement('div');
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Transition duration utilities.
3+
* This file is taken from Angular Material repository.
4+
*/
5+
6+
/* eslint-disable @katoid/prefix-exported-code */
7+
8+
/** Parses a CSS time value to milliseconds. */
9+
function parseCssTimeUnitsToMs(value: string): number {
10+
// Some browsers will return it in seconds, whereas others will return milliseconds.
11+
const multiplier = value.toLowerCase().indexOf('ms') > -1 ? 1 : 1000;
12+
return parseFloat(value) * multiplier;
13+
}
14+
15+
/** Gets the transform transition duration, including the delay, of an element in milliseconds. */
16+
export function getTransformTransitionDurationInMs(element: HTMLElement): number {
17+
const computedStyle = getComputedStyle(element);
18+
const transitionedProperties = parseCssPropertyValue(computedStyle, 'transition-property');
19+
const property = transitionedProperties.find(prop => prop === 'transform' || prop === 'all');
20+
21+
// If there's no transition for `all` or `transform`, we shouldn't do anything.
22+
if (!property) {
23+
return 0;
24+
}
25+
26+
// Get the index of the property that we're interested in and match
27+
// it up to the same index in `transition-delay` and `transition-duration`.
28+
const propertyIndex = transitionedProperties.indexOf(property);
29+
const rawDurations = parseCssPropertyValue(computedStyle, 'transition-duration');
30+
const rawDelays = parseCssPropertyValue(computedStyle, 'transition-delay');
31+
32+
return parseCssTimeUnitsToMs(rawDurations[propertyIndex]) +
33+
parseCssTimeUnitsToMs(rawDelays[propertyIndex]);
34+
}
35+
36+
/** Parses out multiple values from a computed style into an array. */
37+
function parseCssPropertyValue(computedStyle: CSSStyleDeclaration, name: string): string[] {
38+
const value = computedStyle.getPropertyValue(name);
39+
return value.split(',').map(part => part.trim());
40+
}

0 commit comments

Comments
 (0)