<!DOCTYPE html>
<html>

  <head>
    <base href="." />
    <link href="https://fonts.googleapis.com/css?family=Lobster|Open+Sans:400,600" rel="stylesheet">
    <script type="text/javascript" charset="utf-8">
      window.window.AngularVersionForThisPlunker = 'latest'
    </script>
    <title>angular playground</title>
    <link rel="stylesheet" href="app.css" />
    <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
    <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>

  <body>
    <dashboard>loading...</dashboard>
  </body>

</html>
body {
  font-family: 'Open Sans', sans-serif;
}
var angularVersion;
if(window.AngularVersionForThisPlunker === 'latest'){
  angularVersion = ''; //picks up latest
}
else {
  angularVersion = '@' + window.AngularVersionForThisPlunker;
}

System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {
    
    'app': './src',
    '@angular/core': 'npm:@angular/core'+ angularVersion + '/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common' + angularVersion + '/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler' + angularVersion  + '/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic' + angularVersion + '/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http' + angularVersion + '/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router' + angularVersion +'/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms' + angularVersion + '/bundles/forms.umd.js',
    '@angular/animations': 'npm:@angular/animations' + angularVersion + '/bundles/animations.umd.js',
    '@angular/platform-browser/animations': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser-animations.umd.js',
    '@angular/animations/browser': 'npm:@angular/animations' + angularVersion + '/bundles/animations-browser.umd.js',
    
    '@angular/core/testing': 'npm:@angular/core' + angularVersion + '/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common' + angularVersion + '/bundles/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler' + angularVersion + '/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic' + angularVersion + '/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http' + angularVersion + '/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router' + angularVersion + '/bundles/router-testing.umd.js',
    'tslib': 'npm:tslib@1.6.1',
    'rxjs': 'npm:rxjs',
    'typescript': 'npm:typescript@2.2.1/lib/typescript.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {DashboardModule} from './dashboard';

platformBrowserDynamic().bootstrapModule(DashboardModule)
//our root app component
import {Component, NgModule, VERSION} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import { style, animate, group, animateChild, transition, state, trigger, query, stagger } from '@angular/animations';

import {ProfileDetailsComponent} from './profile-details';
import {ProfileStatsComponent} from './profile-stats';

export interface User {
  name: string;
}

@Component({
  selector: 'dashboard',
  styleUrls: ['src/dashboard.css'],
  template: `
    <div>
      <header>
        <span class="primary-font title justify-start">Dashboard</span>
        <div class="image-container" (click)="toggleProfileDetails()" data-tooltip="Profile" >
          <img class="profile-button" src="https://api.adorable.io/avatars/60/me@you.com.png" />
        </div>
      </header>
      
      <profile-details [user]="user" *ngIf="showProfileDetails"></profile-details>
    </div>
  `
})
export class DashboardComponent {
  showProfileDetails: boolean = false;
  user: User;
  
  constructor() {
    this.user = {
      name: 'Dominic Elm',
      title: 'Frontend Developer',
      location: 'Germany',
      hearts: 235,
      views: 23500
    };
  }
  
  toggleProfileDetails() {
    this.showProfileDetails = !this.showProfileDetails;
  }
}

@NgModule({
  imports: [ 
    BrowserModule, 
    BrowserAnimationsModule 
  ],
  declarations: [ DashboardComponent, ProfileDetailsComponent, ProfileStatsComponent ],
  bootstrap: [ DashboardComponent ]
})
export class DashboardModule {}
.title {
  color: white;
  font-weight: bold;
  margin-right: auto;
}

header {
  height: 40px;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  background-color: #272727;
  padding: 0.5em 1em;
}

.profile-button {
  width: 40px;
  cursor: pointer;
  border-radius: 50%;
}

.image-container {
  display: flex;
  align-items: center;
}

.image-container:hover:before {
  opacity: 1;
  transform: translateX(-10px);
}

.image-container:before {
  display: inline-block;
  content: attr(data-tooltip);
  color: white;
  opacity: 0;
  transform: translateX(-30px);
  transition: opacity .2s ease, transform .2s ease;
}
import { Component, Input, HostBinding } from '@angular/core';
import { style, animate, animation, animateChild, useAnimation, group, sequence, transition, state, trigger, query, stagger } from '@angular/animations';
import { User } from './dashboard.ts';
import { fadeAnimation } from './animations.ts';

@Component({
  selector: 'profile-details',
  styleUrls: ['src/profile-details.css'],
  animations: [
    trigger('profileAnimation', [
      transition(':enter', group([
        query('.wrapper', style({ opacity: 0, transform: 'rotateX(-90deg) translateY(150px) translateZ(50px)' })),
        query('.profile-image-border, .profile-image', style({ transform: 'scale(0)' })),
        query('.username, .username-title', style({ opacity: 0, transform: 'translateX(50px)' })),
        query('main', style({ transform: 'translateY(100%)' })),
        
        query('.wrapper', group([
          useAnimation(fadeAnimation, {
            params: {
              duration: '150ms',
              to: 1
            }
          }),
          animate('300ms cubic-bezier(0.68, 0, 0.68, 0.19)', style({ transform: 'matrix(1, 0, 0, 1, 0, 0)' }))  
        ])),
        
        query('.profile-image-border', [
          animate('200ms 250ms ease-out', style('*'))
        ]),
        
        query('.profile-image', [
          animate('200ms 300ms ease-out', style('*'))
        ]),
        
        query('.username, .username-title', stagger('100ms', [
          animate('200ms 250ms ease-out', style('*'))
        ])),
        
        query('main', [
          animate('200ms 250ms ease-out', style('*'))
        ])
        
        query('profile-stats', animateChild())
      ]))
    ])
  ],
  template: `
    <div class="wrapper">
      <header>
        <div class="profile-image-wrapper">
          <div class="profile-image-border"></div>
          <img class="profile-image" src="https://api.adorable.io/avatars/90/me@you.com.png" />
        </div>
        <div class="profile-header-content">
          <span class="username">{{ user.name }}</span>
            <span class="username-title">{{ user.title }}</span>
        </div>
      </header>
      <main>
        <profile-stats [user]="user"></profile-stats>
      </main>
    </div>
  `,
})
export class ProfileDetailsComponent {
  @Input() user: User;
  
  @HostBinding('@profileAnimation')
  public animateProfile = true;
  
  constructor() {}
}
:host {
  position: fixed;
  perspective: 500px;
  top: calc(50% + 30px);
  left: 50%;
  transform: translate(-50%, -50%);
  width: 80%;
  max-width: 400px;
}

.wrapper {
  box-shadow: 0px 0 20px 0px rgba(0,0,0,0.2);
  transform-origin: top center;
  background: white;
  height: 320px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

header {
  padding: 2em;
  display: flex;
  justify-content: center;
  z-index: 0;
}

main {
  background: #f8f8f8;
  flex-grow: 1;
}

.profile-image {
  border-radius: 50%;
}

.profile-image-wrapper {
  position: relative;
  display: inline-block;
  height: 90px;
  width: auto;
}

.profile-image-border {
  background: #e3e3e3;
  position: absolute;
  top: -4px;
  right: -4px;
  bottom: -4px;
  left: -4px;
  border-radius: 50%;
  z-index: -1;
}

.profile-header-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin-left: 1.5em;
  color: #c8c8c8;
}

.profile-header-content > span {
  position: relative;
}

.username {
  font-weight: 600;
  font-size: 1.3em;
}
import { Component, Input, HostBinding } from '@angular/core';
import { style, animate, animation, animateChild, useAnimation, group, sequence, transition, state, trigger, query, stagger } from '@angular/animations';
import { User } from './dashboard.ts';

@Component({
  selector: 'profile-stats',
  styleUrls: ['src/profile-stats.css'],
  animations: [
    trigger('statsAnimation', [
      transition('* => *', group([
        query('.stats-icon', style({ opacity: 0, transform: 'scale(0.8) translateY(10px)' })),
        query('.stats-text', style({ opacity: 0 })),
      
        query('.stats-icon', stagger('100ms', [
          animate('200ms 250ms ease-out', style('*'))
        ])),
        
        query('.stats-text', stagger('100ms', [
          animate('200ms 250ms ease-out', style('*'))
        ])),
      ])
    ])
  ],
  template: `
    <ul class="stats">
      <li class="stats-item">
        <span class="stats-icon icon-eye"></span>
        <span class="stats-text">{{ user.views }}</span>
      </li>
      <li class="stats-item">
        <span class="stats-icon icon-location"></span>
        <span class="stats-text">{{ user.location }}</span>
      </li>
      <li class="stats-item">
        <span class="stats-icon icon-heart"></span>
        <span class="stats-text">{{ user.hearts }}</span>
      </li>
    </ul>
  `,
})
export class ProfileStatsComponent {
  @Input() user: User;
  
  @HostBinding('@statsAnimation')
  public animateProfile = true;
  
  constructor() {}
}
.stats {
  list-style: none;
  display: flex;
  justify-content: space-around;
  padding: 2em;
}

.stats-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #ababab;
}

.stats-icon {
  position: relative;
  margin-bottom: 1em;
  height: 32px;
  width: 32px;
}

.icon-eye {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACyElEQVRYR+1WwXUaMRCdUQN2KohdgenAuANcgdGs78EVhFRg585KUEFwBbE7IBXEriCmAU3eJ9I+7bK74HDwhb3wQMvMn/9n/ojpgx/+4Px0BHBk4N0MeO9PVfWCmQdEdBqb+E1VV8z8y1r79p7G3gsAkhLRjaqOiGi4I8ETMy+JaLEPmF4ASBxCuGdmJN5Uq6prZl6p6lMOhJmHqjpg5pOMlaUx5q4PSCcA7/0XVZ1mNC9CCMvb21tU1/nMZrORMQaAbxIQIpqIyKLtT1sAvPcDVb3PqH5k5om19gUB2lghIvRArVrv/VksIAGBNDbFSWBqAGLyn6haVV+NMWNrbUV1fp7kwGdOOzNfWWtXKYH3fhhCmDPzZwBtnlcAyrIcM7OPf1yIyDinLHb/7yjJMzMDXMUKGCCiy5jkvKm7c24eZQFbd0VR4Ps/K86Tx8OHpl5ZgGcRaZ0E5xzYAojvIjJpxijLcsLMkBfNbAGCG8k3P7Y1S1mWoO+EmVHdpvLmE3UHSy8ict4Rp2IaINg59ydq3pkcgZxzihEsiiKZT+skJKAi0jlhWdFvYGBTWaKka74AAPqKyKe+MXwPADQ6x7n9ketyqAQIXBTF2S4JQgjXbU3YKkXWhE8ictUWfFcTRnPbNHjVhClQoxnnTQuNBvQSZ75mKrH5MMKw47Ux5iwfw8y8NqOdy71lRCEEBIefIxmcq2ZE2Tlipc1X7QljzLBpRKoKcHBGgKudt1kxXDCZCtCCjW8NK8aMY5zgbnjnlYjw3kOqHKyEEL7CsCLLMK9R06D6RgVJpslmAQRrVkQe+6bAOXepqgCX6F4jTlEUW+ZWOWFXwKgdgMDBqjVLRLh81NaxqsIdq0tKXNsAPf2vddzcAyEEVITKLvoYIKLnuBnnB19IOuwWDTcIIdSuZMYYbMDVPknzuHtdyXZUfNDxEcCRgQ9n4C9KZe8Lt5Mt/QAAAABJRU5ErkJggg==");
  background-repeat: no-repeat;
}

.icon-location {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADJElEQVRYR61XwVEcMRDUiACACAwRGCIAIuAcAUjizxEBEAH4j6QjAkMEPiLgHIFxBOAA2HbNlnSl25K0OvD+dksatXp6emZJrPF477cAHAOYENGOEGIvbF8AeCGiByJ6VEq9tYal1oXW2isiOhdCbIU9T4O9B+H9jYhulVLXLbFHAYRb/+TbAvgDYLqxsTEf3pLXvb+/H/LhRPRFCLEgoqMxNqoABodfG2OuWm4V2LpsAVEF4JybCyEOAChjzGx4uPf+EAC01sN0CGvtKRF5IcRca31UAl4EEAMAWLm5936n67pLIjpNgwKYSSmvlVIv8XtkonQBXlcD0AcyxrDa+8d7zzpgPbAQHwEs+iBEXA3HQggWIOe9/86PtZarg1nazbGQBXB3dzeRUv4AcGGMuQ2Hcwn+BkBSyolSitOzfDgdXddxGYKIdqP4rLVTIrrpuu7b2dnZwxBEFoBzjvN9EgL1TDjnGMh5KRCvSYAv08YpY+BCiO9a62krgDmAPWNMrPmeymFKcpTmKLfWcmoWWuvDZgC8MN3gnAPnXWs9qZVirByt9ZLd8G0lXoxRSgHnd1NrvR8XBgBPuVukgHIAAisv6zDAYjnO3OJrKrAhE8G4XodM1djLMhA9IBVc4gszY4zKpcE51wNP6z4RZtbMsgAS5d5rrZeGkxzwIKW8iKYTzOmGiFgfKzpJKmo71xeKRhRyuUJ5oJjL8SQwENturJZ7IprGg8J6LsFfJe2sbcXBlLgHMDPRJdntZkNzilZc845qMxqz0Vo5Rhse844xAL2N1ppJCUQi2qz4qj6QBv0oC63OOToRldpyjf6WNtzMQNJSN2smFANG5QP4m7byEuBRBkpdrpJ7Hl4va8pP9zYBCO24H8/SFp2x4th6R3vGWikItc/T0HNtxouNiIj206moppdmBgILxaEken5p8PiUBlKBdV3HrvcabtlbcRDeM4BtKeXO2L/AhzQQNyXj9nLEahnX/gsDyXASBckDCw+rPCk3C+9TDATK+d+Af7240wkAu1LKvfSfYKxPrF0Fw4Bx3A4AluN768GfBpB4Q3bYbAXyD+fiQj/I0Pj/AAAAAElFTkSuQmCC");
  background-repeat: no-repeat;
}

.icon-heart {
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACuUlEQVRYR8VX7XETMRDdtQuIqYC4AkwFmApIB7GkAogrIFQAKUCS04GpgKQCnAowHTgF+JZ5N9KN7nKf9kE8kx+Jtbtvn1bvbZhe+cN19Z1zn0TkipkXRISfnYjsmXmrtb5vw+ycuw6xl0nsLsT+qMaWAHjvFyLyjYiWLUWQTCmldumZEOtD0abwB2Zep7EFgJDgJxHNQvR9lmXb6XR6OB6Ps8lkchU6uyCiAzN/jInSWBF5RrfVWCK6DnlLsTkA7/1MRH6juIg8oZhSal9tw3t/icTM/C6CwBkRyYF3xC6yLNsksXOl1CEH4JxDgqWI/JlMJgt80cQhwGZZBipzEOFcLL7sEYsrfEtE91rrFaOr0D0FWh+6HkYFBBgAa63FY07vPRpFw6g3Z+fcdyL6TESPWuu24Svhwr2DCfwxFC8NZcdLQdwHIroDgPwXEVkbYwDmn3+stTfMjNf2CAAS6MBUd9I/BrrSNbw6AGst3uVF3wEck4FcM5IZ+GqMuR2jQFcOa+0tM3/JZyD5Za+1nncFj/G9cw6ih+e/LunA/3gJyQuADryJShi1oKTTY3RbY1jRb+601jeFGVlrIZGFxlfd7lwwFcN6MsbA5il1w5LGQybbdH0IoCD3vxLDKmT7xT4QjAaWC0YgTo3G1AdEcFrQjl3jOZhd4bQvNqKo8dCGc0HUFEfnJc+oXcnGANGneGkGapaPwjZFZGOMUX0oj2estZ6ZV8Fn3jcNdS0DSZIVM2PPg+f3BpEWFxFljNk0gW8FgCBr7SAQQ4q3XkGKuAKi0TMSWQdjrZ3H/J0MxIPOOdCYb7Z1yVOQ2HSgcn1mpjcAJGsCUSmeL5t9ive+gjRZFUSY8nxQ46bbt/hJAAITcaks1vKhS+3gGUi7Omctr7IzaAaqIERkG64A/0md5BknAxhyz21n/wLMOAqHi8kHLQAAAABJRU5ErkJggg==');
  background-repeat: no-repeat;
  transform: scale(0.8);
}
import { style, animate, animation, animateChild, useAnimation, group, sequence, transition, state, trigger, query, stagger } from '@angular/animations';

export const fadeAnimation = animation([
  animate('{{ duration }}', style({ opacity: '{{ to }}' }))
], { params: { duration: '1s', to: 1 }});