/** * Describes a gauge needle as a shape defined in SVG path syntax. * * Note: this class and its subclasses are not supposed to be instantiated directly * - an object should be passed the gauge's {@link Ext.ux.gauge.Gauge#needle} * config instead. Needle instances are also not supposed to be moved * between gauges. */Ext.define('Ext.ux.gauge.needle.Abstract', { mixins: [ 'Ext.mixin.Factoryable' ], alias: 'gauge.needle.abstract', isNeedle: true, config: { /** * The generator function for the needle's shape. * Because the gauge component is resizable, and it is generally * desirable to resize the needle along with the gauge, the needle's * shape should have an ability to grow, typically non-uniformly, * which necessitates a generator function that will update the needle's * path, so that its proportions are appropriate for the current gauge size. * * The generator function is given two parameters: the inner and outer * radius of the needle. For example, for a straight arrow, the path * definition is expected to have the base of the needle at the origin * - (0, 0) coordinates - and point downwards. The needle will be automatically * translated to the center of the gauge and rotated to represent the current * gauge {@link Ext.ux.gauge.Gauge#value value}. * * @param {Function} path The path generator function. * @param {Number} path.innerRadius The function's first parameter. * @param {Number} path.outerRadius The function's second parameter. * @return {String} path.return The shape of the needle in the SVG path syntax returned by * the generator function. */ path: null, /** * The inner radius of the needle. This works just like the `innerRadius` * config of the {@link Ext.ux.gauge.Gauge#trackStyle}. * The default value is `25` to make sure the needle doesn't overlap with * the value of the gauge shown at its center by default. * * @param {Number/String} [innerRadius=25] */ innerRadius: 25, /** * The outer radius of the needle. This works just like the `outerRadius` * config of the {@link Ext.ux.gauge.Gauge#trackStyle}. * * @param {Number/String} [outerRadius='100% - 20'] */ outerRadius: '100% - 20', /** * The shape generated by the {@link #path} function is used as the value * for the `d` attribute of the SVG `<path>` element. This element * has the default class name of `.x-gauge-needle`, so that CSS can be used * to give all gauge needles some common styling. To style a particular needle, * one can use this config to add styles to the needle's `<path>` element directly, * or use a custom {@link Ext.ux.gauge.Gauge#cls class} for the needle's gauge * and style the needle from there. * * This config is not supposed to be updated manually, the styles should * always be updated by the means of the `setStyle` call. For example, * this is not allowed: * * gauge.getStyle().fill = 'red'; // WRONG! * gauge.setStyle({ 'fill': 'red' }); // correct * * Subsequent calls to the `setStyle` will add to the styles set previously * or overwrite their values, but won't remove them. If you'd like to style * from a clean slate, setting the style to `null` first will remove the styles * previously set: * * gauge.getNeedle().setStyle(null); * * If an SVG shape was produced by a designer rather than programmatically, * in other words, the {@link #path} function returns the same shape regardless * of the parameters it was given, the uniform scaling of said shape is the only * option, if one wants to use gauges of different sizes. In this case, * it's possible to specify the desired scale by using the `transform` style, * for example: * * transform: 'scale(0.35)' * * @param {Object} style */ style: null, /** * @private * @param {Number} radius */ radius: 0, /** * @private * Expected in the initial config, required during construction. * @param {Ext.ux.gauge.Gauge} gauge */ gauge: null }, constructor: function(config) { this.initConfig(config); }, applyInnerRadius: function(innerRadius) { return this.getGauge().getRadiusFn(innerRadius); }, applyOuterRadius: function(outerRadius) { return this.getGauge().getRadiusFn(outerRadius); }, updateRadius: function() { this.regeneratePath(); }, setTransform: function(centerX, centerY, rotation) { var needleGroup = this.getNeedleGroup(); needleGroup.setStyle( 'transform', 'translate(' + centerX + 'px,' + centerY + 'px) ' + 'rotate(' + rotation + 'deg)' ); }, applyPath: function(path) { return Ext.isFunction(path) ? path : null; }, updatePath: function(path) { this.regeneratePath(path); }, regeneratePath: function(path) { path = path || this.getPath(); // eslint-disable-next-line vars-on-top var me = this, radius = me.getRadius(), inner = me.getInnerRadius()(radius), outer = me.getOuterRadius()(radius), d = outer > inner ? path(inner, outer) : ''; me.getNeedlePath().dom.setAttribute('d', d); }, getNeedleGroup: function() { var gauge = this.getGauge(), group = this.needleGroup; // The gauge positions the needle by calling its `setTransform` method, // which applies a transformation to the needle's group, that contains // the actual path element. This is done because we need the ability to // transform the path independently from it's position in the gauge. // For example, if the needle has to be made bigger, is shouldn't be // part of the transform that centers it in the gauge and rotates it // to point at the current value. if (!group) { group = this.needleGroup = Ext.get(document.createElementNS(gauge.svgNS, 'g')); gauge.getSvg().appendChild(group); } return group; }, getNeedlePath: function() { var me = this, pathElement = me.pathElement; if (!pathElement) { pathElement = me.pathElement = Ext.get(document.createElementNS(me.getGauge().svgNS, 'path')); pathElement.dom.setAttribute('class', Ext.baseCSSPrefix + 'gauge-needle'); me.getNeedleGroup().appendChild(pathElement); } return pathElement; }, updateStyle: function(style) { var pathElement = this.getNeedlePath(); // Note that we are setting the `style` attribute, e.g `style="fill: red"`, // instead of path attributes individually, e.g. `fill="red"` because // the attribute styles defined in CSS classes will override the values // of attributes set on the elements individually. if (Ext.isObject(style)) { pathElement.setStyle(style); } else { pathElement.dom.removeAttribute('style'); } }, destroy: function() { var me = this; me.pathElement = Ext.destroy(me.pathElement); me.needleGroup = Ext.destroy(me.needleGroup); me.setGauge(null); }});