Merge branch 'master' into feature/tooltip

Conflicts:
	src/kibana/components/vislib/lib/handler/handler.js
This commit is contained in:
Shelby Sturgis 2014-09-26 22:18:03 +03:00
commit 549b5f113a
48 changed files with 505 additions and 250 deletions

View file

@ -1,6 +1,6 @@
{
"defaults": {
"branch": "1.x",
"plugins": ["elasticsearch/marvel/latest"]
"plugins": ["elasticsearch/marvel/latest", "mobz/elasticsearch-head"]
}
}

View file

@ -16,7 +16,4 @@ notifications:
format: html
on_success: change
template:
- ! '%{repository_slug}/%{branch} by %{author}: %{commit_message} (<a href="%{build_url}">open</a>)'
env:
global:
secure: AX9xidE0quyS07ZfOcecxEGjlNDT9YlM+fvtQHqOaODBII2jg5rgz0SyyxmTPSG68aqUNk8ML9slbRE4h0iPqNkB6fbDE2Dc6oTrRE7XFGDBjw66OHV2ZbsobdORf4UtWO06JBgLUEU2pzRYphe/B14dyA+ZO6p+bAgBmcdLd8k=
- ! '%{repository_slug}/%{branch} by %{author}: %{commit_message} (<a href="%{build_url}">open</a>)'

View file

@ -447,6 +447,8 @@
- refactor this code to simplify and possibly merge with data converter code below
- **[src/kibana/components/vislib/lib/data.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/vislib/lib/data.js)**
- need to make this more generic
- **[src/kibana/components/vislib/lib/x_axis.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/vislib/lib/x_axis.js)**
- need to add mouseover to show tooltip on truncated labels
- **[src/kibana/components/vislib/vis.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/vislib/vis.js)**
- need to come up with a solution for resizing when no data is available
- **[src/kibana/components/vislib/visualizations/column_chart.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/vislib/visualizations/column_chart.js)**

View file

@ -34,7 +34,7 @@
"elasticsearch": "*",
"Faker": "~1.1.0",
"FileSaver": "*",
"font-awesome": "~4.0.3",
"font-awesome": "~4.2.0",
"gridster": "~0.5.0",
"inflection": "~1.3.5",
"jquery": "~2.1.0",

View file

@ -30,11 +30,21 @@
</form>
<div class="button-group">
<button ng-click="newDashboard()"><i class="fa fa-file-o"></i></button>
<button ng-click="openAdd()"><i class="fa fa-plus"></i></button>
<button ng-click="openSave()"><i class="fa fa-save"></i></button>
<button ng-click="openLoad()"><i class="fa fa-folder-open"></i></button>
<button ng-click="openShare()"><i class="fa fa-code"></i></button>
<kbn-tooltip text="New Dashboard" placement="bottom" append-to-body="1">
<button ng-click="newDashboard()"><i class="fa fa-file-new-o"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Save Dashboard" placement="bottom" append-to-body="1">
<button ng-click="openSave()"><i class="fa fa-save"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Load Saved Dashboard" placement="bottom" append-to-body="1">
<button ng-click="openLoad()"><i class="fa fa-folder-open-o"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Share" placement="bottom" append-to-body="1">
<button ng-click="openShare()"><i class="fa fa-external-link"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Add Visualization" placement="bottom" append-to-body="1">
<button ng-click="openAdd()"><i class="fa fa-plus-circle"></i></button>
</kbn-tooltip>
</div>
</navbar>
</nav>

View file

@ -66,8 +66,7 @@ dashboard-grid {
.justify-content(flex-start);
.panel-heading {
.flex();
min-height: 25px;
.flex(0 0 auto);
white-space: nowrap;
display: flex;

View file

@ -121,8 +121,6 @@ define(function (require) {
$scope.opts = {
// number of records to fetch, then paginate through
sampleSize: config.get('discover:sampleSize'),
// max length for summaries in the table
maxSummaryLength: 100,
// Index to match
index: $state.index,
savedSearch: savedSearch,

View file

@ -86,7 +86,6 @@ define(function (require) {
sorting: '=',
filtering: '=',
refresh: '=',
maxLength: '=',
mapping: '=',
timefield: '=?'
},
@ -120,7 +119,6 @@ define(function (require) {
columns: '=',
filtering: '=',
mapping: '=',
maxLength: '=',
timefield: '=?',
row: '=kbnTableRow'
},
@ -138,11 +136,6 @@ define(function (require) {
// whenever we compile, we should create a child scope that we can then detroy
var $child;
// set the maxLength for summaries
if ($scope.maxLength === void 0) {
$scope.maxLength = 250;
}
// toggle display of the rows details, a full list of the fields from each row
$scope.toggleRow = function () {
var row = $scope.row;
@ -235,10 +228,11 @@ define(function (require) {
/**
* Fill an element with the value of a field
*/
function _displayField(el, row, field, truncate) {
var val = _getValForField(row, field, truncate);
el.text(val);
return el;
function _displayField(el, row, field) {
return el.html(
$('<div>').addClass('truncate-by-height')
.text(_getValForField(row, field))
);
}
/**
@ -247,10 +241,9 @@ define(function (require) {
*
* @param {object} row - the row to pull the value from
* @param {string} field - the name of the field (dot-seperated paths are accepted)
* @param {boolean} untruncate - Should truncated values have a "more" link to expand the text?
* @return {[type]} a string, which should be inserted as text, or an element
*/
function _getValForField(row, field, untruncate) {
function _getValForField(row, field) {
var val;
// discover formats all of the values and puts them in _formatted for display
@ -259,11 +252,6 @@ define(function (require) {
// undefined and null should just be an empty string
val = (val == null) ? '' : val;
// truncate the column text, not the details
if (typeof val === 'string' && val.length > $scope.maxLength) {
val = val.substring(0, $scope.maxLength) + '...';
}
return val;
}

View file

@ -21,10 +21,18 @@
</form>
<div class="button-group">
<button ng-click="newQuery()"><i class="fa fa-file-o"></i></button>
<button ng-click="toggleSave()"><i class="fa fa-save"></i></button>
<button ng-click="toggleLoad()"><i class="fa fa-folder-open"></i></button>
<button ng-click="toggleConfig()"><i class="fa fa-gear"></i></button>
<kbn-tooltip text="New Search" placement="bottom" append-to-body="1">
<button ng-click="newQuery()"><i class="fa fa-file-new-o"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Save Search" placement="bottom" append-to-body="1">
<button ng-click="toggleSave()"><i class="fa fa-save"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Load Saved Search" placement="bottom" append-to-body="1">
<button ng-click="toggleLoad()"><i class="fa fa-folder-open-o"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Settings" placement="bottom" append-to-body="1">
<button ng-click="toggleConfig()"><i class="fa fa-gear"></i></button>
</kbn-tooltip>
</div>
</navbar>
@ -137,7 +145,6 @@
sorting="state.sort"
filtering="filterQuery"
refresh="fetch"
max-length="opts.maxSummaryLength"
timefield="opts.timefield"
mapping="fieldsByName">
</kbn-table>

View file

@ -28,9 +28,7 @@
tooltip-placement="top"
tooltip="No cached mapping for this field. Refresh your mapping from the Settings > Indices page"
class="fa fa-warning text-color-warning ng-scope"></i>
<kbn-truncated class="discover-table-details-value"
orig="{{row._formatted[field] || row[field]}}"
length=100></kbn-truncated>
<span class="discover-table-details-value">{{row._formatted[field] || row[field]}}</span>
</td>
</tr>
</tbody>

View file

@ -13,7 +13,6 @@
mapping="mapping"
sorting="sorting"
timefield="timefield"
max-length="maxLength"
filtering="filtering"
class="discover-table-row"></tr>
</tbody>

View file

@ -20,7 +20,11 @@
<tbody>
<tr ng-repeat="conf in configs | filter:advancedFilter" ng-class="conf.value === undefined ? 'default' : 'custom'">
<td class="name">
<b>{{conf.name}}</b><br>
<b>{{conf.name}}</b>
<span class="smaller" ng-show="conf.value !== undefined">
(Default: <i>{{conf.defVal == undefined ? 'null' : conf.defVal}}</i>)
</span>
<br>
<span class="smaller">{{conf.description}}</span>
</td>
<td class="value">

View file

@ -50,30 +50,35 @@
</select>
</div>
<div class="alert alert-danger" ng-repeat="err in index.patternErrors">{{err}}</div>
<div class="alert alert-danger" ng-repeat="err in index.patternErrors">
{{err}}
</div>
<div class="alert alert-info" ng-if="index.samples">
Sample index names
Attempted to match the following indices:
<ul>
<li ng-repeat="sample in index.samples">{{sample}}</li>
</ul>
<a ng-click="moreSamples(true)" class="alert-link">more</a>
<button ng-click="moreSamples(true)" class="btn btn-default">
Expand Search
</button>
</div>
<div class="alert alert-{{index.existing.class}}" ng-if="index.existing">
Pattern matches {{index.existing.matchPercent}} of similar indices
Pattern matches {{index.existing.matchPercent}} of existing indices
<ul>
<li ng-repeat="match in index.existing.matches | orderBy:'toString()'| limitTo: index.sampleCount">{{match}}</li>
</ul>
<a
<button
ng-if="index.sampleCount < index.existing.matches.length"
ng-click="moreSamples()"
class="alert-link">
more
</a>
class="btn btn-default">
Expand Search
</button>
</div>
<div class="alert alert-danger" ng-if="index.existing.failures.length">
Indices that did not match:
Indices that were found, but did not match the pattern:
<ul>
<li ng-repeat="match in index.existing.failures | limitTo: index.sampleCount">{{match}}</li>
</ul>
@ -83,9 +88,7 @@
class="alert-link">
more
</a>
</section>
</div>
</section>
<div class="form-group" ng-if="index.isTimeBased">

View file

@ -10,7 +10,7 @@
<ul class="nav nav-tabs">
<li class="kbn-settings-tab" ng-class="{ active: state.tab === service.title }" ng-repeat="service in services">
<a ng-click="changeTab(service)">{{ service.title }} <small>({{service.hits}})</small></a>
<a ng-click="changeTab(service)">{{ service.title }} <small>({{service.data.length}})</small></a>
</li>
</ul>
<div class="tab-content">

View file

@ -29,7 +29,7 @@ define(function (require) {
var services = registry.all().map(function (obj) {
var service = $injector.get(obj.service);
return service.find(filter).then(function (data) {
return { service: obj.service, title: obj.title, data: data.hits, hits: data.total };
return { service: obj.service, title: obj.title, data: data.hits };
});
});
$q.all(services).then(function (data) {

View file

@ -44,21 +44,31 @@
</div>
<div class="button-group">
<button ng-click="startOver()"><i class="fa fa-file-o"></i></button>
<kbn-tooltip text="New Visualization" placement="bottom" append-to-body="1">
<button ng-click="startOver()"><i class="fa fa-file-new-o"></i></button>
</kbn-tooltip>
<!-- normal save -->
<button ng-if="!editableVis.dirty" ng-click="toggleSave()">
<i class="fa fa-save"></i>
</button>
<kbn-tooltip text="Save Visualization" placement="bottom" append-to-body="1">
<!-- normal save -->
<button ng-if="!editableVis.dirty" ng-click="toggleSave()">
<i class="fa fa-save"></i>
</button>
<!-- save stub with tooltip -->
<button disabled ng-if="editableVis.dirty" tooltip="Apply or Discard your changes before saving">
<i class="fa fa-save"></i>
</button>
<!-- save stub with tooltip -->
<button disabled ng-if="editableVis.dirty" tooltip="Apply or Discard your changes before saving">
<i class="fa fa-save"></i>
</button>
</kbn-tooltip>
<button ng-click="toggleLoad()"><i class="fa fa-folder-open"></i></button>
<button ng-click="toggleShare()"><i class="fa fa-code"></i></button>
<button ng-click="fetch()"><i class="fa fa-refresh"></i></button>
<kbn-tooltip text="Load Saved Visualization" placement="bottom" append-to-body="1">
<button ng-click="toggleLoad()"><i class="fa fa-folder-open-o"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Share Visualization" placement="bottom" append-to-body="1">
<button ng-click="toggleShare()"><i class="fa fa-external-link"></i></button>
</kbn-tooltip>
<kbn-tooltip text="Refresh" placement="bottom" append-to-body="1">
<button ng-click="fetch()"><i class="fa fa-refresh"></i></button>
</kbn-tooltip>
</div>
</navbar>

View file

@ -10,4 +10,8 @@
}
}
.visualizations .visualization i {
margin-right: @padding-base-horizontal;
}
@import "../editor/editor.less";

View file

@ -3,12 +3,12 @@
<span class="pull-right label label-default">Step 2</span>
</h1>
<ul class="list-group list-group-menu">
<a class="list-group-item list-group-menu-item"
<ul class="visualizations list-group list-group-menu">
<a class="visualization list-group-item list-group-menu-item"
ng-repeat="type in visTypes"
ng-href="{{ visTypeUrl(type) }}">
<li>
<i ng-class="type.icon"></i>{{type.title}}
<i class="fa" ng-class="type.icon"></i>{{type.title}}
</li>
</a>
</ul>

View file

@ -46,5 +46,9 @@ define(function (require) {
value: false,
description: 'Shorten long fields, for example, instead of foo.bar.baz, show f.b.baz',
},
'truncate:maxHeight': {
value: 100,
description: 'The maximum height that a cell in a table should occupy. Set to 0 to disable truncation.'
}
};
});

View file

@ -168,7 +168,8 @@ define(function (require) {
};
obj.saveSource = function (source) {
return docSource.doIndex(source).then(function (id) {
return docSource.doIndex(source)
.then(function (id) {
obj.id = id;
})
.then(function () {

View file

@ -8,6 +8,8 @@ define(function (require) {
var clearTO = clearTimeout;
var consoleGroups = ('group' in window.console) && ('groupCollapsed' in window.console) && ('groupEnd' in window.console);
var fatalSplashScreen = require('text!components/notify/partials/fatal_splash_screen.html');
var log = _.noop;
if (typeof KIBANA_DIST === 'undefined') {
log = function () {
@ -170,16 +172,17 @@ define(function (require) {
});
var $container = $('#fatal-splash-screen');
if ($container.size()) {
$container.append(html);
return;
if (!$container.size()) {
$(document.body)
// in case the app has not completed boot
.removeAttr('ng-cloak')
.html(fatalSplashScreen);
$container = $('#fatal-splash-screen');
}
// in case the app has not completed boot
$(document.body)
.removeAttr('ng-cloak')
.html('<div id="fatal-splash-screen" class="container-fuild">' + html + '</div>');
$container.append(html);
console.error(err.stack);
};

View file

@ -9,9 +9,6 @@
<div class="panel-heading">
<h1 class="panel-title">
<i class="fa fa-warning"></i> Fatal Error
<a class="pull-right" onclick="window.location.reload();" href="#">
Reload <i class="refresh fa fa-refresh"></i>
</a>
</h1>
</div>
<div class="panel-body"><%- msg %></div>

View file

@ -0,0 +1,22 @@
<div id="fatal-splash-screen-header" class="container">
<center>
<h1>Oops!</h1>
<p>
Looks like something went wrong. Refreshing may do the trick.
</p>
<p>
<button class="btn btn-success" onclick="window.location.reload();">
<i class="refresh fa fa-refresh"></i> Reload
</button>
or
<a
onclick="localStorage.clear(); sessionStorage.clear(); window.location.hash = ''; window.location.reload();"
href="#" >
clear your session
</a>
</p>
</center>
<div id="fatal-splash-screen">
</div>
</div>

View file

@ -6,7 +6,7 @@ define(function (require) {
return new VisType({
name: 'histogram',
title: 'Vertical bar chart',
icon: 'icon-chart-bar',
icon: 'fa-bar-chart',
vislibParams: {
shareYAxis: true,
addTooltip: true,

View file

@ -6,7 +6,7 @@ define(function (require) {
return new VisType({
name: 'line',
title: 'Line chart',
icon: 'icon-chart-bar',
icon: 'fa-line-chart',
vislibParams: {
shareYAxis: true,
addTooltip: true,

View file

@ -7,7 +7,7 @@ define(function (require) {
return new VisType({
name: 'pie',
title: 'Pie chart',
icon: 'icon-chart-bar',
icon: 'fa-pie-chart',
vislibParams: {
addEvents: true,
addTooltip: true,

View file

@ -32,10 +32,11 @@ define(function (require) {
return function (selection) {
selection.each(function () {
var div = d3.select(this);
var width = $(this).width();
var height = $(this).height();
var el = this;
var div = d3.select(el);
var width = $(el).width();
var height = $(el).height();
self.validateWidthandHeight(width, height);
div.append('svg')

View file

@ -79,6 +79,7 @@ define(function (require) {
var width = $(this).width();
var height = $(this).height();
var size = dataType === 'rows' ? height : width;
var txtHtOffset = 11;
// Check if width or height are 0 or NaN
self.validateWidthandHeight(width, height);
@ -95,9 +96,9 @@ define(function (require) {
.attr('transform', function () {
if (dataType === 'rows') {
// if `rows`, rotate the chart titles
return 'translate(11,' + height / 2 + ')rotate(270)';
return 'translate(' + txtHtOffset + ',' + height / 2 + ')rotate(270)';
}
return 'translate(' + width / 2 + ',8)';
return 'translate(' + width / 2 + ',' + txtHtOffset + ')';
})
.attr('text-anchor', 'middle')
.text(function (d) {

View file

@ -150,6 +150,19 @@ define(function (require) {
});
};
Data.prototype.getNames = function () {
var data = this.pieData();
var names = [];
data.forEach(function returnNames(obj) {
if (obj.names) {
names = names.concat(obj.names);
}
});
return _.unique(names);
};
// Inject zeros into the data
Data.prototype.injectZeros = function () {
return injectZeros(this.data);
@ -172,7 +185,7 @@ define(function (require) {
// Return a function that does color lookup on names for pie charts
Data.prototype.getPieColorFunc = function () {
return color(this.get('names'));
return color(this.getNames());
};
return Data;

View file

@ -40,11 +40,12 @@ define(function (require) {
// Array of objects to render to the visualization
this.renderArray = _.filter([
this.layout,
this.xAxis,
this.yAxis,
this.chartTitle,
this.axisTitle,
this.legend,
this.tooltip,
this.axisTitle,
this.chartTitle,
this.xAxis,
this.yAxis
], Boolean);
}

View file

@ -10,7 +10,7 @@ define(function (require) {
var data = new Data(vis.data, vis._attr);
var PieHandler = new Handler(vis, {
legend: new Legend(vis, vis.el, data.get('names'), data.getPieColorFunc(), vis._attr),
legend: new Legend(vis, vis.el, data.getNames(), data.getPieColorFunc(), vis._attr),
chartTitle: new ChartTitle(vis.el)
});

View file

@ -24,37 +24,36 @@ define(function (require) {
this.xValues = args.xValues;
this.ordered = args.ordered;
this.xAxisFormatter = args.xAxisFormatter;
this._attr = _.defaults(args._attr || {}, {
// isDiscover: false,
//isRotated: true
});
this._attr = _.defaults(args._attr || {});
}
_(XAxis.prototype).extend(ErrorHandler.prototype);
// Render the x axis
XAxis.prototype.render = function () {
d3.select(this.el).selectAll('.x-axis-div').call(this.draw());
d3.select(this.el)
.selectAll('.x-axis-div')
.call(this.draw());
};
// Get the d3 scale
// if time, return time scale
// return d3 ordinal scale for nominal data
XAxis.prototype.getScale = function (ordered) {
// if time, return time scale
if (ordered && ordered.date) {
return d3.time.scale();
}
// return d3 ordinal scale for nominal data
return d3.scale.ordinal();
};
// Add domain to the scale
// if time, return a time domain
// Calculate the min date, max date, and time interval;
// return a nominal domain, i.e. array of x values
XAxis.prototype.getDomain = function (scale, ordered) {
// if time, return a time domain
if (ordered && ordered.date) {
// Calculate the min date, max date, and time interval;
return this.getTimeDomain(scale, ordered);
}
// return a nominal domain, i.e. array of x values
return this.getOrdinalDomain(scale, this.xValues);
};
@ -70,12 +69,12 @@ define(function (require) {
};
// Return the range for the x axis scale
// if time, return a normal range
// if nominal, return rangeBands with a default (0.1) spacer specified
XAxis.prototype.getRange = function (scale, ordered, width) {
// if time, return a normal range
if (ordered && ordered.date) {
return scale.range([0, width]);
}
// if nominal, return rangeBands with a default (0.1) spacer specified
return scale.rangeBands([0, width], 0.1);
};
@ -89,21 +88,20 @@ define(function (require) {
};
// Create the d3 xAxis function
// save a reference to the xScale
// Scale should never === `NaN`
XAxis.prototype.getXAxis = function (width) {
// save a reference to the xScale
this.xScale = this.getXScale(this.ordered, width);
// Scale should never === `NaN`
if (!this.xScale || _.isNaN(this.xScale)) {
throw new Error('xScale is ' + this.xScale);
}
// save a reference to the xAxis
this.xAxis = d3.svg.axis()
.scale(this.xScale)
.ticks(10)
.tickFormat(this.xAxisFormatter)
.orient('bottom');
.scale(this.xScale)
.ticks(10)
.tickFormat(this.xAxisFormatter)
.orient('bottom');
};
// Returns a function that renders the x axis
@ -114,29 +112,37 @@ define(function (require) {
var width;
var height;
var svg;
var parentWidth;
var n;
this._attr.isRotated = false;
return function (selection) {
n = selection[0].length;
parentWidth = $(self.el)
.find('.x-axis-div-wrapper')
.width();
selection.each(function () {
// Validate that width and height are not 0 or `NaN`
// return access to xAxis variable on the object
// append svg and x axis
div = d3.select(this);
width = $(this).width();
width = parentWidth / n;
height = $(this).height();
// Validate that the width and height are not 0 or `NaN`
self.validateWidthandHeight(width, height);
// Return access to xAxis variable on the object
self.getXAxis(width);
// Append svg and x axis
svg = div.append('svg')
.attr('width', width)
.attr('height', height);
.attr('width', width)
.attr('height', height);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,0)')
.call(self.xAxis);
.attr('class', 'x axis')
.attr('transform', 'translate(0,0)')
.call(self.xAxis);
});
selection.call(self.filterOrRotate());
@ -158,80 +164,90 @@ define(function (require) {
labels = axis.selectAll('.tick text');
if (!self.ordered) {
// nominal/ordinal scale
axis.call(self.rotateAxisLabels());
axis.call(self.truncateLabels(100));
} else {
// time scale
axis.call(self.filterAxisLabels());
}
});
self.updateXaxisHeight();
selection.call(self.fitTitles());
};
};
// Rotate the axis tick labels within selection
XAxis.prototype.rotateAxisLabels = function () {
this._attr.isRotated = true;
var self = this;
var text;
var maxWidth = self.xScale.rangeBand();
var textWidth = 0;
var xAxisPadding = 15;
var svg;
var xAxisLabelHt = 15;
var width;
self._attr.isRotated = false;
return function (selection) {
selection.selectAll('.tick text')
text = selection.selectAll('.tick text');
text.each(function textWidths() {
width = d3.select(this).node().getBBox().width;
if (width > maxWidth) {
self._attr.isRotated = true;
xAxisLabelHt = _.max([textWidth, (Math.ceil(width + xAxisPadding))]);
}
});
self._attr.xAxisLabelHt = xAxisLabelHt;
if (self._attr.isRotated) {
text
.text(function truncate() {
return self.truncateLabel(this, xAxisLabelHt);
})
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '-.60em')
.attr('transform', function () {
.attr('transform', function rotate() {
return 'rotate(-90)';
});
selection.select('svg')
.attr('height', xAxisLabelHt);
}
// TODO: need to add mouseover to show tooltip on truncated labels
};
};
// Returns a function that truncates tick labels
XAxis.prototype.truncateLabels = function (size) {
var self = this;
var labels;
var node;
var str;
var n;
var maxWidth;
var maxLength;
var pixPerChar;
var endChar;
// Returns a string that is truncated to fit size
XAxis.prototype.truncateLabel = function (text, size) {
var node = d3.select(text).node();
var str = $(node).text();
var width = node.getBBox().width;
var chars = str.length;
var pxPerChar = width / chars;
var endChar = 0;
var ellipsesPad = 4;
return function (selection) {
// get label maxWidth
labels = selection.selectAll('.tick text');
maxWidth = 0;
maxLength = 0;
labels.each(function () {
node = d3.select(this).node();
n = node.innerHTML.length;
maxWidth = _.max([maxWidth, node.getComputedTextLength() * 0.9]);
maxLength = _.max([maxLength, n]);
});
pixPerChar = maxWidth / maxLength;
// truncate str
selection.selectAll('.tick text')
.text(function (d) {
str = d;
if (maxWidth > size) {
endChar = 0;
if (Math.floor((size / pixPerChar) - 4) >= 4) {
endChar = Math.floor((size / pixPerChar) - 4);
while (str[endChar - 1] === ' ' || str[endChar - 1] === '-' || str[endChar - 1] === ',') {
endChar = endChar - 1;
}
}
str = str.substr(0, endChar) + '...';
return str;
}
return str;
});
};
if (width > size) {
endChar = Math.floor((size / pxPerChar) - ellipsesPad);
while (str[endChar - 1] === ' ' || str[endChar - 1] === '-' || str[endChar - 1] === ',') {
endChar = endChar - 1;
}
str = str.substr(0, endChar) + '...';
}
return str;
};
// Filter out text labels by width and position on axis
// trims labels that would overlap each other
// or extend past left or right edges
// if prev label pos (or 0) + half of label width is < label pos
// and label pos + half width is not > width of axis
XAxis.prototype.filterAxisLabels = function () {
var self = this;
var startX = 0;
@ -240,62 +256,136 @@ define(function (require) {
var myX;
var myWidth;
var halfWidth;
var padding = 1.1;
return function (selection) {
selection.selectAll('.tick text')
.text(function (d, i) {
par = d3.select(this.parentNode).node();
myX = self.xScale(d);
myWidth = par.getBBox().width;
halfWidth = par.getBBox().width / 2;
maxW = $('.x-axis-div').width();
// trims labels that would overlap each other
// or extend past left or right edges
// if prev label pos (or 0) + half of label width is < label pos
// and label pos + half width is not > width of axis
if ((startX + halfWidth) < myX && maxW > (myX + halfWidth)) {
startX = myX + halfWidth;
return self.xAxisFormatter(d);
} else {
d3.select(this.parentNode).remove();
}
});
.text(function (d, i) {
par = d3.select(this.parentNode).node();
myX = self.xScale(d);
myWidth = par.getBBox().width * padding;
halfWidth = myWidth / 2;
maxW = $(self.el).find('.x-axis-div').width();
if ((startX + halfWidth) < myX && maxW > (myX + halfWidth)) {
startX = myX + halfWidth;
return self.xAxisFormatter(d);
} else {
d3.select(this.parentNode).remove();
}
});
};
};
// Returns a function that adjusts axis title and
// all chart title transforms to fit axis labels
// Returns a function that adjusts axis titles and
// chart title transforms to fit axis label divs.
// Sets transform of x-axis-title to fit .x-axis-title div width
// if x-axis-chart-titles, set transform of x-axis-chart-titles
// to fit .chart-title div width
XAxis.prototype.fitTitles = function () {
var self = this;
var visEl = $(self.el);
var xAxisTitle = visEl.find('.x-axis-title');
var xAxisChartTitle = visEl.find('.x-axis-chart-title');
var visEls = $('.vis-wrapper');
var visEl;
var xAxisTitle;
var yAxisTitle;
var xAxisChartTitle;
var yAxisChartTitle;
var titleWidth;
var titleHeight;
var text;
var titles;
var titleWidth;
return function () {
// set transform of x-axis-title text to fit .x-axis-title div width
titleWidth = xAxisTitle.width();
text = d3.select('.x-axis-title')
.select('svg')
.select('text')
.attr('transform', 'translate(' + (titleWidth / 2) + ',11)');
// set transform of x-axis-chart-titles text to fit .chart-title div width
titleWidth = xAxisChartTitle.find('.chart-title').width();
titles = d3.select('.x-axis-chart-title')
.selectAll('.chart-title');
titles.each(function () {
text = d3.select(this)
visEls.each(function () {
var visEl = d3.select(this);
var $visEl = $(this);
var xAxisTitle = $visEl.find('.x-axis-title');
var yAxisTitle = $visEl.find('.y-axis-title');
var titleWidth = xAxisTitle.width();
var titleHeight = yAxisTitle.height();
text = visEl.select('.x-axis-title')
.select('svg')
.attr('width', titleWidth)
.select('text')
.attr('transform', 'translate(' + (titleWidth / 2) + ',11)');
text = visEl.select('.y-axis-title')
.select('svg')
.attr('height', titleHeight)
.select('text')
.attr('transform', 'translate(11,' + (titleHeight / 2) + ')rotate(-90)');
if ($visEl.find('.x-axis-chart-title').length) {
xAxisChartTitle = $visEl.find('.x-axis-chart-title');
titleWidth = xAxisChartTitle.find('.chart-title').width();
titles = visEl.select('.x-axis-chart-title').selectAll('.chart-title');
titles.each(function () {
text = d3.select(this)
.select('svg')
.attr('width', titleWidth)
.select('text')
.attr('transform', 'translate(' + (titleWidth / 2) + ',11)');
});
}
if ($visEl.find('.y-axis-chart-title').length) {
yAxisChartTitle = $visEl.find('.y-axis-chart-title');
titleHeight = yAxisChartTitle.find('.chart-title').height();
titles = visEl.select('.y-axis-chart-title').selectAll('.chart-title');
titles.each(function () {
text = d3.select(this)
.select('svg')
.attr('height', titleHeight)
.select('text')
.attr('transform', 'translate(11,' + (titleHeight / 2) + ')rotate(-90)');
});
}
});
};
};
// Appends div to make .y-axis-spacer-block
// match height of .x-axis-wrapper
XAxis.prototype.updateXaxisHeight = function () {
var self = this;
var selection = d3.selectAll('.vis-wrapper');
var $selection = $('.vis-wrapper');
var titleHts = 30;
var xAxisLabelHt = 15;
if (self._attr.isRotated && self._attr.xAxisLabelHt) {
xAxisLabelHt = self._attr.xAxisLabelHt;
} else {
xAxisLabelHt = self._attr.xAxisLabelHt = xAxisLabelHt;
}
selection.each(function () {
var visEl = d3.select(this);
var $visEl = $(this);
$visEl.find('.x-axis-wrapper')
.height(xAxisLabelHt + titleHts);
$visEl.find('.x-axis-div-wrapper')
.height(xAxisLabelHt);
if (visEl.select('.inner-spacer-block').node() === null) {
visEl.select('.y-axis-spacer-block')
.append('div')
.attr('class', 'inner-spacer-block');
}
visEl.select('.inner-spacer-block')
.style('height', (xAxisLabelHt + titleHts) + 'px');
});
};
return XAxis;
};
});

View file

@ -3,7 +3,6 @@
/* vislib styles */
.arc path {
stroke: #fff;
/* stroke-width: 3px; */
}
div.columns {
@ -155,14 +154,10 @@ li.color {
/* histogram axis and label styles */
.vis-canvas {
/* background-color: #fff; */
}
.chart-bkgd {
fill: #ffffff;
/*fill: #eff3f4;*/
/*stroke: #ddd;*/
/*shape-rendering: crispEdges;*/
}
p.rows-label, p.columns-label {
@ -189,7 +184,6 @@ p.rows-label, p.columns-label {
.tick text {
font-size: 7pt;
fill: #848e96;
/*cursor: pointer;*/
}
.axis {
@ -233,16 +227,14 @@ p.rows-label, p.columns-label {
}
.rect {
/*shape-rendering: crispEdges;*/
stroke: transparent;
stroke-width: 2;
}
.rect.hover {
/*shape-rendering: crispEdges;*/
stroke: #333;
}
/* Flex Box */
/* Flex Box Layout */
.vis-wrapper {
.display(flex);
.flex(1 1 100%);
@ -252,22 +244,19 @@ p.rows-label, p.columns-label {
.error {
.flex(1 1 100%);
.display(flex);
.align-items(center);
text-align: center;
p {
.flex(1 1 auto);
margin-top: 15%;
font-size: 18px;
text-wrap: wrap;
}
}
/* YAxis logic */
.y-axis-col-wrapper {
.display(flex);
.flex(0 0 auto);
.flex-direction(column);
min-width: 35px;
}
.y-axis-col {
@ -277,27 +266,32 @@ p.rows-label, p.columns-label {
}
.y-axis-spacer-block {
.flex(0 1 50px);
min-height: 45px;
}
.y-axis-div-wrapper {
.display(flex);
.flex-direction(column);
.flex(0 0 33px);
width: 38px;
min-height: 20px;
}
.y-axis-div {
.flex(1 1 100%);
.flex(1 1 25px);
min-width: 14px;
min-height: 14px;
}
.y-axis-title {
.flex(0 0 15px);
min-height: 14px;
min-width: 14px;
}
.y-axis-chart-title {
.display(flex);
.flex-direction(column);
.flex(0 0 15px);
min-height: 14px;
width: 14px;
}
.y-axis-title text {
@ -306,6 +300,8 @@ p.rows-label, p.columns-label {
.chart-title {
.flex(1 1 100%);
min-height: 14px;
min-width: 14px;
}
.chart-title text {
@ -323,7 +319,7 @@ p.rows-label, p.columns-label {
.display(flex);
.flex(1 0 20px);
overflow: visible;
margin: 0 4px 0 0;
margin: 0 5px 0 0;
}
.chart-wrapper-column {
@ -335,11 +331,11 @@ p.rows-label, p.columns-label {
.chart-wrapper-row {
.display(flex);
.flex-direction(column);
.flex(1 0 50px);
.flex(1 0 100%);
}
.chart {
.flex(1 1);
.flex(1 1 100%);
overflow: visible;
}
@ -354,24 +350,28 @@ p.rows-label, p.columns-label {
.x-axis-wrapper {
.display(flex);
.flex-direction(column);
.flex(0 1 50px);
min-height: 45px;
overflow: visible;
}
.x-axis-div-wrapper {
.display(flex);
.flex-direction(row);
.flex(0 1 15px);
min-height: 15px;
}
.x-axis-chart-title {
.display(flex);
.flex-direction(row);
.flex(0 0 15px);
min-height: 15px;
max-height: 15px;
min-width: 20px;
}
.x-axis-title {
.flex(0 0 15px);
min-height: 15px;
max-height: 15px;
min-width: 20px;
}
.x-axis-title text {
@ -379,8 +379,9 @@ p.rows-label, p.columns-label {
}
.x-axis-div {
.flex(1 1 100%);
margin-top: -5px;
min-height: 20px;
min-width: 20px;
}
.x.axis path {

View file

@ -4,6 +4,7 @@ define(function (require) {
var $ = require('jquery');
var Chart = Private(require('components/vislib/visualizations/_chart'));
var errors = require('errors');
// Dynamically adds css file
require('css!components/vislib/styles/main');
@ -120,6 +121,12 @@ define(function (require) {
var div = d3.select(el);
var width = $(el).width();
var height = $(el).height();
var minWidth = 20;
var minHeight = 20;
if (width <= minWidth || height <= minHeight) {
throw new errors.ContainerTooSmall();
}
var svg = div.append('svg')
.attr('width', width)

View file

@ -16,9 +16,11 @@ define(function (require) {
require('components/url/url');
require('directives/click_focus');
require('directives/info');
require('directives/tooltip');
require('directives/spinner');
require('directives/paginate');
require('directives/pretty_duration');
require('directives/style_compile');
require('directives/rows');

View file

@ -0,0 +1,36 @@
define(function (require) {
var _ = require('lodash');
var $ = require('jquery');
require('modules')
.get('kibana')
.directive('styleCompile', function ($compile, config) {
return {
restrict: 'E',
link: function ($scope, $el) {
var truncateGradientHeight = 15;
// watch the value of the truncate:maxHeight config param
$scope.$watch(function () {
return config.get('truncate:maxHeight');
}, function (maxHeight) {
if (maxHeight > 0) {
$scope.truncateMaxHeight = maxHeight + 'px !important';
$scope.truncateGradientTop = maxHeight - truncateGradientHeight + 'px';
} else {
$scope.truncateMaxHeight = 'none';
$scope.truncateGradientTop = '-' + truncateGradientHeight + 'px';
}
});
var $style = $('<style>');
$el.after($style);
$scope.$watch(function () { return $el.html(); }, function (stagedCss) {
$style.html(stagedCss);
});
}
};
});
});

View file

@ -0,0 +1,20 @@
define(function (require) {
var html = require('text!partials/tooltip.html');
require('modules')
.get('kibana')
.directive('kbnTooltip', function () {
return {
restrict: 'E',
template: html,
transclude: true,
replace: true,
link: function ($scope, $el, attr) {
$scope.text = attr.text;
$scope.placement = attr.placement || 'top';
$scope.delay = attr.delay || 400;
$scope.appendToBody = attr.appendToBody || 0;
}
};
});
});

View file

@ -67,5 +67,13 @@
<div class="application" ng-view></div>
</div>
<style-compile>
.truncate-by-height {
max-height: {{ truncateMaxHeight }};
}
.truncate-by-height::before {
top: {{ truncateGradientTop }};
}
</style-compile>
</body>
</html>

View file

@ -64,7 +64,7 @@ define(function (require) {
angular
.bootstrap(document, ['kibana'])
.invoke(function () {
$(document.body).children().show();
$(document.body).children(':not(style-compile)').show();
cb();
});
});

View file

@ -1,4 +1,4 @@
<i class="fa fa-info-circle"
<i class="fa fa-info-circle"
tooltip="{{info}}"
tooltip-placement="{{placement}}"
tooltip-popup-delay="250"></i>

View file

@ -20,7 +20,7 @@
ng-href="{{ makeUrl(hit) }}"
ng-click="onChoose(hit, $event)">
<li>
<i ng-class="hit.icon"></i> {{hit.title}}
<i class="fa" ng-if="hit.icon" ng-class="hit.icon"></i> {{hit.title}}
<p ng-if="hit.description" ng-bind="hit.description"></p>
</li>
</a>

View file

@ -0,0 +1,6 @@
<div
tooltip="{{text}}"
tooltip-placement="{{placement}}"
tooltip-popup-delay="{{delay}}"
tooltip-append-to-body="{{appendToBody}}"
ng-transclude></div>

View file

@ -0,0 +1,5 @@
// shims for font-awesome
// new file icon
.@{fa-css-prefix}-file-new-o:before { content: @fa-var-file-o; }
.@{fa-css-prefix}-file-new-o:after { content: @fa-var-plus; position: relative; margin-left: -1.0em; font-size: 0.5em; }

View file

@ -1,7 +1,3 @@
#fatal-splash-screen {
margin: 15px;
}
.toaster-container {
visibility: visible;
width: 100%;

View file

@ -0,0 +1,19 @@
.truncate-by-height {
position: relative;
overflow: hidden;
&:before {
content: " ";
width: 100%;
height: 15px; // copied into index.html!
position: absolute;
left: 0;
background: -moz-linear-gradient(top, rgba(255,255,255,0) 0%, rgba(255,255,255,0.01) 1%, rgba(255,255,255,0.99) 99%, rgba(255,255,255,1) 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,0)), color-stop(1%,rgba(255,255,255,0.01)), color-stop(99%,rgba(255,255,255,0.99)), color-stop(100%,rgba(255,255,255,1))); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,0.01) 1%,rgba(255,255,255,0.99) 99%,rgba(255,255,255,1) 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,0.01) 1%,rgba(255,255,255,0.99) 99%,rgba(255,255,255,1) 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,0.01) 1%,rgba(255,255,255,0.99) 99%,rgba(255,255,255,1) 100%); /* IE10+ */
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%,rgba(255,255,255,0.01) 1%,rgba(255,255,255,0.99) 99%,rgba(255,255,255,1) 100%); /* W3C */
}
}

View file

@ -16,8 +16,9 @@
// call outs
@import "./_callout.less";
// call outs
// font icons
@import "./icon_font.css";
@import "./_font_icons.less";
// FontAwesome fills for glyphicons in bootstrap components
@import "./_glyphicons.less";
@ -30,8 +31,8 @@
// custom navbar style
@import "./_navbar.less";
@import "./_sidebar.less";
@import "./_truncate.less";
html,
body {
@ -400,5 +401,8 @@ input[type="checkbox"],
margin-left: 10px !important;
}
style-compile {
display: none;
}
@import '../components/filter_bar/filter_bar.less';

View file

@ -13,7 +13,7 @@ module.exports = {
build: buildId,
concurrency: 10,
'max-duration': 60,
maxRetries: 1,
maxRetries: 2,
browsers: [
{
browserName: 'chrome',

View file

@ -270,7 +270,6 @@ define(function (require) {
'columns="columns" ' +
'sorting="sorting"' +
'filtering="filtering"' +
'max-length=maxLength ' +
'mapping="mapping"' +
'timefield="timefield" ' +
'></tr>'