UNPKG

angular-sunburst-radar-chart

Version:
928 lines (912 loc) 44.6 kB
import { __decorate } from 'tslib'; import { Input, Component, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; function hashCode(obj) { let h = 0; obj = getOptionsOrEmpty(obj); let s = JSON.stringify(obj); for (let i = 0; i < s.length; i++) { h = Math.imul(31, h) + s.charCodeAt(i) | 0; } return h; } function getOptionsOrEmpty(options) { return options || {}; } function getItemTitle(item) { item = item || { name: '', value: '' }; const dash = item.name.length > 0 ? '-' : ''; return item.name + dash + item.value; } function clone(obj) { return JSON.parse(JSON.stringify(obj)); } function generateRandomColor() { const letters = '0123456789ABCDEF'; let color = '#'; for (let i = 0; i < 6; i++) { color += letters[(Math.floor(Math.random() * 16))]; } return color; } function formatItems(items) { return items.map(item => { if (!!item.children && item.children.length > 0) { } else { item['children'] = []; item['children'].push({ name: '', value: 0 }); } return item; }); } function getFormattedAngle(angle, center) { const [centerX, centerY] = [center.x, center.y]; return 'rotate(' + angle + ' ' + centerX + ' ' + centerY + ')'; } function getCurrentPointFromEvent(evt) { let [x, y] = [evt.clientX, evt.clientY]; if (evt.targetTouches && evt.targetTouches[0]) { [x, y] = [evt.targetTouches[0].pageX, evt.targetTouches[0].pageY]; } return { x, y }; } function createCircle(options) { const defaults = { x: 0, y: 0, radius: 0, fillColor: 'none', 'stroke-width': '1', 'stroke': '#000000', 'stroke-dasharray': 'none', 'stroke-opacity': '1', 'title': '' }; options = Object.assign(Object.assign({}, defaults), (getOptionsOrEmpty(options))); const circle = { name: 'circle', options, children: [] }; return circle; } function createLine(options) { const defaults = { x1: 0, y1: 0, x2: 0, y2: 0, color: '#000000', width: '2', title: '' }; options = Object.assign(Object.assign({}, defaults), (getOptionsOrEmpty(options))); const line = { name: 'line', options, children: [] }; return line; } function createPath(options) { const defaults = { d: '', fill: 'none', stroke: 'none', 'stroke-width': '0', title: '', id: null }; options = Object.assign(Object.assign({}, defaults), (getOptionsOrEmpty(options))); const { d, color, borderColor } = options; const path = { name: 'path', options, children: [] }; return path; } function createPathForBar(options) { const defaults = { d: '', fill: 'none', stroke: 'none', 'stroke-width': '0', 'stroke-opacity': '1.0', 'fill-opacity': '1.0', title: '', id: null }; options = Object.assign(Object.assign({}, defaults), (getOptionsOrEmpty(options))); const { d, color, borderColor } = options; let gradName = options['fill']; gradName = gradName.replace('#', ''); options['gradientId'] = gradName; options['fillUrl'] = 'url(#' + gradName + ')'; const path = { name: 'path-bar', options, children: [] }; return path; } function polarToCartesian(centerX, centerY, radius, angleInDegrees) { const adjustedViewPortAngle = (angleInDegrees - 90); const angleInRadians = adjustedViewPortAngle * Math.PI / 180.0; return { x: centerX + (radius * Math.cos(angleInRadians)), y: centerY + (radius * Math.sin(angleInRadians)) }; } function getLargeArcFlag(startAngle, endAngle) { return (endAngle - startAngle) <= 180 ? '0' : '1'; } function distanceBetweenTwoPoints(centerX, centerY, radius, startAngle, endAngle) { const startPoint = polarToCartesian(centerX, centerY, radius, startAngle); const endPoint = polarToCartesian(centerX, centerY, radius, endAngle); const distFromStartToEnd = Math.sqrt(Math.pow((startPoint.x - endPoint.x), 2) + Math.pow((startPoint.y - endPoint.y), 2)); return Math.abs(distFromStartToEnd); } function calculateAngleRadian({ x, y, centerX, centerY, maxRad }) { let angleInRadian = Math.atan2(x - centerY, y - centerX); angleInRadian = adjustAngleRadianDifference(angleInRadian, maxRad); return angleInRadian; } function adjustAngleRadianDifference(input, maxRad) { let angleInRadian = input; angleInRadian += maxRad / 4; if (angleInRadian < 0) { angleInRadian += maxRad; } return angleInRadian; } function createArcToWriteText({ startPoint, radius, id, startAngle, endAngle }) { const [centerX, centerY] = [startPoint.x, startPoint.y]; const start = polarToCartesian(centerX, centerY, radius, endAngle); const end = polarToCartesian(centerX, centerY, radius, startAngle); const largeArcFlag = getLargeArcFlag(startAngle, endAngle); const d = [ 'M', start.x, start.y, 'A', radius, radius, 0, largeArcFlag, 0, end.x, end.y ].join(' '); return createPath({ d, borderColor: '', id }); } function getTextForAngle(text, distance, fontSize) { let result = text; let perCharacter = 10.07; if (fontSize < 18) { perCharacter = 8.7; } const totalTextLength = Math.round(distance / perCharacter); if (text.length > 0 && text.length > totalTextLength) { result = text.substring(0, totalTextLength - 1) + '..'; } return result; } function writeTextOnArc(options) { const defaults = { text: '', label: '', pathId: '', 'font-size': '14px', }; options = Object.assign(Object.assign({}, defaults), (getOptionsOrEmpty(options))); const { text, pathId, label } = options; if (pathId !== '') { options['href'] = '#' + pathId; options['startOffset'] = '50%'; options['text-anchor'] = 'middle'; options['title'] = text; } const textOnArc = { name: 'text-on-arc', options, children: [] }; return textOnArc; } function createText(options) { const defaults = { content: '', x: 0, y: 0, stroke: 'white', 'stroke-width': '1px', 'font-size': '6px', 'text-anchor': 'middle' }; options = Object.assign(Object.assign({}, defaults), (getOptionsOrEmpty(options))); const textElement = { name: 'text', options, children: [] }; return textElement; } function convertToPercentage(input) { let { plotMax, actualScore, maxScore } = input; if (actualScore > maxScore) { actualScore = maxScore; } const perValue = plotMax / maxScore; return actualScore * perValue; } function createOuterChartBarWithInArc({ item, startAngle, endAngle, middleAngle, color, middleRadius, maxScore, innerRadiusBorder, center }) { const [centerX, centerY] = [center.x, center.y]; const currentVal = item.value; const totalRadiusInside = middleRadius - innerRadiusBorder; const innerRadius = convertToPercentage({ plotMax: totalRadiusInside, actualScore: currentVal, maxScore }); const radiusFromCenter = innerRadius + innerRadiusBorder; const firstPoint = polarToCartesian(centerX, centerY, radiusFromCenter, startAngle); const secondPoint = polarToCartesian(centerX, centerY, radiusFromCenter, endAngle); const startPoint = polarToCartesian(centerX, centerY, innerRadiusBorder, startAngle); const endPoint = polarToCartesian(centerX, centerY, innerRadiusBorder, endAngle); const startMiddlePoint = polarToCartesian(centerX, centerY, innerRadiusBorder, middleAngle); const distFromStartToFirst = Math.sqrt(Math.pow((startPoint.x - firstPoint.x), 2) + Math.pow((startPoint.y - firstPoint.y), 2)); const distFromStartToSecond = Math.sqrt(Math.pow((startPoint.x - secondPoint.x), 2) + Math.pow((startPoint.y - secondPoint.y), 2)); const { updatedFirstPoint, updatedSecondPoint } = getUpdatedPoints(firstPoint, secondPoint, distFromStartToFirst, distFromStartToSecond); const d = getDrawPositions(updatedFirstPoint, middleRadius, updatedSecondPoint, endPoint, startMiddlePoint, startPoint); const title = getItemTitle(item); return createPathForBar({ d: d.join(' '), stroke: 'none', fill: color, "fill-opacity": '0.5', title }); } function getDrawPositions(firstPoint, middleRadius, secondPoint, endPoint, startMiddlePoint, startPoint) { const d = [ 'M', firstPoint.x, firstPoint.y, 'A', middleRadius, middleRadius, 0, 0, 1, secondPoint.x, secondPoint.y, 'L', endPoint.x, endPoint.y, 'C', endPoint.x, endPoint.y, startMiddlePoint.x, startMiddlePoint.y, startPoint.x, startPoint.y, 'L', firstPoint.x, firstPoint.y, 'Z' ]; return d; } function getUpdatedPoints(firstPoint, secondPoint, distFromStartToFirst, distFromStartToSecond) { let [updatedFirstPoint, updatedSecondPoint] = [firstPoint, secondPoint]; if (distFromStartToSecond < distFromStartToFirst) { updatedSecondPoint = firstPoint; updatedFirstPoint = secondPoint; } return { updatedSecondPoint, updatedFirstPoint }; } function createInnerChartBarWithInArc({ startPoint, item, radius, startAngle, endAngle, maxScore }) { const [centerX, centerY] = [startPoint.x, startPoint.y]; const currentVal = item.value; const color = item.color; const arcRadius = convertToPercentage({ plotMax: radius, actualScore: currentVal, maxScore }); const start = polarToCartesian(centerX, centerY, arcRadius, endAngle); const end = polarToCartesian(centerX, centerY, arcRadius, startAngle); const largeArcFlag = getLargeArcFlag(startAngle, endAngle); //const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1'; const d = [ 'M', centerX, centerY, 'L', start.x, start.y, 'A', arcRadius, arcRadius, 0, largeArcFlag, 0, end.x, end.y ].join(' '); const title = getItemTitle(item); const arcForInnerChart = createPathForBar({ d, fill: color, title }); return arcForInnerChart; } function getRadiusForBorderAndText(radius, borderHeight) { const borderRadius = radius + borderHeight; const textRadius = radius + ((borderRadius - radius) / 2); return [borderRadius, textRadius]; } function getMaxDepth(items, currentLevel = 0) { let depths = []; if (items && items.length > 0) { const nextLevel = currentLevel + 1; depths = items .filter(item => !!item.children) .filter(item => item.children.length > 0) .map(item => getMaxDepth(item.children, nextLevel)); } return depths.length !== 0 ? Math.max(...depths) : currentLevel; } function getGlobalPositions({ size, maxScore, items }) { const center = { x: size / 2, y: size / 2 }; const maxDepth = getMaxDepth(items); const totalLevels = maxDepth + 1; const innerCircleRadius = Math.abs(size / 5.33); const totalRadiusToBeDrawn = 2 * Math.abs(size / 5.33); const borderHeight = 0.0375 * size; const everyLevelRadius = totalRadiusToBeDrawn / totalLevels; const levels = []; for (let i = 1; i <= totalLevels; i++) { const startRadius = i * everyLevelRadius; const [endRadius, textRadius] = getRadiusForBorderAndText(startRadius, borderHeight); levels.push({ startRadius, endRadius, textRadius }); } const innerRadius = innerCircleRadius; const [innerRadiusBorder, innerTextRadius] = getRadiusForBorderAndText(innerRadius, borderHeight); const middleRadius = innerCircleRadius * 2; const [middleRadiusBorder, middleTextRadius] = getRadiusForBorderAndText(middleRadius, borderHeight); const outerRadius = middleRadius + borderHeight; const [outerRadiusBorder, outerTextRadius] = getRadiusForBorderAndText(outerRadius, borderHeight * 2); const textSize = 0.0175 * size; const outerTextSize = 0.0225 * size; const result = { textSize, outerTextSize, levels, innerRadius, innerRadiusBorder, innerTextRadius, middleRadius, middleTextRadius, middleRadiusBorder, outerRadius, outerRadiusBorder, outerTextRadius, center }; return result; } function calculatePointBetween({ centerX, centerY, startAngle, middleAngle, endAngle, radius }) { const start = polarToCartesian(centerX, centerY, radius, startAngle); const middle = polarToCartesian(centerX, centerY, radius, middleAngle); const end = polarToCartesian(centerX, centerY, radius, endAngle); return { start, middle, end }; } function createLegendWithOptions({ startPoint, center, startRadius, endRadius, degreeToBeDrawn, maxScore }) { const [centerX, centerY] = [center.x, center.y]; const actRadius = endRadius - startRadius; const axisIncrement = maxScore / 4; const legendRadius = convertToPercentage({ plotMax: actRadius, actualScore: axisIncrement, maxScore }); const smallCircleRadius = Math.round(0.083 * actRadius); const smallCircleFontSize = Math.round(0.067 * actRadius); const groups = [1, 2, 3] .map(val => { return { radius: val * legendRadius, content: Math.round(val * axisIncrement) }; }) .map(res => { const { radius, content } = res; const { x, y } = polarToCartesian(startPoint.x, startPoint.y, radius, degreeToBeDrawn); const circle = createCircle({ x, y, radius: smallCircleRadius, 'fillColor': '#000000' }); const legendCircle = createCircle({ x: centerX, y: centerY, radius: startRadius + radius, 'stroke-dasharray': 4, 'stroke-opacity': 0.3 }); const fontSize = smallCircleFontSize + "px"; //console.log("font-size",fontSize) const text = createText({ content, x, y, 'stroke': 'white', 'font-size': fontSize }); return [circle, legendCircle, text]; }); return [].concat.apply([], groups); } function createLegends({ startPoint, radius, degreeToBeDrawn, maxScore }) { return createLegendWithOptions({ startPoint, center: startPoint, startRadius: 0, endRadius: radius, degreeToBeDrawn, maxScore }); } //Not Used now ,Might Use later // function anglesBasedOnPercentage(items) { // // const total = items.reduce((prevValue, curValue) => prevValue + //curValue, 0); // if (Math.round(total) != 100) { // console.error('Cannnot draw as the sum of values should be 100'); // throw new Error('Cannnot draw as the sum of values should be 100'); // } // let prevDegree = 0; // const results = []; // for (let i = 0; i < items.length; i++) { // // const item = items[i]; // const currentDegree = item * 3.6; // // const degreeToBeDrawn = currentDegree + prevDegree; // results.push(degreeToBeDrawn); // prevDegree = prevDegree + currentDegree; // } // // // return results; // } // function getInitialPosition(startLocation, totalDegrees, num) { const perLocation = totalDegrees / num; const middleLocation = perLocation / 2; const startMiddle = startLocation + middleLocation; return { startLocation, perLocation, middleLocation, startMiddle }; } function getDegreeForIndexBasedOnSplitAngle(index, data) { const { perLocation, middleLocation, startMiddle, startLocation } = data; let position = index; // + 1; const startDegree = startLocation + (position * perLocation); const middleDegree = startMiddle + (position * perLocation); const endDegree = startLocation + (position * perLocation) + perLocation; return { startDegree, middleDegree, endDegree }; } function getAllAnglesBasedOnChild(items) { const num = items.length; const results = []; const totalChildren = items.filter(item => item.children).map(item => item.children.length).reduce(((a, b) => a + b), 0); const childAngleDifference = 360 / totalChildren; let currentStartLocation = 0; for (let i = 0; i < items.length; i++) { const item = items[i]; const childCount = item.children ? item.children.length : 0; const totalDegrees = childCount * childAngleDifference; const endDegree = currentStartLocation + (childCount * childAngleDifference); const middleDegree = (endDegree - currentStartLocation) / 2; const currentItem = {}; currentItem['item'] = item; currentItem['startDegree'] = currentStartLocation; currentItem['middleDegree'] = middleDegree; currentItem['endDegree'] = endDegree; if (childCount > 0) { currentItem['children'] = iterateChildrenAndSetAngles({ children: item.children, currentItem, angleDifference: totalDegrees }); } currentStartLocation = currentStartLocation + (childCount * childAngleDifference); results.push(currentItem); } return results; } function getAllAnglesBasedOnParent(items) { const num = items.length; let results = []; let angleDifference = 360 / num; const data = getInitialPosition(0, 360, num); for (let i = 0; i < items.length; i++) { const { startDegree, middleDegree, endDegree } = getDegreeForIndexBasedOnSplitAngle(i, data); const item = items[i]; const currentItem = {}; currentItem['item'] = item; currentItem['startDegree'] = startDegree; currentItem['middleDegree'] = middleDegree; currentItem['endDegree'] = endDegree; const hasChildren = items.filter(item => !!item.children).length > 0; if (hasChildren) { currentItem['children'] = iterateChildrenAndSetAngles({ children: item.children, currentItem, angleDifference }); } results.push(currentItem); } return results; } function iterateChildrenAndSetAngles({ children, currentItem, angleDifference }) { const count = children.length; const childItems = []; const data = getInitialPosition(currentItem['startDegree'], angleDifference, count); for (let j = 0; j < count; j++) { const childItem = {}; childItem['item'] = children[j]; const { startDegree, middleDegree, endDegree } = getDegreeForIndexBasedOnSplitAngle(j, data); childItem['startDegree'] = startDegree; childItem['middleDegree'] = middleDegree; childItem['endDegree'] = endDegree; childItems.push(childItem); } return childItems; } function positionsOnAngles(centerX, centerY, radius, angels) { const results = []; for (let i = 0; i < angels.length; i++) { const degreeToBeDrawn = angels[i]; results.push(polarToCartesian(centerX, centerY, radius, degreeToBeDrawn)); } return results; } function getPointsOnCircleAtAngels(centerX, centerY, radius, angels) { const results = []; for (let i = 0; i < angels.length; i++) { const degreeToBeDrawn = angels[i]; results.push(polarToCartesian(centerX, centerY, radius, degreeToBeDrawn)); } return results; } function createSvgHandlerWithSelector(selectorName, center) { const svg = document.querySelector(selectorName); const pt = svg.createSVGPoint(); const maxRad = 6.283185307179586; const maxDeg = 360; return { initPoint: 0, cursorPoint(evt) { const { x, y } = getCurrentPointFromEvent(evt); pt.x = x; pt.y = y; return pt.matrixTransform(svg.getScreenCTM().inverse()); }, startDrag(evt, initialPoint) { this.previousPoint = this.angleFromCenter(evt); this.initPoint = initialPoint; }, updateInitPointFromCurrentPoint(currentPoint) { const difference = Math.abs(this.previousPoint - currentPoint); if (currentPoint < this.previousPoint) { this.initPoint += difference; } else { this.initPoint -= difference; } }, getRotationAngle(evt) { const currentPoint = this.angleFromCenter(evt); this.updateInitPointFromCurrentPoint(currentPoint); this.previousPoint = currentPoint; return this.initPoint; }, angleFromCenter(evt) { const [centerX, centerY] = [center.x, center.y]; const { x, y } = this.cursorPoint(evt); const angleInRadian = calculateAngleRadian({ x, y, centerX, centerY, maxRad }); const currentDegree = maxDeg * angleInRadian / (2 * Math.PI); return currentDegree; } }; } let AngularSunburstRadarChartComponent = class AngularSunburstRadarChartComponent { constructor() { this.componentDisplayed = false; this.showToolTip = false; this.tooltipTopInPx = '0px'; this.tooltipLeftInPx = '0px'; this.tooltipText = ''; this.svgId = null; this.svgGroupId = null; this.svgHandler = null; this.currentRotationAngle = 10; this.svgCursor = 'default'; this.initialized = false; this.innerBorderHeight = 30; this.outerBorderHeight = 30; this.elements = []; this.hasChildren = false; this.outerBorderCircleRef = 'outerBorderCircle'; this.error = null; this.hasError = false; this.startRotation = false; } showError(msg) { this.error = msg; this.hasError = true; } hideError() { this.error = null; this.hasError = false; } appendToSvg(element) { this.elements.push(element); } ngOnChanges(changes) { this.hideError(); const isFirstChange = Object.values(changes).some(c => c.isFirstChange()); this.modifyOnFirstChange(isFirstChange); } modifyOnFirstChange(isFirstChange) { if (!isFirstChange) { this.initialize(); } } ngOnInit() { this.hideError(); this.initialize(); } initialize() { const defaults = { size: 300, maxScore: 100, animateChart: true, splitBasedOnChildren: true, legendAxisLinePosition: 1 }; const options = Object.assign(Object.assign({}, defaults), (getOptionsOrEmpty(this.options))); this.size = options.size; this.maxScore = options.maxScore; this.animateChart = options.animateChart; this.splitBasedOnChildren = options.splitBasedOnChildren; this.legendAxisLinePosition = options.legendAxisLinePosition; if (!this.hasValidParameters()) { this.showError('Input Values not set or Items was improper'); return; } this.initialized = false; this.svgId = 'svg' + hashCode(this.items); this.svgGroupId = 'svg-group' + hashCode(this.items); this.viewBox = '0 0 ' + this.size + ' ' + this.size; this.innerCircleRadius = Math.abs(this.size / 5.33); this.innerBorderHeight = this.innerCircleRadius / 5; this.outerBorderHeight = this.innerCircleRadius / 5; this.globalPosition = getGlobalPositions({ size: this.size, maxScore: this.maxScore, items: this.items }); this.hasChildren = this.items.filter(item => !!item.children).length > 0; this.drawLayout(); const { innerRadius, innerTextRadius, textSize, outerTextSize, outerRadiusBorder, outerTextRadius, center } = this.globalPosition; const [centerX, centerY] = [center.x, center.y]; let items = this.items; let allAngels = []; const hasChildren = this.hasChildren; if (this.splitBasedOnChildren) { // Cannot have no children items = formatItems(items); allAngels = getAllAnglesBasedOnChild(items); } else { allAngels = getAllAnglesBasedOnParent(items); } const angleDifference = 360 / items.length; const angles = allAngels.map(value => value.startDegree); const middleAngles = allAngels.map(value => value.middleDegree); const points = positionsOnAngles(centerX, centerY, this.chartBorder, angles); const lines = []; let elements = []; let nextLevelElements = []; for (let i = 0; i < points.length; i++) { const { x, y } = points[i]; let endAngleIndex = i + 1; if (endAngleIndex >= points.length) { endAngleIndex = 0; } const endAngle = angles[endAngleIndex]; const startAngle = angles[i]; const middleAngle = middleAngles[i]; let item = items[i]; if (!!item.color === false) { const colorDefaults = { color: generateRandomColor() }; item = Object.assign(Object.assign({}, colorDefaults), item); } if (this.hasChildren && item.children && item.children.length > 0) { const childAngels = allAngels[i].children; nextLevelElements = nextLevelElements.concat(this.drawOnLevel({ items: item.children, totalDegrees: angleDifference, childAngels, color: item.color })); } lines.push(createLine({ x1: centerX, y1: centerY, x2: x, y2: y, width: 2 })); // Create Arc for inner values const barWithinArc = createInnerChartBarWithInArc({ startPoint: center, item, radius: innerRadius, startAngle, endAngle, maxScore: this.maxScore }); elements.push(barWithinArc); const innerTextElements = this.addArcText({ arcForTextId: 'arc-text-inner' + this.getUniqueCode() + '-' + i, radius: innerTextRadius, fontSize: textSize, startAngle, endAngle, perAngle: angleDifference, item }); elements = elements.concat(innerTextElements); if (hasChildren) { elements.push(this.drawOuterBackgroundWithMiddle({ item, startAngle, middleAngle, endAngle })); const outerBackgroundTextElements = this.addArcText({ arcForTextId: 'arc-text-outer' + this.getUniqueCode() + '-' + i, radius: outerTextRadius, fontSize: outerTextSize, perAngle: angleDifference, startAngle, endAngle, item }); elements = elements.concat(outerBackgroundTextElements); } } nextLevelElements.forEach(line => { this.appendToSvg(line); }); this.drawInnerBorders(); elements.forEach(line => { this.appendToSvg(line); }); lines.forEach(line => { this.appendToSvg(line); }); const legendAxisIndex = this.getLegendAxisIndex(angles); this.drawLegends(angles[legendAxisIndex]); this.addSmallCirclesAtCenter(centerX, centerY); this.initialized = true; this.currentRotationAngle = 10; this.rotationPoint = getFormattedAngle(this.currentRotationAngle, center); } getLegendAxisIndex(angles) { let legendAxisIndex = this.legendAxisLinePosition - 1; if (legendAxisIndex < 0 || legendAxisIndex >= angles.length) { legendAxisIndex = 0; } return legendAxisIndex; } hasValidParameters() { return this.items && this.items.length > 1; } drawOuterBackgroundWithMiddle({ item, startAngle, middleAngle, endAngle }) { const color = item.color; const { outerTextRadius, center } = this.globalPosition; const [centerX, centerY] = [center.x, center.y]; const middleCircle = calculatePointBetween({ centerX, centerY, startAngle, middleAngle, endAngle, radius: outerTextRadius }); const d = [ 'M', middleCircle.start.x, middleCircle.start.y, 'A', outerTextRadius, outerTextRadius, 0, 0, 1, middleCircle.end.x, middleCircle.end.y ]; const title = item.name + '-' + item.value; const strokeWidth = 0.0775 * this.size; return createPath({ d: d.join(' '), stroke: color, 'stroke-width': strokeWidth, title }); } drawOnLevel({ items, totalDegrees, childAngels, color }) { const { innerRadiusBorder, middleRadius, textSize, middleTextRadius, center } = this.globalPosition; const [centerX, centerY] = [center.x, center.y]; const angles = childAngels.map(item => item.startDegree); const middleAngles = childAngels.map(item => item.middleDegree); const endAngles = childAngels.map(item => item.endDegree); const perAngle = totalDegrees / items.length; const pointsOnInnerRadiusBorder = getPointsOnCircleAtAngels(centerX, centerY, innerRadiusBorder, angles); const pointsOnMiddle = getPointsOnCircleAtAngels(centerX, centerY, middleRadius, angles); let elements = []; const lines = []; let currentDegree = angles[0]; for (let i = 0; i < pointsOnInnerRadiusBorder.length; i++) { const pointOnInnerRadiusBorder = pointsOnInnerRadiusBorder[i]; const pointOnMiddle = pointsOnMiddle[i]; lines.push(createLine({ x1: pointOnInnerRadiusBorder.x, y1: pointOnInnerRadiusBorder.y, x2: pointOnMiddle.x, y2: pointOnMiddle.y, width: 0.5 })); const startAngle = angles[i]; const middleAngle = middleAngles[i]; const endAngle = endAngles[i]; const item = items[i]; const params = { startPoint: pointOnInnerRadiusBorder, item, startAngle, endAngle, middleAngle, middleRadius, innerRadiusBorder, center, maxScore: this.maxScore, color, index: i }; const arcForChart = createOuterChartBarWithInArc(params); elements.push(arcForChart); const middleTextElements = this.addArcText({ arcForTextId: 'arc-text-middle' + this.getUniqueCode() + '-' + startAngle + i, radius: middleTextRadius, fontSize: textSize, perAngle, startAngle, endAngle, item }); elements = elements.concat(middleTextElements); currentDegree = currentDegree + perAngle; } lines.forEach(line => { elements.push(line); }); return elements; } getUniqueCode() { return hashCode(this.items) + '-' + this.size; } drawInnerBorders() { const { innerRadius, innerRadiusBorder, center } = this.globalPosition; const [centerX, centerY] = [center.x, center.y]; const innerContainer = createCircle({ x: centerX, y: centerY, radius: innerRadius, fillColor: 'none' }); const innerBorderContainer = createCircle({ x: centerX, y: centerY, radius: innerRadiusBorder, fillColor: '#FFFFFF' }); this.appendToSvg(innerBorderContainer); this.appendToSvg(innerContainer); } addSmallCirclesAtCenter(centerX, centerY) { const outerRadius = 0.0025 * 2 * this.size; const innerRadius = 0.0005 * 2 * this.size; this.appendToSvg(createCircle({ x: centerX, y: centerY, radius: outerRadius, fillColor: '#FFFFFF' })); this.appendToSvg(createCircle({ x: centerX, y: centerY, radius: innerRadius, fillColor: '#FFFFFF' })); } drawLegends(degreeToBeDrawn) { const { innerRadius, innerRadiusBorder, middleRadius, center } = this.globalPosition; const [centerX, centerY] = [center.x, center.y]; const maxScore = this.maxScore; let legends = createLegends({ startPoint: center, radius: innerRadius, degreeToBeDrawn, maxScore }); const startFrom = polarToCartesian(centerX, centerY, innerRadiusBorder, degreeToBeDrawn); if (this.hasChildren) { const nextLevelLegends = createLegendWithOptions({ startPoint: startFrom, center, startRadius: innerRadiusBorder, endRadius: middleRadius, maxScore, degreeToBeDrawn }); legends = legends.concat(nextLevelLegends); } legends.forEach(elem => { this.appendToSvg(elem); }); } addArcText({ arcForTextId, radius, startAngle, fontSize, endAngle, perAngle, item }) { const elements = []; const { center } = this.globalPosition; const [centerX, centerY] = [center.x, center.y]; const arcForText = createArcToWriteText({ id: arcForTextId, startPoint: center, radius, startAngle, endAngle }); elements.push(arcForText); const distance = distanceBetweenTwoPoints(centerX, centerY, radius, startAngle, endAngle); const label = getTextForAngle(item.name, distance, fontSize); elements.push(writeTextOnArc({ label, text: item.name, pathId: arcForTextId, 'font-size': fontSize + 'px' })); return elements; } drawLayout() { const { innerRadius, innerRadiusBorder, middleRadius, middleRadiusBorder, outerRadius, outerRadiusBorder, center } = this.globalPosition; const [centerX, centerY] = [center.x, center.y]; let innerRadiusEnd = innerRadius; let innerRadiusBorderEnd = innerRadiusBorder; this.chartBorder = innerRadiusBorder; if (this.hasChildren) { const outerBorderCircle = createCircle({ x: centerX, y: centerY, radius: outerRadiusBorder, fillColor: 'none', 'stroke-width': '5', ref: this.outerBorderCircleRef }); this.appendToSvg(outerBorderCircle); const outerCircle = createCircle({ x: centerX, y: centerY, radius: outerRadius, fillColor: 'none' }); this.appendToSvg(outerCircle); innerRadiusEnd = middleRadius; innerRadiusBorderEnd = middleRadiusBorder; this.chartBorder = outerRadiusBorder; } this.appendToSvg(createCircle({ x: centerX, y: centerY, radius: innerRadiusEnd })); this.appendToSvg(createCircle({ x: centerX, y: centerY, radius: innerRadiusBorderEnd })); } hideTooltip() { this.showToolTip = false; } showTooltipText($event, text) { this.tooltipLeftInPx = $event.pageX + 10 + 'px'; this.tooltipTopInPx = $event.pageY + 10 + 'px'; this.tooltipText = text; this.showToolTip = true; } onOutOfComponent() { this.hideTooltip(); this.stopRotate(); } stopRotate() { this.svgCursor = 'default'; this.startRotation = false; } startRotate($event) { this.startRotation = true; this.svgCursor = 'grab'; const { outerRadiusBorder, center } = this.globalPosition; this.svgHandler = createSvgHandlerWithSelector('#' + this.svgId, center); this.svgHandler.startDrag($event, this.currentRotationAngle); $event.preventDefault(); } rotateChart($event) { if (this.startRotation == false) { return; } this.svgCursor = 'grabbing'; const { center } = this.globalPosition; this.currentRotationAngle = this.svgHandler.getRotationAngle($event); this.rotationPoint = getFormattedAngle(Math.round(this.currentRotationAngle), center); } }; __decorate([ Input() ], AngularSunburstRadarChartComponent.prototype, "items", void 0); __decorate([ Input() ], AngularSunburstRadarChartComponent.prototype, "options", void 0); AngularSunburstRadarChartComponent = __decorate([ Component({ selector: 'lib-sunburst-radar-chart', template: "<ng-container (mouseout)=\"onOutOfComponent();\">\n\n\n <div *ngIf=\"hasError\" style=\" color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n margin-top: 5px;\npadding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n\">\n <strong>Error!</strong> {{error}}\n </div>\n\n\n\n<div class=\"app-tooltip\" *ngIf=\"showToolTip\" style=\"position: absolute; display: block;background: cornsilk; border: 1px solid black; border-radius: 5px; padding: 5px; z-index: 1002;\"\n\n [style.left]=\"tooltipLeftInPx\" [style.top]=\"tooltipTopInPx\" >\n {{tooltipText}}\n</div>\n\n<svg [attr.id]=\"svgId\"\n [attr.height]=\"size\"\n [attr.width]=\"size\"\n [attr.viewBox]=\"viewBox\"\n [style.cursor]=\"svgCursor\"\n xmlns=\"http://www.w3.org/2000/svg\">\n\n\n\n\n <animateTransform\n *ngIf=\"animateChart\"\n attributeName=\"transform\"\n begin=\"4s\"\n dur=\"500ms\"\n type=\"rotate\"\n from=\"0\"\n to=\"10\"\n\n additive=\"sum\"\n fill=\"freeze\"\n repeatCount=\"1\"\n />\n\n\n<g\n (mousemove)=\"rotateChart($event);\"\n (mousedown)=\"startRotate($event)\"\n (mouseup)=\"stopRotate()\"\n (touchmove)=\"rotateChart($event);\"\n (touchstart)=\"startRotate($event)\"\n (touchend)=\"stopRotate()\"\n [attr.transform]=\"rotationPoint\"\n [attr.id]=\"svgGroupId\"\n\n\n >\n <ng-container *ngFor=\"let element of elements\" >\n\n\n\n <ng-container [ngSwitch]=\"element.name\" >\n\n\n <circle *ngSwitchCase=\"'circle'\" [attr.cx]=\"element.options.x\" [attr.cy]=[element.options.y]\n [attr.r]=\"element.options.radius\"\n [attr.stroke-width]=\"element.options['stroke-width']\"\n [attr.stroke]=\"element.options['stroke']\"\n [attr.stroke-dasharray]=\"element.options['stroke-dasharray']\"\n [attr.stroke-opacity]=\"element.options['stroke-opacity']\"\n [attr.fill]=\"element.options['fillColor']\"\n >\n\n\n\n\n </circle>\n\n <path *ngSwitchCase=\"'path'\" [attr.d]=\"element.options.d\" [attr.fill]=[element.options.fill]\n\n [attr.stroke]=\"element.options['stroke']\"\n [attr.stroke-width]=\"element.options['stroke-width']\"\n [attr.id]=\"element.options['id']\"\n (mousemove)=\"showTooltipText($event, element.options['title']);\"\n (mouseout)=\"hideTooltip()\" >\n\n\n\n\n\n </path>\n\n<g *ngSwitchCase=\"'path-bar'\" >\n\n\n\n <path [attr.d]=\"element.options.d\"\n [attr.fill]=[element.options.fill]\n\n [attr.stroke]=\"element.options['stroke']\"\n [attr.stroke-width]=\"element.options['stroke-width']\"\n [attr.stroke-opacity]=\"element.options['stroke-opacity']\"\n [attr.fill-opacity]=\"element.options['fill-opacity']\"\n [attr.id]=\"element.options['id']\"\n (mousemove)=\"showTooltipText($event, element.options['title']);\" (mouseout)=\"hideTooltip()\" >\n\n\n\n\n <ng-container\n\n *ngIf=\"animateChart\"\n >\n\n <animate\n attributeName=\"fill\"\n [attr.from]=\"element.options['fill']\"\n to=\"transparent\"\n dur=\"1ms\"\n fill=\"freeze\" />\n <animate\n attributeName=\"stroke\"\n [attr.from]=\"element.options['fill']\"\n [attr.to]=\"element.options['fill']\"\n dur=\"1ms\"\n fill=\"freeze\" />\n <animate\n attributeName=\"stroke-width\"\n from=\"8\"\n to=\"8\"\n dur=\"1ms\"\n fill=\"freeze\" />\n <animate\n attributeName=\"stroke-dasharray\"\n from=\"1000\"\n to=\"1000\"\n dur=\"1ms\"\n fill=\"freeze\" />\n\n\n <animate\n attributeName=\"fill\"\n from=\"#FFFFFF\"\n [attr.to]=\"element.options['fill']\"\n begin=\"2s\"\n dur=\"3s\"\n fill=\"freeze\" />\n\n <animate\n attributeName=\"stroke-dashoffset\"\n from=\"1000\"\n to=\"0\"\n begin=\"1ms\"\n dur=\"3s\"\n fill=\"freeze\" />\n\n <animate\n attributeName=\"stroke-width\"\n from=\"8\"\n [attr.to]=\"element.options['stroke']\"\n begin=\"3s\"\n dur=\"1s\"\n fill=\"freeze\" />\n </ng-container>\n </path>\n\n\n\n\n</g>\n\n <text *ngSwitchCase=\"'text-on-arc'\"\n [attr.font-size]=\"element.options['font-size']\"\n\n >\n <textPath\n [attr.href]=\"element.options.href\"\n [attr.startOffset]=\"element.options.startOffset\"\n [attr.text-anchor]=\"element.options['text-anchor']\"\n >{{element.options['label']}}</textPath>\n <title>{{element.options['title']}}</title>\n\n </text>\n\n\n <line *ngSwitchCase=\"'line'\"\n [attr.x1]=\"element.options.x1\"\n [attr.y1]=\"element.options.y1\"\n [attr.x2]=\"element.options.x2\"\n [attr.y2]=\"element.options.y2\"\n [attr.stroke-width]=\"element.options.width\"\n\n [attr.stroke]=\"element.options['color']\" >\n <title>{{element.options['title']}}</title>\n </line>\n\n\n <text *ngSwitchCase=\"'text'\"\n [attr.x]=\"element.options.x\"\n [attr.y]=\"element.options.y\"\n [attr.stroke]=\"element.options.stroke\"\n [attr.stroke-width]=\"element.options['stroke-width']\"\n [attr.font-size]=\"element.options['font-size']\"\n [attr.text-anchor]=\"element.options['text-anchor']\"\n >{{element.options.content}}\n <title>{{element.options['content']}}</title>\n </text>\n\n </ng-container>\n\n\n\n </ng-container>\n\n\n </g>\n</svg>\n</ng-container>\n", styles: [".app-tooltip{background:#fff8dc;border:1px solid #000;border-radius:5px;padding:5px;z-index:1002}.growAnimation{-moz-transition:transform 2s ease-in-out;-webkit-transition:transform 2s ease-in-out;-o-transition:transform 2s ease-in-out;transform:scale(1)}.growAnimation:active{transform:scale(.5)}"] }) ], AngularSunburstRadarChartComponent); let AngularSunburstRadarChartModule = class AngularSunburstRadarChartModule { }; AngularSunburstRadarChartModule = __decorate([ NgModule({ declarations: [AngularSunburstRadarChartComponent], imports: [ CommonModule ], exports: [AngularSunburstRadarChartComponent] }) ], AngularSunburstRadarChartModule); /* * Public API Surface of angular-sunburst-radar-chart */ /** * Generated bundle index. Do not edit. */ export { AngularSunburstRadarChartComponent, AngularSunburstRadarChartModule }; //# sourceMappingURL=angular-sunburst-radar-chart.js.map