jsim-lazy-expandable
Version:
Light weight and easy to use expansion panel with lazy loaded content.
200 lines (194 loc) • 13.2 kB
JavaScript
import * as i0 from '@angular/core';
import { Directive, EventEmitter, Component, Input, Output, ViewChild, ContentChild, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
class LazyContentDirective {
constructor(templateRef, viewContainer) {
this.templateRef = templateRef;
this.viewContainer = viewContainer;
this.viewLoaded = false;
this.viewContainer.clear();
this.viewLoaded = false;
}
showContent() {
if (!this.viewLoaded) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.viewLoaded = true;
}
}
}
LazyContentDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LazyContentDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
LazyContentDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.2.7", type: LazyContentDirective, selector: "ng-template[jsimLazyContent]", ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LazyContentDirective, decorators: [{
type: Directive,
args: [{
selector: 'ng-template[jsimLazyContent]'
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }]; } });
class LazyExpandableComponent {
constructor() {
/** Is the panel open? */
this.isOpen = false;
/** Event emitted when isOpen changes */
this.openChanged = new EventEmitter();
/** Hide the arrow icon */
this.hideIcon = false;
/** Height of the header when collapsed (in pixels) */
this.headerHeight = 48;
/** Height of the header when expanded (in pixels) */
this.headerHeightExpanded = 64;
/** Speed of the expand and collapse animation (in pixel by milisecond) */
this.animationSpeed = 0.5;
/** Event emitted when animation ends */
this.animationEnd = new EventEmitter();
/** Is expanding and collapsing disabled? */
this.disabled = false;
this.headerHeightStyle = this.headerHeight + 'px';
this.contentHeight = this.headerHeight + 'px';
this.currentContentHeight = 0;
this.desiredContentHeight = 0;
this.timeLastFrame = Date.now();
this.animationDuration = 0;
this.currentAnimationTime = 0;
this.lerp = (x, y, a) => x * (1 - a) + y * a;
}
ngOnInit() { }
ngAfterContentInit() {
setTimeout(() => {
this.setContentHeight(false);
if (this.isOpen && this.directive) {
this.directive.showContent();
}
}, 0);
}
toggleOpen() {
if (this.disabled) {
return;
}
this.isOpen = !this.isOpen;
if (this.isOpen && this.directive) {
this.directive.showContent();
}
this.openChanged.emit(this.isOpen);
this.setContentHeight();
}
setContentHeight(animate = true) {
if (animate) {
if (this.isOpen) {
this.headerHeightStyle = this.headerHeightExpanded + 'px';
this.desiredContentHeight = this.content.nativeElement.clientHeight;
this.currentContentHeight = 0;
}
else {
this.headerHeightStyle = this.headerHeight + 'px';
this.desiredContentHeight = 0;
this.currentContentHeight = this.content.nativeElement.clientHeight;
}
this.animationDuration = Math.abs(this.desiredContentHeight - this.currentContentHeight) / this.animationSpeed;
this.startAnimation();
}
else {
if (this.isOpen) {
this.headerHeightStyle = this.headerHeightExpanded + 'px';
this.contentHeight = 'unset';
}
else {
this.headerHeightStyle = this.headerHeight + 'px';
this.contentHeight = this.headerHeight + 'px';
}
}
}
startAnimation() {
this.timeLastFrame = Date.now();
this.currentAnimationTime = 0;
if (this.animationFrameRef) {
cancelAnimationFrame(this.animationFrameRef);
}
this.animationFrameRef = requestAnimationFrame(this.animateHeader.bind(this));
}
animateHeader() {
const time = Date.now();
const deltatime = time - this.timeLastFrame;
this.timeLastFrame = time;
this.currentAnimationTime += deltatime;
const amount = this.currentAnimationTime / this.animationDuration;
this.currentContentHeight = this.lerp(this.currentContentHeight, this.desiredContentHeight, amount);
if (this.isOpen) {
this.currentContentHeight = Math.min(this.currentContentHeight, this.desiredContentHeight);
this.contentHeight = this.headerHeightExpanded + this.currentContentHeight + 'px';
}
else {
this.currentContentHeight = Math.max(this.currentContentHeight, this.desiredContentHeight);
this.contentHeight = this.headerHeight + this.currentContentHeight + 'px';
}
if (this.currentContentHeight == this.desiredContentHeight) {
this.animationEnded();
return;
}
this.animationFrameRef = requestAnimationFrame(this.animateHeader.bind(this));
}
animationEnded() {
this.animationEnd.emit(this.isOpen);
if (this.isOpen) {
this.contentHeight = 'unset';
}
}
}
LazyExpandableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LazyExpandableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
LazyExpandableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: LazyExpandableComponent, selector: "jsim-lazy-expandable", inputs: { isOpen: "isOpen", hideIcon: "hideIcon", headerHeight: "headerHeight", headerHeightExpanded: "headerHeightExpanded", animationSpeed: "animationSpeed", disabled: "disabled" }, outputs: { openChanged: "openChanged", animationEnd: "animationEnd" }, queries: [{ propertyName: "directive", first: true, predicate: LazyContentDirective, descendants: true }], viewQueries: [{ propertyName: "content", first: true, predicate: ["lazyContent"], descendants: true }], ngImport: i0, template: "<div class=\"lazy-exp-container\" [class.is-open]=\"isOpen\" [style.height]=\"contentHeight\" [class.disabled]=\"disabled\">\n <div class=\"lazy-exp-header\" (click)=\"toggleOpen()\" [style.height]=\"headerHeightStyle\">\n <div class=\"lazy-exp-header-title\">\n <ng-content select=\"[exp-header-title]\"></ng-content>\n </div>\n <div class=\"lazy-exp-header-subtitle\">\n <ng-content select=\"[exp-header-subtitle]\"></ng-content>\n </div>\n <div *ngIf=\"!hideIcon\" class=\"lazy-exp-header-icon\"></div>\n </div>\n <div #lazyContent class=\"lazy-exp-content\">\n <div class=\"lazy-exp-content-container\">\n <ng-content select=\"[exp-content]\"></ng-content>\n </div>\n </div>\n</div>\n", styles: [".lazy-exp-container{--background-color: white;--background-color-hover: rgba(0,0,0,.04);--icon-color: rgb(119, 119, 119);border-radius:4px;box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;background-color:var(--background-color);overflow:hidden}.lazy-exp-header{display:flex;align-items:center;cursor:pointer;transition:height 225ms cubic-bezier(.4,0,.2,1)}.lazy-exp-container:not(.is-open):not(.disabled) .lazy-exp-header:hover{background-color:var(--background-color-hover)}.lazy-exp-container.disabled .lazy-exp-header{cursor:not-allowed}.lazy-exp-header-title{margin-left:1rem;width:50%}.lazy-exp-header-subtitle{width:50%;color:#2e2e2e}.lazy-exp-header-icon{justify-self:flex-end;margin-right:1rem;transform:rotate(0);transition:transform .2s ease-out}.is-open .lazy-exp-header-icon{transform:rotate(180deg)}.lazy-exp-header-icon:after{border-color:var(--icon-color);border-style:solid;border-width:0 2px 2px 0;content:\"\";display:inline-block;padding:3px;transform:rotate(45deg);vertical-align:middle}.lazy-exp-content{box-sizing:border-box;padding:1rem}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LazyExpandableComponent, decorators: [{
type: Component,
args: [{ selector: 'jsim-lazy-expandable', template: "<div class=\"lazy-exp-container\" [class.is-open]=\"isOpen\" [style.height]=\"contentHeight\" [class.disabled]=\"disabled\">\n <div class=\"lazy-exp-header\" (click)=\"toggleOpen()\" [style.height]=\"headerHeightStyle\">\n <div class=\"lazy-exp-header-title\">\n <ng-content select=\"[exp-header-title]\"></ng-content>\n </div>\n <div class=\"lazy-exp-header-subtitle\">\n <ng-content select=\"[exp-header-subtitle]\"></ng-content>\n </div>\n <div *ngIf=\"!hideIcon\" class=\"lazy-exp-header-icon\"></div>\n </div>\n <div #lazyContent class=\"lazy-exp-content\">\n <div class=\"lazy-exp-content-container\">\n <ng-content select=\"[exp-content]\"></ng-content>\n </div>\n </div>\n</div>\n", styles: [".lazy-exp-container{--background-color: white;--background-color-hover: rgba(0,0,0,.04);--icon-color: rgb(119, 119, 119);border-radius:4px;box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;background-color:var(--background-color);overflow:hidden}.lazy-exp-header{display:flex;align-items:center;cursor:pointer;transition:height 225ms cubic-bezier(.4,0,.2,1)}.lazy-exp-container:not(.is-open):not(.disabled) .lazy-exp-header:hover{background-color:var(--background-color-hover)}.lazy-exp-container.disabled .lazy-exp-header{cursor:not-allowed}.lazy-exp-header-title{margin-left:1rem;width:50%}.lazy-exp-header-subtitle{width:50%;color:#2e2e2e}.lazy-exp-header-icon{justify-self:flex-end;margin-right:1rem;transform:rotate(0);transition:transform .2s ease-out}.is-open .lazy-exp-header-icon{transform:rotate(180deg)}.lazy-exp-header-icon:after{border-color:var(--icon-color);border-style:solid;border-width:0 2px 2px 0;content:\"\";display:inline-block;padding:3px;transform:rotate(45deg);vertical-align:middle}.lazy-exp-content{box-sizing:border-box;padding:1rem}\n"] }]
}], ctorParameters: function () { return []; }, propDecorators: { isOpen: [{
type: Input
}], openChanged: [{
type: Output
}], hideIcon: [{
type: Input
}], headerHeight: [{
type: Input
}], headerHeightExpanded: [{
type: Input
}], animationSpeed: [{
type: Input
}], animationEnd: [{
type: Output
}], disabled: [{
type: Input
}], content: [{
type: ViewChild,
args: ['lazyContent']
}], directive: [{
type: ContentChild,
args: [LazyContentDirective]
}] } });
class LazyExpandableModule {
}
LazyExpandableModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LazyExpandableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
LazyExpandableModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LazyExpandableModule, declarations: [LazyExpandableComponent,
LazyContentDirective], imports: [CommonModule], exports: [LazyExpandableComponent,
LazyContentDirective] });
LazyExpandableModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LazyExpandableModule, imports: [[
CommonModule
]] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LazyExpandableModule, decorators: [{
type: NgModule,
args: [{
declarations: [
LazyExpandableComponent,
LazyContentDirective
],
imports: [
CommonModule
],
exports: [
LazyExpandableComponent,
LazyContentDirective
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { LazyContentDirective, LazyExpandableComponent, LazyExpandableModule };
//# sourceMappingURL=jsim-lazy-expandable.mjs.map