This commit is contained in:
George Michael Brower 2014-08-15 12:32:49 -04:00
parent c70498ebcd
commit 1b5100e8e4
20 changed files with 575 additions and 395 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
bower_components
node_modules

View File

@ -1,66 +1,3 @@
<link rel="import" href="../components/polymer/polymer.html"> <link rel="import" href="../components/polymer/polymer.html">
<script src="base-controller.js"></script>
<polymer-element name="base-controller"> <polymer-element name="base-controller"></polymer-element>
<template>
<style>
</style>
</template>
</div>
<script>
Polymer('base-controller', {
object: null,
property: null,
ready: function() {
this.update();
},
objectChanged: function() {
if ( this.object && this.property ) {
this.bindToObject();
}
},
propertyChanged: function() {
if ( this.object && this.property ) {
this.bindToObject();
}
},
valueChanged: function() {
if ( this._boundToObject ) this.object[ this.property ] = this.value;
else this.update();
},
bindToObject: function() {
var _this = this;
this._boundToObject = true;
Object.observe( this.object, function( changes ) {
changes.forEach( function( c ) {
if ( c.name == _this.property ) {
_this.update()
}
} );
} );
},
});
</script>
</polymer-element>

View File

@ -0,0 +1,87 @@
Polymer('base-controller', {
value: null,
object: null,
property: null,
ready: function() {
var _this = this;
this._observer = function( changes ) {
changes.forEach( function( c ) {
if ( c.name == _this.property ) {
_this.value = _this.object[ _this.property ];
}
} );
};
this.update();
},
// Observers
// -------------------------------
objectChanged: function( oldObject, newObject ) {
if ( oldObject && this.property ) {
this.unbind( oldObject );
}
if ( newObject && this.property ) {
this.bind();
}
},
propertyChanged: function( oldProperty, newProperty ) {
if ( oldProperty && this.object ) {
this.unbind( this.object );
}
if ( newProperty && this.object ) {
this.bind();
}
},
valueChanged: function() {
if ( this.object && this.property ) {
this.object[ this.property ] = this.value;
}
this.update();
},
bind: function() {
Object.observe( this.object, this._observer );
this.value = this.object[ this.property ];
},
unbind: function( object ) {
Object.unobserve( object, this._observer );
},
update: function() {},
// Helpers
// -------------------------------
map: function( x, a, b, c, d ) {
return ( x - a ) / ( b - a ) * ( d - c ) + c;
}
});

6
elements/gui-panel.css Normal file
View File

@ -0,0 +1,6 @@
:host {
position: absolute;
top: 0;
right: 20px;
width: 300px;
}

View File

@ -1,17 +1,12 @@
<link rel="import" href="../components/polymer/polymer.html"> <link rel="import" href="../components/polymer/polymer.html">
<polymer-element name="gui-panel"> <polymer-element
name="gui-panel"
constructor="GUI">
<template> <template>
<style> <link rel="stylesheet" href="gui-panel.css">
:host {
position: absolute;
top: 0;
right: 20px;
width: 300px;
}
</style>
<div> <div>
<content></content> <content></content>
@ -21,14 +16,6 @@
</div> </div>
<script> <script src="gui-panel.js"></script>
Polymer('gui-panel', {
ready: function() {
}
});
</script>
</polymer-element> </polymer-element>

7
elements/gui-panel.js Normal file
View File

@ -0,0 +1,7 @@
Polymer('gui-panel', {
ready: function() {
}
});

6
elements/gui-panel.styl Normal file
View File

@ -0,0 +1,6 @@
:host {
position: absolute;
top: 0;
right: 20px;
width: 300px;
}

18
elements/gui-row.css Normal file
View File

@ -0,0 +1,18 @@
#row {
-webkit-text-select: none;
background: #e74400;
}
#name {
font: 500 11px 'Roboto', sans-serif;
color: #fff;
-webkit-font-smoothing: antialiased;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
word-wrap: break-word;
overflow: hidden;
width: 33%;
padding: 0 10px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}

View File

@ -4,25 +4,7 @@
<template> <template>
<style> <link rel="stylesheet" href="gui-row.css">
#row {
-webkit-text-select: none;
background: #e74400;
}
#name {
font: 11px 'Montserrat', sans-serif;
color: #fff;
-webkit-font-smoothing: antialiased;
width: 35%;
padding: 0 10px;
box-sizing: border-box;
}
</style>
<div id="row" layout horizontal center> <div id="row" layout horizontal center>
<div id="name">{{ name }}</div> <div id="name">{{ name }}</div>
@ -35,14 +17,6 @@
</div> </div>
<script> <script src="gui-row.js"></script>
Polymer('gui-row', {
ready: function() {
}
});
</script>
</polymer-element> </polymer-element>

7
elements/gui-row.js Normal file
View File

@ -0,0 +1,7 @@
Polymer('gui-row', {
ready: function() {
}
});

20
elements/gui-row.styl Normal file
View File

@ -0,0 +1,20 @@
@import 'shared';
#row {
-webkit-text-select: none;
background: #e74400;
}
#name {
font();
text-overflow: ellipsis;
word-wrap: break-word;
overflow: hidden;
width: 33%;
padding: 0 10px;
box-sizing: border-box;
}

View File

@ -1,97 +1,87 @@
:host { :host {
display: block; display: block;
-webkit-text-select: none; text-select: none;
font-size: 0; font-size: 0;
height: 100%; height: 100%;
} }
#track-container {
:host * { height: 100%;
margin: 0; width: 100%;
height: 30px;
} }
#track {
#track-container { width: 100%;
height: 100%; height: 1px;
width: 100%; -webkit-border-radius: 1px;
height: 30px; border-radius: 1px;
display: inline-block;
position: relative;
background: rgba(0,0,0,0.1);
} }
#track {
width: 100%;
height: 11px;
display: inline-block;
position: relative;
border-radius: 20px;
background: rgba( 0, 0, 0, 0.1 );
}
#fill { #fill {
height: 9px; height: 1px;
margin-top: 1px; margin-top: 0px;
margin-left: 1px; margin-left: 1px;
border-radius: 10px; -webkit-border-radius: 1px;
position: absolute; border-radius: 1px;
background: #fff; position: absolute;
pointer-events: none; background: #fff;
pointer-events: none;
} }
#knob {
width: 6px;
#container.transition #knob { height: 6px;
-webkit-transition: left 0.1s ease-out; margin-left: -3px;
margin-top: -3px;
-webkit-transition: -webkit-transform 0.2s cubic-bezier(0.25, 0.25, 0, 1);
-moz-transition: -moz-transform 0.2s cubic-bezier(0.25, 0.25, 0, 1);
-o-transition: -o-transform 0.2s cubic-bezier(0.25, 0.25, 0, 1);
-ms-transition: -ms-transform 0.2s cubic-bezier(0.25, 0.25, 0, 1);
transition: transform 0.2s cubic-bezier(0.25, 0.25, 0, 1);
pointer-events: none;
position: absolute;
background-color: #fff;
-webkit-border-radius: 100%;
border-radius: 100%;
} }
#track-container:hover #knob {
#container.transition #fill { -webkit-transform: scale(1.8);
-webkit-transition: width 0.1s ease-out; -moz-transform: scale(1.8);
-o-transform: scale(1.8);
-ms-transform: scale(1.8);
transform: scale(1.8);
} }
#track-container:active #knob {
.straddle-zero #knob { -webkit-transform: scale(1.4);
position: absolute; -moz-transform: scale(1.4);
pointer-events: none; -o-transform: scale(1.4);
width: 9px; -ms-transform: scale(1.4);
height: 9px; transform: scale(1.4);
margin-top: 1px;
background-color: #fff;
border-radius: 100%;
} }
input {
.straddle-zero.negative #knob { font: 500 11px 'Roboto', sans-serif;
margin-left: -3px; color: #fff;
-webkit-font-smoothing: antialiased;
height: 30px;
padding-left: 5px;
display: inline-block;
background: transparent;
border: 0;
text-align: center;
outline: none;
width: 20%;
-webkit-transition: width 0.2s cubic-bezier(0.25, 0.25, 0, 1);
-moz-transition: width 0.2s cubic-bezier(0.25, 0.25, 0, 1);
-o-transition: width 0.2s cubic-bezier(0.25, 0.25, 0, 1);
-ms-transition: width 0.2s cubic-bezier(0.25, 0.25, 0, 1);
transition: width 0.2s cubic-bezier(0.25, 0.25, 0, 1);
} }
.straddle-zero.positive #knob {
margin-left: -5px;
}
/*
.negative #knob,
.negative #fill {
background: #333;
}
*/
input {
font: 11px 'Montserrat', sans-serif;
color: #fff;
-webkit-font-smoothing: antialiased;
padding-left: 5px;
display: inline-block;
vertical-align: top;
background: transparent;
border-radius: 10px;
height: 30px;
border: 0;
text-align: center;
outline: none;
width: 20%;
-webkit-transition: width 0.24s cubic-bezier(.25,.25,0,1);
}
input:hover { input:hover {
width: 28%; width: 28%;
} }
input:focus {
input:focus { width: 50%;
width: 65%; }
input::selection {
background-color: rgba(0,0,0,0.1);
} }

View File

@ -1,17 +1,30 @@
<!-- <!--
todo: [ ] step
step [ ] sig figs
decimals
only validate input box on blur, not on keydown
dy to drag friction [ ] arrow keys
--> [ ] only validate input box on blur, not on keydown
[ ] enter key blurs
[x] dy to drag friction
[x] negative slider
[x] hover behavior
-->
<link rel="import" href="../components/polymer/polymer.html"> <link rel="import" href="../components/polymer/polymer.html">
<link rel="import" href="base-controller.html">
<polymer-element name="gui-number" attributes="min max value step" constructor="NumberController"> <script src="number-controller.js"></script>
<polymer-element
name="number-controller"
attributes="min max value step"
extends="base-controller"
constructor="NumberController"
>
<template> <template>
@ -22,19 +35,15 @@ dy to drag friction
<div id="track-container" <div id="track-container"
on-down="{{ down }}" on-down="{{ down }}"
on-trackx="{{ trackx }}" on-trackx="{{ trackx }}"
on-tracky="{{ tracky }}"
on-trackstart="{{ trackstart }}" on-trackstart="{{ trackstart }}"
horizontal layout center horizontal layout center
flex> flex>
<div id="track"> <div id="track">
<div id="fill"> <div id="fill"></div>
<div id="knob"></div>
</div>
<div id="knob">
</div>
</div> </div>
@ -49,167 +58,4 @@ dy to drag friction
</div> </div>
<script> </polymer-element>
Polymer('gui-number', {
min: 0,
max: 100,
step: 1,
value: 50,
object: null,
property: null,
ready: function() {
var _this = this;
window.addEventListener( 'keydown', function( e ) {
if ( e.keyCode == 18 ) _this._alt = true;
if ( e.keyCode == 16 ) _this._shift = true;
}, false );
window.addEventListener( 'keyup', function( e ) {
if ( e.keyCode == 18 ) _this._alt = false;
if ( e.keyCode == 16 ) _this._shift = false;
}, false );
this.update();
},
objectChanged: function() {
if ( this.object && this.property ) {
this.bindToObject();
}
},
propertyChanged: function() {
if ( this.object && this.property ) {
this.bindToObject();
}
},
valueChanged: function() {
this.value = Math.max( this.value, this.min );
this.value = Math.min( this.value, this.max );
if ( this._boundToObject ) this.object[ this.property ] = this.value;
else this.update();
},
minChanged: function() {
this.value = Math.max( this.value, this.min );
this.update();
},
maxChanged: function() {
this.value = Math.min( this.value, this.max );
this.update();
},
bindToObject: function() {
var _this = this;
this._boundToObject = true;
Object.observe( this.object, function( changes ) {
changes.forEach( function( c ) {
if ( c.name == _this.property ) {
_this.update()
}
} );
} );
},
update: function() {
var ratio = this.map( this.value, this.min, this.max, 0, 1 );
if ( this.min < 0 && this.max > 0 ) {
this.$.container.classList.add( 'straddle-zero' );
var zero = this.map( 0, this.min, this.max, 0, 1 );
if ( this.value >= 0 ) {
this.$.fill.style.left = zero * 100 + '%';
this.$.fill.style.width = (ratio - zero) * 100 + '%';
this.$.fill.style.right = '';
} else {
this.$.fill.style.left = '';
this.$.fill.style.width = (zero - ratio) * 100 + '%';
this.$.fill.style.right = ( 1 - zero ) * 100 + '%';
}
} else {
this.$.container.classList.remove( 'straddle-zero' );
this.$.fill.style.left = 0;
this.$.fill.style.width = ratio * 100 + '%';
this.$.fill.style.right = '';
}
this.$.knob.style.left = ratio * 100 + '%';
this.$.container.classList.toggle( 'positive', this.value >= 0 );
this.$.container.classList.toggle( 'negative', this.value < 0 );
},
// Events
// -------------------------------
click: function( e ) {
this.$.input.select();
},
down: function( e ) {
e.preventDefault();
this.$.container.classList.add( 'transition' );
this._rect = this.$.track.getBoundingClientRect();
if ( !this._alt ) this.setValueFromX( e.x );
},
trackstart: function( e ) {
this.$.container.classList.remove( 'transition' );
},
trackx: function( e ) {
var dv = this.setValueFromDX( e.ddx );
if ( this._alt ) dv /= 10;
if ( this._shift ) dv /= 10;
this.value += dv;
},
// Helpers
// -------------------------------
setValueFromX: function( x ) {
this.value = this.map( x, this._rect.left, this._rect.right, this.min, this.max );
},
setValueFromDX: function( dx ) {
return this.map( dx, 0, this._rect.width, 0, this.max - this.min );
},
map: function( x, a, b, c, d ) {
return ( x - a ) / ( b - a ) * ( d - c ) + c;
}
});
</script>
</polymer-element>

View File

@ -0,0 +1,136 @@
Polymer('number-controller', {
min: 0,
max: 100,
step: 1,
value: 50,
ready: function() {
var _this = this;
window.addEventListener( 'keydown', function( e ) {
if ( e.keyCode == 18 ) _this._alt = true;
}, false );
window.addEventListener( 'keyup', function( e ) {
if ( e.keyCode == 18 ) _this._alt = false;
}, false );
this.super();
},
// Observers
// -------------------------------
valueChanged: function() {
this.value = Math.max( this.value, this.min );
this.value = Math.min( this.value, this.max );
this.super();
},
minChanged: function() {
this.value = Math.max( this.value, this.min );
this.update();
},
maxChanged: function() {
this.value = Math.min( this.value, this.max );
this.update();
},
update: function() {
var ratio = this.map( this.value, this.min, this.max, 0, 1 );
if ( this.min < 0 && this.max > 0 ) {
this.$.container.classList.add( 'straddle-zero' );
var zero = this.map( 0, this.min, this.max, 0, 1 );
if ( this.value >= 0 ) {
this.$.fill.style.left = zero * 100 + '%';
this.$.fill.style.width = (ratio - zero) * 100 + '%';
this.$.fill.style.right = '';
} else {
this.$.fill.style.left = '';
this.$.fill.style.width = (zero - ratio) * 100 + '%';
this.$.fill.style.right = ( 1 - zero ) * 100 + '%';
}
} else {
this.$.container.classList.remove( 'straddle-zero' );
if ( this.max > 0 ) {
this.$.fill.style.left = 0;
this.$.fill.style.width = ratio * 100 + '%';
this.$.fill.style.right = '';
} else {
this.$.fill.style.left = '';
this.$.fill.style.width = ( 1 - ratio ) * 100 + '%';
this.$.fill.style.right = 0;
}
}
this.$.knob.style.left = ratio * 100 + '%';
this.$.container.classList.toggle( 'positive', this.value >= 0 );
this.$.container.classList.toggle( 'negative', this.value < 0 );
this.super();
},
// Events
// -------------------------------
click: function( e ) {
this.$.input.select();
},
down: function( e ) {
e.preventDefault();
this._rect = this.$.track.getBoundingClientRect();
if ( !this._alt ) this.setValueFromX( e.x );
},
trackstart: function( e ) {
this._dragFriction = 1;
},
trackx: function( e ) {
var dv = this.setValueFromDX( e.ddx );
if ( this._alt ) dv /= 10;
this.value += dv * this._dragFriction;
},
tracky: function( e ) {
this._dragFriction = Math.max( 0.01, Math.min( 1, this.map( e.dy, 50, 300, 1, 0.1 ) ) );
},
// Helpers
// -------------------------------
setValueFromX: function( x ) {
this.value = this.map( x, this._rect.left, this._rect.right, this.min, this.max );
},
setValueFromDX: function( dx ) {
return this.map( dx, 0, this._rect.width, 0, this.max - this.min );
}
});

View File

@ -0,0 +1,98 @@
@import 'shared';
track-height = 30px
track-size = 1px;
fill-size = 1px;
knob-size = 6px
:host {
display: block;
text-select: none;
font-size: 0;
height: 100%;
}
#track-container {
height: 100%;
width: 100%;
height: track-height;
}
#track {
width: 100%;
height: track-size;
border-radius: track-size;
display: inline-block;
position: relative;
background: dark;
}
#fill {
height: fill-size;
margin-top: ( ( track-size - fill-size ) / 2 );
margin-left: 1px;
border-radius: fill-size;
position: absolute;
background: #fff;
pointer-events: none;
}
#knob {
width: knob-size;
height: knob-size;
margin-left: -( knob-size / 2 );
margin-top: -( knob-size / 2 );
transition: transform 0.2s ease;
pointer-events: none;
position: absolute;
background-color: #fff;
border-radius: 100%;
}
#track-container:hover #knob {
transform: scale( 1.8 )
}
#track-container:active #knob {
transform: scale( 1.4 )
}
input {
font();
height: track-height;
padding-left: 5px;
display: inline-block;
background: transparent;
border: 0;
text-align: center;
outline: none;
width: 20%;
transition: width 0.2s ease;
}
input:hover {
width: 28%;
}
input:focus {
width: 50%;
}
input::selection {
background-color: dark;
}

0
elements/shared.css Normal file
View File

9
elements/shared.styl Normal file
View File

@ -0,0 +1,9 @@
@import 'nib';
ease = cubic-bezier( .25, .25, 0, 1 )
dark = rgba( 0, 0, 0, 0.1 );
font()
font: 500 11px 'Roboto', sans-serif;
color: #fff;
-webkit-font-smoothing: antialiased;

33
gulpfile.js Normal file
View File

@ -0,0 +1,33 @@
/* jshint node: true */
"use strict";
var gulp = require("gulp"),
stylus = require("gulp-stylus"),
nib = require("nib"),
watch = require("gulp-watch"),
argv = require("yargs").argv
function compileCss() {
var deferred = Q.defer();
gulp.src("elements/*.styl")
.pipe(stylus({use: [nib()]}))
.pipe(gulp.dest("elements"))
.on("end", function() {
deferred.resolve();
});
return deferred.promise;
}
gulp.task("stylus", function () {
if (argv.watch) {
watch({glob: "elements/*.styl"}, function(files) {
return files
.pipe(stylus({use: [nib()]}))
.pipe(gulp.dest("elements"));
});
} else {
return compileCss();
}
});

View File

@ -6,7 +6,7 @@
<script src="components/platform/platform.js"></script> <script src="components/platform/platform.js"></script>
<link href="//fonts.googleapis.com/css?family=Montserrat" rel="stylesheet" type="text/css"> <link href="http://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" type="text/css">
<link rel="import" href="elements/number-controller.html"> <link rel="import" href="elements/number-controller.html">
<link rel="import" href="elements/gui-panel.html"> <link rel="import" href="elements/gui-panel.html">
@ -20,23 +20,23 @@
<gui-panel> <gui-panel>
<gui-row name="Number"> <gui-row name="Number">
<gui-number></gui-number> <number-controller id="test"></number-controller>
</gui-row> </gui-row>
<gui-row name="Number Controller"> <gui-row name="variableWithSeriousRange">
<gui-number value="5000" min="0" max="10000"></gui-number> <number-controller value="5000" min="0" max="10000"></number-controller>
</gui-row> </gui-row>
<gui-row name="Straddle Zero"> <gui-row name="Straddle Zero">
<gui-number min="-1" max="1" value="0"></gui-number> <number-controller min="-1" max="1" value="0"></number-controller>
</gui-row> </gui-row>
<gui-row name="Weird Range"> <gui-row name="Weird Range">
<gui-number min="-1" max="2" value="0"></gui-number> <number-controller min="-1" max="2" value="0"></number-controller>
</gui-row> </gui-row>
<gui-row name="Negative"> <gui-row name="Negative">
<gui-number min="-1" max="0" value="0"></gui-number> <number-controller min="-1" max="0" value="0"></number-controller>
</gui-row> </gui-row>
</gui-panel> </gui-panel>
@ -44,30 +44,36 @@
<script> <script>
var object = { "x": 0 }; var object = { "x": 25 };
document.addEventListener("polymer-ready",function() {
document.addEventListener("polymer-ready", function() {
// Added via JavaScript // Added via JavaScript
var test1 = new NumberController(); // var test1 = new NumberController();
test1.min = 0; // test1.min = 0;
test1.max = 60; // test1.max = 60;
test1.value = 50; // test1.value = 50;
// document.body.appendChild( test1 ); // document.body.appendChild( test1 );
var test2 = new NumberController(); // var test2 = new NumberController();
var test2 = document.getElementById( 'test' );
test2.object = object test2.object = object
test2.property = "x"; test2.property = "x";
Object.observe( object, function(changes) { window.test2 = test2;
changes.forEach( function( c ) {
if ( c.name == "x" ) { console.log( test2 );
console.log( "object.x was changed to " + c.object.x, typeof c.object.x ); // Object.observe( object, function(changes) {
} // changes.forEach( function( c ) {
} ); // if ( c.name == "x" ) {
} ); // console.log( "object.x was changed to " + c.object.x, typeof c.object.x );
// }
// } );
// } );
// document.body.appendChild( test2 ); // document.body.appendChild( test2 );
// window.test2 = test2; // window.test2 = test2;

11
package.json Normal file
View File

@ -0,0 +1,11 @@
{
"name": "polymer-gui",
"version": "0.0.0",
"devDependencies": {
"gulp": "^3.8.7",
"gulp-stylus": "^1.3.0",
"gulp-watch": "^0.6.9",
"nib": "^1.0.3",
"yargs": "^1.3.1"
}
}