/**
* Chart captions can be used to place titles, subtitles, credits and other captions
* inside a chart. Please see the chart's {@link Ext.chart.AbstractChart#captions}
* config documentation for the general description of the way captions work, and
* refer to the documentation of this class' configs for details.
*/
Ext.define('Ext.chart.Caption', {
mixins: [
'Ext.mixin.Observable',
'Ext.mixin.Bindable'
],
isCaption: true,
config: {
/**
* The weight controls the order in which the captions are created.
* Captions with lower weights are created first.
* This affects chart's layout. For example, if two captions are docked
* to the 'top', the one with the lower weight will end up on top
* of the other.
*/
weight: 0,
/**
* @cfg {String} text
* The text displayed by the caption.
* Multi-line captions are allowed, e.g.:
*
* captions: {
* title: {
* text: 'India\'s tiger population\n'
* + 'from 1970 to 2015'
* }
* }
*
*/
text: '',
/**
* @cfg {'left'/'center'/'right'} [align='center']
* Determines the horizontal alignment of the caption's text.
*/
align: 'center',
/**
* @cfg {'series'/'chart'} [alignTo='series']
* Whether to align the caption to the 'series' (default) or the 'chart'.
*/
alignTo: 'series',
/**
* @cfg {Number} padding
* The uniform padding applied to both top and bottom of the caption's text.
*/
padding: 0,
/**
* @cfg {Boolean} [hidden=false]
* Controls the visibility of the caption.
*/
hidden: false,
/**
* @cfg {'top'/'bottom'} [docked='top']
* The position of the caption in a chart.
*/
docked: 'top',
/**
* @cfg {Object} style
* Style attributes for the caption's text.
* All attributes that are valid {@link Ext.draw.sprite.Text text sprite} attributes
* are valid here. However, only font attributes (such as `fontSize`, `fontFamily`, ...),
* color attributes (such as `fillStyle`) and `textAlign` attribute are guaranteed to
* produce correct behavior. For example, transform attributes are not officially supported.
*/
style: {
fontSize: '14px',
fontWeight: 'bold',
fontFamily: 'Verdana, Aria, sans-serif'
},
/**
* @private
* @cfg {Ext.chart.AbstractChart} chart
* The chart the label belongs to.
*/
chart: null,
/**
* @private
* The text sprite used to render caption's text.
*/
sprite: {
type: 'text',
preciseMeasurement: true,
zIndex: 10
},
//<debug>
/**
* @private
* @cfg {Boolean} debug
* Whether to show the bounding boxes or not.
*/
debug: false,
//</debug>
/**
* @private
* The logical rect of the caption in the `surfaceName` surface.
*/
rect: null
},
surfaceName: 'caption',
constructor: function(config) {
var me = this,
id;
if ('id' in config) {
id = config.id;
}
else if ('id' in me.config) {
id = me.config.id;
}
else {
id = me.getId();
}
me.setId(id);
me.mixins.observable.constructor.call(me, config);
me.initBindable();
},
updateChart: function() {
if (!this.isConfiguring) {
// Re-create caption's sprite in another chart.
this.setSprite({
type: 'text'
});
}
},
applySprite: function(sprite) {
var me = this,
chart = me.getChart(),
surface = me.surface = chart.getSurface(me.surfaceName);
//<debug>
me.rectSprite = surface.add({
type: 'rect',
fillStyle: 'yellow',
strokeStyle: 'red'
});
//</debug>
return sprite && surface.add(sprite);
},
updateSprite: function(sprite, oldSprite) {
if (oldSprite) {
oldSprite.destroy();
}
},
updateText: function(text) {
this.getSprite().setAttributes({
text: text
});
},
updateStyle: function(style) {
this.getSprite().setAttributes(style);
},
//<debug>
updateDebug: function(debug) {
var me = this,
sprite = me.getSprite();
if (debug && !me.rectSprite) {
me.rectSprite = me.surface.add({
type: 'rect',
fillStyle: 'yellow',
strokeStyle: 'red'
});
}
if (sprite) {
sprite.setAttributes({
debug: debug ? { bbox: true } : null
});
}
if (me.rectSprite) {
me.rectSprite.setAttributes({
hidden: !debug
});
}
if (!me.isConfiguring) {
me.surface.renderFrame();
}
},
//</debug>
updateRect: function(rect) {
if (this.rectSprite) {
this.rectSprite.setAttributes({
x: rect[0],
y: rect[1],
width: rect[2],
height: rect[3]
});
}
},
updateDocked: function() {
var chart = this.getChart();
if (chart && !this.isConfiguring) {
chart.scheduleLayout();
}
},
/**
* @private
* Computes and sets the caption's rect.
* Shrinks the given chart rect to accomodate the caption.
* The chart rect is [top, left, width, height] in chart's
* body element coordinates.
* The shrink rect is {left, top, right, bottom} in `caption`
* surface coordinates.
*/
computeRect: function(chartRect, shrinkRect) {
if (this.getHidden()) {
return null;
}
// eslint-disable-next-line vars-on-top
var rect = [0, 0, chartRect[2], 0],
docked = this.getDocked(),
padding = this.getPadding(),
textSize = this.getSprite().getBBox(),
height = textSize.height + padding * 2;
switch (docked) {
case 'top':
rect[1] = shrinkRect.top;
rect[3] = height;
chartRect[1] += height;
chartRect[3] -= height;
shrinkRect.top += height;
break;
case 'bottom':
chartRect[3] -= height;
shrinkRect.bottom -= height;
rect[1] = shrinkRect.bottom;
rect[3] = height;
break;
}
this.setRect(rect);
},
alignRect: function(seriesRect) {
var surfaceRect = this.surface.getRect(),
rect = this.getRect();
rect[0] = seriesRect[0] - surfaceRect[0];
rect[2] = seriesRect[2];
// Slice to trigger the applier/updater.
this.setRect(rect.slice());
},
performLayout: function() {
var me = this,
rect = me.getRect(),
x = rect[0],
y = rect[1],
width = rect[2],
height = rect[3],
sprite = me.getSprite(),
tx = sprite.attr.translationX,
ty = sprite.attr.translationY,
bbox = sprite.getBBox(),
align = me.getAlign(),
dx, dy;
switch (align) {
case 'left':
dx = x - bbox.x;
break;
case 'right':
dx = (x + width) - (bbox.x + bbox.width);
break;
case 'center':
dx = x + (width - bbox.width) / 2 - bbox.x;
break;
}
dy = y + (height - bbox.height) / 2 - bbox.y;
sprite.setAttributes({
translationX: tx + dx,
translationY: ty + dy
});
},
destroy: function() {
var me = this;
//<debug>
if (me.rectSprite) {
me.rectSprite.destroy();
}
//</debug>
me.getSprite().destroy();
me.callParent();
}
});