Skip to content

Commit

Permalink
feat(collapse): add animations
Browse files Browse the repository at this point in the history
  • Loading branch information
fbasso authored and maxokorokov committed May 18, 2020
1 parent 12d753b commit 9bffcab
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<p>
<button type="button" class="btn btn-outline-primary" (click)="isCollapsed = !isCollapsed"
[attr.aria-expanded]="!isCollapsed" aria-controls="collapseExample">
<button type="button" class="btn btn-outline-primary" (click)="collapse.toggle()" [attr.aria-expanded]="!isCollapsed"
aria-controls="collapseExample">
Toggle
</button>
</p>
<div id="collapseExample" [ngbCollapse]="isCollapsed">
<div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed">
<div class="card">
<div class="card-body">
You can collapse this card by clicking Toggle
</div>
</div>
</div>
</div>
32 changes: 31 additions & 1 deletion src/collapse/collapse.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {Directive, Input} from '@angular/core';
import {Directive, Input, ElementRef, Output, EventEmitter} from '@angular/core';
import {ngbRunTransition} from '../util/transition/ngbTransition';
import {ngbCollapsingTransition} from '../util/transition/ngbCollapseTransition';
import {NgbConfig} from '../ngb-config';

/**
* A directive to provide a simple way of hiding and showing elements on the page.
Expand All @@ -9,8 +12,35 @@ import {Directive, Input} from '@angular/core';
host: {'[class.collapse]': 'true', '[class.show]': '!collapsed'}
})
export class NgbCollapse {
/**
* If `true`, collapse will be animated.
*
* Animation is triggered only when clicked on triggering element
* or via the `.toggle()` function
*/
@Input() animation = false;

/**
* If `true`, will collapse the element or show it otherwise.
*/
@Input('ngbCollapse') collapsed = false;

@Output() ngbCollapseChange = new EventEmitter<boolean>();

constructor(private _element: ElementRef, ngbConfig: NgbConfig) { this.animation = ngbConfig.animation; }

/**
* Triggers collapsing programmatically.
*
* If there is a collapsing transition running already, it will be reversed.
* If the animations are turned off this happens synchronously.
*/
toggle(open: boolean = this.collapsed) {
this.collapsed = !open;
ngbRunTransition(this._element.nativeElement, ngbCollapsingTransition, {
animation: this.animation,
runningTransition: 'stop',
context: {direction: open ? 'show' : 'hide'}
}).subscribe(() => this.ngbCollapseChange.next(this.collapsed));
}
}
63 changes: 63 additions & 0 deletions src/util/transition/ngbCollapseTransition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {NgbTransitionStartFn} from './ngbTransition';
import {reflow} from '../util';

export interface NgbCollapseCtx {
direction: 'show' | 'hide';
maxHeight?: string;
}

function measureCollapsingElementHeightPx(element: HTMLElement): string {
const {classList} = element;
const hasShownClass = classList.contains('show');
if (!hasShownClass) {
classList.add('show');
}

element.style.height = '';
const height = element.getBoundingClientRect().height + 'px';

if (!hasShownClass) {
classList.remove('show');
}

return height;
}

export const ngbCollapsingTransition: NgbTransitionStartFn<NgbCollapseCtx> =
(element: HTMLElement, context: NgbCollapseCtx) => {
let {direction, maxHeight} = context;
const {classList} = element;

// No maxHeight -> running the transition for the first time
if (!maxHeight) {
maxHeight = measureCollapsingElementHeightPx(element);
context.maxHeight = maxHeight;

// Fix the height before starting the animation
element.style.height = direction !== 'show' ? maxHeight : '0px';

classList.remove('collapse');
classList.remove('collapsing');
classList.remove('show');

reflow(element);

// Start the animation
classList.add('collapsing');
}

// Start or revert the animation
element.style.height = direction === 'show' ? maxHeight : '0px';

return () => {
classList.remove('collapsing');
classList.add('collapse');
if (direction === 'show') {
classList.add('show');
} else {
classList.remove('show');
}

element.style.height = '';
};
};
12 changes: 8 additions & 4 deletions src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,13 @@ if (typeof Element !== 'undefined' && !Element.prototype.closest) {
}

export function closest(element: HTMLElement, selector): HTMLElement | null {
if (!selector) {
return null;
}
return selector ? element.closest(selector) : null;
}

return element.closest(selector);
/**
* Force a browser reflow
* @param element element where to apply the reflow
*/
export function reflow(element: HTMLElement) {
return (element || document.body).offsetHeight;
}

0 comments on commit 9bffcab

Please sign in to comment.