Skip to content

Instantly share code, notes, and snippets.

@Scapal
Last active April 21, 2016 13:32
Show Gist options
  • Save Scapal/27e46dd56fb48f5b02aa725ac04b26ca to your computer and use it in GitHub Desktop.
Save Scapal/27e46dd56fb48f5b02aa725ac04b26ca to your computer and use it in GitHub Desktop.
Aurelia navigation-view Demo
/* animate page transitions */
div.page {
position: absolute;
left: 0;
top: 0;
right: 0;
/*width: 100%;*/
height: 100%;
}
.nav-forward div.page.au-enter-active {
-webkit-animation: fadeInRight 1s;
animation: fadeInRight 1s;
}
.nav-forward div.page.au-leave-active {
-webkit-animation: fadeOutLeft 1s;
animation: fadeOutLeft 1s;
}
.nav-back div.page.au-enter-active {
-webkit-animation: fadeInLeft 1s;
animation: fadeInLeft 1s;
}
.nav-back div.page.au-leave-active {
-webkit-animation: fadeOutRight 1s;
animation: fadeOutRight 1s;
}
.animate-title-forward-add {
-webkit-animation: fadeInRight 1s;
animation: fadeInRight 1s;
}
.animate-title-back-add {
-webkit-animation: fadeInRight 1s;
animation: fadeInLeft 1s;
}
/* animation definitions */
@-webkit-keyframes fadeInRight {
0% {
/*opacity: 0;*/
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0)
}
100% {
/*opacity: 1;*/
-webkit-transform: none;
transform: none
}
}
@keyframes fadeInRight {
0% {
/*opacity: 0;*/
-webkit-transform: translate3d(100%, 0, 0);
-ms-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0)
}
100% {
/*opacity: 1;*/
-webkit-transform: none;
-ms-transform: none;
transform: none
}
}
@-webkit-keyframes fadeOutLeft {
0% {
/*opacity: 1;*/
-webkit-transform: none;
transform: none
}
100% {
/*opacity: 0;*/
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0)
}
}
@keyframes fadeOutLeft {
0% {
/*opacity: 1;*/
-webkit-transform: none;
transform: none
}
100% {
/*opacity: 0;*/
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0)
}
}
/* animation definitions */
@-webkit-keyframes fadeInLeft {
0% {
/*opacity: 0;*/
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0)
}
100% {
/*opacity: 1;*/
-webkit-transform: none;
transform: none
}
}
@keyframes fadeInLeft {
0% {
/*opacity: 0;*/
-webkit-transform: translate3d(-100%, 0, 0);
-ms-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0)
}
100% {
/*opacity: 1;*/
-webkit-transform: none;
-ms-transform: none;
transform: none
}
}
@-webkit-keyframes fadeOutRight {
0% {
/*opacity: 1;*/
-webkit-transform: none;
transform: none
}
100% {
/*opacity: 0;*/
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0)
}
}
@keyframes fadeOutRight {
0% {
/*opacity: 1;*/
-webkit-transform: none;
transform: none
}
100% {
/*opacity: 0;*/
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0)
}
}
<template>
<require from="bootstrap/css/bootstrap.css"></require>
<require from="navigation-view"></require>
<require from="nav-bar"></require>
<require from="animate.css"></require>
<nav-bar nav.bind="nav"></nav-bar>
<div style="position: absolute;top: 100px;width: 100%;">
<navigation-view navigation-view.ref="nav" title="Page 1" view-model="page1" model="{my-data: 'test'}"></navigation-view>
</div>
</template>
export class App {
}
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
</head>
<body aurelia-app="main">
<h1>Loading...</h1>
<script src="https://cdn.rawgit.com/jdanyow/aurelia-bundle/v1.0.3/jspm_packages/system.js"></script>
<script src="https://cdn.rawgit.com/jdanyow/aurelia-bundle/v1.0.3/config.js"></script>
<script>
System.import('aurelia-bootstrapper');
</script>
</body>
</html>
import 'bootstrap';
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
aurelia.use.plugin('aurelia-animator-css');
aurelia.start().then(() => aurelia.setRoot());
}
<template bindable="nav">
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<ul class="nav navbar-nav navbar-left">
<li if.bind="nav != unknown && nav.length > 0">
<a href="#" click.delegate="nav.navigateBack()">Back</a>
</li>
</ul>
<p class="navbar-text" ref="titleElement">${nav.title}</p>
<ul class="nav navbar-nav navbar-right">
<li class="loader" if.bind="router.isNavigating">
<i class="fa fa-spinner fa-spin fa-2x"></i>
</li>
</ul>
</div>
</nav>
</template>
import {inject, bindable} from 'aurelia-framework';
import {CssAnimator} from 'aurelia-animator-css';
@inject(CssAnimator)
export class NavBar {
@bindable nav;
subscription = null;
constructor(cssAnimator) {
this.cssAnimator = cssAnimator;
}
detached() {
if (this.subscription) this.subscription.dispose();
}
navChanged() {
if (this.subscription) this.subscription.dispose();
if (this.nav) {
this.subscription = this.nav.subscribe((direction) => {
return this.cssAnimator.animate(this.titleElement, `animate-title-${direction}`);
});
}
}
}
import {
inject, Container, ViewSlot, ViewLocator,
customElement, noView, CompositionTransaction,
CompositionEngine, bindable
} from 'aurelia-framework';
@noView()
@customElement('navigation-view')
@inject(Element, Container, ViewSlot, ViewLocator, CompositionEngine, CompositionTransaction)
export class NavigationView {
@bindable viewModel;
@bindable model;
@bindable title;
viewChanged = null;
subscribers = [];
stack = [];
busy = false;
constructor(element, container, viewSlot, viewLocator, compositionEngine, compositionTransaction) {
this.element = element;
this.container = container;
this.viewSlot = viewSlot;
this.viewLocator = viewLocator;
this.compositionEngine = compositionEngine;
this.compositionTransaction = compositionTransaction;
this.controller = null;
if (!('initialComposition' in compositionTransaction)) {
compositionTransaction.initialComposition = true;
this.compositionTransactionNotifier = compositionTransaction.enlist();
}
}
get length() {
return this.stack.length;
}
_getViewModel(instruction) {
if (typeof instruction.viewModel === 'function') {
instruction.viewModel = Origin.get(instruction.viewModel).moduleId;
}
if (typeof instruction.viewModel === 'string') {
return this.compositionEngine.ensureViewModel(instruction);
}
return Promise.resolve(instruction);
}
created(owningView) {
this.owningView = owningView;
}
bind(bindingContext, overrideContext) {
this.container.viewModel = bindingContext;
this.overrideContext = overrideContext;
}
attached() {
this.navigate(this.viewModel, this.title, this.model);
}
_navigate(controller, title, swapDirection) {
let oldController = this.controller;
this.controller = controller;
let classList = this.element.classList;
this.title = title;
let compositionHandler = () => {
if (this.compositionTransactionNotifier) {
this.compositionTransactionNotifier.done();
this.compositionTransactionNotifier = null;
}
classList.remove(`nav-${swapDirection}`);
this.busy = false;
};
if (!oldController) {
return Promise.resolve(this.viewSlot.add(this.controller.view)).then( () => compositionHandler() );
} else {
classList.add(`nav-${swapDirection}`);
this.invokeSubscribers(swapDirection);
return Promise.all([
this.viewSlot.add(this.controller.view),
this.viewSlot.remove(oldController.view, true)
]).then( () => compositionHandler() );
}
}
navigate(viewModel, title, model) {
if (this.busy) return;
this.busy = true;
let childContainer = this.container.createChild();
let instruction = {
viewModel: viewModel,
container: this.container,
childContainer: childContainer,
model: model,
skipActivation: true
};
this._getViewModel(instruction).then(returnedInstruction => {
this.invokeLifecycle(returnedInstruction.viewModel, 'canActivate', model).then(canActivate => {
if (canActivate) {
this.compositionEngine.createController(returnedInstruction).then(controller => {
this.invokeLifecycle(returnedInstruction.viewModel, 'activate', model).then( () => {
if (!this.compositionTransactionNotifier) {
this.compositionTransactionOwnershipToken = this.compositionTransaction.tryCapture();
}
controller.automate(this.overrideContext, this.owningView);
if (this.compositionTransactionOwnershipToken) {
return this.compositionTransactionOwnershipToken.waitForCompositionComplete().then(() => {
this.compositionTransactionOwnershipToken = null;
if (this.controller !== null) {
this.stack.push({controller: this.controller, title: this.title});
}
this._navigate(controller, title, 'forward');
});
}
});
});
}
});
});
}
navigateBack() {
if (!this.busy && this.stack.length > 0) {
this.busy = true;
let previousState = this.stack.pop();
let controller = previousState.controller;
controller.bind(this);
this._navigate(controller, previousState.title, 'back');
}
}
subscribe(callback) {
let subscribers = this.subscribers;
subscribers.push(callback);
return {
dispose() {
let idx = subscribers.indexOf(callback);
if (idx !== -1) {
subscribers.splice(idx, 1);
}
}
};
}
invokeSubscribers(direction) {
let i = this.subscribers.length;
while (i--) {
this.subscribers[i](direction);
}
}
invokeLifecycle(instance, name, model) {
if (typeof instance[name] === 'function') {
let result = instance[name](model, this);
if (result instanceof Promise) {
return result;
}
if (result !== null && result !== undefined) {
return Promise.resolve(result);
}
return Promise.resolve(true);
}
return Promise.resolve(true);
}
}
<template>
<div class="page au-animate">
<p>This view instance title: ${nav.title}</p>
<a href="#" click.delegate="nav.navigate('page2', 'Page 2', {someData: 'test2'})">Create and navidate to a new page 2</a>
<div>
<form class="col-xs-10">
<div class="form-group">
<label for="testInput">This content will be intact when you navigate back to it</label>
<input type="text" class="form-control" id="testInput">
</div>
</form>
</div>
</section>
</template>
export class Page1 {
activate(model, nav) {
this.nav = nav;
}
}
<template>
<div class="page au-animate">
<p>This view instance title: ${nav.title}</p>
<a href="#" click.delegate="nav.navigate('page1', 'Another Page 1', {someData: 'test2'})">Create and navidate to a new page 1</a>
<div>
<form class="col-xs-10">
<div class="form-group">
<label for="testInput">This content will be intact when you navigate back to it</label>
<input type="text" class="form-control" id="testInput">
</div>
</form>
</div>
</section>
</template>
export class Page1 {
activate(model, nav) {
this.nav = nav;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment