diff --git a/src/app/app.js b/src/app/app.js index 67233bd..5787228 100644 --- a/src/app/app.js +++ b/src/app/app.js @@ -1,51 +1,51 @@ -/* eslint no-unused-vars: [2, {"args": "after-used"}] */ -'use strict'; +/* eslint no-unused-vars: [1, {"args": "after-used"}] */ +'use strict' // HACK to disable transitions // when the doc is not in view function flushD3Transitions() { - var now = Date.now; + var now = Date.now Date.now = function() { - return Infinity; - }; + return Infinity + } - d3.timer.flush(); - Date.now = now; + d3.timer.flush() + Date.now = now } -var D3transition = d3.selection.prototype.transition; +var D3transition = d3.selection.prototype.transition d3.selection.prototype.transition = function() { if (document.hidden) { - setImmediate(flushD3Transitions); + setImmediate(flushD3Transitions) } - return D3transition.apply(this, arguments); -}; + return D3transition.apply(this, arguments) +} // TODO: change landing.js and elsewhere // to use local version function commas(number, precision) { if (number === 0) { - return 0; + return 0 } else if (!number) { - return null; + return null } - var parts = number.toString().split('.'); + var parts = number.toString().split('.') - parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') if (precision && parts[1]) { - parts[1] = parts[1].substring(0, precision); + parts[1] = parts[1].substring(0, precision) while (precision > parts[1].length) { - parts[1] += '0'; + parts[1] += '0' } } else if (precision === 0) { - return parts[0]; + return parts[0] } - return parts.join('.'); + return parts.join('.') } angular.element(document).ready(function() { @@ -81,189 +81,182 @@ angular.element(document).ready(function() { 'jsonFormatter' ]) .config(function myAppConfig($urlRouterProvider) { - $urlRouterProvider.otherwise('/'); + $urlRouterProvider.otherwise('/') }) .run(function($window, $rootScope) { if (typeof navigator.onLine !== 'undefined') { - $rootScope.online = navigator.onLine; + $rootScope.online = navigator.onLine $window.addEventListener('offline', function() { $rootScope.$apply(function() { - $rootScope.online = false; - }); - }, false); + $rootScope.online = false + }) + }, false) $window.addEventListener('online', function() { $rootScope.$apply(function() { - $rootScope.online = true; - }); - }, false); + $rootScope.online = true + }) + }, false) } }) .controller('AppCtrl', function AppCtrl($scope, gateways) { - var last; + var last function checkLast() { if (last && moment().diff(last) > 6000) { - $scope.connectionStatus = 'disconnected'; - last = null; + $scope.connectionStatus = 'disconnected' + last = null } } function handleLedger(d) { if (d) { - var drops = d.totalDrops; - var totalXRP = [ - commas(Number(drops.slice(0, -6))), - drops.slice(-6, -4) - ].join('.'); - - $scope.connectionStatus = 'connected'; - $scope.ledgerLabel = 'Ledger #'; - $scope.ledgerIndex = commas(d.ledgerVersion); - $scope.totalCoins = totalXRP; - $scope.totalXRP = parseFloat(drops) / 1000000.0; - $scope.$apply(); + $scope.connectionStatus = 'connected' + $scope.ledgerLabel = 'Ledger #' + $scope.ledgerIndex = d.ledgerVersion + $scope.totalXRP = parseFloat(d.totalDrops) / 1000000.0 + $scope.$apply() } } - $scope.theme = store.get('theme') || Options.theme || 'dark'; + $scope.theme = store.get('theme') || Options.theme || 'dark' $scope.$watch('theme', function() { - store.set('theme', $scope.theme); - }); + store.set('theme', $scope.theme) + }) $scope.toggleTheme = function() { if ($scope.theme === 'dark') { - $scope.theme = 'light'; + $scope.theme = 'light' } else { - $scope.theme = 'dark'; + $scope.theme = 'dark' } - }; + } $scope.snapOptions = { disable: 'right', maxPosition: 267 - }; + } // disable touch drag for desktop devices if (!Modernizr.touch) { - $scope.snapOptions.touchToDrag = false; + $scope.snapOptions.touchToDrag = false } $scope.$on('$stateChangeSuccess', function(event, toState) { if (ga) { - ga('send', 'pageview', toState.name); + ga('send', 'pageview', toState.name) } if (angular.isDefined(toState.data.pageTitle)) { - $scope.pageTitle = toState.data.pageTitle + ' | Ripple Charts'; + $scope.pageTitle = toState.data.pageTitle + ' | Ripple Charts' } else { - $scope.pageTitle = 'Ripple Charts'; + $scope.pageTitle = 'Ripple Charts' } - }); + }) - // connect to the ripple network; - remote = new ripple.RippleAPI(Options.ripple); + // connect to the ripple network + remote = new ripple.RippleAPI(Options.ripple) remote.connect() .then(function() { - $scope.connectionStatus = 'connected'; - $scope.$apply(); + $scope.connectionStatus = 'connected' + $scope.$apply() }) .catch(function(e) { - console.log(e.stack); - }); + console.log(e.stack) + }) - $scope.ledgerLabel = 'connecting...'; - $scope.ledgerIndex = ''; - $scope.connectionStatus = 'disconnected'; + $scope.ledgerLabel = 'connecting...' + $scope.ledgerIndex = '' + $scope.connectionStatus = 'disconnected' // get ledger number and total coins remote.on('ledger', function(d) { - last = moment(); + last = moment() remote.getLedger({ ledgerVersion: d.ledgerVersion }).then(handleLedger) .catch(function(e) { - console.log(e.stack); - }); - }); + console.log(e.stack) + }) + }) remote.on('error', function(e) { - console.log(e); - }); + console.log(e) + }) - setInterval(checkLast, 2000); + setInterval(checkLast, 2000) // remove loader after gateways resolves gateways.promise.then(function() { - var loading = d3.select('#loading'); + var loading = d3.select('#loading') loading.transition() .duration(600) .style('opacity', 0) .each('end', function() { - loading.style('display', 'none'); - }); - }); + loading.style('display', 'none') + }) + }) // reconnect when coming back online $scope.$watch('online', function(online) { if (online) { - remote.connect(); + remote.connect() } - }); - }); + }) + }) - var api; - var banner; - var wrap; - var maintenance; - var bannerPads; - var started = false; + var api + var banner + var wrap + var maintenance + var bannerPads + var started = false function checkStatus() { api.getMaintenanceStatus(function(err, resp) { - var mode = 'maintenance'; - var title = 'This site is under maintenance.'; - var html = ''; - var style = ''; - var height; + var mode = 'maintenance' + var title = 'This site is under maintenance.' + var html = '' + var style = '' + var height if (err) { - console.log(err); + console.log(err) if (err.status === 0) { - title = 'Unable to connect to the data service.'; + title = 'Unable to connect to the data service.' } else { - title = err.message || err.text; - html += err.status; + title = err.message || err.text + html += err.status } } else { - mode = resp && resp.mode ? resp.mode : 'normal'; - html = resp && resp.html ? resp.html : ''; - style = resp && resp.style ? resp.style : ''; + mode = resp && resp.mode ? resp.mode : 'normal' + html = resp && resp.html ? resp.html : '' + style = resp && resp.style ? resp.style : '' } // start the app if (!started && mode !== 'maintenance') { - angular.bootstrap(document, ['ripplecharts']); - started = true; + angular.bootstrap(document, ['ripplecharts']) + started = true } // show maintenance if (mode === 'maintenance') { maintenance.select('.title') - .html(title); + .html(title) maintenance.select('.subtitle') - .html(html); + .html(html) maintenance .style('display', 'block') .transition() .duration(1000) - .style('opacity', 1); + .style('opacity', 1) // hide maintenance } else { @@ -272,63 +265,63 @@ angular.element(document).ready(function() { .duration(1000) .style('opacity', 0) .each('end', function() { - maintenance.style('display', 'none'); - }); + maintenance.style('display', 'none') + }) } // show banner if (mode === 'banner') { - height = banner.style('height'); + height = banner.style('height') banner.html(html) - .style(style); + .style(style) wrap.style('height', height) .transition() .delay(2000) .duration(1000) - .style('height', banner.style('height')); + .style('height', banner.style('height')) banner .transition() .delay(2000) .duration(1000) - .style('opacity', 1); + .style('opacity', 1) bannerPads .transition() .delay(2000) .duration(1000) - .style('height', banner.style('height')); + .style('height', banner.style('height')) // hide banner } else { wrap.transition() .duration(1000) - .style('height', '0px'); + .style('height', '0px') bannerPads.transition() .duration(1000) - .style('height', '0px'); + .style('height', '0px') banner.transition() .duration(1000) .style('opacity', 0) .each('end', function() { - banner.html(''); - }); + banner.html('') + }) } - }); + }) } setTimeout(function() { - api = new ApiHandler(API); - wrap = d3.select('.banner-wrap'); - banner = wrap.select('.banner'); - maintenance = d3.select('#maintenance'); - bannerPads = d3.selectAll('.banner-pad'); - checkStatus(); - }); - - setInterval(checkStatus, 2 * 60 * 1000); -}); + api = new ApiHandler(API) + wrap = d3.select('.banner-wrap') + banner = wrap.select('.banner') + maintenance = d3.select('#maintenance') + bannerPads = d3.selectAll('.banner-pad') + checkStatus() + }) + + setInterval(checkStatus, 2 * 60 * 1000) +}) diff --git a/src/app/landing/landing.js b/src/app/landing/landing.js index 5b8ad72..bb8d00d 100644 --- a/src/app/landing/landing.js +++ b/src/app/landing/landing.js @@ -1,9 +1,14 @@ /* global MultiMarket, ValueSummary */ -'use strict'; +'use strict' angular.module('ripplecharts.landing', [ 'ui.state' ]) +.filter('trust', ['$sce', function($sce) { + return function(htmlCode) { + return $sce.trustAsHtml(htmlCode) + } +}]) .config(function config($stateProvider) { $stateProvider.state('landing', { url: '/', @@ -16,154 +21,123 @@ angular.module('ripplecharts.landing', [ data: {}, resolve: { gateInit: function(gateways) { - return gateways.promise; + return gateways.promise } } - }); + }) }) .controller('LandingCtrl', function LandingCtrl($scope, $state, gateways) { - var api = new ApiHandler(API); + var api = new ApiHandler(API) var donut = new ValueSummary({ id: 'metricDetail', gateways: gateways - }); + }) + + var exchangeRates = {} + var refreshInterval - var exchangeRates = {}; var valueCurrencies = { USD: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', // bitstamp EUR: 'rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq', // gatehub JPY: 'r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN', // tokoyo jpy CNY: 'rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y', // ripplefox XRP: '' - }; + } - var totalAccounts; - var paymentVolumeXRP; - var tradeVolumeXRP; - var valueInterval; + $scope.metrics = { + totalTradeVolume: { + label: 'Total XRP Trade Volume (All Exchanges)' + }, + tradeVolumeRCL: { + label: 'Ripple Network Trade Volume', + link: '#/trade-volume' + }, + paymentVolumeRCL: { + label: 'Ripple Network Payment Volume' + }, + capitalizationXRP: { + label: 'XRP Capitalization' + }, + numAccounts: { + label: '# of Ripple Accounts' + } + } + + for (var key in $scope.metrics) { + $scope.metrics[key].key = key + } - var formatNumber = d3.format(',g'); + $scope.currencies = Object.keys(valueCurrencies) + $scope.selectedCurrency = 'USD' - // present amount in human readable format - function commas(d, precision) { - return formatNumber(Number(d.toFixed(precision || 0))); + + $scope.showMetricDetails = function(name) { + + if (name) { + $scope.selectedMetric = $scope.metrics[name] + } + + donut.load($scope.selectedMetric, { + rate: 1 / $scope.valueRate, + currency: $scope.selectedCurrency + }) } + /** + * getTotalAccounts + */ - // get num accounts function getTotalAccounts() { api.getTotalAccounts(null, function(err, total) { if (err) { - console.log(err); - } - - if (total) { - totalAccounts = total; // save for new account updates; + console.log(err) } - $scope.totalAccounts = total ? commas(total) : ' '; - $scope.$apply(); - }); + $scope.metrics.numAccounts.total = total + $scope.$apply() + }) } - // look for new accounts from the websocket feed + /** + * handleNewAccount + */ + function handleNewAccount(tx) { - var meta = tx.meta; + var meta = tx.meta if (meta.TransactionResult !== 'tesSUCCESS') { - return; + return } meta.AffectedNodes.forEach(function(affNode) { if (affNode.CreatedNode && affNode.CreatedNode.LedgerEntryType === 'AccountRoot') { - $scope.totalAccounts = totalAccounts ? commas(++totalAccounts) : ' '; - $scope.$apply(); - } - }); - } - - // display the selected metric on the page, if its ready - function showValue(metric) { - var ex = { - rate: $scope.valueRate, - currency: $scope.valueCurrency - }; - var sign; - var value; - var precision; - - if (typeof $scope.valueRate === 'undefined') { - return; - } - - if (metric === 'paymentVolume') { - if (typeof paymentVolumeXRP === 'undefined') { - return; + $scope.metrics.numAccounts.total++ + $scope.$apply() } - - if (metric === $scope.metricDetail) { - donut.load(paymentVolumeXRP, ex); - } - - value = paymentVolumeXRP.total / $scope.valueRate; - precision = 2; - - } else if (metric === 'tradeVolume') { - if (typeof tradeVolumeXRP === 'undefined') { - return; - } - - if (metric === $scope.metricDetail) { - donut.load(tradeVolumeXRP, ex); - } - - value = tradeVolumeXRP.total / $scope.valueRate; - precision = 2; - - } else if (metric === 'xrpCapitalization') { - if (!$scope.totalXRP) { - return; - } - - value = $scope.totalXRP / $scope.valueRate; - precision = 0; - } - - switch ($scope.valueCurrency) { - case 'USD': sign = '$'; break; - case 'JPY': sign = '¥'; break; - case 'CNY': sign = '¥'; break; - case 'EUR': sign = '€'; break; - case 'XRP': sign = ''; break; - default: sign = ''; - } - - $scope[metric] = value ? sign + commas(value, precision) : ' '; - $scope.$apply(); + }) } // get the exchange rate from the API function getExchangeRate(c, callback) { api.exchangeRate({ base: { - currency: c.currency, - issuer: c.issuer + currency: 'XRP' }, counter: { - currency: 'XRP' + currency: c.currency, + issuer: c.issuer } }, function(err, rate) { if (err) { - callback(err); - return; + callback(err) + return } // cache for future reference - exchangeRates[c.currency + '.' + c.issuer] = rate; - - callback(null, rate); - }); + exchangeRates[c.currency + '.' + c.issuer] = Number(rate) + callback(null, rate) + }) } // set the value rate for the selected @@ -171,116 +145,220 @@ angular.module('ripplecharts.landing', [ // API if its not cached or // if we are updating the cache function setValueRate(currency, useCached, callback) { - var issuer = valueCurrencies[currency]; + var issuer = valueCurrencies[currency] + $scope.valueRate = undefined + $scope.valueRatePair = '' + + function apply() { + $scope.valueRate = exchangeRates[currency + '.' + issuer] + $scope.valueRate = $scope.valueRate.toPrecision(4) + $scope.valueRatePair = 'XRP/' + currency + callback() + } if (currency === 'XRP') { - $scope.valueRate = 1; - $scope.valueRateDisplay = ''; - callback(); - return; - } + $scope.valueRate = 1 + $scope.valueRatePair = '' + callback() + return // check for cached - if (useCached && exchangeRates[currency + '.' + issuer]) { - $scope.valueRate = exchangeRates[currency + '.' + issuer]; - $scope.valueRateDisplay = commas(1 / $scope.valueRate, 4) + - ' XRP/' + currency; - callback(); - return; + } else if (useCached && exchangeRates[currency + '.' + issuer]) { + apply() + return } - getExchangeRate({ currency: currency, issuer: issuer }, function(err) { if (err) { - console.log(err); - $scope.valueRate = 0; - callback(err); - return; + console.log(err) + callback(err) + $scope.$apply() + return } - $scope.valueRate = exchangeRates[currency + '.' + issuer] || 0; - if ($scope.valueRate) { - $scope.valueRateDisplay = commas(1 / $scope.valueRate, 4) + - ' XRP/' + currency; + apply() + $scope.$apply() + }) + } + + /** + * setMetricValue + */ + + function setMetricValue(metric, value) { + if (value) { + $scope.metrics[metric].total = value + } + + if ($scope.metrics[metric].total && $scope.valueRate) { + $scope.metrics[metric].converted = + $scope.valueRate * $scope.metrics[metric].total + } + } + + /** + * filterXRPVolume + */ + + function filterXRPVolume(components) { + var total = 0 + var count = 0 + + components.forEach(function(c) { + if (c.base.currency === 'XRP' || + c.counter.currency === 'XRP') { + total += c.converted_amount + count += c.count } - callback(); - }); + }) + + return { + source: 'rcl', + base_volume: total, + count: count + } } - // get values for the various metrics - function getValues() { + /** + * getMetricValues + */ - setValueRate($scope.valueCurrency, false, function() { - showValue('paymentVolume'); - showValue('tradeVolume'); - showValue('xrpCapitalization'); - }); + function getMetricValues() { + // get payments api.getPaymentVolume({}, function(err, resp) { - var data; - if (err || !resp || !resp.rows) { - console.log(err); - data = {total: 0}; + var total = 0 + var components + + if (err || !resp || !resp.rows || !resp.rows.length) { + console.log(err) } else { - data = resp.rows[0]; + components = resp.rows[0].components + total = resp.rows[0].total } - paymentVolumeXRP = data; - showValue('paymentVolume'); - }); + $scope.metrics.paymentVolumeRCL.components = components + setMetricValue('paymentVolumeRCL', total) + if ($scope.selectedMetric === $scope.metrics.paymentVolumeRCL) { + $scope.showMetricDetails() + } + $scope.$apply() + }) + // get RCL exchanges api.getExchangeVolume({}, function(err, resp) { - var data; - if (err || !resp || !resp.rows) { - console.log(err); - data = {total: 0}; + var total = 0 + var components + var xrpVolume + + if (err || !resp || !resp.rows || !resp.rows.length) { + console.log(err) + + } else { + components = resp.rows[0].components + total = resp.rows[0].total + } + + $scope.metrics.tradeVolumeRCL.components = components + xrpVolume = filterXRPVolume(components) + + // add RCL XRP volume to total metric + if ($scope.metrics.totalTradeVolume.components && + !$scope.metrics.totalTradeVolume.withRCL) { + $scope.metrics.totalTradeVolume.components.unshift(xrpVolume) + setMetricValue('totalTradeVolume', + $scope.metrics.totalTradeVolume.total + xrpVolume.base_volume) + } + + setMetricValue('tradeVolumeRCL', total) + if ($scope.selectedMetric === $scope.metrics.tradeVolumeRCL) { + $scope.showMetricDetails() + } + $scope.$apply() + }) + + // get external exchanges + api.getExternalMarkets({}, function(err, resp) { + var total = 0 + var components + var xrpVolume + + if (err || !resp) { + console.log(err) + } else { - data = resp.rows[0]; + components = resp.data.components + total = Number(resp.data.total) } - tradeVolumeXRP = data; - showValue('tradeVolume'); - }); + $scope.metrics.totalTradeVolume.withRCL = false + $scope.metrics.totalTradeVolume.components = components + + + // add RCL XRP volume + if ($scope.metrics.tradeVolumeRCL.components) { + xrpVolume = filterXRPVolume($scope.metrics.tradeVolumeRCL.components) + $scope.metrics.totalTradeVolume.components.unshift(xrpVolume) + total += xrpVolume.base_volume + $scope.metrics.totalTradeVolume.withRCL = true + } + + setMetricValue('totalTradeVolume', total) + if ($scope.selectedMetric === $scope.metrics.totalTradeVolume) { + $scope.showMetricDetails() + } + $scope.$apply() + }) } - $scope.valueCurrency = 'USD'; - $scope.metricDetail = 'tradeVolume'; - $scope.metricDetailTitle = 'Trade Volume (last 24 hours)'; - - // dropdown to change currency for metrics - var valueSelect = d3.select('#valueCurrency') - .on('change', function() { - var currency = this.value; - setValueRate(currency, true, function() { - $scope.valueCurrency = currency; - showValue('paymentVolume'); - showValue('tradeVolume'); - showValue('xrpCapitalization'); - }); - }); - - valueSelect.selectAll('option') - .data(d3.keys(valueCurrencies)) - .enter().append('option') - .html(function(d) { - return d; + $scope.$watch('selectedCurrency', function(d) { + var name + + switch (d) { + case 'USD': + $scope.sign = '$' + break + case 'JPY': + $scope.sign = '¥' + break + case 'CNY': + $scope.sign = '¥' + break + case 'EUR': + $scope.sign = '€' + break + case 'XRP': + $scope.sign = '' + break + default: + $scope.sign = '' + } + + for (name in $scope.metrics) { + $scope.metrics[name].converted = undefined + } + + setValueRate(d, true, function() { + for (name in $scope.metrics) { + setMetricValue(name) + } + + $scope.showMetricDetails() + }) }) - .attr('selected', function(d) { - return d === $scope.valueCurrency.currency; - }); // add to new accounts total - remote.on('transaction_all', handleNewAccount); + remote.on('transaction_all', handleNewAccount) remote.on('connect', function() { - getTotalAccounts(); - }); + getTotalAccounts() + }) - getTotalAccounts(); + getTotalAccounts() // get 'fixed' multimarket charts for // the most important markets @@ -291,9 +369,9 @@ angular.module('ripplecharts.landing', [ clickable: true, updateInterval: 60, gateways: gateways - }); + }) - markets.list(9); + markets.list(9) markets.on('chartClick', function(chart) { $state.transitionTo('markets.pair', { @@ -304,64 +382,46 @@ angular.module('ripplecharts.landing', [ interval: '5m', range: '1d', type: store.get('chartType') || 'line' - }); - }); + }) + }) // show the helper text the first time we visit the page if (!store.get('returning')) { setTimeout(function() { - d3.select('#helpButton_new').node().click(); - }, 100); + d3.select('#helpButton_new').node().click() + }, 100) } - $scope.$watch('totalCoins', function() { - setTimeout(function() { - showValue('xrpCapitalization'); - }); - }); - - $scope.$watch('metricDetail', function() { - - var ex = { - rate: $scope.valueRate, - currency: $scope.valueCurrency - }; - - if ($scope.metricDetail === 'paymentVolume') { - $scope.metricDetailTitle = 'Payment Volume (last 24 hours)'; - donut.load(paymentVolumeXRP, ex); - - } else if ($scope.metricDetail === 'tradeVolume') { - $scope.metricDetailTitle = 'Trade Volume (last 24 hours)'; - donut.load(tradeVolumeXRP, ex); - } - }); + $scope.$watch('totalXRP', function(d) { + setMetricValue('capitalizationXRP', d) + }) // stuff to do when leaving the page $scope.$on('$destroy', function() { - markets.list([]); + markets.list([]) if (!store.get('returning') && $scope.showHelp) { setTimeout(function() { - d3.select('#helpButton_new').node().click(); - }, 50); + d3.select('#helpButton_new').node().click() + }, 50) } - store.set('returning', true); - clearInterval(valueInterval); - }); + store.set('returning', true) + clearInterval(refreshInterval) + }) // reload data when coming back online $scope.$watch('online', function(online) { if (online) { - markets.reload(); + markets.reload() } - }); + }) // get value metrics at load time and every 5 minutes - getValues(); - valueInterval = setInterval(getValues, 300000); -}); + getMetricValues() + refreshInterval = setInterval(getMetricValues, 60 * 5 * 1000) + $scope.showMetricDetails('totalTradeVolume') +}) diff --git a/src/app/landing/landing.less b/src/app/landing/landing.less index e1d3eba..6fa0dd9 100644 --- a/src/app/landing/landing.less +++ b/src/app/landing/landing.less @@ -14,101 +14,92 @@ } .landing h5 { - font-size:14px; + font-size:12px; font-weight:bold; text-align:left; - margin:5px; - margin-top:15px; - overflow:hidden; + margin:15px 5px 5px 5px; +} + +.landing sign { + margin-right: -4px; +} + +.landing h5 b { + margin-top: 5px; + display: inline-block +} + +.landing h5 small { + font-size:90%; } .landing .stats ul { list-style:none; - margin:0; + margin:10px 5px 30px 5px; display:table; width:100%; - height:250px; border:1px solid #eee; background:#f8f8f8; - border-radius:2px; color:#555; } .landing .stats li { - padding:8px; - border-radius:2px; - margin:3px; - line-height:25px; + line-height:27px; + font-size: 13px; display:table-row; + color:#999; +} + +.landing .stats .volume-stats li:hover { + background: #eee; +} + +.landing .stats .status { + width: 5px; + display: table-cell; + border-bottom: 1px solid #e5e5e5; +} + +.landing .stats li.selected { + background: #e5e5e5 !important; + color: #000; +} + +.landing .stats li.selected .status { + background: #2A98D0; } .landing .stats label { vertical-align:middle; display:table-cell; text-align:left; - color:#888; - padding:5px 10px 5px 20px; + padding:5px 12px; border-bottom:1px solid #e5e5e5; + cursor: pointer; +} + +.landing .stats .volume-stats label { + padding-left: 7px; } .landing .stats label small { - font-size:70%; - color:#aaa; + font-size: 85%; + opacity: .65; } .landing .stat { vertical-align: middle; display: table-cell; - font-size: 18px; + font-size: 16px; text-align: right; - padding: 5px; + padding: 5px 10px; border-bottom: 1px solid #e5e5e5; - font-weight: bold; -} - -.landing .details { - vertical-align:middle; - display:table-cell; - font-size:10px; - cursor:pointer; - color:#ccc; - min-width: 65px; - border-bottom:1px solid #e5e5e5; - font-weight:bold; - - img { - width:21px; - margin-bottom:2px; - opacity:.15; - padding: 3px; - border-radius: 20px; - background:#aaa; - } -} - -.landing .details:hover { - text-decoration:underline; - color:#999; - - img { - width: 21px; - opacity: .4; - background: #aaa; - } -} - -.landing .details .selected { - color:#666; - - img { - opacity:1; - background:#ccc; - } + cursor: pointer; + color: #555; } -.landing .stat small { - color:#777; - font-weight:normal; +.landing li.selected .stat { + color: #111; } .landing .stat .loader { @@ -118,13 +109,14 @@ } .landing .stats li:last-child .stat, +.landing .stats li:last-child .status, .landing .stats li:last-child label, .landing .stats li:last-child .details { border-bottom:none; } .landing #metricDetail { - height:250px; + height:320px; margin-top:20px; } @@ -169,13 +161,26 @@ } .dark .landing ul { - background:#1a1a1a; - border-color:#252525; + background:#171717; + border-color:#333; +} + +.dark .landing .stats .volume-stats li:hover { + background: #343638; +} + +.dark .landing .stats li.selected { + background: #485057 !important; + color: #ddd; +} + +.dark .landing .stats li.selected .stat { + color: #fff; } .dark .landing .stat, .dark .stats label, -.dark .stats .details { +.dark .stats .status { border-bottom-color:#2a2a2a; } @@ -206,9 +211,6 @@ } } -.dark .landing .stats label small { - color:#666; -} .dark .landing .stat small { font-weight:bold; diff --git a/src/app/landing/landing.tpl.html b/src/app/landing/landing.tpl.html index 44b4928..f68a00b 100644 --- a/src/app/landing/landing.tpl.html +++ b/src/app/landing/landing.tpl.html @@ -9,78 +9,89 @@