Skip to content

Commit

Permalink
feat(animations): added new hierarchical Route Animation
Browse files Browse the repository at this point in the history
added new hierarchical Animation, now using CustomRouterStateSerializer to extract breadcrumbs and
anumation depth from route config
  • Loading branch information
xmlking committed Oct 28, 2018
1 parent 861c846 commit 0accc9e
Show file tree
Hide file tree
Showing 14 changed files with 86 additions and 81 deletions.
2 changes: 1 addition & 1 deletion apps/api/tsconfig.app.json
Expand Up @@ -4,7 +4,7 @@
"outDir": "../../dist/apps/api",
"module": "commonjs",
"target": "es6",
"types": []
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"include": ["**/*.ts"]
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/tsconfig.app.json
Expand Up @@ -2,7 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc/apps/backend",
"types": []
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"include": ["**/*.ts"]
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/src/app/app.module.ts
Expand Up @@ -28,7 +28,7 @@ export class MyHammerConfig extends HammerGestureConfig {
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', loadChildren: '@ngx-starter-kit/home#HomeModule', data: { preload: true } },
{ path: 'dashboard', loadChildren: '@ngx-starter-kit/dashboard#DashboardModule', data: { preload: true } },
{ path: '404', loadChildren: '@ngx-starter-kit/not-found#NotFoundModule', data: { title: '404' } },
{ path: '404', loadChildren: '@ngx-starter-kit/not-found#NotFoundModule', data: { title: '404', preload: false } },
// 404 should be last
{ path: '**', redirectTo: '404', pathMatch: 'full' },
],
Expand Down
36 changes: 12 additions & 24 deletions libs/animations/src/lib/route.animation.ts
Expand Up @@ -32,41 +32,29 @@ export const routeAnimation = trigger('routeAnimation', [
]),
]);

export const routeAnimation2 = trigger('routeAnimation', [
transition('void => *', [
style({
opacity: 0,
}),
animate(
'400ms 150ms ease-in-out',
style({
opacity: 1,
}),
),
]),
]);

export const hierarchicalRouteAnimation = trigger('routeAnimation', [
transition('1 => 2, 2 => 3', [
transition(':increment', [
style({ height: '!' }),
query(':enter', style({ transform: 'translateX(100%)' })),
query(':enter, :leave', style({ position: 'absolute', top: 0, left: 0, right: 0 })),
query(':enter', style({ transform: 'translateX(100%)' }), {optional: true}),
query(':enter, :leave', style({ position: 'absolute', top: 0, left: 0, right: 0 }), {optional: true}),
// animate the leave page away
group([
query(':leave', [animate('0.3s cubic-bezier(.35,0,.25,1)', style({ transform: 'translateX(-100%)' }))]),
query(':leave', [animate('0.3s cubic-bezier(.35,0,.25,1)', style({ transform: 'translateX(-100%)' }))], {optional: true}),
// and now reveal the enter
query(':enter', animate('0.3s cubic-bezier(.35,0,.25,1)', style({ transform: 'translateX(0)' }))),
query(':enter', animate('0.3s cubic-bezier(.35,0,.25,1)', style({ transform: 'translateX(0)' })), {optional: true}),
// query('@fadeAnimation', animateChild(), {optional: true}),
]),
]),
transition('3 => 2, 2 => 1', [
transition(':decrement', [
style({ height: '!' }),
query(':enter', style({ transform: 'translateX(-100%)' })),
query(':enter, :leave', style({ position: 'absolute', top: 0, left: 0, right: 0 })),
query(':enter', style({ transform: 'translateX(-100%)' }), {optional: true}),
query(':enter, :leave', style({ position: 'absolute', top: 0, left: 0, right: 0 }), {optional: true}),
// animate the leave page away
group([
query(':leave', [animate('0.3s cubic-bezier(.35,0,.25,1)', style({ transform: 'translateX(100%)' }))]),
query(':leave', [animate('0.3s cubic-bezier(.35,0,.25,1)', style({ transform: 'translateX(100%)' }))], {optional: true}),
// and now reveal the enter
query(':enter', animate('0.3s cubic-bezier(.35,0,.25,1)', style({ transform: 'translateX(0)' }))),
query(':enter', animate('0.3s cubic-bezier(.35,0,.25,1)', style({ transform: 'translateX(0)' })), {optional: true}),
// query('@fadeAnimation', animateChild() , {optional: true}),
]),
]),
]);
1 change: 1 addition & 0 deletions libs/core/src/index.ts
Expand Up @@ -3,3 +3,4 @@ export { PageTitleService } from './lib/services/page-title.service';
export { ServiceWorkerService } from './lib/services/service-worker.service';
export { MediaQueryService } from './lib/services/media-query.service';
export { DeepLinkService } from './lib/services/deep-link.service';
export { RouterStateData} from './lib/state/custom-router-state.serializer';
7 changes: 6 additions & 1 deletion libs/core/src/lib/core.module.ts
Expand Up @@ -7,7 +7,7 @@ import { NgxsModule } from '@ngxs/store';
import { NgxsFormPluginModule } from '@ngxs/form-plugin';
import { NgxPageScrollModule } from 'ngx-page-scroll';
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
import { NgxsRouterPluginModule } from '@ngxs/router-plugin';
import { NgxsRouterPluginModule, RouterStateSerializer } from '@ngxs/router-plugin';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faTwitter, faGithub, faGoogle } from '@fortawesome/free-brands-svg-icons';
Expand All @@ -22,6 +22,7 @@ import { PreferenceState } from './state/preference.state';
import { InMemoryDataService } from './services/in-memory-data.service';
import { ErrorInterceptor } from './interceptors/error.interceptor';
import { JwtInterceptor } from './interceptors/jwt.interceptor';
import { CustomRouterStateSerializer } from './state/custom-router-state.serializer';

// Noop handler for factory function
export function noop() {
Expand Down Expand Up @@ -79,6 +80,10 @@ library.add(faTwitter, faGithub, faGoogle);
deps: [EventBus],
multi: true,
},
{
provide: RouterStateSerializer,
useClass: CustomRouterStateSerializer,
},
],
})
export class CoreModule {
Expand Down
27 changes: 9 additions & 18 deletions libs/core/src/lib/services/page-title.service.ts
@@ -1,7 +1,8 @@
import { Injectable } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ActivatedRouteSnapshot, NavigationEnd, Router, RouterState } from '@angular/router';
import { filter } from 'rxjs/operators';
import { Store } from '@ngxs/store';
import { RouterState } from '@ngxs/router-plugin';
import { RouterStateData } from '../state/custom-router-state.serializer';

declare var ga: any;
/**
Expand All @@ -12,30 +13,20 @@ declare var ga: any;
})
export class PageTitleService {
private readonly defaultTitle;
public titleSet: Set<string>;

constructor(private router: Router, private bodyTitle: Title) {
constructor(private store: Store, private bodyTitle: Title) {
this.defaultTitle = bodyTitle.getTitle() || 'WebApp';

// Automatically set pageTitle from route data
router.events.pipe(filter((event: any) => event instanceof NavigationEnd)).subscribe((n: NavigationEnd) => {
const titleSet = new Set();
let root = this.router.routerState.snapshot.root;
do {
root = root.children[0];
if (root.data['title']) {
titleSet.add(root.data['title']);
}
} while (root.children.length > 0);

this.titleSet = titleSet;
// Automatically set pageTitle from router state data
store.select<any>(RouterState.state).subscribe((routerStateData: RouterStateData) => {
console.log(routerStateData);
bodyTitle.setTitle(
`${Array.from(titleSet)
`${Array.from(routerStateData.breadcrumbs.keys())
.reverse()
.join(' | ')} | ${this.defaultTitle}`,
);

ga('send', 'pageview', n.urlAfterRedirects);
ga('send', 'pageview', routerStateData.url);
});
}
}
23 changes: 15 additions & 8 deletions libs/core/src/lib/state/custom-router-state.serializer.ts
@@ -1,25 +1,32 @@
import { Params, RouterStateSnapshot } from '@angular/router';
import { NgxsModule } from '@ngxs/store';
import { NgxsRouterPluginModule, RouterStateSerializer } from '@ngxs/router-plugin';
import { Params, RouterStateSnapshot, UrlSegment } from '@angular/router';
import { RouterStateSerializer } from '@ngxs/router-plugin';

export interface RouterStateParams {
export interface RouterStateData {
url: string;
params: Params;
queryParams: Params;
breadcrumbs: Map<string, string>;
data: any;
}

// Map the router snapshot to { url, params, queryParams }
export class CustomRouterStateSerializer implements RouterStateSerializer<RouterStateParams> {
serialize(routerState: RouterStateSnapshot): RouterStateParams {
// Map the router snapshot to { url, params, queryParams, titleSet }
export class CustomRouterStateSerializer implements RouterStateSerializer<RouterStateData> {
serialize(routerState: RouterStateSnapshot): RouterStateData {
const {
url,
root: { queryParams },
} = routerState;
let { root: route } = routerState;

const breadcrumbs = new Map();
while (route.firstChild) {
route = route.firstChild;
if (route.data['title']) {
breadcrumbs.set(route.data['title'], route.pathFromRoot.flatMap(segment => segment.url).join('/'));
}
}
const { params } = route;
return { url, params, queryParams };
const { data } = route;
return { url, params, queryParams, breadcrumbs, data };
}
}
Expand Up @@ -13,8 +13,9 @@
<ngx-toolbar [quickpanel]="quickpanel" [sidenav]="sidenav"></ngx-toolbar>

<perfect-scrollbar>
<div class="main-container" [@routeAnimation]="getRouteAnimation(dashboardOutlet)">
<router-outlet #dashboardOutlet="outlet"></router-outlet>
<div class="route-container" [@routeAnimation]="depth$ | async">
<!--<ngx-breadcrumbs title="Dashboard" [crumbs]="crumbs$ | async"></ngx-breadcrumbs>-->
<router-outlet></router-outlet>
<ngx-chat-box></ngx-chat-box>
</div>
</perfect-scrollbar>
Expand Down
Expand Up @@ -2,17 +2,20 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Subscription } from 'rxjs';
import { MediaChange, ObservableMedia } from '@angular/flex-layout';
import { NavigationEnd, Router } from '@angular/router';
import { routeAnimation } from '@ngx-starter-kit/animations';
import { routeAnimation, hierarchicalRouteAnimation } from '@ngx-starter-kit/animations';
import { Actions, Store } from '@ngxs/store';
import { ConnectWebSocket, DisconnectWebSocket } from '@ngx-starter-kit/socketio-plugin';
import { OAuthService } from 'angular-oauth2-oidc';
import { environment } from '@env/environment';
import { RouterState } from '@ngxs/router-plugin';
import { map } from 'rxjs/operators';

@Component({
selector: 'ngx-dashboard-layout',
templateUrl: './dashboard-layout.component.html',
styleUrls: ['./dashboard-layout.component.scss'],
animations: [routeAnimation],
// animations: [hierarchicalRouteAnimation],
// encapsulation: ViewEncapsulation.None
})
export class DashboardLayoutComponent implements OnInit, OnDestroy {
Expand All @@ -26,6 +29,8 @@ export class DashboardLayoutComponent implements OnInit, OnDestroy {
sidenavOpen = true;
sidenavMode = 'side';
isMobile = false;
crumbs$;
depth$;

constructor(
private router: Router,
Expand All @@ -36,6 +41,12 @@ export class DashboardLayoutComponent implements OnInit, OnDestroy {
) {}

ngOnInit() {
this.crumbs$ = this.store
.select<any>(RouterState.state)
.pipe(map(state => Array.from(state.breadcrumbs, ([key, value]) => ({ name: key, link: '/' + value }))));

this.depth$ = this.store.select<any>(RouterState.state).pipe(map(state => state.data.depth));

this._mediaSubscription = this.media.subscribe((change: MediaChange) => {
const isMobile = change.mqAlias === 'xs' || change.mqAlias === 'sm';

Expand Down Expand Up @@ -70,8 +81,8 @@ export class DashboardLayoutComponent implements OnInit, OnDestroy {
this.store.dispatch(new DisconnectWebSocket());
}

getRouteAnimation(outlet) {
return outlet.activatedRouteData['animation'] || 'one';
getRouteDepth(outlet) {
return outlet.activatedRouteData['depth'] || 1;
// return outlet.isActivated ? outlet.activatedRoute : ''
}
}
8 changes: 4 additions & 4 deletions libs/dashboard/src/lib/dashboard.module.ts
Expand Up @@ -29,7 +29,7 @@ import { environment } from '@env/environment';
path: '',
component: DashboardLayoutComponent,
canActivate: [AuthGuard],
data: { title: 'Dashboard', animation: 'dashboard' },
data: { title: 'Dashboard', depth: 1 },
children: [
{
path: 'overview',
Expand All @@ -39,17 +39,17 @@ import { environment } from '@env/environment';
{
path: '',
loadChildren: '@ngx-starter-kit/widgets#WidgetsModule',
data: { title: 'Widgets', animation: 'overview', preload: true },
data: { title: 'Widgets', preload: true },
},
{
path: 'grid',
loadChildren: '@ngx-starter-kit/grid#GridModule',
data: { title: 'Grid', animation: 'grid', preload: true },
data: { title: 'Grid', depth: 2, preload: false },
},
{
path: 'experiments',
loadChildren: '@ngx-starter-kit/experiments#ExperimentsModule',
data: { title: 'Experiments', animation: 'experiments' },
data: { title: 'Experiments', depth: 2, preload: false },
},
],
},
Expand Down
22 changes: 11 additions & 11 deletions libs/experiments/src/lib/experiments.module.ts
Expand Up @@ -41,56 +41,56 @@ registerPlugin(FilePondPluginFileValidateType, FilepondPluginFileValidateSize, F
ImageComparisonModule,
RouterModule.forChild([
/* {path: '', pathMatch: 'full', component: InsertYourComponentHere} */
{ path: '', redirectTo: 'animations', pathMatch: 'full', data: { animation: 'experiments' } },
{ path: '', redirectTo: 'animations', pathMatch: 'full' },
{
path: 'animations',
component: AnimationsComponent,
data: { title: 'Animations', animations: 'animations' },
data: { title: 'Animations', depth: 2 },
},
{
path: 'file-upload',
component: FileUploadComponent,
data: { title: 'File Upload', animation: 'file-upload' },
data: { title: 'File Upload', depth: 3 },
},
{
path: 'context-menu',
component: ContextMenuComponent,
data: { title: 'Context Menu', animation: 'context-menu' },
data: { title: 'Context Menu', depth: 3 },
},
{
path: 'virtual-scroll',
component: VirtualScrollComponent,
data: { title: 'Virtual Scroll', animation: 'virtual-scroll' },
data: { title: 'Virtual Scroll', depth: 3 },
},
{
path: 'table',
component: StickyTableComponent,
data: { title: 'Sticky Table', animation: 'sticky-table' },
data: { title: 'Sticky Table', depth: 3 },
},
{
path: 'clap',
component: ClapButtonComponent,
data: { title: 'Clap', animation: 'clap' },
data: { title: 'Clap', depth: 3 },
},
{
path: 'led',
component: LedDemoComponent,
data: { title: 'Led', animation: 'led' },
data: { title: 'Led', depth: 3 },
},
{
path: 'knob',
component: KnobDemoComponent,
data: { title: 'Knob', animation: 'Knob' },
data: { title: 'Knob', depth: 3 },
},
{
path: 'image-comp',
component: ImageCompComponent,
data: { title: 'ImageComp', animation: 'imagecomp' },
data: { title: 'ImageComp', depth: 3 },
},
{
path: 'layout',
component: LayoutComponent,
data: { title: 'Layout', animation: 'layout' },
data: { title: 'Layout', depth: 3 },
},
]),
],
Expand Down

0 comments on commit 0accc9e

Please sign in to comment.