foam-framework
Version:
MVC metaprogramming framework
251 lines (233 loc) • 6.46 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({
name: 'CodeSnippetView',
package: 'foam.flow',
extends: 'foam.flow.Element',
requires: [
'foam.ui.ActionButton',
'foam.flow.CodeView',
'foam.flow.CodeSnippet'
],
imports: [
'actionButtonName',
'codeViewName',
'codeViewLoadState$'
],
exports: [ 'codeViewLoadState$' ],
constants: { ELEMENT_NAME: 'code-snippet' },
properties: [
{
model_: 'StringProperty',
name: 'extraClassName',
defaultValue: 'loading'
},
{
model_: 'StringProperty',
name: 'mode',
defaultValue: 'read-write'
},
{
model_: 'BooleanProperty',
name: 'showActions',
defaultValue: true
},
{
model_: 'BooleanProperty',
name: 'scroll',
defaultValue: true
},
{
model_: 'StringProperty',
name: 'codeViewLoadState',
defaultValue: 'unloaded',
postSet: function(old, nu) {
if ( old === nu ) return;
this.onCodeViewLoadStateChanged();
}
},
{
model_: 'StringProperty',
name: 'codeViewName',
defaultValue: 'foam.flow.CodeView'
},
{
model_: 'ModelProperty',
name: 'actionButtonName',
defaultValue: 'foam.ui.ActionButton'
}
],
methods: [
{
name: 'init',
code: function() {
this.SUPER.apply(this, arguments);
// TODO(markdittmer): Should be able to use foam.ui.ModeProperty here
// but it doesn't seem to be working. It should eliminate the need for
// a postSet.
Events.dynamic(function() {
this.srcView; this.mode; this.scroll;
if ( ! this.srcView ) return;
this.srcView.mode = this.mode;
this.srcView.scroll = this.scroll;
}.bind(this));
}
},
{
name: 'isLoadedOrFailed',
code: function() {
var state = this.codeViewLoadState;
return state === 'loaded' || state === 'failed';
}
}
],
listeners: [
{
name: 'onCodeViewLoadFailed',
isFramed: true,
code: function(_, topics) {
console.warn('Failed to load code view: ' + this.codeViewName);
this.codeViewName = 'foam.flow.CodeView';
this.extraClassName = '';
this.updateHTML();
}
},
{
name: 'onCodeViewLoadStateChanged',
code: function() {
if ( this.codeViewLoadState !== 'failed' ) return;
this.onCodeViewLoadFailed();
}
},
{
name: 'onEditActionFlareStateChanged',
code: function(_, __, ___, nu) {
if ( nu === 'default' ) {
this.enterReadWriteMode_();
this.editView.halo.state_$.removeListener(this.onEditActionFlareStateChanged);
}
}
},
{
name: 'enterReadWriteMode_',
code: function() {
// Button should hide before editor changes modes.
this.editView.className = (this.editView.className || '') + ' hide';
this.mode = 'read-write';
}
}
],
actions: [
{
name: 'edit',
iconUrl: 'https://www.gstatic.com/images/icons/material/system/1x/mode_edit_black_18dp.png',
isEnabled: function() { return this.mode === 'read-only'; },
isAvailable: function() { return this.mode === 'read-only'; },
code: function() {
// TODO(markdittmer): Components involved in animated reactive updates
// should be better decoupled. For now, use halo as indicator that we
// need to synchronize with a flare state change.
if ( this.editView.halo ) {
this.editView.halo.state_$.addListener(this.onEditActionFlareStateChanged);
return;
}
this.enterReadWriteMode_();
}
}
],
templates: [
function toInnerHTML() {/*
<%
this.setClass('loading', function() {
this.codeViewLoadState;
return ! this.isLoadedOrFailed();
}.bind(this), this.id);
%>
<% if ( this.data.title ) { %>
<heading>{{{this.data.title}}}</heading>
<% } %>
$$src{ model_: this.codeViewName }
<% if ( this.showActions ) { %>
<actions>
$$edit{
model_: this.actionButtonName,
className: 'actionButton editButton',
color: 'black',
font: '20px Roboto, Arial',
alpha: 1.0,
radius: 15,
background: '#F7CB4D'
}
</actions>
<% } %>
*/},
function CSS() {/*
code-snippet {
display: block;
position: relative;
}
code-snippet.loading * {
display: none;
}
code-snippet.loading heading {
display: initial;
}
code-sample code-snippet actions, code-snippet actions {
position: absolute;
right: 30px;
bottom: -15px;
z-index: 15;
}
code-snippet canvas.editButton {
background: rgba(0,0,0,0);
box-shadow: 2px 2px 7px #aaa;
border-radius: 50%;
}
code-snippet canvas.editButton.hide {
display: none;
}
@media not print {
aside code-sample code-snippet heading,
code-sample code-snippet heading,
code-snippet heading {
background: initial;
padding: 5px 20px;
font-size: 18px;
border-top: 1px solid #E0E0E0;
}
aside code-sample code-snippet heading,
code-sample code-snippet heading,
code-snippet heading {
z-index: 10;
position: relative;
}
code-snippet heading::after {
bottom: -4px;
content: '';
height: 4px;
left: 0;
position: absolute;
right: 0;
background-image: -webkit-linear-gradient(top,rgba(0,0,0,.12) 0%,rgba(0,0,0,0) 100%);
background-image: linear-gradient(to bottom,rgba(0,0,0,.12) 0%,rgba(0,0,0,0) 100%);
}
}
@media print {
aside code-sample code-snippet heading,
code-sample code-snippet heading,
code-snippet heading {
margin: 3pt;
font-size: 13pt;
}
}
*/}
]
});