foam-framework
Version:
MVC metaprogramming framework
279 lines (264 loc) • 8.05 kB
JavaScript
/**
* @license
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
CLASS({
package: 'foam.apps.builder.wizard',
name: 'WizardPage',
extends: 'foam.ui.md.DetailView',
requires: [
'foam.ui.md.UpdateDetailView',
],
imports: [
'stack',
'dao',
'popup',
'wizardStack',
'appSelection$',
],
properties: [
{
model_: 'foam.apps.builder.wizard.WizardViewFactoryProperty',
name: 'nextViewFactory',
postSet: function(old,nu) {
this.nextView = ( nu ) ? nu({ data$: this.data$ }, this.X) : null;
}
},
{
name: 'nextView',
label: 'Next',
lazyFactory: function() {
return ( this.nextViewFactory ) ? this.nextViewFactory({ data$: this.data$ }, this.X) : null;
},
postSet: function(old,nu) {
if ( old ) {
//Events.unfollow(old.title$, this.nextTitle$);
old.destroy();
if ( old.$ ) old.$.outerHTML = '';
}
if ( nu ) {
//Events.follow(nu.title$, this.nextTitle$);
this.nextTitle = this.model_.NEXT_VIEW.label; // use 'Next' text instead of page title
if ( this.hidden ) {
this.propertyChange('title', null, this.title); // notify title listeners it might have changed
}
} else {
this.nextTitle = this.model_.NEXT_TITLE.defaultValue;
}
}
},
{
model_: 'StringProperty',
name: 'nextTitle',
defaultValue: 'Finish',
},
{
model_: 'StringProperty',
name: 'title',
getter: function(name) {
if ( ! this.hidden ) {
var value = this.instance_[name];
if ( typeof value === 'undefined' ) {
var prop = this.model_.getProperty(name);
if ( prop.lazyFactory ) {
value = this.instance_[prop.name] = prop.lazyFactory.call(this, prop);
} else if ( prop.factory ) {
value = this.instance_[prop.name] = prop.factory.call(this, prop);
} else if ( prop.defaultValueFn ) {
value = prop.defaultValueFn.call(this, prop);
} else if ( typeof prop.defaultValue !== undefined ) {
value = prop.defaultValue;
}
}
return value;
}
if ( this.nextView ) return this.nextView.title;
return "Finish";
}
},
{
model_: 'BooleanProperty',
name: 'hidden',
defaultValue: false,
},
{
model_: 'BooleanProperty',
name: 'scrollContent',
documentation: 'If true, contentHTML will not be padded and occupy the full page width',
defaultValue: false,
},
{
model_: 'BooleanProperty',
name: 'showWizardHeading',
documentation: 'If true, heading is shown.',
defaultValue: true,
},
{
model_: 'BooleanProperty',
name: 'showWizardInstructions',
documentation: 'If true, instructions are shown.',
defaultValue: true,
},
{
model_: 'BooleanProperty',
name: 'showWizardActions',
documentation: 'If true, exit/back/next actions are shown.',
defaultValue: true,
},
],
actions: [
{
name: 'nextAction',
labelFn: function() {
return this.nextTitle;
},
code: function() {
this.onNext();
var X = this.X;
var nextV = this.nextView;
if ( ! nextV ) {
// no next view, so we're finished
if ( this.popup ) this.popup.close();
else this.stack.popView();
return;
}
if ( nextV.hidden ) {
nextV.nextAction(); // don't add the hidden views to the stack, just plow straight through
} else {
this.stack.pushView(nextV);
nextV.onShown(); // TODO: do this in the stackview?
}
}
},
{
name: 'exit',
label: 'Cancel',
isAvailable: function() { return this.stack.views_.length <= 1; },
code: function() {
this.onCancel();
this.popup && this.popup.close();
}
},
{
name: 'back',
label: 'Back',
isAvailable: function() { return this.stack.views_.length > 1; },
code: function() {
this.onBack();
//this.popup && this.popup.close();
if ( this.stack.views_.length <= 1 ) {
this.onCancel();
this.popup && this.popup.close();
}
this.stack.popView();
}
}
],
methods: [
function init() {
this.SUPER();
this.nextViewFactory = this.nextViewFactory;
},
function initHTML() {
this.SUPER();
if ( this.popup ) {
this.popup.subscribe(this.popup.CANCEL, this.onCancel.bind(this));
}
},
function onNext() {
/* if you need to do anything when the user picks the 'next' action,
implement this method. Remember to call this.SUPER() at the end of your
implementation, or handle saving this.data yourself. */
this.dao && this.dao.put(this.data, { put: function(cfg) {
if ( ( ! this.appSelection ) || ( this.appSelection.appId !== cfg.appId ) ) {
this.appSelection = cfg;
}
}.bind(this) });
},
function onCancel() {
/* if you need to do anything when the user picks the 'cancel' action, implement this method */
},
function onBack() {
/* if you need to do anything when the user picks the 'back' action, implement this method */
},
function onShown() {
/* if you need to do anything when the page is shown, implement this method */
// TODO: skip to next page if hidden?
},
],
templates: [
function titleHTML() {/*
<p class="md-title"><%# this.title %></p>
*/},
function instructionHTML() {/*
*/},
function contentHTML() {/*
*/},
function toHTML() {/*
<wizard id="%%id" <%= this.cssClassAttr() %>>
<div id="%%id-heading" class="wizard-section">
<div class="md-card-heading">
<% this.titleHTML(out); %>
</div>
<div class="md-card-heading-content-spacer"></div>
</div>
<% if ( this.scrollContent ) { %>
<div id="%%id-instructions" class="md-card-content no-shrink wizard-section">
<% this.instructionHTML(out); %>
</div>
<% this.contentHTML(out); %>
<% } else { %>
<div class="md-card-content">
<div id="%%id-instructions" class="wizard-section">
<% this.instructionHTML(out); %>
</div>
<% this.contentHTML(out); %>
</div>
<div class="md-card-content-footer-spacer"></div>
<% } %>
<div id="%%id-actions" class="wizard-section md-actions md-card-footer horizontal">
$$exit
$$back
<div class="spacer"></div>
$$nextAction
</div>
</wizard>
<% this.setClass('hidden', function() {
return ! this.showWizardHeading;
}.bind(this), this.id + '-heading');
this.setClass('hidden', function() {
return ! this.showWizardInstructions;
}.bind(this), this.id + '-instructions');
this.setClass('hidden', function() {
return ! this.showWizardActions;
}.bind(this), this.id + '-actions'); %>
*/},
function CSS() {/*
wizard {
display: flex;
flex-direction: column;
height: 100%
}
wizard .no-shrink {
flex-shrink: 0;
}
.wizard-section.hidden {
display: none;
}
.wizard-section.md-actions.md-card-footer.horizontal flat-button {
margin: 8px;
padding: 8px;
}
.wizard-section.md-card-footer .spacer {
flex-grow: 9999;
flex-basis: 0;
}
*/},
],
});