/**
* @class Ext.chart.Legend
* Defines a legend for a chart's series.
* The 'chart' member must be set prior to rendering.
* @constructor
*/
Ext.define('Ext.chart.Legend', {
/* Begin Definitions */
requires: ['Ext.chart.LegendItem'],
/* End Definitions */
/**
* @cfg {Boolean} visible
* Whether or not the legend should be displayed.
*/
visible: true,
/**
* @cfg {String} position
* The position of the legend in relation to the chart. One of: "top",
* "bottom", "left", "right", or "float". If set to "float", then the legend
* box will be positioned at the point denoted by the x and y parameters.
*/
position: 'bottom',
/**
* @cfg {Number} x
* X-position of the legend box. Used directly if position is set to "float", otherwise
* it will be calculated dynamically.
*/
x: 0,
/**
* @cfg {Number} y
* Y-position of the legend box. Used directly if position is set to "float", otherwise
* it will be calculated dynamically.
*/
y: 0,
/**
* @cfg {String} labelFont
* Font to be used for the legend labels.
*/
labelFont: '12px Helvetica, sans-serif',
/**
* @cfg {String} boxStroke
* Style of the stroke for the legend box
*/
boxStroke: '#000',
/**
* @cfg {String} boxStrokeWidth
* Width of the stroke for the legend box
*/
boxStrokeWidth: 1,
/**
* @cfg {String} boxFill
* Fill style for the legend box
*/
boxFill: '#FFF',
/**
* @cfg {Number} itemSpacing
* Amount of space between legend items
*/
itemSpacing: 10,
/**
* @cfg {Number} padding
* Amount of padding between the legend box's border and its items
*/
padding: 5,
// @private
width: 0,
// @private
height: 0,
/**
* @cfg {Number} boxZIndex
* Sets the z-index for the legend. Defaults to 100.
*/
boxZIndex: 100,
constructor: function(config) {
var me = this;
if (config) {
Ext.apply(me, config);
}
me.items = [];
/**
* Whether the legend box is oriented vertically, i.e. if it is on the left or right side or floating.
* @type {Boolean}
*/
me.isVertical = ("left|right|float".indexOf(me.position) !== -1);
},
/**
* @private Create all the sprites for the legend
*/
create: function() {
var me = this;
if (!me.created && me.isDisplayed()) {
me.createItems();
me.createBox();
me.created = true;
}
},
/**
* @private Determine whether the legend should be displayed. Looks at the legend's 'visible' config,
* and also the 'showInLegend' config for each of the series.
*/
isDisplayed: function() {
return this.visible && this.chart.series.findIndex('showInLegend', true) !== -1;
},
/**
* @private Create the series markers and labels
*/
createItems: function() {
var me = this,
chart = me.chart,
padding = me.padding,
itemSpacing = me.itemSpacing,
maxWidth = 0,
maxHeight = 0,
totalWidth = 0,
totalHeight = 0,
vertical = me.isVertical,
math = Math,
mfloor = math.floor,
mmax = math.max,
x, y, spacing, item, bbox, height, width;
// Create all the item labels, collecting their dimensions and positioning each one
// properly in relation to the previous item
chart.series.each(function(series, i) {
if (series.showInLegend) {
Ext.each([].concat(series.yField), function(field, j) {
item = new Ext.chart.LegendItem({
legend: this,
series: series,
surface: chart.surface,
yFieldIndex: j
});
bbox = item.getBBox();
//always measure from x=0, since not all markers go all the way to the left
width = bbox.width;
height = bbox.height;
if (i + j === 0) {
spacing = vertical ? padding + height / 2 : padding;
}
else {
spacing = itemSpacing / (vertical ? 2 : 1);
}
// Set the item's position relative to the legend box
item.x = mfloor(vertical ? padding : totalWidth + spacing);
item.y = mfloor(vertical ? totalHeight + spacing : padding + height / 2);
// Collect cumulative dimensions
totalWidth += width + spacing;
totalHeight += height + spacing;
maxWidth = mmax(maxWidth, width);
maxHeight = mmax(maxHeight, height);
this.items.push(item);
}, this);
}
}, me);
// Store the collected dimensions for later
me.width = mfloor((vertical ? maxWidth : totalWidth) + padding * 2);
me.height = mfloor((vertical ? totalHeight - 2 * spacing : maxHeight) + (padding * 2));
me.itemHeight = maxHeight;
},
/**
* @private Get the bounds for the legend's outer box
*/
getBBox: function() {
var me = this;
return {
x: me.x,
y: me.y,
width: me.width,
height: me.height
};
},
/**
* @private Create the box around the legend items
*/
createBox: function() {
var me = this,
box = me.boxSprite = me.chart.surface.add(Ext.apply({
type: 'rect',
stroke: me.boxStroke,
"stroke-width": me.boxStrokeWidth,
fill: me.boxFill,
zIndex: me.boxZIndex
}, me.getBBox()));
box.redraw();
},
/**
* @private Update the position of all the legend's sprites to match its current x/y values
*/
updatePosition: function() {
var me = this,
x, y,
legendWidth = me.width,
legendHeight = me.height,
padding = me.padding,
chart = me.chart,
chartBBox = chart.chartBBox,
insets = chart.insetPadding,
chartWidth = chartBBox.width - (insets * 2),
chartHeight = chartBBox.height - (insets * 2),
chartX = chartBBox.x + insets,
chartY = chartBBox.y + insets,
surface = chart.surface,
mfloor = Math.floor;
if (me.isDisplayed()) {
// Find the position based on the dimensions
switch(me.position) {
case "left":
x = insets;
y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
break;
case "right":
x = mfloor(surface.width - legendWidth) - insets;
y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
break;
case "top":
x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
y = insets;
break;
case "bottom":
x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
y = mfloor(surface.height - legendHeight) - insets;
break;
default:
x = mfloor(me.x) + insets;
y = mfloor(me.y) + insets;
}
me.x = x;
me.y = y;
// Update the position of each item
Ext.each(me.items, function(item) {
item.updatePosition();
});
// Update the position of the outer box
me.boxSprite.setAttributes(me.getBBox(), true);
}
}
});