From b26b370e067b509f600435b99c7b525a206cc310 Mon Sep 17 00:00:00 2001 From: Mike Wilson Date: Thu, 15 Oct 2015 20:55:10 -0700 Subject: [PATCH 1/2] Rewrite progress-circular to match angular implementation with new styles. Fixes #173 --- addon/components/paper-progress-circular.js | 107 +++++++- app/services/util.js | 24 ++ app/styles/paper-progress-circular.scss | 238 +++++++++--------- .../components/paper-progress-circular.hbs | 10 +- .../dummy/app/templates/progress-circular.hbs | 20 +- .../paper-progress-circular-test.js | 16 +- 6 files changed, 258 insertions(+), 157 deletions(-) diff --git a/addon/components/paper-progress-circular.js b/addon/components/paper-progress-circular.js index 6ef712755..d8427e2a7 100644 --- a/addon/components/paper-progress-circular.js +++ b/addon/components/paper-progress-circular.js @@ -1,34 +1,121 @@ import Ember from 'ember'; import ColorMixin from 'ember-paper/mixins/color-mixin'; -var BASE_DIAMETER = 48; +const BASE_DIAMETER = 48; +const DEFAULT_PROGRESS_SIZE = 100; +const DEFAULT_SCALING = 0.5; + +const MODE_DETERMINATE = 'determinate', + MODE_INDETERMINATE = 'indeterminate'; + export default Ember.Component.extend(ColorMixin, { tagName: 'md-progress-circular', classNames: ['md-default-theme'], - attributeBindings: ['value', 'mode:md-mode'], + attributeBindings: ['value', 'mode:md-mode', 'circleStyle:style'], mode: Ember.computed('value', function() { var value = this.get('value'); - return Ember.isPresent(value) ? 'determinate' : 'indeterminate'; + return Ember.isPresent(value) ? MODE_DETERMINATE : MODE_INDETERMINATE; + }), + + spinnerClass: Ember.computed('mode', function() { + const mode = this.get('mode'); + + switch (mode) { + case MODE_DETERMINATE: + case MODE_INDETERMINATE: + return `md-mode-${mode}`; + default: + return `ng-hide`; + } }), diameter: BASE_DIAMETER, constants: Ember.inject.service(), - - scale: Ember.computed('diameter', function() { - return this.get('diameter') / BASE_DIAMETER; - }), + util: Ember.inject.service(), clampedValue: Ember.computed('value', function() { - var value = this.get('value'); + const value = this.get('value'); return Math.max(0, Math.min(value || 0, 100)); }), - circleStyle: Ember.computed('scale', function() { - return Ember.String.htmlSafe(this.get('constants.CSS.TRANSFORM') + ': ' + 'scale(' + this.get('scale').toString() + ')'); + circleStyle: Ember.computed('diameterRatio', function() { + return Ember.String.htmlSafe(`${this.get('constants.CSS.TRANSFORM')}: scale(${this.get('diameterRatio')})`); + }), + + gapStyle: Ember.computed('clampedValue', function() { + const value = this.get('clampedValue'); + const borderBottomColor = (value <= 50) ? 'transparent !important' : '', + transition = (value <= 50) ? '' : 'borderBottomColor 0.1s linear'; + + var style = ''; + + if (borderBottomColor) { + style = `border-bottom-color: ${borderBottomColor}; `; + } + + if (transition) { + style = style + `${this.get('constants.CSS.TRANSITION')}: ${transition}`; + } + + return Ember.String.htmlSafe(style); + }), + + leftStyle: Ember.computed('mode', 'clampedValue', function() { + if (this.get('mode') !== MODE_DETERMINATE) { + return Ember.String.htmlSafe(''); + } + const value = this.get('clampedValue'); + const transition = (value <= 50) ? 'transform 0.1s linear' : '', + transform = this.get('util').supplant('rotate({0}deg)', [value <= 50 ? 135 : (((value - 50) / 50 * 180) + 135)]); + + var style = ''; + + if (transition) { + style = `${this.get('constants.CSS.TRANSITION')}: ${transition}; `; + } + + if (transform) { + style = style + `${this.get('constants.CSS.TRANSFORM')}: ${transform}`; + } + + return Ember.String.htmlSafe(style); + }), + + rightStyle: Ember.computed('mode', 'clampedValue', function() { + if (this.get('mode') !== MODE_DETERMINATE) { + return Ember.String.htmlSafe(''); + } + const value = this.get('clampedValue'); + const transition = (value >= 50) ? 'transform 0.1s linear' : '', + transform = this.get('util').supplant('rotate({0}deg)', [value >= 50 ? 45 : (value / 50 * 180 - 135)]); + + var style = ''; + + if (transition) { + style = `${this.get('constants.CSS.TRANSITION')}: ${transition}; `; + } + + if (transform) { + style = style + `${this.get('constants.CSS.TRANSFORM')}: ${transform}`; + } + + return Ember.String.htmlSafe(style); + }), + + diameterRatio: Ember.computed('md-diameter', function() { + if (!this.get('md-diameter')) { + return DEFAULT_SCALING; + } + + const match = /([0-9]*)%/.exec(this.get('md-diameter')); + const value = Math.max(0, (match && match[1] / 100) || parseFloat(this.get('md-diameter'))); + + // should return ratio; DEFAULT_PROGRESS_SIZE === 100px is default size + return (value > 1) ? value / DEFAULT_PROGRESS_SIZE : value; }) }); diff --git a/app/services/util.js b/app/services/util.js index 236484e00..84138cbe9 100644 --- a/app/services/util.js +++ b/app/services/util.js @@ -102,6 +102,30 @@ var Util = Ember.Service.extend({ enableScrolling: function () { var method = this.disableScrollAround._enableScrolling; method && method(); + }, + + /** + * supplant() method from Crockford's `Remedial Javascript` + * Equivalent to use of $interpolate; without dependency on + * interpolation symbols and scope. Note: the '{}' can + * be property names, property chains, or array indices. + */ + supplant: function(template, values, pattern) { + pattern = pattern || /\{([^\{\}]*)\}/g; + return template.replace(pattern, function(a, b) { + var p = b.split('.'), + r = values; + try { + for (var s in p) { + if (p.hasOwnProperty(s) ) { + r = r[p[s]]; + } + } + } catch (e) { + r = a; + } + return (typeof r === 'string' || typeof r === 'number') ? r : a; + }); } }); diff --git a/app/styles/paper-progress-circular.scss b/app/styles/paper-progress-circular.scss index 68ea8a59b..413f9500e 100644 --- a/app/styles/paper-progress-circular.scss +++ b/app/styles/paper-progress-circular.scss @@ -3,179 +3,169 @@ $progress-circular-duration : 5.25s !default; $progress-circular-circle-duration : $progress-circular-duration * 0.25 !default; $progress-circular-outer-duration : $progress-circular-duration * (5 / 9) !default; $progress-circular-sporadic-duration : $progress-circular-duration !default; -$progress-circular-size : 50px !default; - -@keyframes outer-rotate { - 100% { transform: rotate(360deg); } -} -@keyframes left-wobble { - 0%, 100% { transform: rotate(130deg); } - 50% { transform: rotate( -5deg); } -} -@keyframes right-wobble { - 0%, 100% { transform: rotate(-130deg); } - 50% { transform: rotate( 5deg); } -} -@keyframes sporadic-rotate { - 12.5% { transform: rotate( 135deg); } - 25% { transform: rotate( 270deg); } - 37.5% { transform: rotate( 405deg); } - 50% { transform: rotate( 540deg); } - 62.5% { transform: rotate( 675deg); } - 75% { transform: rotate( 810deg); } - 87.5% { transform: rotate( 945deg); } - 100% { transform: rotate(1080deg); } -} +$progress-border-width : 10px; +$progress-circular-size : 10 * $progress-border-width !default; md-progress-circular { - width: $progress-circular-size; - height: $progress-circular-size; display: block; position: relative; + + width: $progress-circular-size; + height: $progress-circular-size; + padding-top: 0 !important; margin-bottom: 0 !important; - overflow: hidden; - .md-inner { - width: $progress-circular-size; - height: $progress-circular-size; + + transform: scale(0.5); + + .md-spinner-wrapper { + display:block; position: relative; - .md-gap { - position: absolute; - left: $progress-circular-size * 0.5 - 1; - right: $progress-circular-size * 0.5 - 1; - top: 0; - bottom: 0; - border-top-width: 5px; - border-top-style: solid; - box-sizing: border-box; - } - .md-left, .md-right { - position: absolute; - top: 0; + overflow: hidden; + + .md-inner { + width: $progress-circular-size; height: $progress-circular-size; - width: $progress-circular-size * 0.5; - overflow: hidden; - .md-half-circle { + position: relative; + .md-gap { position: absolute; + left: $progress-circular-size * 0.5 - 1; + right: $progress-circular-size * 0.5 - 1; top: 0; - width: $progress-circular-size; - height: $progress-circular-size; + bottom: 0; + border-top-width: $progress-border-width; + border-top-style: solid; box-sizing: border-box; - border-width: 5px; - border-style: solid; - border-bottom-color: transparent; - border-radius: 50%; } - } - .md-left { - left: 0; - .md-half-circle { + .md-left, .md-right { + position: absolute; + top: 0; + height: $progress-circular-size; + width: $progress-circular-size * 0.50; + overflow: hidden; + .md-half-circle { + position: absolute; + top: 0; + width: $progress-circular-size; + height: $progress-circular-size; + box-sizing: border-box; + border-width: $progress-border-width; + border-style: solid; + border-bottom-color: transparent; + border-radius: 50%; + } + } + .md-left { left: 0; - border-right-color: transparent; + .md-half-circle { + left: 0; + border-right-color: transparent; + } } - } - .md-right { - right: 0; - .md-half-circle { + .md-right { right: 0; - border-left-color: transparent; + .md-half-circle { + right: 0; + border-left-color: transparent; + } } } } - // TODO: This while-loop generates about 2 kilobytes of css after gzip. - // Refactor progressCircular to animate with javascript. - $i: 0; - @while $i <= 100 { - &[value="#{$i}"] { - .md-inner { - .md-left { - .md-half-circle { - @if $i <= 50 { - transform: rotate(135deg); - } @else { - transition: transform 0.1s linear; - $deg: ($i - 50) / 50 * 180 + 135; - transform: rotate(#{$deg}deg); - } - } + + .md-spinner-wrapper.md-mode-indeterminate { + animation: outer-rotate $progress-circular-outer-duration linear infinite; + .md-inner { + animation: sporadic-rotate $progress-circular-sporadic-duration $progress-circular-ease-in-out infinite; + .md-left, .md-right { + .md-half-circle { + animation-iteration-count: infinite; + animation-duration: ($progress-circular-duration * 0.25); + animation-timing-function: $progress-circular-ease-in-out; } - .md-right { - .md-half-circle { - @if $i <= 50 { - transition: transform 0.1s linear; - $deg: $i / 50 * 180 - 135; - transform: rotate(#{$deg}deg); - } @else { - transform: rotate(45deg); - } - } + } + .md-left { + .md-half-circle { + animation-name: left-wobble; } - .md-gap { - border-bottom-width: 5px; - border-bottom-style: solid; - @if $i <= 50 { - border-bottom-color: transparent !important; - } @else { - transition: border-bottom-color 0.1s linear; - } + } + .md-right { + .md-half-circle { + animation-name: right-wobble; } } } - $i: $i + 1; } - &[md-mode=indeterminate] { - .md-spinner-wrapper { - animation: outer-rotate $progress-circular-outer-duration linear infinite; + md-progress-circular.ng-hide { + .md-spinner-wrapper { + animation: none; .md-inner { - animation: sporadic-rotate $progress-circular-sporadic-duration $progress-circular-ease-in-out infinite; - .md-left, .md-right { - .md-half-circle { - animation-iteration-count: infinite; - animation-duration: ($progress-circular-duration * 0.25); - animation-timing-function: $progress-circular-ease-in-out; - } - } + animation: none; .md-left { .md-half-circle { - animation-name: left-wobble; + animation-name: none; } } .md-right { .md-half-circle { - animation-name: right-wobble; + animation-name: none; } } } } } -} -.ng-hide md-progress-circular, -md-progress-circular.ng-hide { - &[md-mode=indeterminate] { - .md-spinner-wrapper { + .md-spinner-wrapper.ng-hide { + animation: none; + .md-inner { animation: none; - .md-inner { - animation: none; - .md-left { - .md-half-circle { - animation-name: none; - } + .md-left { + .md-half-circle { + animation-name: none; } - .md-right { - .md-half-circle { - animation-name: none; - } + } + .md-right { + .md-half-circle { + animation-name: none; } } } } + +} + + +// +// Keyframe animation for the Indeterminate Progress +// +@keyframes outer-rotate { + 100% { transform: rotate(360deg); } +} +@keyframes left-wobble { + 0%, 100% { transform: rotate(130deg); } + 50% { transform: rotate( -5deg); } +} +@keyframes right-wobble { + 0%, 100% { transform: rotate(-130deg); } + 50% { transform: rotate( 5deg); } +} +@keyframes sporadic-rotate { + 12.5% { transform: rotate( 135deg); } + 25% { transform: rotate( 270deg); } + 37.5% { transform: rotate( 405deg); } + 50% { transform: rotate( 540deg); } + 62.5% { transform: rotate( 675deg); } + 75% { transform: rotate( 810deg); } + 87.5% { transform: rotate( 945deg); } + 100% { transform: rotate(1080deg); } } + + // THEME md-progress-circular.md-#{$theme-name}-theme { + background-color: transparent; .md-inner { .md-gap { diff --git a/app/templates/components/paper-progress-circular.hbs b/app/templates/components/paper-progress-circular.hbs index cdf1af793..2fe862779 100644 --- a/app/templates/components/paper-progress-circular.hbs +++ b/app/templates/components/paper-progress-circular.hbs @@ -1,11 +1,11 @@ -
-
+
+
-
+
-
-
+
+
\ No newline at end of file diff --git a/tests/dummy/app/templates/progress-circular.hbs b/tests/dummy/app/templates/progress-circular.hbs index 00a57ce29..2bd6488c4 100644 --- a/tests/dummy/app/templates/progress-circular.hbs +++ b/tests/dummy/app/templates/progress-circular.hbs @@ -24,11 +24,11 @@

Theming

- {{paper-progress-circular class="md-hue-2"}} - {{paper-progress-circular accent=true}} - {{paper-progress-circular accent=true class="md-hue-1"}} - {{paper-progress-circular warn=true class="md-hue-3"}} - {{paper-progress-circular warn=true}} + {{paper-progress-circular class="md-hue-2" md-diameter="20px"}} + {{paper-progress-circular accent=true md-diameter=40}} + {{paper-progress-circular accent=true class="md-hue-1" md-diameter=60}} + {{paper-progress-circular warn=true class="md-hue-3" md-diameter=70}} + {{paper-progress-circular warn=true md-diameter=96}}
@@ -37,11 +37,11 @@ \{{paper-progress-circular value=determinateValue}} \{{paper-progress-circular}} -\{{paper-progress-circular class="md-hue-2"}} -\{{paper-progress-circular accent=true}} -\{{paper-progress-circular accent=true class="md-hue-1"}} -\{{paper-progress-circular warn=true class="md-hue-3"}} -\{{paper-progress-circular warn=true}} +\{{paper-progress-circular class="md-hue-2" md-diameter="20px"}} +\{{paper-progress-circular accent=true md-diameter=40}} +\{{paper-progress-circular accent=true class="md-hue-1" md-diameter=60}} +\{{paper-progress-circular warn=true class="md-hue-3" md-diameter=70}} +\{{paper-progress-circular warn=true md-diameter=96}} {{/code-block}} diff --git a/tests/unit/components/paper-progress-circular-test.js b/tests/unit/components/paper-progress-circular-test.js index 7cbef0fd0..78d8af2e0 100644 --- a/tests/unit/components/paper-progress-circular-test.js +++ b/tests/unit/components/paper-progress-circular-test.js @@ -4,7 +4,7 @@ moduleForComponent('paper-progress-circular', 'Unit | Component | paper progress // Specify the other units that are required for this test // needs: ['component:foo', 'helper:bar'], unit: true, - needs: ['service:constants', 'service:sniffer'] + needs: ['service:constants', 'service:sniffer', 'service:util'] }); test('it renders', function(assert) { @@ -21,7 +21,7 @@ test('it renders', function(assert) { -test('it sets transform scale 1 by default', function(assert) { +test('it sets transform scale 0.5 by default', function(assert) { var component = this.subject({ value: 50 @@ -29,27 +29,27 @@ test('it sets transform scale 1 by default', function(assert) { this.render(); - var spinnerWrapper = this.$().find('.md-spinner-wrapper')[0]; + var spinnerWrapper = this.$()[0]; var spinnerWrapperStyle = spinnerWrapper.style[component.get('constants.CSS.TRANSFORM')]; - assert.equal(spinnerWrapperStyle, 'scale(1)', 'Transform set correctly'); + assert.equal(spinnerWrapperStyle, 'scale(0.5)', 'Transform set correctly'); }); -test('it sets transform scale 2 when diameter is 96', function(assert) { +test('it sets transform scale 0.96 when diameter is 96', function(assert) { var component = this.subject({ value: 50, - diameter: 96 + 'md-diameter': 96 }); this.render(); - var spinnerWrapper = this.$().find('.md-spinner-wrapper')[0]; + var spinnerWrapper = this.$()[0]; var spinnerWrapperStyle = spinnerWrapper.style[component.get('constants.CSS.TRANSFORM')]; - assert.equal(spinnerWrapperStyle, 'scale(2)', 'Transform set correctly'); + assert.equal(spinnerWrapperStyle, 'scale(0.96)', 'Transform set correctly'); }); From 40e46bd18d4f8a454788874ae6c6ac0223fe8e55 Mon Sep 17 00:00:00 2001 From: Mike Wilson Date: Thu, 15 Oct 2015 21:00:38 -0700 Subject: [PATCH 2/2] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2841363e0..cc51b06a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#140](https://github.com/miguelcobain/ember-paper/pull/140) Implement Material Menu and Select component. - [#171](https://github.com/miguelcobain/ember-paper/pull/171) Add support for custom validations in paper-input component. - [#192](https://github.com/miguelcobain/ember-paper/pull/192) Add support for paper-grid-list component. +- [#194](https://github.com/miguelcobain/ember-paper/pull/194) Fixed [#173](https://github.com/miguelcobain/ember-paper/issues/173) - issue with paper-progress-circular. ### 0.2.8 (Aug 19, 2015)