Skip to content

Commit

Permalink
feat(tree): add IgxTree, initial commit, #7475
Browse files Browse the repository at this point in the history
  • Loading branch information
ViktorSlavov committed Apr 7, 2021
1 parent 5896af7 commit 0c07a75
Show file tree
Hide file tree
Showing 23 changed files with 858 additions and 56 deletions.
12 changes: 6 additions & 6 deletions projects/igniteui-angular/src/lib/banner/banner.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Component, NgModule, EventEmitter, Output, Input, ViewChild, ElementRef,
ContentChild, HostBinding } from '@angular/core';
import { IgxExpansionPanelModule } from '../expansion-panel/expansion-panel.module';
import { AnimationSettings } from '../expansion-panel/expansion-panel.component';
import { IgxExpansionPanelComponent } from '../expansion-panel/public_api';
import { IgxIconModule, IgxIconComponent } from '../icon/public_api';
import { IToggleView } from '../core/navigation';
Expand All @@ -10,6 +9,7 @@ import { IgxRippleModule } from '../directives/ripple/ripple.directive';
import { IgxBannerActionsDirective } from './banner.directives';
import { CommonModule } from '@angular/common';
import { CancelableEventArgs, IBaseEventArgs } from '../core/utils';
import { ToggleAnimationSettings } from '../expansion-panel/toggle-animation-component';

export interface BannerEventArgs extends IBaseEventArgs {
banner: IgxBannerComponent;
Expand Down Expand Up @@ -111,11 +111,11 @@ export class IgxBannerComponent implements IToggleView {
/**
* Get the animation settings used by the banner open/close methods
* ```typescript
* let currentAnimations: AnimationSettings = banner.animationSettings
* let currentAnimations: ToggleAnimationSettings = banner.animationSettings
* ```
*/
@Input()
public get animationSettings(): AnimationSettings {
public get animationSettings(): ToggleAnimationSettings {
return this._animationSettings ? this._animationSettings : this._expansionPanel.animationSettings;
}

Expand All @@ -124,10 +124,10 @@ export class IgxBannerComponent implements IToggleView {
* ```typescript
* import { slideInLeft, slideOutRight } from 'igniteui-angular';
* ...
* banner.animationSettings: AnimationSettings = { openAnimation: slideInLeft, closeAnimation: slideOutRight };
* banner.animationSettings: ToggleAnimationSettings = { openAnimation: slideInLeft, closeAnimation: slideOutRight };
* ```
*/
public set animationSettings(settings: AnimationSettings) {
public set animationSettings(settings: ToggleAnimationSettings) {
this._animationSettings = settings;
}
/**
Expand Down Expand Up @@ -166,7 +166,7 @@ export class IgxBannerComponent implements IToggleView {
private _bannerActionTemplate: IgxBannerActionsDirective;

private _bannerEvent: BannerEventArgs;
private _animationSettings: AnimationSettings;
private _animationSettings: ToggleAnimationSettings;

constructor(public elementRef: ElementRef) { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,20 @@ import {
ContentChild,
AfterContentInit
} from '@angular/core';
import { AnimationBuilder, AnimationReferenceMetadata, useAnimation } from '@angular/animations';
import { growVerOut, growVerIn } from '../animations/main';
import { AnimationBuilder } from '@angular/animations';
import { IgxExpansionPanelBodyComponent } from './expansion-panel-body.component';
import { IgxExpansionPanelHeaderComponent } from './expansion-panel-header.component';
import { IGX_EXPANSION_PANEL_COMPONENT, IgxExpansionPanelBase, IExpansionPanelEventArgs } from './expansion-panel.common';
import { ToggleAnimationPlayer, ToggleAnimationSettings } from './toggle-animation-component';

let NEXT_ID = 0;

export interface AnimationSettings {
openAnimation: AnimationReferenceMetadata;
closeAnimation: AnimationReferenceMetadata;
}
@Component({
selector: 'igx-expansion-panel',
templateUrl: 'expansion-panel.component.html',
providers: [{ provide: IGX_EXPANSION_PANEL_COMPONENT, useExisting: IgxExpansionPanelComponent }]
})
export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterContentInit {
export class IgxExpansionPanelComponent extends ToggleAnimationPlayer implements IgxExpansionPanelBase, AfterContentInit {
/**
* Sets/gets the animation settings of the expansion panel component
* Open and Close animation should be passed
Expand Down Expand Up @@ -58,10 +54,12 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC
* ```
*/
@Input()
public animationSettings: AnimationSettings = {
openAnimation: growVerIn,
closeAnimation: growVerOut
};
public get animationSettings(): ToggleAnimationSettings {
return this._animationSettings;
}
public set animationSettings(value: ToggleAnimationSettings) {
this._animationSettings = value;
}

/**
* Sets/gets the `id` of the expansion panel component.
Expand Down Expand Up @@ -165,10 +163,12 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC

private _collapsed = true;

constructor(private cdr: ChangeDetectorRef, private builder: AnimationBuilder) { }
constructor(private cdr: ChangeDetectorRef, protected builder: AnimationBuilder) {
super(builder);
}

/** @hidden */
ngAfterContentInit(): void {
public ngAfterContentInit(): void {
if (this.body && this.header) {
// schedule at end of turn:
Promise.resolve().then(() => {
Expand All @@ -188,11 +188,12 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC
* <button (click)="myPanel.collapse($event)">Collpase Panel</button>
* ```
*/
collapse(evt?: Event) {
public collapse(evt?: Event) {
if (this.collapsed) { // If expansion panel is already collapsed, do nothing
return;
}
this.playCloseAnimation(
this.body?.element,
() => {
this.onCollapsed.emit({ event: evt, panel: this, owner: this });
this.collapsed = true;
Expand All @@ -211,13 +212,14 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC
* <button (click)="myPanel.expand($event)">Expand Panel</button>
* ```
*/
expand(evt?: Event) {
public expand(evt?: Event) {
if (!this.collapsed) { // If the panel is already opened, do nothing
return;
}
this.collapsed = false;
this.cdr.detectChanges();
this.playOpenAnimation(
this.body?.element,
() => {
this.onExpanded.emit({ event: evt, panel: this, owner: this });
}
Expand All @@ -234,49 +236,19 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC
* <button (click)="myPanel.toggle($event)">Expand Panel</button>
* ```
*/
toggle(evt?: Event) {
public toggle(evt?: Event) {
if (this.collapsed) {
this.open(evt);
} else {
this.close(evt);
}
}

open(evt?: Event) {
public open(evt?: Event) {
this.expand(evt);
}
close(evt?: Event) {
this.collapse(evt);
}

private playOpenAnimation(cb: () => void) {
if (!this.body) { // if not body element is passed, there is nothing to animate
return;
}
const animation = useAnimation(this.animationSettings.openAnimation);
const animationBuilder = this.builder.build(animation);
const openAnimationPlayer = animationBuilder.create(this.body.element.nativeElement);

openAnimationPlayer.onDone(() => {
cb();
openAnimationPlayer.reset();
});

openAnimationPlayer.play();
}

private playCloseAnimation(cb: () => void) {
if (!this.body) { // if not body element is passed, there is nothing to animate
return;
}
const animation = useAnimation(this.animationSettings.closeAnimation);
const animationBuilder = this.builder.build(animation);
const closeAnimationPlayer = animationBuilder.create(this.body.element.nativeElement);
closeAnimationPlayer.onDone(() => {
cb();
closeAnimationPlayer.reset();
});

closeAnimationPlayer.play();
public close(evt?: Event) {
this.collapse(evt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { AnimationBuilder, AnimationPlayer, AnimationReferenceMetadata, useAnimation } from '@angular/animations';
import { Directive, ElementRef, EventEmitter, OnDestroy } from '@angular/core';
import { noop, Subject } from 'rxjs';
import { growVerIn, growVerOut } from '../animations/grow';

/**@hidden @internal */
export interface ToggleAnimationSettings {
openAnimation: AnimationReferenceMetadata;
closeAnimation: AnimationReferenceMetadata;
}

export interface ToggleAnimationOwner {
animationSettings: ToggleAnimationSettings;
openAnimationStart: EventEmitter<void>;
openAnimationDone: EventEmitter<void>;
closeAnimationStart: EventEmitter<void>;
closeAnimationDone: EventEmitter<void>;
openAnimationPlayer: AnimationPlayer;
closeAnimationPlayer: AnimationPlayer;
playOpenAnimation(element: ElementRef, onDone: () => void): void;
playCloseAnimation(element: ElementRef, onDone: () => void): void;
}

enum ANIMATION_TYPE {
OPEN = 'open',
CLOSE = 'close',
}

/**@hidden @internal */
@Directive()
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export abstract class ToggleAnimationPlayer implements ToggleAnimationOwner, OnDestroy {


/** @hidden @internal */
public openAnimationDone: EventEmitter<void> = new EventEmitter();
/** @hidden @internal */
public closeAnimationDone: EventEmitter<void> = new EventEmitter();
/** @hidden @internal */
public openAnimationStart: EventEmitter<void> = new EventEmitter();
/** @hidden @internal */
public closeAnimationStart: EventEmitter<void> = new EventEmitter();

public get animationSettings(): ToggleAnimationSettings {
return this._animationSettings;
}
public set animationSettings(value: ToggleAnimationSettings) {
this._animationSettings = value;
}

/** @hidden @internal */
public openAnimationPlayer: AnimationPlayer = null;

/** @hidden @internal */
public closeAnimationPlayer: AnimationPlayer = null;



protected destroy$: Subject<void> = new Subject();
protected players: Map<string, AnimationPlayer> = new Map();
protected _animationSettings: ToggleAnimationSettings = {
openAnimation: growVerIn,
closeAnimation: growVerOut
};

private _defaultClosedCallback = noop;
private _defaultOpenedCallback = noop;
private onClosedCallback: () => any = this._defaultClosedCallback;
private onOpenedCallback: () => any = this._defaultOpenedCallback;

constructor(protected builder: AnimationBuilder) {
}

/** @hidden @internal */
public playOpenAnimation(targetElement: ElementRef, onDone?: () => void): void {
this.startPlayer(ANIMATION_TYPE.OPEN, targetElement, onDone || this._defaultOpenedCallback);
}

/** @hidden @internal */
public playCloseAnimation(targetElement: ElementRef, onDone?: () => void): void {
this.startPlayer(ANIMATION_TYPE.CLOSE, targetElement, onDone || this._defaultClosedCallback);
}
public ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}

private startPlayer(type: ANIMATION_TYPE, targetElement: ElementRef, callback: () => void): void {
if (!targetElement) { // if no element is passed, there is nothing to animate
return;
}

let target = this.getPlayer(type);
if (!target) {
target = this.initializePlayer(type, targetElement, callback);
}

if (target.hasStarted()) {
return;
}

const targetEmitter = type === ANIMATION_TYPE.OPEN ? this.openAnimationStart : this.closeAnimationStart;
targetEmitter.emit();
target.play();
}

private initializePlayer(type: ANIMATION_TYPE, targetElement: ElementRef, callback: () => void): AnimationPlayer {
const oppositeType = type === ANIMATION_TYPE.OPEN ? ANIMATION_TYPE.CLOSE : ANIMATION_TYPE.OPEN;
const animationSettings = type === ANIMATION_TYPE.OPEN ?
this.animationSettings.openAnimation : this.animationSettings.closeAnimation;
const animation = useAnimation(animationSettings);
const animationBuilder = this.builder.build(animation);
const opposite = this.getPlayer(oppositeType);
let oppositePosition = 1;
if (opposite) {
if (opposite.hasStarted()) {
// .getPosition() still returns 0 sometimes, regardless of the fix for https://github.com/angular/angular/issues/18891;
oppositePosition = (opposite as any)._renderer.engine.players[0].getPosition();
}
this.cleanUpPlayer(oppositeType);
}
if (type === ANIMATION_TYPE.OPEN) {
this.openAnimationPlayer = animationBuilder.create(targetElement.nativeElement);
} else if (type === ANIMATION_TYPE.CLOSE) {
this.closeAnimationPlayer = animationBuilder.create(targetElement.nativeElement);
}
const target = this.getPlayer(type);
target.init();
this.getPlayer(type).setPosition(1 - oppositePosition);
if (type === ANIMATION_TYPE.OPEN) {
this.onOpenedCallback = callback;
} else if (type === ANIMATION_TYPE.CLOSE) {
this.onClosedCallback = callback;
}
const targetCallback = type === ANIMATION_TYPE.OPEN ? this.onOpenedCallback : this.onClosedCallback;
const targetEmitter = type === ANIMATION_TYPE.OPEN ? this.openAnimationDone : this.closeAnimationDone;
target.onDone(() => {
targetCallback();
targetEmitter.emit();
this.cleanUpPlayer(type);
});
return target;
}


private cleanUpPlayer(target: ANIMATION_TYPE) {
switch (target) {
case ANIMATION_TYPE.CLOSE:
if (this.closeAnimationPlayer != null) {
this.closeAnimationPlayer.reset();
this.closeAnimationPlayer.destroy();
this.closeAnimationPlayer = null;
}
this.onClosedCallback = this._defaultClosedCallback;
break;
case ANIMATION_TYPE.OPEN:
if (this.openAnimationPlayer != null) {
this.openAnimationPlayer.reset();
this.openAnimationPlayer.destroy();
this.openAnimationPlayer = null;
}
this.onOpenedCallback = this._defaultOpenedCallback;
break;
default:
break;
}
}

private getPlayer(type: ANIMATION_TYPE): AnimationPlayer {
switch (type) {
case ANIMATION_TYPE.OPEN:
return this.openAnimationPlayer;
case ANIMATION_TYPE.CLOSE:
return this.closeAnimationPlayer;
default:
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,7 @@ export class IgxOverlayService implements OnDestroy {
// is done, 0.75 if 3/4 of the animation is done. As we need to start next animation from where
// the previous has finished we need the amount up to 1, therefore we are subtracting what
// getPosition() returns from one
// TODO: This assumes opening and closing animations are mirrored.
const position = 1 - info.openAnimationInnerPlayer.getPosition();
info.openAnimationPlayer.reset();
// calling reset does not change hasStarted to false. This is why we are doing it her via internal field
Expand Down
Empty file.
Loading

0 comments on commit 0c07a75

Please sign in to comment.