This commit is contained in:
burhan 2016-11-29 11:41:48 -06:00
Родитель ba81ef9d46
Коммит affdb5d46b
6 изменённых файлов: 1148 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1 @@
{"Name":"strike_chart","Id":"0Ab410000009DwFCAU","Files":[{"FileName":"/Users/admin/Dropbox/www/strike/src/aura/strike_chart/strike_chartRenderer.js","ComponentId":"0Ad41000000BUT9CAO"},{"FileName":"/Users/admin/Dropbox/www/strike/src/aura/strike_chart/strike_chartController.js","ComponentId":"0Ad41000000BUPfCAO"},{"FileName":"/Users/admin/Dropbox/www/strike/src/aura/strike_chart/strike_chartStyle.css","ComponentId":"0Ad41000000BUTECA4"},{"FileName":"/Users/admin/Dropbox/www/strike/src/aura/strike_chart/strike_chartHelper.js","ComponentId":"0Ad41000000BUT4CAO"},{"FileName":"/Users/admin/Dropbox/www/strike/src/aura/strike_chart/strike_chart.cmp","ComponentId":"0Ad41000000BUPaCAO"}]}

Просмотреть файл

@ -0,0 +1,39 @@
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" access="global">
<ltng:require scripts="/resource/d3/d3.js" afterScriptsLoaded="{!c.onInit}" />
<aura:attribute name="triggerRedraw" type="Boolean" default="{!false}" description="Flag that triggers redraw of the chart." />
<!-- CHANGE THE DOM AND FORCE RUN RERENDERER, WITHOUT CHANGING THE DOM RERENDER DOESN'T RUN -->
<div class="slds-hide">{!v.triggerRedraw}</div>
<aura:attribute name="type" type="String" required="{!true}" description="Type of the chart user wants to render." />
<aura:attribute name="title" type="String" description="Title of the chart. Shows up at the top-left corner of the chart." />
<aura:attribute name="subtitle" type="String" description="Subtitle of the chart. Shows up at the bottom-right corner of the chart." />
<aura:attribute name="data" type="Map[]" required="{!true}" description="The data based on which chart renders." />
<!-- LINE CHART SPECIFIC ATTRIBUTES -->
<aura:attribute name="xAxisLabel" type="String" description="Label that appears along X axis." />
<aura:attribute name="yAxisLabel" type="String" description="Label that appears along Y axis." />
<aura:attribute name="xAxisDataType" type="String" default="Number" description="Data type on X axis." />
<aura:attribute name="yAxisDataType" type="String" default="Number" description="Data type on Y axis." />
<aura:attribute name="thresholdLabel" type="String" default="Threshhold" description="Label that appears at the threshold value." />
<aura:attribute name="thresholdValue" type="String" description="Value where the threshold line appears. " />
<!-- BAR CHART SPECIFIC ATTRIBUTES -->
<aura:attribute name="orientation" type="String" default="vertical" description="Determines orientation of the bar chart. It defaults to vertical." />
<!-- GAUGE SPECIFIC ATTRIBUTES -->
<aura:attribute name="lowLabel" type="String" default="Not Good" description="Tooltip text of the low section in gauge type chart." />
<aura:attribute name="midLabel" type="String" default="Good" description="Tooltip text of the mid section in gauge type chart." />
<aura:attribute name="highLabel" type="String" default="Excellent" description="Tooltip text of the high section in gauge type chart." />
<div class="slds-box">
<!-- D3 Chart -->
<div aura:id="chartContainer" class="slds-grid">
<div aura:id="chart"></div>
</div>
<!-- /D3 Chart -->
</div>
</aura:component>

Просмотреть файл

@ -0,0 +1,9 @@
({
onInit : function(component, event, helper) {
component.set('v.triggerRedraw', !component.get('v.triggerRedraw'))
window.addEventListener('resize', function(){
component.set('v.triggerRedraw', !component.get('v.triggerRedraw'));
}, true);
}
})

Просмотреть файл

@ -0,0 +1,939 @@
({
drawChart: function(component, helper, chartConfig) {
var chartType = chartConfig.type;
var chartToDraw = chartType + 'Chart';
if(chartType === 'pie' || chartType === 'donut') chartToDraw = 'pieDonutChart';
helper[chartToDraw](component, helper, chartConfig);
},
lineChart: function(component, helper, chartConfig){
var divContainer = d3.select(component.find(chartConfig.containerAuraId).getElement());
var chartWidth = helper.getWidth(chartConfig.containerWidth);
var chartHeight = helper.getHeight(chartWidth);
helper.addDiv(component, chartConfig.containerAuraId, 'title', chartConfig.title, chartWidth);
var svg = divContainer.append('svg')
.attr('width', chartConfig.containerWidth)
.attr('height', helper.getHeight(chartConfig.containerWidth));
helper.addDiv(component, chartConfig.containerAuraId, 'subtitle', chartConfig.subtitle, chartWidth);
var xDomain = helper.addDomain(chartConfig.data, 'x');
var yDomain = helper.addDomain(chartConfig.data, 'y');
var xAxisDataType = chartConfig.xAxisDataType;
var yAxisDataType = chartConfig.yAxisDataType;
var xScale = xAxisDataType === 'number' ? d3.scaleLinear() : d3.scaleTime();
xScale = xScale.range([0, chartWidth]).domain(xDomain);
var yScale = yAxisDataType === 'number' ? d3.scaleLinear() : d3.scaleTime();
yScale = yScale.range([chartHeight, 0]).domain(yDomain);
var numberValuesFormatFunc = function (d) { return helper.abbreviateNumber(d) };
var dateValuesFormatFunc = function (d) { return d3.timeFormat('%b %d')(d) };
var xAxis = helper.addAxis(helper, 'bottom', xScale, 0, xAxisDataType === 'number' ? numberValuesFormatFunc : dateValuesFormatFunc);
var yAxis = helper.addAxis(helper, 'left', yScale, 0, yAxisDataType === 'number' ? numberValuesFormatFunc : dateValuesFormatFunc);
var g = svg.append('g').attr('transform', 'translate(' + (chartWidth * .2) + ', ' + (chartWidth * .07) + ')');
g.append('g')
.attr('transform', 'translate(0,' + chartHeight + ')')
.attr('class', 'x sc-axis')
.call(xAxis)
g.append('g')
.attr('class', 'y sc-axis')
.call(yAxis)
var shouldShowAxisLabels = helper.shouldShowAxisLabels(g, chartWidth);
if (shouldShowAxisLabels) {
helper.addLabel(g, 'x label', 0, chartWidth / 2, chartHeight * 1.2, chartWidth * .03,'middle', chartConfig.xAxisLabel, '#8b8b8b');
helper.addLabel(g, 'y label', -90, -(chartWidth * .34), -(chartHeight * .22), chartWidth * .03, 'middle', chartConfig.yAxisLabel, '#8b8b8b');
}
var line = d3.line()
.x(function(d) { return xScale(d.x); })
.y(function(d) { return yScale(d.y); });
g.append('path')
.datum(chartConfig.data)
.attr('class', 'sc-line')
.attr('d', line);
var area = d3.area()
.x(function(d) { return xScale(d.x); })
.y0(function(d) { return yScale(d.y); })
.y1(chartHeight);
g.append('path')
.datum(chartConfig.data)
.attr('class', 'sc-lc-area')
.attr('d', area);
var circles = g.selectAll('circles')
.data(chartConfig.data)
.enter()
.append('g');
// Add outer circle.
helper.addCircle(circles, function(d) { return xScale(d.x); }, function(d) { return yScale(d.y); }, 20, '', 'transparent', 'transparent', 0)
helper.addCircle(circles, function(d) { return xScale(d.x); }, function(d) { return yScale(d.y); }, 5, '', 'white', '#00a1e0', 2)
var focus = g.append('g').style('display', 'none');
focus.append('line')
.attr('id', 'focusLineX')
.attr('class', 'sc-lc-focus-line');
focus.append('line')
.attr('id', 'focusLineY')
.attr('class', 'sc-lc-focus-line');
focus.append('circle')
.attr('id', 'focusCircle')
.attr('r', 5)
.attr('class', 'sc-lc-focus-circle');
var tooltip = helper.addDiv(component, chartConfig.containerAuraId, 'tooltip', 'tooltip', 'label');
circles.on('mouseover', function(d) {
focus.style('display', null);
});
circles.on('mouseout', function(d) {
tooltip.style('display', 'none');
focus.style('display', 'none');
});
circles.on('mousemove', function(d) {
var x = xScale(d.x);
var y = yScale(d.y);
focus.select('#focusCircle')
.attr('cx', x)
.attr('cy', y);
focus.select('#focusLineX')
.attr('x1', x).attr('y1', yScale(yDomain[0]))
.attr('x2', x).attr('y2', yScale(yDomain[1]));
focus.select('#focusLineY')
.attr('x1', xScale(xDomain[0])).attr('y1', y)
.attr('x2', xScale(xDomain[1])).attr('y2', y);
var formatFirstLine = xAxisDataType === 'number' ? function (d) { return helper.abbreviateNumber(d) } : function (d) { return d3.timeFormat('%b %d')(d) };
var formatSecondLine = yAxisDataType === 'number' ? function (d) { return helper.abbreviateNumber(d) } : function (d) { return d3.timeFormat('%b %d')(d) };
tooltip.style('top', (d3.event.layerY + 10) + 'px').style('left', (d3.event.layerX + 10) + 'px');
tooltip.select('.label').html(
'<span class="sc-axis-label">' + chartConfig.xAxisLabel + ': </span><span class="sc-axis-value">' + formatFirstLine(d.x) + '</span><br/>' +
'<span class="sc-axis-label">' + chartConfig.yAxisLabel + ': </span><span class="sc-axis-value">' + formatSecondLine(d.y) + '</span>'
)
tooltip.style('display', 'block');
});
helper.addThreshold(chartConfig.thresholdValue, chartConfig.thresholdLabel, chartWidth, yDomain, g, 0, chartWidth, yScale(chartConfig.thresholdValue), yScale(chartConfig.thresholdValue), 'end', chartWidth, yScale(chartConfig.thresholdValue), 0);
},
bubbleChart: function(component, helper, chartConfig){
var data = chartConfig.data;
var divContainer = d3.select(component.find(chartConfig.containerAuraId).getElement());
var chartWidth = helper.getWidth(chartConfig.containerWidth);
var chartHeight = helper.getHeight(chartWidth);
helper.addDiv(component, chartConfig.containerAuraId, 'title', chartConfig.title, chartWidth);
var svg = divContainer.append('svg')
.attr('width', chartConfig.containerWidth)
.attr('height', helper.getHeight(chartConfig.containerWidth));
helper.addDiv(component, chartConfig.containerAuraId, 'subtitle', chartConfig.subtitle, chartWidth);
// Circles Calculations
var xAxisMin = d3.min(data, function(d) { return d.x; });
var xAxisMax = d3.max(data, function(d) { return d.x; });
var yAxisMin = d3.min(data, function(d) { return d.y; });
var yAxisMax = d3.max(data, function(d) { return d.y; });
var sizeMin = 5;
var sizeMax = Math.max(Math.min(chartWidth, chartHeight) * .05, sizeMin);
var dataSizeMax = 1,
dataSizeMin = 0,
dataXMax = 1,
dataXMin = 0,
dataYMax = 1,
dataYMin = 0;
if (data.length > 0) {
// make sure the smaller circles don't get covered up
data.sort(function(a, b) {
return a.size < b.size;
});
dataSizeMax = data[0].size;
dataSizeMin = data[0].size;
dataXMax = data[0].x;
dataXMin = data[0].x;
dataYMax = data[0].y;
dataYMin = data[0].y;
for (var i = 0; i < data.length; i++) {
dataSizeMax = Math.max(dataSizeMax, data[i].size);
dataSizeMin = Math.min(dataSizeMin, data[i].size);
dataXMax = Math.max(dataXMax, data[i].x);
dataXMin = Math.min(dataXMin, data[i].x);
dataYMax = Math.max(dataYMax, data[i].y);
dataYMin = Math.min(dataYMin, data[i].y);
}
}
dataXMax = xAxisMax;
dataXMin = xAxisMin;
dataYMax = yAxisMax;
dataYMin = yAxisMin;
dataSizeMax = dataSizeMin == dataSizeMax ? dataSizeMax + 1 : dataSizeMax;
dataXMax = dataXMin == dataXMax ? dataXMax + 1 : dataXMax;
dataYMax = dataYMin == dataYMax ? dataYMax + 1 : dataYMax;
var xScaleSizeBuffer = function() {
return Math.abs(sizeMax / chartWidth * (dataXMax - dataXMin));
}
var yScaleSizeBuffer = function() {
return Math.abs(sizeMax / chartHeight * (dataYMax - dataYMin));
}
var xDomain = [xAxisMin - xScaleSizeBuffer() * 1.2, xAxisMax];
var yDomain = [yAxisMin - yScaleSizeBuffer() * 1.2, yAxisMax];
var xAxisDataType = chartConfig.xAxisDataType;
var yAxisDataType = chartConfig.yAxisDataType;
var xScale = xAxisDataType === 'number' ? d3.scaleLinear() : d3.scaleTime();
xScale = xScale.range([0, chartWidth]).domain(xDomain);
var yScale = yAxisDataType === 'number' ? d3.scaleLinear() : d3.scaleTime();
yScale = yScale.range([chartHeight, 0]).domain(yDomain);
var numberValuesFormatFunc = function (d) { return helper.abbreviateNumber(d) };
var dateValuesFormatFunc = function (d) { return d3.timeFormat('%b %d')(d) };
var xAxis = helper.addAxis(helper, 'bottom', xScale, 0, xAxisDataType === 'number' ? numberValuesFormatFunc : dateValuesFormatFunc);
var yAxis = helper.addAxis(helper, 'left', yScale, 0, yAxisDataType === 'number' ? numberValuesFormatFunc : dateValuesFormatFunc);
var g = svg.append('g').attr('transform', 'translate(' + (chartWidth * .2) + ', ' + (chartWidth * .07) + ')');
g.append('g')
.attr('transform', 'translate(0,' + chartHeight + ')')
.attr('class', 'x sc-axis')
.call(xAxis)
g.append('g')
.attr('class', 'y sc-axis')
.call(yAxis)
var shouldShowAxisLabels = helper.shouldShowAxisLabels(g, chartWidth);
if (shouldShowAxisLabels) {
helper.addLabel(g, 'x label', 0, chartWidth / 2, chartHeight * 1.2, chartWidth * .03,'middle', chartConfig.xAxisLabel, '#8b8b8b');
helper.addLabel(g, 'y label', -90, -(chartWidth * .34), -(chartHeight * .22), chartWidth * .03, 'middle', chartConfig.yAxisLabel, '#8b8b8b');
}
var color = d3.scaleOrdinal(d3.schemeCategory10);
var sizeScale = function(size) {
return Math.abs(((size - dataSizeMin) / (dataSizeMax - dataSizeMin) * (sizeMax - sizeMin)) + sizeMin);
}
var circles = g.selectAll('circles')
.data(data)
.enter()
.append('g');
circles.append('circle')
.attr('cx', function(d) { return xScale(d.x); })
.attr('cy', function(d) { return yScale(d.y); })
.attr('r', function(d) {
return sizeScale(d.size);
})
.style('fill', function(d, i) {
return d.color ? d.color : color(i);
})
.style('stroke', function(d, i) {
return d.color ? d.color : color(i);
})
.style('stroke-width', 2)
var focus = g.append('g').style('display', 'none');
var tooltip = helper.addDiv(component, chartConfig.containerAuraId, 'tooltip', 'tooltip', 'label');
circles.on('mouseover', function(d) {
focus.style('display', null);
});
circles.on('mouseout', function(d) {
tooltip.style('display', 'none');
focus.style('display', 'none');
});
circles.on('mousemove', function(d) {
var x = xScale(d.x);
var y = yScale(d.y);
var formatFirstLine = xAxisDataType === 'number' ? function (d) { return helper.abbreviateNumber(d) } : function (d) { return d3.timeFormat('%b %d')(d) };
var formatSecondLine = yAxisDataType === 'number' ? function (d) { return helper.abbreviateNumber(d) } : function (d) { return d3.timeFormat('%b %d')(d) };
tooltip.style('top', (d3.event.layerY + 10) + 'px').style('left', (d3.event.layerX + 10) + 'px');
tooltip.select('.label').html(
'<span class="sc-axis-label">' + chartConfig.xAxisLabel + ': </span><span class="sc-axis-value">' + formatFirstLine(d.x) + '</span><br/>' +
'<span class="sc-axis-label">' + chartConfig.yAxisLabel + ': </span><span class="sc-axis-value">' + formatSecondLine(d.y) + '</span>'
)
tooltip.style('display', 'block');
});
helper.addThreshold(chartConfig.thresholdValue, chartConfig.thresholdLabel, chartWidth, yDomain, g, 0, chartWidth, yScale(chartConfig.thresholdValue), yScale(chartConfig.thresholdValue), 'end', chartWidth, yScale(chartConfig.thresholdValue), 0);
},
barChart: function(component, helper, chartConfig){
var isVertical = chartConfig.orientation === 'vertical';
var xAxisLabel = isVertical ? chartConfig.xAxisLabel : chartConfig.yAxisLabel;
var yAxisLabel = isVertical ? chartConfig.yAxisLabel : chartConfig.xAxisLabel;
var divContainer = d3.select(component.find(chartConfig.containerAuraId).getElement());
var chartWidth = helper.getWidth(chartConfig.containerWidth);
var chartHeight = helper.getHeight(chartWidth);
helper.addDiv(component, chartConfig.containerAuraId, 'title', chartConfig.title, chartWidth);
var svg = divContainer.append('svg')
.attr('width', chartConfig.containerWidth)
.attr('height', helper.getHeight(chartConfig.containerWidth));
helper.addDiv(component, chartConfig.containerAuraId, 'subtitle', chartConfig.subtitle, chartWidth);
var xDomain = helper.addDomain(chartConfig.data, 'label');
var yDomain = helper.addDomain(chartConfig.data, 'value');
var horizontalXScale = d3.scaleBand().range([chartHeight, 0]).padding(0.1).domain(chartConfig.data.map(function(d) { return d.label; }));
var horizontalYScale = d3.scaleLinear().range([0, chartWidth]).domain([d3.min(chartConfig.data, function(d) { return d.value; }) * (-1.01), d3.max(chartConfig.data, function(d) { return d.value; })]);
var verticalXScale = d3.scaleBand().range([0, chartWidth]).padding(0.1).domain(chartConfig.data.map(function(d) { return d.label; }));
var verticalYScale = d3.scaleLinear().range([chartHeight, 0]).domain([d3.min(chartConfig.data, function(d) { return d.value; }) * (-1.01), d3.max(chartConfig.data, function(d) { return d.value; })]);
var xScale = isVertical ? verticalXScale : horizontalYScale;
var yScale = isVertical ? verticalYScale : horizontalXScale;
var valueFormatFunc = function (d) {
var isDateOrNumber = (d instanceof Date) || (!isNaN(parseFloat(d)) && isFinite(d));
return isDateOrNumber ? d3.timeFormat('%b %d')(d) : d;
};
var labelFormatFunc = function (d) { return helper.abbreviateNumber(d) };
var horizontalXAxis = helper.addAxis(helper, 'bottom', xScale, 0, labelFormatFunc);
var horizontalYAxis = helper.addAxis(helper, 'left', yScale, 0, valueFormatFunc);
var verticalXAxis = helper.addAxis(helper, 'bottom', xScale, 0, valueFormatFunc);
var verticalYAxis = helper.addAxis(helper, 'left', yScale, 0, labelFormatFunc);
var xAxis = isVertical ? verticalXAxis : horizontalXAxis;
var yAxis = isVertical ? verticalYAxis : horizontalYAxis;
var g = svg.append('g').attr('transform', 'translate(' + (chartWidth * .2) + ', ' + (chartWidth * .05) + ')');
var barX = isVertical ? function(d) { return xScale(d.label); } : function(d) { return yScale(d.value); };
var barY = isVertical ? function(d) { return yScale(d.value); } : function(d) { return yScale(d.label); };
var barWidth = isVertical ? xScale.bandwidth() : function(d) { return xScale(d.value); };
var barHeight = isVertical ? function(d) { return chartHeight - yScale(d.value); } : yScale.bandwidth();
var bars = g.selectAll('.sc-bc-bar')
.data(chartConfig.data)
.enter()
.append('rect')
.attr('class', 'sc-bc-bar')
.attr('x', barX)
.attr('width', barWidth)
.attr('y', barY)
.attr('height', barHeight);
var tooltip = helper.addDiv(component, chartConfig.containerAuraId, 'tooltip', 'tooltip', 'label');
bars.on('mouseover', function(d) {
var xAxisValue = isVertical ? valueFormatFunc(d.label) : labelFormatFunc(d.value);
var yAxisValue = isVertical ? labelFormatFunc(d.value) : valueFormatFunc(d.label);
formatHtml(xAxisLabel, xAxisValue, yAxisLabel, yAxisValue);
function formatHtml(xAxisLabel, xAxisValue, yAxisLabel, yAxisValue){
tooltip.select('.label').html(
'<span class="sc-axis-label">' + xAxisLabel + ': </span><span class="sc-axis-value">' + xAxisValue + '</span><br/>' +
'<span class="sc-axis-label">' + yAxisLabel + ': </span><span class="sc-axis-value">' + yAxisValue + '</span>'
);
};
tooltip.style('display', 'block');
});
bars.on('mouseout', function(d) {
tooltip.style('display', 'none');
});
bars.on('mousemove', function(d) {
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX + 10) + 'px');
});
if(isVertical){
helper.addThreshold(chartConfig.thresholdValue, chartConfig.thresholdLabel, chartWidth, yDomain, g, 0, chartWidth, yScale(chartConfig.thresholdValue),
yScale(chartConfig.thresholdValue), 'end', chartWidth, yScale(chartConfig.thresholdValue), 0);
} else {
helper.addThreshold(chartConfig.thresholdValue, chartConfig.thresholdLabel, chartWidth, yDomain, g, xScale(chartConfig.thresholdValue), xScale(chartConfig.thresholdValue),
0, chartHeight, 'start', xScale(chartConfig.thresholdValue), 0, 90);
}
g.append('g')
.attr('transform', 'translate(0,' + chartHeight + ')')
.attr('class', 'x sc-axis')
.call(xAxis)
g.append('g')
.attr('class', 'y sc-axis')
.call(yAxis)
var shouldShowAxisLabels = helper.shouldShowAxisLabels(g, chartWidth);
if (shouldShowAxisLabels) {
helper.addLabel(g, 'x label', 0, chartWidth / 2, chartHeight * 1.2, chartWidth * .03,'middle', chartConfig.xAxisLabel, '#8b8b8b');
helper.addLabel(g, 'y label', -90, -(chartWidth * .34), -(chartHeight * .22), chartWidth * .03, 'middle', chartConfig.yAxisLabel, '#8b8b8b');
}
},
pieDonutChart: function(component, helper, chartConfig){
var isDonut = chartConfig.type === 'donut';
var total = d3.sum(chartConfig.data.map(function(d) {
return d.value;
}));
var size = chartConfig.containerWidth * .6;
var radius = size / 2;
var colors = d3.scaleOrdinal().range(['#00a4e3', '#16325c', '#76ded9', '#08a69e', '#e2cd81',
'#e49e24', '#c03a38', '#fdb665', '#63b07f', '#0d716a']);
var divContainer = d3.select(component.find(chartConfig.containerAuraId).getElement());
var chartWidth = helper.getWidth(chartConfig.containerWidth);
var chartHeight = helper.getHeight(chartWidth);
helper.addDiv(component, chartConfig.containerAuraId, 'title', chartConfig.title, chartWidth);
var svg = divContainer.append('svg')
.attr('width', chartConfig.containerWidth)
.attr('height', helper.getHeight(chartConfig.containerWidth))
.append('g')
.attr('transform', 'translate(' + (size * .55) + ',' + (size * .55) + ')');
helper.addDiv(component, chartConfig.containerAuraId, 'subtitle', chartConfig.subtitle, chartWidth);
var donutWidth = size * .2;
var arc = d3.arc()
.innerRadius(isDonut ? radius - donutWidth : 0)
.outerRadius(radius);
var pie = d3.pie()
.value(function(d) { return d.value; })
.sort(null);
var tooltip = helper.addDiv(component, chartConfig.containerAuraId, 'tooltip', 'tooltip', 'label');
var path = svg.selectAll('g')
.data(pie(chartConfig.data))
.enter()
.append('path')
.attr('class', 'sc-section')
.attr('d', arc)
.attr('fill', function(d, i) {
return colors(d.data.label);
});
path.on('mouseover', function(d) {
var percent = Math.round(1000 * d.data.value / total) / 10;
tooltip.select('.label').html(
'<span class="sc-axis-label">Territory: </span><span class="sc-axis-value">' + d.data.label + '</span><br/>' +
'<span class="sc-axis-label">' + 'Sales' + ': </span><span class="sc-axis-value">' + helper.abbreviateNumber(d.data.value) + '</span><br/>' +
'<span class="sc-pdc-section-percent">' + percent + '% of ' + helper.abbreviateNumber(total) + '</span>');
tooltip.style('display', 'block');
});
path.on('mouseout', function(d) {
tooltip.style('display', 'none');
});
path.on('mousemove', function(d) {
tooltip.style('top', (d3.event.layerY + 10) + 'px').style('left', (d3.event.layerX + 10) + 'px');
});
var legendCircleRadius = size * .027;
var legendSpacing = size * .02;
var legend = svg.selectAll('.sc-pdc-legend')
.data(pie(chartConfig.data))
.enter()
.append('g')
.attr('class', 'sc-pdc-legend')
.attr('transform', function(d, i) {
var legendRectHeight = 2 * legendCircleRadius + legendSpacing;
var vert = (i * legendRectHeight) - 180;
var offset = legendRectHeight * 6 / 2;
var horz = -2 * (2 * legendCircleRadius);
var vert = i * legendRectHeight - (offset * .555);
return 'translate(' + (size * .6) + ',' + vert + ')';
});
helper.addCircle(legend, size * .44, -size * .2, legendCircleRadius, '', function(d) { return colors(d.data.label); }, '', 0);
helper.addLabel(legend, '', 0, size * .37, -size * .187, size * .04, 'end', function(d) { return d.data.label; })
legend.on('mouseover', function(d) {
var percent = Math.round(1000 * d.data.value / total) / 10;
tooltip.select('.label').html(
'<span class="sc-axis-label">Territory: </span><span class="sc-axis-value">' + d.data.label + '</span><br/>' +
'<span class="sc-axis-label">' + 'Sales' + ': </span><span class="sc-axis-value">' + helper.abbreviateNumber(d.data.value) + '</span><br/>' +
'<span class="sc-pdc-section-percent">' + percent + '% of ' + helper.abbreviateNumber(total) + '</span>');
tooltip.style('display', 'block');
});
legend.on('mouseout', function(d) {
tooltip.style('display', 'none');
});
legend.on('mousemove', function(d) {
tooltip.style('top', (d3.event.layerY + 10) + 'px').style('left', (d3.event.layerX + 10) + 'px');
});
if (isDonut) helper.addLabel(svg, '', 0, 0, (size * .07) / 2, size * .07, 'middle', helper.abbreviateNumber(total));
},
gaugeChart: function(component, helper, chartConfig){
var data = chartConfig.data[0];
var minValue = data.minValue;
var maxValue = data.maxValue;
if(minValue >= maxValue){
var error = helper.throwError('minValue should be lower than maxValue');
console.log(error.stack);
};
var size = chartConfig.containerWidth * .9;
var minAngle = -90;
var maxAngle = 90;
var range = maxAngle - minAngle;
var arcWidth = size * .04;
var arcInset = size * .05;
var radius = size / 2;
var pointerHeadLength = Math.round(radius * .65);
var pointerWidth = size * .013;
var pointerTailLength = size * .013;
var arcColors = d3.scaleOrdinal().range(['#c23934', '#ffb75d', '#00716b']);
var divContainer = d3.select(component.find(chartConfig.containerAuraId).getElement());
var chartWidth = helper.getWidth(chartConfig.containerWidth);
var chartHeight = helper.getHeight(chartWidth);
helper.addDiv(component, chartConfig.containerAuraId, 'title', chartConfig.title, chartWidth);
var svg = divContainer.append('svg')
.attr('class', 'gaugeChartSVG')
.attr('width', chartConfig.containerWidth) // push it to the right
.attr('height', helper.getHeight(chartConfig.containerWidth))
.append('g')
.attr('transform', 'translate(' + (size * .0575) + ',' + (size * .04) + ')');
helper.addDiv(component, chartConfig.containerAuraId, 'subtitle', chartConfig.subtitle, chartWidth);
function deg2rad(deg) {
return deg * Math.PI / 180;
};
function centerTranslation() {
return 'translate('+ radius + ',' + radius + ')';
};
// a linear scale that maps domain values to a percent from minValue to maxValue
var scale = d3.scaleLinear().range([0, 1]).domain([minValue, maxValue]);
var sections = {
0: chartConfig.lowLabel,
1: chartConfig.midLabel,
2: chartConfig.highLabel
};
var numberOfSections = Object.keys(sections).length;
function percentToRange(percent, min, max) {
var ret = ((max - min) * percent + min);
return Math.round(ret * 100) / 100;
}
var ticks = [
minValue,
percentToRange(.33, minValue, maxValue).toFixed(2),
percentToRange(.66, minValue, maxValue).toFixed(2),
maxValue
];
var tickData = d3.range(numberOfSections).map(function() { return 1 / numberOfSections; });
var arc = d3.arc()
.innerRadius(radius - arcWidth - arcInset)
.outerRadius(radius - arcInset)
.startAngle(function(d, i) {
var ratio = d * i;
return deg2rad(minAngle + (ratio * range));
})
.endAngle(function(d, i) {
var ratio = d * (i+1);
return deg2rad(minAngle + (ratio * range));
});
var arcs = svg.append('g')
.attr('class', 'arc')
.attr('transform', centerTranslation);
var path = arcs.selectAll('path')
.data(tickData)
.enter()
.append('path')
.attr('class', 'sc-section')
.attr('fill', function(d, i) {
return arcColors(d * i);
})
.attr('d', arc);
var labelInset = size * .02;
var labels = svg.append('g')
.attr('class', 'label')
.attr('transform', centerTranslation);
labels.selectAll('text')
.data(ticks)
.enter()
.append('text')
.attr('text-anchor', 'middle')
.attr('font-size', size * .04)
.attr('transform', function(d) {
var ratio = scale(d);
var newAngle = minAngle + (ratio * range);
return 'rotate(' + newAngle +') translate(0,' + (labelInset - radius) +')';
})
.text(function(d){ return helper.abbreviateNumber(d)});
var whiteTicks = svg.append('g')
.attr('class', 'whiteTicks')
.attr('transform', centerTranslation);
var whiteTicksData = ticks.map(function(tick){ return tick; });
whiteTicksData.shift(); // Remove first item
whiteTicksData.pop(); // Remove last item
function whiteTicksTransformFunc(d) {
var ratio = scale(d);
var newAngle = minAngle + (ratio * range) + .5;
return 'rotate(' + newAngle +') translate(0,' + ( - radius + arcInset) +')';
}
helper.addTicks(helper, whiteTicks, 'rect', whiteTicksData, arcWidth * .2, arcWidth * 1.15, 'white', whiteTicksTransformFunc);
var innerArcWidth = size * .08;
var innerArcInset = size * .095;
var innerArc = d3.arc()
.innerRadius(radius - innerArcWidth - innerArcInset)
.outerRadius(radius - innerArcInset)
.startAngle(deg2rad(minAngle))
.endAngle(deg2rad(maxAngle));
var innerArcs = svg.append('g')
.attr('class', 'arc777')
.attr('transform', centerTranslation);
innerArcs.selectAll('path')
.data(tickData)
.enter()
.append('path')
.attr('fill', '#F4F6F9')
.attr('d', innerArc);
var smallTicks = svg.append('g')
.attr('class', 'smallTicks')
.attr('transform', centerTranslation);
var numOfSmallTicks = 12;
var partition = (maxValue - minValue) / numOfSmallTicks;
var smallTicksData = [];
for (var i=0; i<=numOfSmallTicks; i++) {
smallTicksData.push(minValue + partition * i);
}
function smallTicksTransformFunc(d){
var ratio = scale(d);
var newAngle = minAngle + (ratio * range) - .5;
return 'rotate(' + newAngle +') translate(0,' + (-radius * .81) +')';
}
helper.addTicks(helper, smallTicks, 'rect', smallTicksData, innerArcWidth * .078, innerArcWidth / 2, 'lightgray', smallTicksTransformFunc);
var lineData = [
[pointerWidth / 2, 0],
[0, -pointerHeadLength],
[-(pointerWidth / 2), 0],
[0, pointerTailLength],
[pointerWidth / 2, 0]
];
helper.addCircle(svg, radius, radius, size * .04, '', '#F4F6F9', '#E2E7EF', size * .0055);
helper.addCircle(svg, radius, radius, size * .02, '', 'black', '', 0);
var pointerLine = d3.line().curve(d3.curveMonotoneX);
var pg = svg.append('g').data([lineData])
.attr('class', 'pointer')
.attr('transform', centerTranslation);
var pointer = pg.append('path')
.attr('d', pointerLine ) //function(d) { return pointerLine(d) +'Z';}
.attr('transform', 'rotate(' + minAngle +')');
var tooltip = helper.addDiv(component, chartConfig.containerAuraId, 'tooltip', 'tooltip', 'label');
path.on('mouseover', function(d, i) {
tooltip.select('.label').html('<span class="axisValue">' + sections[i] + '</span>');
tooltip.style('display', 'block');
});
path.on('mouseout', function(d) {
tooltip.style('display', 'none');
});
path.on('mousemove', function(d) {
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX + 10) + 'px');
});
var valueLabel = helper.addLabel(svg, 'label valueLabel', 0, radius, radius + size * .11, size * .05, 'middle', '');
var rangeLabel = helper.addLabel(svg, 'label valueLabel', 0, radius, radius + size * .155, size * .035, 'middle', '');
var valueRanges = [];
ticks.forEach(function(item, index) {
if (!$A.util.isEmpty(ticks[index]) && !$A.util.isEmpty(ticks[index + 1])) {
var tempArray = [];
tempArray.push(Number(item), Number(ticks[index + 1]));
valueRanges.push(tempArray);
};
});
setTimeout(function(){
moveTheNeedle(data.value);
}, 1500);
function moveTheNeedle(value, newConfiguration){
function getValueProperties(value) {
var rangeProperties = {};
valueRanges.forEach(function(valueRange, index){
if(valueRange[0] <= value && value <= valueRange[1]) {
rangeProperties.value = '(' + helper.abbreviateNumber(valueRange[0]) + ' to ' + helper.abbreviateNumber(valueRange[1]) + ')';
rangeProperties.color = arcColors((1 / valueRanges.length) * index);
return;
};
});
return rangeProperties;
};
var rangeProperties = getValueProperties(value);
valueLabel.text(helper.abbreviateNumber(value))
.attr('fill', rangeProperties.color);
rangeLabel.text(rangeProperties.value)
.attr('fill', rangeProperties.color);
var ratio = scale(value);
var angle = minAngle + (ratio * range);
function tween(d, i, a) {
return d3.interpolateString('rotate(' + minAngle + ', 0, 0)', 'rotate(' + angle + ', 0, 0)');
}
var transitionMs = 4000;
pointer.transition()
.duration(transitionMs)
.ease(d3.easeElasticOut)
.attrTween('transform', tween);
};
},
getWidth: function(basedOnWidth){
return basedOnWidth * .75;
},
getHeight: function(basedOnWidth){
return basedOnWidth * .66;
},
shouldShowAxisLabels: function(parent, chartWidth){
var xAxisLabels = d3.selectAll('g .x text');
var numberOfXAxisTicks = xAxisLabels.size();
var widestXAxisLabel = 0;
xAxisLabels.each(function() {
widestXAxisLabel = Math.max(widestXAxisLabel, this.clientWidth);
});
var shouldRotateXAxisLabels = ((widestXAxisLabel + 8) * numberOfXAxisTicks) > chartWidth;
if(shouldRotateXAxisLabels){
parent.selectAll('.x text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '-.55em')
.attr('transform', function(){
return 'translate(' + (0) + ',' + (0) +') ' + 'rotate(-90)';
})
}
var yAxisLabels = d3.selectAll('g .y text');
var widestYAxisLabel = 0;
yAxisLabels.each(function() {
widestYAxisLabel = Math.max(widestYAxisLabel, this.clientWidth);
});
var shouldHideYAxisLabels = widestYAxisLabel > chartWidth * .1;
return (chartWidth > 280 && (!shouldRotateXAxisLabels && !shouldHideYAxisLabels))
},
addCircle: function(parent, circleX, circleY, radius, cssClass, fill, stroke, strokeWidth){
parent.append('circle')
.attr('cx', circleX)
.attr('cy', circleY)
.attr('r', radius)
.attr('class', cssClass)
.style('fill', fill)
.style('stroke', stroke)
.style('stroke-width', strokeWidth);
},
addTicks: function(helper, ticks, shape, data, chartWidth, chartHeight, fill, transformFunc){
ticks.selectAll(shape)
.data(data)
.enter()
.append('rect')
.attr('width', chartWidth)
.attr('height', chartHeight)
.style('fill', fill)
.attr('transform', transformFunc)
.text(function(d){ return helper.abbreviateNumber(d)});
},
addLabel: function(parent, cssClass, rotation, x, y, fontSize, textAnchor, label, fill){
var label = parent.append('text')
.attr('class', cssClass)
.attr('text-anchor', textAnchor)
.attr('transform', 'rotate(' + rotation + ')')
.attr('x', x)
.attr('y', y)
.attr('font-size', fontSize)
.attr('dy', '0em')
.text(label);
if(fill !== undefined) label.attr('fill', fill);
return label;
},
addDiv: function(component, containerAuraId, type, label, chartWidth){
var div = d3.select(component.find(containerAuraId).getElement()).append('div');
if(type === 'tooltip'){
div.attr('class', 'sc-tooltip')
div.append('div').attr('class', 'label');
} else if (type === 'title'){
div.attr('class', 'slds-p-bottom--small slds-text-align--left slds-text-body--small slds-p-left--small')
.style('font-size', chartWidth * .0376 + 'px')
.html(label);
} else if(type === 'subtitle'){
div.attr('class', 'slds-p-top--small slds-text-align--right slds-text-body--small slds-p-right--small')
.style('font-size', chartWidth * .0376 + 'px')
.html(label);
}
return div;
},
addThreshold: function(thresholdValue, thresholdLabel, chartWidth, domain, parent, lineX1, lineX2, lineY1, lineY2, textAnchor, textX, textY, rotateAngle){
if(thresholdValue && domain[0] <= thresholdValue && domain[1] >= thresholdValue) {
parent.append('line')
.attr('x1', lineX1)
.attr('y1', lineY1)
.attr('x2', lineX2)
.attr('y2', lineY2)
.attr('class', 'sc-threshold-line');
parent.append('text')
.attr('dy', '-.4em')
.attr('text-anchor', textAnchor)
.attr('font-size', chartWidth * .025)
.text(thresholdLabel)
.attr('transform', 'translate(' + textX + ', ' + textY + ') rotate(' + rotateAngle + ')')
.attr('class', 'sc-threshold-text')
}
},
addDomain: function(data, axis) {
return d3.extent(data, function(d) { return d[axis]; })
},
addAxis: function(helper, orientation, scale, tickSizeOuter, tickFormat) {
if(orientation === 'bottom'){
return d3.axisBottom().scale(scale).tickSizeOuter(tickSizeOuter).tickFormat(tickFormat);
} else if(orientation === 'left'){
return d3.axisLeft().scale(scale).tickSizeOuter(tickSizeOuter).tickFormat(tickFormat);
}
},
abbreviateNumber: function(amount){
amount = Number(amount);
if ((Math.abs(amount) / Math.pow(10, 12)) >= 1) {
return parseFloat((amount / Math.pow(10, 12)).toFixed(1)) + 'T';
} else if ((Math.abs(amount) / Math.pow(10, 9)) >= 1) {
return parseFloat((amount / Math.pow(10, 9)).toFixed(1)) + 'B';
} else if ((Math.abs(amount) / Math.pow(10, 6)) >= 1) {
return parseFloat((amount / Math.pow(10, 6)).toFixed(1)) + 'M';
} else if ((Math.abs(amount) / Math.pow(10, 3)) >= 1) {
return parseFloat((amount / Math.pow(10, 3)).toFixed(2)) + 'K';
}
return parseFloat(amount.toFixed(2));
},
throwError: function(errorMessage){
function ErrorMessage() {
var temp = Error.apply(this, arguments);
temp.name = this.name = 'Error Message';
this.message = temp.message;
if(Object.defineProperty) {
/*this.stack = */Object.defineProperty(this, 'stack', {
get: function() {
return temp.stack
},
configurable: true
})
} else {
this.stack = temp.stack
}
}
//inherit prototype using ECMAScript 5 (IE 9+)
ErrorMessage.prototype = Object.create(Error.prototype, {
constructor: {
value: ErrorMessage,
writable: true,
configurable: true
}
});
return new ErrorMessage(errorMessage);
}
})

Просмотреть файл

@ -0,0 +1,58 @@
({
render : function(component, helper) {
var ret = this.superRender();
return ret;
},
rerender : function(component, helper){
var ret = this.superRerender();
var chartConfig = {
containerAuraId: 'chart',
containerWidth: component.find('chartContainer').getElement().clientWidth,
type: component.get('v.type'),
data: component.get('v.data'),
title: component.get('v.title'),
subtitle: component.get('v.subtitle'),
// LINE CHART SPECIFIC ATTRIBUTES
xAxisLabel: component.get('v.xAxisLabel'),
yAxisLabel: component.get('v.yAxisLabel'),
xAxisDataType: component.get('v.xAxisDataType'),
yAxisDataType: component.get('v.yAxisDataType'),
thresholdValue: component.get('v.thresholdValue'),
// BAR CHART SPECIFIC ATTRIBUTES
orientation: component.get('v.orientation'),
// GAUGE CHART SPECIFIC ATTRIBUTES
lowLabel: component.get('v.lowLabel'),
midLabel: component.get('v.midLabel'),
highLabel: component.get('v.highLabel')
};
// Format Threshold label based on data type and value
var formattedThresholdLabel = '';
if(component.get('v.type') === 'bar' || component.get('v.yAxisDataType') === 'number'){
formattedThresholdLabel = component.get('v.thresholdLabel') + ' (' + helper.abbreviateNumber(component.get('v.thresholdValue')) + ')';
} else {
formattedThresholdLabel = component.get('v.thresholdLabel') + ' (' + d3.timeFormat('%b %d')(component.get('v.thresholdValue')) + ')';
}
chartConfig.thresholdLabel = formattedThresholdLabel;
var chartDiv = component.find('chart').getElement();
chartDiv.innerHTML = '';
chartConfig.containerWidth = component.find('chartContainer').getElement().clientWidth;
helper.drawChart(component, helper, chartConfig);
return ret;
},
afterRender: function (component, helper) {
this.superAfterRender();
},
unrender: function () {
this.superUnrender();
}
})

Просмотреть файл

@ -0,0 +1,102 @@
.THIS .sc-axis-label {
color: #bfbebe;
}
.THIS .sc-axis-value {
color: white;
font-size: 13px;
}
.THIS .sc-tooltip {
background: #2c2b2b;
box-shadow: 0 0 5px #999999;
color: white;
display: none;
font-size: 12px;
left: 130px;
padding: 10px;
position: absolute;
text-align: left;
top: 95px;
z-index: 10;
}
.THIS .sc-threshold-line {
fill: none;
stroke: #c23934;
stroke-width: 0.5px;
stroke-dasharray: 5 5;
}
.THIS .sc-threshold-text {
fill: #c23934;
}
.THIS .sc-axis path,
.THIS .sc-axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.THIS .sc-axis-text {
font: 10px sans-serif;
fill: #4d4d4d;
}
.THIS .sc-section:hover {
fill: #4682b4;
}
/* LINE CHART */
.THIS .sc-line {
fill: none;
stroke: #00a1e0;
stroke-width: 2px;
}
.THIS .sc-lc-area {
fill: #e6f6fc;
stroke: none;
}
.THIS .sc-lc-focus-line {
fill: none;
stroke: #00a1e0;
stroke-width: 0.5px;
}
.THIS .sc-lc-focus-circle {
fill: red;
}
/* /LINE CHART */
/* BAR CHART */
.THIS .sc-bc-bar {
fill: #00a1e0;
}
.THIS .sc-bc-bar:hover {
fill: #005170;
}
/* /BAR CHART */
/* PIE DONUT CHART */
.THIS .sc-pdc-section-percent {
color: #c6c663;
font-size: 12px;
}
.THIS .sc-pdc-legend {
font-size: 12px;
letter-spacing: .0625rem;
}
.THIS rect {
stroke-width: 2;
}
/* /PIE DONUT CHART */