/** * Abstract class that provides default styles for non-specified things. * Should be sub-classed when creating new themes. * For example: * * Ext.define('Ext.chart.theme.Custom', { * extend: 'Ext.chart.theme.Base', * singleton: true, * alias: 'chart.theme.custom', * config: { * baseColor: '#ff9f00' * } * }); * * Theme provided values will not override the values provided in an instance config. * However, if a theme provided value is an object, it will be merged with the value * from the instance config, unless the theme provided object has a '$default' key * set to 'true'. * * Certain chart theme configs (e.g. 'fontSize') may use the 'default' value to indicate * that they should inherit a value from the corresponding CSS style provided by * a framework theme. Additionally, one can use basic binary operators like multiplication, * addition and subtraction to derive from the default value, e.g. fontSize: 'default*1.3'. * * Important: the theme should not use the 'font' shorthand to specify the font of labels * and other text elements of a chart. Instead, individual font properties should be used: * 'fontStyle', 'fontVariant', 'fontWeight', 'fontSize' and 'fontFamily'. */Ext.define('Ext.chart.theme.Base', { extend: 'Ext.chart.theme.BaseTheme', mixins: { factoryable: 'Ext.mixin.Factoryable' }, requires: ['Ext.draw.Color'], factoryConfig: { type: 'chart.theme' }, isTheme: true, isBase: true, config: { /** * @cfg {String/Ext.util.Color} baseColor * The base color used to generate the {@link Ext.chart.AbstractChart#colors} of the theme. */ baseColor: null, /** * @cfg {Array} colors * * Array of colors/gradients to be used by the theme. * Defaults to {@link #colorDefaults}. */ colors: undefined, /** * @cfg {Object} gradients * * The gradient config to be used by series' sprites. E.g.: * * { * type: 'linear', * degrees: 90 * } * * Please refer to the documentation for the {@link Ext.draw.gradient.Linear linear} * and {@link Ext.draw.gradient.Radial radial} gradients for all possible options. * The color {@link Ext.draw.gradient.Gradient#stops stops} for the gradients * will be generated by the theme based on the {@link #colors} config. */ gradients: null, /** * @cfg {Object} chart * Theme defaults for the chart. * Can apply to all charts or just a specific type of chart. * For example: * * chart: { * defaults: { * background: 'lightgray' * }, * polar: { * background: 'green' * } * } * * The values from the chart.defaults and chart.*type* configs (where *type* is a valid * chart xtype, e.g. '{@link Ext.chart.CartesianChart cartesian}' or * '{@link Ext.chart.PolarChart polar}') will be applied to corresponding chart configs. * E.g., the chart.defaults.background config will set the * {@link Ext.chart.AbstractChart#background} config of all charts, where the * chart.cartesian.flipXY config will only set the * {@link Ext.chart.CartesianChart#flipXY} config of all cartesian charts. */ chart: { defaults: { captions: { title: { docked: 'top', padding: 5, style: { textAlign: 'center', fontFamily: 'default', fontWeight: '500', fillStyle: 'black', fontSize: 'default*1.6' } }, subtitle: { docked: 'top', style: { textAlign: 'center', fontFamily: 'default', fontWeight: 'normal', fillStyle: 'black', fontSize: 'default*1.3' } }, credits: { docked: 'bottom', padding: 5, style: { textAlign: 'left', fontFamily: 'default', fontWeight: 'lighter', fillStyle: 'black', fontSize: 'default' } } }, background: 'white' } }, /** * @cfg {Object} axis * Theme defaults for the axes. * Can apply to all axes or only axes with a specific position. * For example: * * axis: { * defaults: { * style: {strokeStyle: 'red'} * }, * left: { * title: {fillStyle: 'green'} * } * } * * The values from the axis.defaults and axis.*position* configs (where *position* * is a valid axis {@link Ext.chart.axis.Axis#position}, e.g. 'bottom') will be * applied to corresponding {@link Ext.chart.axis.Axis axis} configs. * E.g., the axis.defaults.label config will apply to the {@link Ext.chart.axis.Axis#label} * config of all axes, where the axis.left.titleMargin config will only apply to the * {@link Ext.chart.axis.Axis#titleMargin} config of all axes positioned to the left. */ axis: { defaults: { label: { x: 0, y: 0, textBaseline: 'middle', textAlign: 'center', fontSize: 'default', fontFamily: 'default', fontWeight: 'default', fillStyle: 'black' }, title: { fillStyle: 'black', fontSize: 'default*1.23', fontFamily: 'default', fontWeight: 'default' }, style: { strokeStyle: 'black' }, grid: { strokeStyle: 'rgb(221, 221, 221)' } }, top: { style: { textPadding: 5 } }, bottom: { style: { textPadding: 5 } } }, /** * @cfg {Object} series * Theme defaults for the series. * Can apply to all series or just a specific type of series. * For example: * * series: { * defaults: { * style: { * lineWidth: 2 * } * }, * bar: { * animation: { * easing: 'bounceOut', * duration: 1000 * } * } * } * * The values from the series.defaults and series.*type* configs (where *type* * is a valid series {@link Ext.chart.series.Series#type}, e.g. 'line') will be * applied to corresponding series configs. * E.g., the series.defaults.label config will apply to the * {@link Ext.chart.series.Series#label} config of all series, where the series.line.step * config will only apply to the * {@link Ext.chart.series.Line#step} config of {@link Ext.chart.series.Line line} series. */ series: { defaults: { label: { fillStyle: 'black', strokeStyle: 'none', fontFamily: 'default', fontWeight: 'default', fontSize: 'default*1.077', textBaseline: 'middle', textAlign: 'center' }, labelOverflowPadding: 5 } }, /** * @cfg {Object} sprites * Default style for the custom chart sprites by type. * For example: * * sprites: { * text: { * fontWeight: 300 * } * } * * These sprite attribute overrides will apply to custom sprites of all charts * specified using the {@link Ext.draw.Container#sprites} config. * The overrides are specified by sprite type, e.g. sprites.text config * tells to apply given attributes to all {@link Ext.draw.sprite.Text text} sprites. */ sprites: { text: { fontSize: 'default', fontWeight: 'default', fontFamily: 'default', fillStyle: 'black' } }, /** * Style information for the {Ext.chart.legend.SpriteLegend sprite legend}. * If the {@link Ext.chart.legend.Legend DOM} legend is used, this config is ignored. * For additional details see {@link Ext.chart.AbstractChart#legend}. * @cfg {Object} legend * @cfg {Ext.chart.legend.sprite.Item} legend.item * @cfg {Object} legend.border See {@link Ext.chart.legend.SpriteLegend#border}. */ legend: { label: { fontSize: 14, fontWeight: 'default', fontFamily: 'default', fillStyle: 'black' }, border: { lineWidth: 1, radius: 4, fillStyle: 'none', strokeStyle: 'gray' }, background: 'white' }, /** * @private * An object with the following structure: * { * fillStyle: [color, color, ...], * strokeStyle: [color, color, ...], * ... * } * If missing, generated from the other configs: 'baseColor, 'gradients', 'colors'. */ seriesThemes: undefined, markerThemes: { type: ['circle', 'cross', 'plus', 'square', 'triangle', 'diamond'] }, /** * @deprecated 5.0.1 Use the {@link Ext.draw.Container#gradients} config instead. */ useGradients: false, /** * @deprecated 5.0.1 Use the {@link Ext.chart.AbstractChart#background} config instead. */ background: null }, colorDefaults: [ '#94ae0a', '#115fa6', '#a61120', '#ff8809', '#ffd13e', '#a61187', '#24ad9a', '#7c7474', '#a66111' ], constructor: function(config) { this.initConfig(config); this.resolveDefaults(); }, defaultRegEx: /^default([+\-/*]\d+(?:\.\d+)?)?$/, defaultOperators: { '*': function(v1, v2) { return v1 * v2; }, '+': function(v1, v2) { return v1 + v2; }, '-': function(v1, v2) { return v1 - v2; } }, resolveChartDefaults: function() { var chart = Ext.clone(this.getChart()), chartType, captionName, chartConfig, captionConfig; for (chartType in chart) { chartConfig = chart[chartType]; if ('captions' in chartConfig) { for (captionName in chartConfig.captions) { captionConfig = chartConfig.captions[captionName]; if (captionConfig) { this.replaceDefaults(captionConfig.style); } } } } this.setChart(chart); }, resolveDefaults: function() { var me = this; Ext.onInternalReady(function() { var sprites = Ext.clone(me.getSprites()), legend = Ext.clone(me.getLegend()), axis = Ext.clone(me.getAxis()), series = Ext.clone(me.getSeries()), div, key, config; if (!me.superclass.defaults) { div = Ext.getBody().createChild({ tag: 'div', cls: me.defaultsDivCls }); me.superclass.defaults = { fontFamily: div.getStyle('fontFamily'), fontWeight: div.getStyle('fontWeight'), fontSize: parseFloat(div.getStyle('fontSize')), fontVariant: div.getStyle('fontVariant'), fontStyle: div.getStyle('fontStyle') }; div.destroy(); } me.resolveChartDefaults(); me.replaceDefaults(sprites.text); me.setSprites(sprites); me.replaceDefaults(legend.label); me.setLegend(legend); for (key in axis) { config = axis[key]; me.replaceDefaults(config.label); me.replaceDefaults(config.title); } me.setAxis(axis); for (key in series) { config = series[key]; me.replaceDefaults(config.label); } me.setSeries(series); }); }, replaceDefaults: function(target) { var me = this, defaults = me.superclass.defaults, defaultRegEx = me.defaultRegEx, key, value, match, binaryFn; if (Ext.isObject(target)) { for (key in defaults) { match = defaultRegEx.exec(target[key]); if (match) { value = defaults[key]; match = match[1]; if (match) { binaryFn = me.defaultOperators[match.charAt(0)]; value = Math.round(binaryFn(value, parseFloat(match.substr(1)))); } target[key] = value; } } } }, applyBaseColor: function(baseColor) { var midColor, midL; if (baseColor) { midColor = baseColor.isColor ? baseColor : Ext.util.Color.fromString(baseColor); midL = midColor.getHSL()[2]; if (midL < 0.15) { midColor = midColor.createLighter(0.3); } else if (midL < 0.3) { midColor = midColor.createLighter(0.15); } else if (midL > 0.85) { midColor = midColor.createDarker(0.3); } else if (midL > 0.7) { midColor = midColor.createDarker(0.15); } this.setColors([ midColor.createDarker(0.3).toString(), midColor.createDarker(0.15).toString(), midColor.toString(), midColor.createLighter(0.12).toString(), midColor.createLighter(0.24).toString(), midColor.createLighter(0.31).toString() ]); } return baseColor; }, applyColors: function(newColors) { return newColors || this.colorDefaults; }, updateUseGradients: function(useGradients) { if (useGradients) { this.updateGradients({ type: 'linear', degrees: 90 }); } }, updateBackground: function(background) { var chart; if (background) { chart = this.getChart(); chart.defaults.background = background; this.setChart(chart); } }, updateGradients: function(gradients) { var colors = this.getColors(), items = [], gradient, midColor, color, i, ln; if (Ext.isObject(gradients)) { for (i = 0, ln = colors && colors.length || 0; i < ln; i++) { midColor = Ext.util.Color.fromString(colors[i]); if (midColor) { color = midColor.createLighter(0.15).toString(); gradient = Ext.apply(Ext.Object.chain(gradients), { stops: [ { offset: 1, color: midColor.toString() }, { offset: 0, color: color.toString() } ] }); items.push(gradient); } } this.setColors(items); } }, applySeriesThemes: function(newSeriesThemes) { var colors, color; // Init the 'colors' config with solid colors generated from the 'baseColor'. this.getBaseColor(); // Init the 'gradients' config with a hardcoded value, if the legacy 'useGradients' // config was set to 'true'. This in turn updates the 'colors' config. this.getUseGradients(); // Init the 'gradients' config normally. This also updates the 'colors' config. this.getGradients(); colors = this.getColors(); // Final colors. if (!newSeriesThemes) { newSeriesThemes = { fillStyle: Ext.Array.clone(colors), strokeStyle: Ext.Array.map(colors, function(value) { color = Ext.util.Color.fromString(value.stops ? value.stops[0].color : value); return color.createDarker(0.15).toString(); }) }; } return newSeriesThemes; }});