diff --git a/doc/source/topics/customizing.rst b/doc/source/topics/customizing.rst index 784e88de26..e5d92107c2 100644 --- a/doc/source/topics/customizing.rst +++ b/doc/source/topics/customizing.rst @@ -128,6 +128,7 @@ full use of the Bootstrap theme architecture. ~~~~~~~~~~~~~~ * Tables_ +* `Bar Charts`_ * Login_ * Tabs_ @@ -171,16 +172,35 @@ The side navigation component has been refactored to use the native Stacked Pills element from Bootstrap. See **Pills** section of your variables file for specific variables to customize. -Pie Charts ----------- +Charts +------ -Pie Charts, in Horizon, are SVG elements. SVG elements allow CSS -customizations for only a basic element's look and feel (i.e. colors, size). +Pie Charts +~~~~~~~~~~ + +Pie Charts are SVG elements. SVG elements allow CSS customizations for +only a basic element's look and feel (i.e. colors, size). Since there is no native element in Bootstrap specifically for pie charts, the look and feel of the charts are inheriting from other elements of the theme. Please see ``_pie_charts.scss`` for specifics. +.. _Bar Charts: + +Bar Charts +~~~~~~~~~~ + +Bar Charts can be either a Bootstrap Progress Bar or an SVG element. Either +implementation will use the Bootstrap Progress Bar styles. + +The SVG implementation will not make use of the customized Progress Bar +height though, so it is recommended that Bootstrap Progress Bars are used +whenever possible. + +Please see ``_bar_charts.scss`` for specifics on what can be customized for +SVGs. See the **Progress bars** section of your variables file for specific +variables to customize. + Tables ------ diff --git a/horizon/static/horizon/js/horizon.d3barchart.js b/horizon/static/horizon/js/horizon.d3barchart.js index f2980eb049..658745a1cf 100644 --- a/horizon/static/horizon/js/horizon.d3barchart.js +++ b/horizon/static/horizon/js/horizon.d3barchart.js @@ -13,7 +13,7 @@ data-tooltip-used='Used' data-tooltip-free='Free' data-tooltip-average='Average' - data-settings='{"orientation": "horizontal", "color_scale_range": ["#000060", "#99FFFF"]}' + data-settings='{"orientation": "horizontal"}' data-used="20" data-average="30"> @@ -41,9 +41,9 @@ data-settings="JSON" Json with variety of settings described below. - used-label-placement='string' OPTIONAL - String determining where the floating label stating number of percent - will be placed. So far only left is supported. + used-label-placement='string' OPTIONAL + String determining where the floating label stating number of percent + will be placed. So far only left is supported. width="integer" OPTIONAL Integer in pixels. Determines the total width of the bar. Handy when @@ -55,20 +55,7 @@ auto-scale-selector OPTIONAL Jquery selector of bar elements that have Integer - used attribute. It then takes maximum of these - values as 100% of the linear scale of the colors. - So the array representing linear scale interval is set - automatically.This then maps to color-scale-range. - (arrays must have the same structure) - - color-scale-range OPTIONAL - Array representing linear scale interval that is set manually. - E.g "[0,10]". This then maps to color-scale-range. - (arrays must have the same structure) - - color-scale-range OPTIONAL - Array representing linear scale of colors. - E.g '["#000060", "#99FFFF"]' + used attribute. orientation OPTIONAL String representing orientation of the bar.Can be "horizontal" @@ -148,8 +135,7 @@ horizon.d3_bar_chart = { } } - } - else { + } else { if (!isNaN(self.max_value) && !isNaN(self.data.used)) { self.data.percentage_used = Math.round((self.data.used / self.max_value) * 100); } else { // If NaN self.data.percentage_used is 0 @@ -180,15 +166,9 @@ horizon.d3_bar_chart = { self.data.settings = {}; - // Placement of the used label - self.data.settings.used_label_placement = undefined; // Orientation of the Bar chart self.data.settings.orientation = 'horizontal'; - // Color scales - self.data.settings.color_scale_domain = [0,100]; - self.data.settings.color_scale_range = ['#000000', '#0000FF']; - // Width and height of bar self.data.settings.width = self.jquery_element.data('width'); self.data.settings.height = self.jquery_element.data('height'); @@ -216,7 +196,6 @@ horizon.d3_bar_chart = { self.apply_settings = function(settings){ var self = this; var allowed_settings = ['orientation', 'used_label_placement', - 'color_scale_domain', 'color_scale_range', 'width', 'height']; $.each(allowed_settings, function(index, setting_name) { @@ -254,13 +233,8 @@ horizon.d3_bar_chart = { // Initialize wrapper var wrapper = new self.chart_module.Wrapper(self.chart_module, self.html_element, self.data); - // Initialize Tool-tips - var tooltip_average = (new self.chart_module.TooltipComponent(wrapper)).render(self.data.tooltip_average); - var tooltip_free = (new self.chart_module.TooltipComponent(wrapper)).render(self.data.tooltip_free); - var tooltip_used = (new self.chart_module.TooltipComponent(wrapper)).render(self.data.tooltip_used); - // Append Unused resources Bar - (new self.chart_module.UnusedComponent(wrapper)).render(tooltip_free); + (new self.chart_module.UnusedComponent(wrapper)).render(self.data.tooltip_free); if (wrapper.used_multi()){ // If UsedComponent is shown as multiple values in one chart @@ -268,13 +242,8 @@ horizon.d3_bar_chart = { // FIXME write proper iterator wrapper.used_multi_iterator = i; - /* Use general tool-tip, content of tool-tip will be changed by inner - used components on their hover. HTML content is taken from JSON sent - from the server. */ - tooltip_used = (new self.chart_module.TooltipComponent(wrapper)).render(''); - // Append used so it will be shown as multiple values in one chart - (new self.chart_module.UsedComponent(wrapper)).render(tooltip_used); + (new self.chart_module.UsedComponent(wrapper)).render(self.data.tooltip_used); // Compute total value as a start point for next Used bar wrapper.total_used_perc += wrapper.percentage_used_value(); @@ -283,9 +252,9 @@ horizon.d3_bar_chart = { } else { // Used is show as one value it the chart - (new self.chart_module.UsedComponent(wrapper)).render(tooltip_used); + (new self.chart_module.UsedComponent(wrapper)).render(self.data.tooltip_used); // Append average value to Bar - (new self.chart_module.AverageComponent(wrapper)).render(tooltip_average); + (new self.chart_module.AverageComponent(wrapper)).render(self.data.tooltip_average); } }; }, @@ -305,8 +274,7 @@ horizon.d3_bar_chart = { self.bar_html = d3.select(html_element); // Bar layout for bar chart self.bar = self.bar_html.append('svg:svg') - .attr('class', 'chart') - .style('background-color', 'white'); + .attr('class', 'legacy-bar-chart'); // Get correct size of chart and the wrapper. chart_module.get_size(self.html_element); @@ -342,11 +310,8 @@ horizon.d3_bar_chart = { } self.chart_wrapper_h = self.h; - // Basic settigns of the chart + // Basic settings of the chart self.lvl_curve = 3; - self.bkgrnd = '#F2F2F2'; - self.frgrnd = 'grey'; - self.color_scale_max = 25; // Percentage used self.percentage_used = data.percentage_used; @@ -360,11 +325,6 @@ horizon.d3_bar_chart = { self.percentage_average = data.percentage_average; self.tooltip_used_contents = data.tooltip_used_contents; - // Set scales for multi bar chart - self.usage_color = d3.scale.linear() - .domain(data.settings.color_scale_domain) - .range(data.settings.color_scale_range); - // Border of the chart self.border_width = 1; @@ -431,37 +391,23 @@ horizon.d3_bar_chart = { } self.render = function(tooltip){ - self.wrapper.bar.append('rect') - .attr('class', 'used_component') + var elem = self.wrapper.bar.append('rect') + .attr('class', 'used_component legacy-bar-chart-section') .attr('y', self.y) .attr('x', self.x) .attr('width', self.width) .attr('height', self.height) - .style('fill', self.wrapper.usage_color(self.wrapper.percentage_used_value())) - .style('stroke', '#bebebe') - .style('stroke-width', 0) .attr('d', self.wrapper.percentage_used_value()) - .attr('tooltip-used', self.wrapper.tooltip_used_value()) - .on('mouseover', function(){ - if ($(this).attr('tooltip-used')){ - tooltip.html($(this).attr('tooltip-used')); - } - tooltip.style('visibility', 'visible'); - }) - .on('mousemove', function(){ - var eventX = event.offsetX || event.layerX; - var eventY = event.offsetY || event.layerY; - tooltip - .style('top', (eventY - 10) + 'px') - .style('left',(eventX + 10) + 'px'); - }) - .on('mouseout', function(){ - tooltip.style('visibility', 'hidden'); - }) .transition() .duration(500) .attr(self.trasition_attr, self.trasition_value); + $(elem).tooltip({ + placement: self.wrapper.data.settings.orientation === 'horizontal' ? 'bottom' : 'left', + container: 'body', + title: $.isArray(self.wrapper.data.percentage_used) ? self.wrapper.tooltip_used_value() : tooltip + }); + if (self.wrapper.used_label_placement === 'left') { // Now it works only for vertical bar chart placed left form the chart var label_placement_y = self.wrapper.h - self.wrapper.used_value_in_pixels; @@ -480,7 +426,6 @@ horizon.d3_bar_chart = { .attr('y', label_placement_y) .attr('x', 0) .attr('dominant-baseline', 'middle') - .attr('font-size', 12) .transition() .duration(500) .attr('x', function() { @@ -508,12 +453,12 @@ horizon.d3_bar_chart = { .data([poly]) .enter() .append('polygon') + .attr('class', 'used_component_label_arrow') .attr('points',function(d) { return d.map(function(d) { return [d.x,d.y].join(','); }).join(' '); }) - .attr('stroke','black') .attr('stroke-width', 2); } }; @@ -549,47 +494,20 @@ horizon.d3_bar_chart = { self.render = function(tooltip){ if (self.wrapper.percentage_average > 0) { // Only show average when it is bigger than 0 - // A dashed line, so it's pretty - self.wrapper.bar.append('line') - .attr('class', 'average_component') - .attr('y1', self.y) - .attr('x1', self.x) - .attr('class', 'average') - .attr('y2', self.y + self.height) - .attr('x2', self.x + self.width) - .style('stroke', 'black') - .style('stroke-width', 3) - .style('stroke-dasharray', ('6, 2')) - .on('mouseover', function(){tooltip.style('visibility', 'visible');}) - .on('mousemove', function(){ - var eventX = event.offsetX || event.layerX; - var eventY = event.offsetY || event.layerY; - tooltip - .style('top',(eventY - 10) + 'px') - .style('left',(eventX + 10) + 'px'); - }) - .on('mouseout', function(){tooltip.style('visibility', 'hidden');}); + // A dashed line - // A normal line, so it shows popup even in spaces, it's also bigger so - // it's easier to show popup - self.wrapper.bar.append('line') + var elem = self.wrapper.bar.append('line') .attr('class', 'average_component') .attr('y1', self.y) .attr('x1', self.x) - .attr('class', 'average') .attr('y2', self.y + self.height) - .attr('x2', self.x + self.width) - .style('stroke', 'transparent') - .style('stroke-width', 5) - .on('mouseover', function(){tooltip.style('visibility', 'visible');}) - .on('mousemove', function(){ - var eventX = event.offsetX || event.layerX; - var eventY = event.offsetY || event.layerY; - tooltip - .style('top',(eventY - 10) + 'px') - .style('left',(eventX + 10) + 'px'); - }) - .on('mouseout', function(){tooltip.style('visibility', 'hidden');}); + .attr('x2', self.x + self.width); + + $(elem).tooltip({ + placement: self.wrapper.data.settings.orientation === 'horizontal' ? 'top' : 'right', + container: 'body', + title: tooltip + }); } }; }, @@ -603,65 +521,21 @@ horizon.d3_bar_chart = { var self = this; self.wrapper = wrapper; - self.render = function(tooltip_free){ - self.wrapper.bar.append('rect') - .attr('class', 'unused_component') + self.render = function(tooltip){ + var elem = self.wrapper.bar.append('rect') + .attr('class', 'unused_component legacy-bar-chart-section') .attr('y', 0) .attr('x', self.wrapper.chart_start_x) .attr('width', self.wrapper.w) .attr('height', self.wrapper.h) .attr('rx', self.wrapper.lvl_curve) - .attr('ry', self.wrapper.lvl_curve) - .style('fill', self.wrapper.bkgrnd) + .attr('ry', self.wrapper.lvl_curve); - .on('mouseover', function(){ - tooltip_free.style('visibility', 'visible'); - }) - .on('mousemove', function(){ - var eventX = event.offsetX || event.layerX; - var eventY = event.offsetY || event.layerY; - tooltip_free - .style('top',(eventY - 10) + 'px') - .style('left',(eventX + 10) + 'px'); - }) - .on('mouseout', function(){tooltip_free.style('visibility', 'hidden');}); - - self.wrapper.bar.append('rect') - .attr('class', 'unused_component_border') - .attr('x', self.wrapper.chart_start_x) - .attr('y', 0) - .attr('height', self.wrapper.h) - .attr('width', self.wrapper.w - self.wrapper.border_width) // a space for right border line - .style('stroke', '#bebebe') - .style('fill', 'none') - .style('stroke-width', 1); - }; - }, - /** - * Component rendering tool-tip HTML code. - * the chart data. - * @param wrapper Wrapper object - * @return HTML code of tool-tip - */ - TooltipComponent: function(wrapper){ - var self = this; - self.wrapper = wrapper; - self.tooltip_html = self.wrapper.bar_html.append('div'); - - self.render = function(html_content){ - var display = 'none'; - if (html_content){ - // Display only when there is some HTML content - display = 'block'; - } - - return self.tooltip_html - .attr('class', 'tooltip_detail') - .style('position', 'absolute') - .style('z-index', '10') - .style('visibility', 'hidden') - .style('display', display) - .html(html_content); + $(elem).tooltip({ + placement: self.wrapper.data.settings.orientation === 'horizontal' ? 'bottom' : 'left', + container: 'body', + title: tooltip + }); }; }, /** diff --git a/horizon/static/horizon/js/horizon.quota.js b/horizon/static/horizon/js/horizon.quota.js index d7e21d0b34..bf5b03a1e5 100644 --- a/horizon/static/horizon/js/horizon.quota.js +++ b/horizon/static/horizon/js/horizon.quota.js @@ -66,12 +66,6 @@ horizon.Quota = { return ('#' + $(elm).attr('data-progress-indicator-for')); })); - // Draw the initial progress bars - this._initialCreation(this.user_value_progress_bars); - this._initialCreation(this.auto_value_progress_bars); - this._initialCreation(this.flavor_progress_bars); - - this._initialAnimations(); this._attachInputHandlers(); }, @@ -358,99 +352,57 @@ horizon.Quota = { // Does the math to calculate what percentage to update a progress bar by. updateUsageFor: function(progress_element, increment_by) { - progress_element = $(progress_element); + var $progress_element = $(progress_element); //var update_indicator = progress_element.find('.progress_bar_selected'); - var quota_limit = parseInt(progress_element.attr('data-quota-limit'), 10); + var quota_limit = parseInt($progress_element.attr('data-quota-limit'), 10); var percentage_to_update = ((increment_by / quota_limit) * 100); - this.update($(progress_element).attr('id'), percentage_to_update); - }, - - // Create a new d3 bar and populate it with the current amount used - drawUsed: function(element, used) { - var w = "100%"; - var h = 20; - var lvl_curve = 4; - var bkgrnd = "#F2F2F2"; - var frgrnd = "#006CCF"; - var full = "#D0342B"; - var addition = "#00D300"; - var nearlyfull = "orange"; - - // Horizontal Bars - var bar = d3.select("#"+element).append("svg:svg") - .attr("class", "chart") - .attr("width", w) - .attr("height", h) - .style("background-color", "white") - .append("g"); - - // background - unused resources - bar.append("rect") - .attr("y", 0) - .attr("width", w) - .attr("height", h) - .attr("rx", lvl_curve) - .attr("ry", lvl_curve) - .style("fill", bkgrnd) - .style("stroke", "#CCCCCC") - .style("stroke-width", 1); - - // new resources - bar.append("rect") - .attr("y",0) - .attr("class", "newbar") - .attr("width", 0) - .attr("height", h) - .attr("rx", lvl_curve) - .attr("ry", lvl_curve) - .style("fill", function () { return addition; }); - - // used resources - bar.insert("rect") - .attr("class", "usedbar") - .attr("y", 0) - .attr("id", "test") - .attr("width", 0) - .attr("height", h) - .attr("rx", lvl_curve) - .attr("ry", lvl_curve) - .style("fill", function () { return frgrnd; }) - .attr("d", used) - .transition() - .duration(500) - .attr("width", used + "%") - .style("fill", function () { - if (used >= 100) { return full; } - else if (used >= 80) { return nearlyfull; } - else { return frgrnd; } - }); + this.update($progress_element.attr('id'), percentage_to_update); }, // Update the progress Bar update: function(element, value) { - var full = "#D0342B"; - var addition = "#00D300"; - var already_used = parseInt(d3.select("#"+element).select(".usedbar").attr("d"), 10); - d3.select("#"+element).select(".newbar") - .transition() - .duration(500) - .attr("width", function () { - if ((value + already_used) >= 100) { - return "100%"; - } else { - return (value + already_used)+ "%"; - } - }) - .style("fill", function() { - if (value > (100 - already_used)) { - return full; - } else { - return addition; - } - }); + // Find Progress Bars, we'll need both of them + var bars = $('#' + element).find('.progress-bar'); + + // Determine how much is already used -> this is the first bar + // Also, convert it to an int ;) + var used_val = +$(bars[0]).attr('aria-valuenow'); + + // Calculate new total + var total = used_val + value; + + // Make sure to normalize the value to 100 or less + if (total > 100) { + value = 100 - used_val; + } + + // Turn percentage into a proper percentage string for style + var percent_str = value + '%'; + + // jQuery construct it and then cache it, we need it more than once + var $bar = $(bars[1]); + + // Update the second progress bar + $bar.css('width', percent_str) + .attr('aria-valuenow', value) + .find('.sr-only') + .html(percent_str); + + // If the value is going to set total to 100+, set danger class + if (total > 99) { + $bar.removeClass('progress-bar-warning').addClass('progress-bar-danger'); + } else { + $bar.removeClass('progress-bar-danger'); + + /*eslint-disable */ + total > 89 ? + $bar.addClass('progress-bar-warning') : + $bar.removeClass('progress-bar-warning'); + /*eslint-enable */ + } }, /* @@ -476,9 +428,10 @@ horizon.Quota = { } $(this.user_value_form_inputs).each(function(index, element) { - $(element).on('input', function(evt) { - var progress_element = $('div[data-progress-indicator-for=' + $(evt.target).attr('id') + ']'); - var integers_in_input = $(evt.target).val().match(/\d+/g); + $(element).on('input', function() { + var $this = $(this); + var $progress_element = $('div[data-progress-indicator-for=' + $this.attr('id') + ']'); + var integers_in_input = $this.val().match(/\d+/g); var user_integer; if(integers_in_input === null) { @@ -496,44 +449,8 @@ horizon.Quota = { var progress_amount = parseInt(user_integer, 10); - scope.updateUsageFor(progress_element, progress_amount); + scope.updateUsageFor($progress_element, progress_amount); }); }); - }, - - /* - Animate the progress bars of elements which indicate they should - automatically be incremented, as opposed to elements which trigger - progress updates based on form element input or changes. - */ - _initialAnimations: function() { - var scope = this; - - $(this.auto_value_progress_bars).each(function(index, element) { - var auto_progress = $(element); - var update_amount = parseInt(auto_progress.attr('data-progress-indicator-step-by'), 10); - - scope.updateUsageFor(auto_progress, update_amount); - }); - }, - - // Draw the initial d3 bars - _initialCreation: function(bars) { - // Draw the initial progress bars - var scope = this; - $(bars).each(function(index, element) { - var progress_element = $(element); - - var quota_limit = parseInt(progress_element.attr('data-quota-limit'), 10); - var quota_used = parseInt(progress_element.attr('data-quota-used'), 10); - var percentage_used = 0; - - if (!isNaN(quota_limit) && !isNaN(quota_used)) { - // If NaN percentage_used is 0 - percentage_used = (quota_used / quota_limit) * 100; - } - - scope.drawUsed($(element).attr('id'), percentage_used); - }); } }; diff --git a/horizon/templates/bootstrap/progress_bar.html b/horizon/templates/bootstrap/progress_bar.html new file mode 100644 index 0000000000..05be8c333e --- /dev/null +++ b/horizon/templates/bootstrap/progress_bar.html @@ -0,0 +1,29 @@ +{% load horizon %} + +{% minifyspace %} +
tag - Example usage:: - {% minifyspace %} {% endminifyspace %} - This example would return this HTML:: - """ nodelist = parser.parse(('endminifyspace',)) diff --git a/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html index e47dd01d86..56a9bc27f5 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html +++ b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/floating_ips/_allocate.html @@ -1,5 +1,5 @@ {% extends "horizon/common/_modal_form.html" %} -{% load horizon i18n %} +{% load horizon i18n bootstrap %} {% block modal-body-right %}@@ -8,11 +8,20 @@{% trans "Project Quotas" %}
- {% trans "Floating IP" %} ({{ usages.floating_ips.used }}) -+ - -{{ usages.floating_ips.available|quota }}
++ {% trans "Floating IP" %} + ({{ usages.floating_ips.used }}) ++ {{ usages.floating_ips.available|quota }} +