diff --git a/src/fixtures/mock_ui_state.js b/src/fixtures/mock_ui_state.js new file mode 100644 index 0000000000000..b0e794722f794 --- /dev/null +++ b/src/fixtures/mock_ui_state.js @@ -0,0 +1,15 @@ +define(function (require) { + var _ = require('lodash'); + var keys = {}; + return { + get: function (path, def) { + return keys[path] == null ? def : keys[path]; + }, + set: function (path, val) { + keys[path] = val; + return val; + }, + on: _.noop, + off: _.noop + } +}) \ No newline at end of file diff --git a/src/plugins/kbn_vislib_vis_types/public/controls/vislib_basic_options.html b/src/plugins/kbn_vislib_vis_types/public/controls/vislib_basic_options.html index 5a694765f77e5..cfc12aae2631f 100644 --- a/src/plugins/kbn_vislib_vis_types/public/controls/vislib_basic_options.html +++ b/src/plugins/kbn_vislib_vis_types/public/controls/vislib_basic_options.html @@ -5,10 +5,4 @@ Show Tooltip -
- -
diff --git a/src/plugins/table_vis/public/__tests__/_table_vis.js b/src/plugins/table_vis/public/__tests__/_table_vis.js index e2068932f61cc..14790fbe83fd1 100644 --- a/src/plugins/table_vis/public/__tests__/_table_vis.js +++ b/src/plugins/table_vis/public/__tests__/_table_vis.js @@ -30,7 +30,8 @@ describe('Integration', function () { $rootScope.vis = vis; $rootScope.esResponse = esResponse; - $el = $(''); + $rootScope.uiState = require('fixtures/mock_ui_state'); + $el = $(''); $compile($el)($rootScope); $rootScope.$apply(); diff --git a/src/ui/public/filter_bar/filter_bar_click_handler.js b/src/ui/public/filter_bar/filter_bar_click_handler.js index 6a5c7bbf18854..59844f9207bf7 100644 --- a/src/ui/public/filter_bar/filter_bar_click_handler.js +++ b/src/ui/public/filter_bar/filter_bar_click_handler.js @@ -2,21 +2,11 @@ define(function (require) { var _ = require('lodash'); var dedupFilters = require('./lib/dedupFilters'); var uniqFilters = require('./lib/uniqFilters'); - - // given an object or array of objects, return the value of the passed param - // if the param is missing, return undefined - function findByParam(values, param) { - if (_.isArray(values)) { // point series chart - var index = _.findIndex(values, param); - if (index === -1) return; - return values[index][param]; - } - return values[param]; // pie chart - } + var findByParam = require('ui/utils/find_by_param'); return function (Notifier) { return function ($state) { - return function (event) { + return function (event, simulate) { var notify = new Notifier({ location: 'Filter bar' }); @@ -58,9 +48,20 @@ define(function (require) { if (!filters.length) return; + if (event.negate) { + _.each(filters, function (filter) { + filter.meta = filter.meta || {}; + filter.meta.negate = true; + }); + } + filters = dedupFilters($state.filters, uniqFilters(filters)); // We need to add a bunch of filter deduping here. - $state.$newFilters = filters; + if (!simulate) { + $state.$newFilters = filters; + } + + return filters; } }; }; diff --git a/src/ui/public/utils/find_by_param.js b/src/ui/public/utils/find_by_param.js new file mode 100644 index 0000000000000..c22a5c6ed00f7 --- /dev/null +++ b/src/ui/public/utils/find_by_param.js @@ -0,0 +1,13 @@ +define(function (require) { + var _ = require('lodash'); + // given an object or array of objects, return the value of the passed param + // if the param is missing, return undefined + return function findByParam(values, param) { + if (_.isArray(values)) { // point series chart + var index = _.findIndex(values, param); + if (index === -1) return; + return values[index][param]; + } + return values[param]; // pie chart + }; +}); \ No newline at end of file diff --git a/src/ui/public/vislib/__tests__/lib/layout/layout.js b/src/ui/public/vislib/__tests__/lib/layout/layout.js index 7b07b929c8f33..f10949009c951 100644 --- a/src/ui/public/vislib/__tests__/lib/layout/layout.js +++ b/src/ui/public/vislib/__tests__/lib/layout/layout.js @@ -52,7 +52,6 @@ dateHistogramArray.forEach(function (data, i) { expect($(vis.el).find('.vis-wrapper').length).to.be(1); expect($(vis.el).find('.y-axis-col-wrapper').length).to.be(1); expect($(vis.el).find('.vis-col-wrapper').length).to.be(1); - expect($(vis.el).find('.legend-col-wrapper').length).to.be(1); expect($(vis.el).find('.y-axis-col').length).to.be(1); expect($(vis.el).find('.y-axis-title').length).to.be(1); expect($(vis.el).find('.y-axis-div-wrapper').length).to.be(1); diff --git a/src/ui/public/vislib/__tests__/lib/legend.js b/src/ui/public/vislib/__tests__/lib/legend.js deleted file mode 100644 index 2d80681dde185..0000000000000 --- a/src/ui/public/vislib/__tests__/lib/legend.js +++ /dev/null @@ -1,129 +0,0 @@ -var d3 = require('d3'); -var angular = require('angular'); -var _ = require('lodash'); -var $ = require('jquery'); -var ngMock = require('ngMock'); -var expect = require('expect.js'); - -var slices = require('fixtures/vislib/mock_data/histogram/_slices'); -var stackedSeries = require('fixtures/vislib/mock_data/date_histogram/_stacked_series'); -var histogramSlices = require('fixtures/vislib/mock_data/histogram/_slices'); - -var dataArray = [ - stackedSeries, - slices, - histogramSlices, - stackedSeries, - stackedSeries -]; - -var chartTypes = [ - 'histogram', - 'pie', - 'pie', - 'area', - 'line' -]; - -var chartSelectors = { - histogram: '.chart rect', - pie: '.chart path', - area: '.chart path', - line: '.chart circle' -}; - -describe('Vislib Legend Class', function () { - dataArray.forEach(function (data, i) { - describe(chartTypes[i] + ' data', function () { - var visLibParams = { - type: chartTypes[i], - addLegend: true - }; - var Legend; - var vis; - var persistedState; - var $el; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - vis = Private(require('fixtures/vislib/_vis_fixture'))(visLibParams); - persistedState = new (Private(require('ui/persisted_state/persisted_state')))(); - Legend = Private(require('ui/vislib/lib/legend')); - $el = d3.select('body').append('div').attr('class', 'fake-legend'); - vis.render(data, persistedState); - })); - - afterEach(function () { - $(vis.el).remove(); - $('.fake-legend').remove(); - vis = null; - }); - - describe('legend item label matches vis item label', function () { - it('should match the slice label', function () { - var chartType = chartTypes[i]; - var paths = $(vis.el).find(chartSelectors[chartType]).toArray(); - var items = vis.handler.legend.labels; - - items.forEach(function (d) { - var path = _.find(paths, function (path) { - return path.getAttribute('data-label') === String(d.label); - }); - - expect(path).to.be.ok(); - }); - }); - }); - - describe('header method', function () { - it('should append the legend header', function () { - expect($(vis.el).find('.header').length).to.be(1); - expect($(vis.el).find('.column-labels').length).to.be(1); - }); - }); - - describe('list method', function () { - it('should append the legend list', function () { - expect($(vis.el).find('.legend-ul').length).to.be(1); - }); - - it('should contain a list of items', function () { - expect($(vis.el).find('li').length).to.be.greaterThan(1); - }); - - it('should not return an undefined value', function () { - var emptyObject = { - label: '' - }; - var labels = [emptyObject, emptyObject, emptyObject]; - var args = { - _attr: {isOpen: true}, - color: function () { return 'blue'; } - }; - - Legend.prototype._list($el, labels, args); - - $el.selectAll('li').each(function (d) { - expect(d.label).not.to.be(undefined); - }); - }); - }); - - describe('render method', function () { - it('should create a legend toggle', function () { - expect($('.legend-toggle').length).to.be(1); - }); - - it('should have an onclick listener', function () { - expect(!!$('.legend-toggle')[0].__onclick).to.be(true); - expect(!!$('li.color')[0].__onclick).to.be(true); - }); - - it('should attach onmouseover listener', function () { - expect(!!$('li.color')[0].__onmouseover).to.be(true); - }); - }); - }); - }); -}); - diff --git a/src/ui/public/vislib/lib/dispatch.js b/src/ui/public/vislib/lib/dispatch.js index fb13291d038d7..68de13d3f46f9 100644 --- a/src/ui/public/vislib/lib/dispatch.js +++ b/src/ui/public/vislib/lib/dispatch.js @@ -226,16 +226,8 @@ define(function (require) { */ Dispatch.prototype.highlightLegend = function (element) { var label = this.getAttribute('data-label'); - if (!label) return; - - d3.select(element) - .select('.legend-ul') - .selectAll('li.color') - .filter(function (d, i) { - return String(d.label) !== label; - }) - .classed('blur_shape', true); + $('[data-label]', element.parentNode).not('[data-label="' + label + '"]').css('opacity', 0.5); }; /** @@ -245,10 +237,7 @@ define(function (require) { * @method unHighlightLegend */ Dispatch.prototype.unHighlightLegend = function (element) { - d3.select(element) - .select('.legend-ul') - .selectAll('li.color') - .classed('blur_shape', false); + $('[data-label]', element.parentNode).css('opacity', 1); }; /** diff --git a/src/ui/public/vislib/lib/handler/handler.js b/src/ui/public/vislib/lib/handler/handler.js index 9892268db9019..8b7ba300bdbcd 100644 --- a/src/ui/public/vislib/lib/handler/handler.js +++ b/src/ui/public/vislib/lib/handler/handler.js @@ -7,7 +7,6 @@ define(function (require) { var Data = Private(require('ui/vislib/lib/data')); var Layout = Private(require('ui/vislib/lib/layout/layout')); - var Legend = Private(require('ui/vislib/lib/legend')); /** * Handles building all the components of the visualization @@ -39,15 +38,10 @@ define(function (require) { this.axisTitle = opts.axisTitle; this.alerts = opts.alerts; - if (this._attr.addLegend) { - this.legend = opts.legend; - } - this.layout = new Layout(vis.el, vis.data, vis._attr.type, opts); this.binder = new Binder(); this.renderArray = _.filter([ this.layout, - this.legend, this.axisTitle, this.chartTitle, this.alerts, @@ -96,12 +90,6 @@ define(function (require) { this._validateData(); this.renderArray.forEach(function (property) { - if (property instanceof Legend) { - self.vis.activeEvents().forEach(function (event) { - self.enable(event, property); - }); - } - if (typeof property.render === 'function') { property.render(); } diff --git a/src/ui/public/vislib/lib/handler/types/pie.js b/src/ui/public/vislib/lib/handler/types/pie.js index 33a29c81257c4..09f8d9cc6c65f 100644 --- a/src/ui/public/vislib/lib/handler/types/pie.js +++ b/src/ui/public/vislib/lib/handler/types/pie.js @@ -2,7 +2,6 @@ define(function (require) { return function PieHandler(Private) { var Handler = Private(require('ui/vislib/lib/handler/handler')); var Data = Private(require('ui/vislib/lib/data')); - var Legend = Private(require('ui/vislib/lib/legend')); var ChartTitle = Private(require('ui/vislib/lib/chart_title')); /* @@ -11,7 +10,6 @@ define(function (require) { return function (vis) { return new Handler(vis, { - legend: new Legend(vis), chartTitle: new ChartTitle(vis.el) }); }; diff --git a/src/ui/public/vislib/lib/handler/types/point_series.js b/src/ui/public/vislib/lib/handler/types/point_series.js index ff3fb08a3b911..69c345ccaefb3 100644 --- a/src/ui/public/vislib/lib/handler/types/point_series.js +++ b/src/ui/public/vislib/lib/handler/types/point_series.js @@ -3,7 +3,6 @@ define(function (require) { var injectZeros = Private(require('ui/vislib/components/zero_injection/inject_zeros')); var Handler = Private(require('ui/vislib/lib/handler/handler')); var Data = Private(require('ui/vislib/lib/data')); - var Legend = Private(require('ui/vislib/lib/legend')); var XAxis = Private(require('ui/vislib/lib/x_axis')); var YAxis = Private(require('ui/vislib/lib/y_axis')); var AxisTitle = Private(require('ui/vislib/lib/axis_title')); @@ -29,7 +28,6 @@ define(function (require) { return new Handler(vis, { data: data, - legend: new Legend(vis, vis.data), axisTitle: new AxisTitle(vis.el, data.get('xAxisLabel'), data.get('yAxisLabel')), chartTitle: new ChartTitle(vis.el), xAxis: new XAxis({ diff --git a/src/ui/public/vislib/lib/layout/types/column_layout.js b/src/ui/public/vislib/lib/layout/types/column_layout.js index 0dfea45abfc28..0caa60c5b0299 100644 --- a/src/ui/public/vislib/lib/layout/types/column_layout.js +++ b/src/ui/public/vislib/lib/layout/types/column_layout.js @@ -100,10 +100,6 @@ define(function (require) { ] } ] - }, - { - type: 'div', - class: 'legend-col-wrapper' } ] } diff --git a/src/ui/public/vislib/lib/legend.js b/src/ui/public/vislib/lib/legend.js deleted file mode 100644 index 5d976d4554d01..0000000000000 --- a/src/ui/public/vislib/lib/legend.js +++ /dev/null @@ -1,246 +0,0 @@ -define(function (require) { - return function LegendFactory(Private) { - var d3 = require('d3'); - var _ = require('lodash'); - var Dispatch = Private(require('ui/vislib/lib/dispatch')); - var Data = Private(require('ui/vislib/lib/data')); - var legendHeaderTemplate = _.template(require('ui/vislib/partials/legend_header.html')); - var dataLabel = require('ui/vislib/lib/_data_label'); - var color = Private(require('ui/vislib/components/color/color')); - - const NUM_COLORS = 5 * 5; // rows x columns - const colorPalette = Private(require('ui/vislib/components/color/color_palette'))(NUM_COLORS); - - /** - * Appends legend to the visualization - * - * @class Legend - * @constructor - * @param vis {Object} Reference to Vis Constructor - */ - function Legend(vis) { - if (!(this instanceof Legend)) { - return new Legend(vis); - } - - var data = vis.data.columns || vis.data.rows || [vis.data]; - var type = vis._attr.type; - var labels = this.labels = this._getLabels(data, type); - var labelsArray = labels.map(function (obj) { return obj.label; }); - - this.events = new Dispatch(); - this.vis = vis; - this.el = vis.el; - this.color = color(labelsArray, vis.uiState.get('vis.colors')); - this._attr = _.defaults({}, vis._attr || {}, { - 'legendClass' : 'legend-col-wrapper', - 'blurredOpacity' : 0.3, - 'focusOpacity' : 1, - 'defaultOpacity' : 1, - 'legendDefaultOpacity': 1 - }); - } - - Legend.prototype._getPieLabels = function (data) { - return Data.prototype.pieNames(data); - }; - - Legend.prototype._getSeriesLabels = function (data) { - var isOneSeries = data.every(function (chart) { - return chart.series.length === 1; - }); - - var values = data.map(function (chart) { - var yLabel = isOneSeries ? chart.yAxisLabel : undefined; - - return chart.series.map(function (series) { - if (yLabel) series.label = yLabel; - return series; - }); - }) - .reduce(function (a, b) { - return a.concat(b); - }, []); - - return _.uniq(values, 'label'); - }; - - Legend.prototype._getLabels = function (data, type) { - if (type === 'pie') return this._getPieLabels(data); - return this._getSeriesLabels(data); - }; - - /** - * Adds legend header - * - * @method header - * @param el {HTMLElement} Reference to DOM element - * @param args {Object|*} Legend options - * @returns {*} HTML element - */ - Legend.prototype._header = function (el, args) { - var self = this; - return el.append('div') - .attr('class', 'header') - .append('div') - .attr('class', 'column-labels') - .html(function () { - return legendHeaderTemplate({ isOpen: self.vis.get('legendOpen') }); - }); - }; - - Legend.prototype._colorPicker = function (el, label) { - const self = this; - - const container = el.selectAll('div'); - if (!container.empty()) return container.remove(); - - return el - .append('div') - .attr('class', 'color-selector') - .selectAll('i').data(colorPalette) - .enter() - .append('i') - .attr('class', 'fa fa-circle dots') - .attr('style', function (d) { - return `color: ${d};`; - }).on('click', function (d) { - const colors = self.vis.uiState.get('vis.colors') || {}; - colors[label] = d; - self.vis.uiState.set('vis.colors', colors); - }); - }; - - /** - * Adds list to legend - * - * @method list - * @param el {HTMLElement} Reference to DOM element - * @param arrOfLabels {Array} Array of labels - * @param args {Object|*} Legend options - * @returns {D3.Selection} HTML element with list of labels attached - */ - Legend.prototype._list = function (el, data, args) { - var self = this; - - return el.append('ul') - .attr('class', function () { - var className = 'legend-ul'; - if (self.vis && !self.vis.get('legendOpen')) className += ' hidden'; - return className; - }) - .selectAll('li') - .data(data) - .enter() - .append('li') - .attr('class', 'color') - .each(function (d) { - var li = d3.select(this); - self._addIdentifier.call(this, d); - - li.append('i') - .attr('class', 'fa fa-circle dots') - .attr('style', 'color:' + args.color(d.label)); - - li.append('span').text(d.label); - }); - }; - - /** - * Append the data label to the element - * - * @method _addIdentifier - * @param label {string} label to use - */ - Legend.prototype._addIdentifier = function (d) { - dataLabel(this, d.label); - }; - - /** - * Renders legend - * - * @method render - * @return {HTMLElement} Legend - */ - Legend.prototype.render = function () { - var self = this; - var visEl = d3.select(this.el); - var legendDiv = visEl.select('.' + this._attr.legendClass); - var items = this.labels; - this._header(legendDiv, this); - this._list(legendDiv, items, this); - - var headerIcon = visEl.select('.legend-toggle'); - - // toggle legend open and closed - headerIcon - .on('click', function legendClick() { - var legendOpen = !self.vis.get('legendOpen'); - self.vis.set('legendOpen', legendOpen); - - visEl.select('ul.legend-ul').classed('hidden', legendOpen); - self.vis.resize(); - }); - - legendDiv.select('.legend-ul').selectAll('li') - .on('mouseover', function (d) { - var label = d.label; - var charts = visEl.selectAll('.chart'); - - function filterLabel() { - var pointLabel = this.getAttribute('data-label'); - return pointLabel !== label.toString(); - } - - if (label && label !== 'Count') { - d3.select(this).style('cursor', 'pointer'); - } - - // legend - legendDiv.selectAll('li') - .filter(filterLabel) - .classed('blur_shape', true); - - // all data-label attribute - charts.selectAll('[data-label]') - .filter(filterLabel) - .classed('blur_shape', true); - - var eventEl = d3.select(this); - eventEl.style('white-space', 'inherit'); - eventEl.style('word-break', 'break-all'); - }) - .on('mouseout', function () { - /* - * The default opacity of elements in charts may be modified by the - * chart constructor, and so may differ from that of the legend - */ - - var charts = visEl.selectAll('.chart'); - - // legend - legendDiv.selectAll('li') - .classed('blur_shape', false); - - // all data-label attribute - charts.selectAll('[data-label]') - .classed('blur_shape', false); - - var eventEl = d3.select(this); - eventEl.style('white-space', 'nowrap'); - eventEl.style('word-break', 'inherit'); - }); - - legendDiv.selectAll('li.color').each(function (d) { - d3.select(this).on('click', function (d) { - self._colorPicker(d3.select(this), d.label); - }); - //if (label !== undefined && label !== 'Count') { - // d3.select(this).call(self.events.addClickEvent()); - //} - }); - }; - - return Legend; - }; -}); diff --git a/src/ui/public/vislib/partials/legend_header.html b/src/ui/public/vislib/partials/legend_header.html deleted file mode 100644 index 7c909bcee9920..0000000000000 --- a/src/ui/public/vislib/partials/legend_header.html +++ /dev/null @@ -1,8 +0,0 @@ -
- - <%= (isOpen) ? - 'Legend ' : - '' - %> - -
\ No newline at end of file diff --git a/src/ui/public/vislib/styles/_legend.less b/src/ui/public/vislib/styles/_legend.less index b7d6d89fc35b8..2422aa7cd5db9 100644 --- a/src/ui/public/vislib/styles/_legend.less +++ b/src/ui/public/vislib/styles/_legend.less @@ -1,25 +1,33 @@ @import "~ui/styles/mixins"; @import "~ui/styles/variables"; +visualize-legend { + display: flex; + flex-direction: row; +} + .legend-col-wrapper { .flex-parent(0, 0, auto); + flex-direction: row; z-index: 10; min-height: 0; - .legend-toggle { + .header { cursor: pointer; - opacity: 0.75; - white-space: nowrap; - font-size: 0.9em; + width: 15px; + } - .legend-open { - margin-right: 20px; + .legend-toggle { + &:hover { + color: @sidebar-hover-color; + background-color: @sidebar-hover-bg; } - .legend-closed { - font-size: 1.5em; - padding-left: 5px; - } + background-color: @sidebar-bg; + font-size: 10px; + height: 30px; + padding: 8px 3px; + border-bottom-left-radius: @border-radius-small; } .column-labels { @@ -27,46 +35,85 @@ } .legend-ul { + border-left: 1px solid @sidebar-bg; + width: 150px; flex: 1 1 1px; overflow-x: hidden; - overflow-y: auto; - + overflow-y: scroll; + color: @legend-item-color; list-style-type: none; padding: 0; + margin-bottom: 0; visibility: visible; min-height: 0; - min-width: 60px; - margin-right: 5px; - - li.color { - min-height: 22px; - padding: 3px 0 3px 0; - text-align: left; - font-size: 12px; - line-height: 13px; - color: @legend-item-color; - cursor: default; - text-align: left; - white-space: nowrap; - overflow-x: hidden; - text-overflow: ellipsis; - max-width: 150px; - - .dots { - padding-right: 5px; - cursor: pointer; - } - } + font-size: 12px; + line-height: 13px; + text-align: left; - .color-selector { - width: 80px; - } + display: flex; + flex-direction: column; } .legend-ul.hidden { visibility: hidden; } +} - .legend-toggle { +.legend-value { + &-title { + padding: 3px; + + &:hover { + background-color: @sidebar-hover-bg; + } + } + + &-truncate { + overflow-x: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + &-full { + white-space: normal; + word-break: break-all; + background-color: @sidebar-hover-bg; + } + + &-details { + border-bottom: 1px solid @sidebar-bg; + + .filter-button { + display: block; + float: left; + width: 50%; + border-radius: 0px; + background-color: @sidebar-bg; + + &:hover { + background-color: @sidebar-hover-bg; + } + } + } + + &-color-picker { + width: 130px; + + .dot { + line-height: 14px; + margin: 2px; + font-size: 14px; + } + + .dot:hover { + margin: 0px; + font-size: 18px + } } } + +.legend-value:hover { + cursor: pointer; +} + + diff --git a/src/ui/public/visualize/visualize.html b/src/ui/public/visualize/visualize.html index 6b105e5c8fab9..9a307ead8e583 100644 --- a/src/ui/public/visualize/visualize.html +++ b/src/ui/public/visualize/visualize.html @@ -7,9 +7,12 @@

No results found

-
+
+
+ +
diff --git a/src/ui/public/visualize/visualize.js b/src/ui/public/visualize/visualize.js index fa4dffe04cb88..89ed633d4df6b 100644 --- a/src/ui/public/visualize/visualize.js +++ b/src/ui/public/visualize/visualize.js @@ -1,10 +1,12 @@ define(function (require) { require('ui/modules') .get('kibana/directive') - .directive('visualize', function (Notifier, SavedVis, indexPatterns, Private, config) { + .directive('visualize', function (Notifier, SavedVis, indexPatterns, Private, config, $timeout) { require('ui/visualize/spy'); require('ui/visualize/visualize.less'); + require('ui/visualize/visualize_legend'); + var $ = require('jquery'); var _ = require('lodash'); var visTypes = Private(require('ui/registry/vis_types')); @@ -40,6 +42,7 @@ define(function (require) { } var getVisEl = getter('.visualize-chart'); + var getVisContainer = getter('.vis-container'); var getSpyEl = getter('visualize-spy'); $scope.fullScreenSpy = false; @@ -47,19 +50,16 @@ define(function (require) { $scope.spy.mode = ($scope.uiState) ? $scope.uiState.get('spy.mode', {}) : {}; var applyClassNames = function () { - var $spyEl = getSpyEl(); - var $visEl = getVisEl(); + var $visEl = getVisContainer(); var fullSpy = ($scope.spy.mode && ($scope.spy.mode.fill || $scope.fullScreenSpy)); - // external - $el.toggleClass('only-visualization', !$scope.spy.mode); - $el.toggleClass('visualization-and-spy', $scope.spy.mode && !fullSpy); - $el.toggleClass('only-spy', Boolean(fullSpy)); - if ($spyEl) $spyEl.toggleClass('only', Boolean(fullSpy)); - - // internal - $visEl.toggleClass('spy-visible', Boolean($scope.spy.mode)); $visEl.toggleClass('spy-only', Boolean(fullSpy)); + + $timeout(function () { + if ($visEl.height() < 100) { + $visEl.addClass('spy-only'); + }; + }, 0); }; // we need to wait for some watchers to fire at least once diff --git a/src/ui/public/visualize/visualize.less b/src/ui/public/visualize/visualize.less index 6290045efcdcc..6879cf173d088 100644 --- a/src/ui/public/visualize/visualize.less +++ b/src/ui/public/visualize/visualize.less @@ -12,20 +12,20 @@ visualize { white-space: pre-line; } - .visualize-chart { + .vis-container { + height: 1px; + display: flex; + flex-direction: row; + flex: 1 1 auto; overflow: auto; -webkit-transition: opacity 0.01s; transition: opacity 0.01s; - &.spy-visible { - margin-bottom: 10px; - height: 0px; - } - &.spy-only { display: none; } + } .loading { @@ -181,3 +181,4 @@ visualize-spy { white-space: pre-wrap; } } + diff --git a/src/ui/public/visualize/visualize_legend.html b/src/ui/public/visualize/visualize_legend.html new file mode 100644 index 0000000000000..4abb1493e4a97 --- /dev/null +++ b/src/ui/public/visualize/visualize_legend.html @@ -0,0 +1,41 @@ +
+
+ +
+
    + +
  • + +
    +
    + {{legendData.label}} +
    + +
    +
    + + +
    + +
    + + +
    + +
    +
    + +
  • +
+
\ No newline at end of file diff --git a/src/ui/public/visualize/visualize_legend.js b/src/ui/public/visualize/visualize_legend.js new file mode 100644 index 0000000000000..a3c60e9c5f86f --- /dev/null +++ b/src/ui/public/visualize/visualize_legend.js @@ -0,0 +1,103 @@ +define(function (require) { + var _ = require('lodash'); + var html = require('ui/visualize/visualize_legend.html'); + + var $ = require('jquery'); + var d3 = require('d3'); + var findByParam = require('ui/utils/find_by_param'); + + require('ui/modules').get('kibana') + .directive('visualizeLegend', function (Private, getAppState) { + var Data = Private(require('ui/vislib/lib/data')); + var colorPalette = Private(require('ui/vislib/components/color/color')); + var filterBarClickHandler = Private(require('ui/filter_bar/filter_bar_click_handler')); + + return { + restrict: 'E', + template: html, + link: function ($scope, $elem) { + var $state = getAppState(); + var clickHandler = filterBarClickHandler($state); + $scope.open = $scope.uiState.get('vis.legendOpen', true); + + $scope.$watch('renderbot.chartData', function (data) { + if (!data) return; + $scope.data = data; + refresh(); + }); + + $scope.highlightSeries = function (label) { + $('[data-label]', $elem.siblings()).not('[data-label="' + label + '"]').css('opacity', 0.5); + }; + + $scope.unhighlightSeries = function () { + $('[data-label]', $elem.siblings()).css('opacity', 1); + }; + + $scope.setColor = function (label, color) { + var colors = $scope.uiState.get('vis.colors') || {}; + colors[label] = color; + $scope.uiState.set('vis.colors', colors); + refresh(); + }; + + $scope.toggleLegend = function () { + var bwcAddLegend = $scope.renderbot.vislibVis._attr.addLegend; + var bwcLegendStateDefault = bwcAddLegend == null ? true : bwcAddLegend; + $scope.open = !$scope.uiState.get('vis.legendOpen', bwcLegendStateDefault); + $scope.uiState.set('vis.legendOpen', $scope.open); + }; + + $scope.filter = function (legendData, negate) { + clickHandler({point: legendData, negate: negate}); + }; + + $scope.canFilter = function (legendData) { + var filters = clickHandler({point: legendData}, true) || []; + return filters.length; + }; + + $scope.colors = [ + '#3F6833', '#967302', '#2F575E', '#99440A', '#58140C', '#052B51', '#511749', '#3F2B5B', //6 + '#508642', '#CCA300', '#447EBC', '#C15C17', '#890F02', '#0A437C', '#6D1F62', '#584477', //2 + '#629E51', '#E5AC0E', '#64B0C8', '#E0752D', '#BF1B00', '#0A50A1', '#962D82', '#614D93', //4 + '#7EB26D', '#EAB839', '#6ED0E0', '#EF843C', '#E24D42', '#1F78C1', '#BA43A9', '#705DA0', // Normal + '#9AC48A', '#F2C96D', '#65C5DB', '#F9934E', '#EA6460', '#5195CE', '#D683CE', '#806EB7', //5 + '#B7DBAB', '#F4D598', '#70DBED', '#F9BA8F', '#F29191', '#82B5D8', '#E5A8E2', '#AEA2E0', //3 + '#E0F9D7', '#FCEACA', '#CFFAFF', '#F9E2D2', '#FCE2DE', '#BADFF4', '#F9D9F9', '#DEDAF7' //7 + ]; + + function refresh() { + var vislibVis = $scope.renderbot.vislibVis; + + if ($scope.uiState.get('vis.legendOpen') == null && vislibVis._attr.addLegend != null) { + $scope.open = vislibVis._attr.addLegend; + } + + $scope.labels = getLabels($scope.data, vislibVis._attr.type); + $scope.getColor = colorPalette(_.pluck($scope.labels, 'label'), $scope.uiState.get('vis.colors')); + } + + // Most of these functions were moved directly from the old Legend class. Not a fan of this. + function getLabels(data, type) { + if (!data) return []; + var data = data.columns || data.rows || [data]; + if (type === 'pie') return Data.prototype.pieNames(data); + return getSeriesLabels(data); + }; + + function getSeriesLabels(data) { + var values = data.map(function (chart) { + return chart.series; + }) + .reduce(function (a, b) { + return a.concat(b); + }, []); + return _.compact(_.uniq(values, 'label')); + } + + + } + }; + }); +});