UNPKG

framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

250 lines (248 loc) 6.91 kB
import $ from '../../shared/dom7.js'; import { extend, deleteProps } from '../../shared/utils.js'; import Framework7Class from '../../shared/class.js'; /** @jsx $jsx */ import $jsx from '../../shared/$jsx.js'; class PieChart extends Framework7Class { constructor(app, params) { if (params === void 0) { params = {}; } super(params, [app]); const self = this; const defaults = extend({}, app.params.pieChart); // Extend defaults with modules params self.useModulesParams(defaults); self.params = extend(defaults, params); const { el } = self.params; if (!el) return self; const $el = $(el); if ($el.length === 0) return self; if ($el[0].f7PieChart) return $el[0].f7PieChart; extend(self, { app, $el, el: $el && $el[0], currentIndex: null, f7Tooltip: null }); $el[0].f7PieChart = self; // Install Modules self.useModules(); self.showTooltip = self.showTooltip.bind(this); self.hideTooltip = self.hideTooltip.bind(this); self.init(); return self; } getSummValue() { const { datasets } = this.params; let summ = 0; datasets.map(d => d.value || 0).forEach(value => { summ += value; }); return summ; } getPaths() { const { datasets, size } = this.params; const paths = []; let cumulativePercentage = 0; function getCoordinatesForPercentage(percentage) { const x = Math.cos(2 * Math.PI * percentage) * (size / 3); const y = Math.sin(2 * Math.PI * percentage) * (size / 3); return [x, y]; } datasets.forEach(_ref => { let { value, label, color } = _ref; const percentage = value / this.getSummValue(); const [startX, startY] = getCoordinatesForPercentage(cumulativePercentage); cumulativePercentage += percentage; const [endX, endY] = getCoordinatesForPercentage(cumulativePercentage); const largeArcFlag = percentage > 0.5 ? 1 : 0; const points = [`M ${startX} ${startY}`, // Move `A ${size / 3} ${size / 3} 0 ${largeArcFlag} 1 ${endX} ${endY}`, // Arc 'L 0 0' // Line ].join(' '); paths.push({ points, label, color }); }); return paths; } formatTooltipText() { const { datasets } = this.params; const { currentIndex } = this; if (currentIndex === null) return ''; const { value, label, color } = datasets[currentIndex]; const percentage = value / this.getSummValue() * 100; const round = v => { if (parseInt(v, 10) === v) return v; return Math.round(v * 100) / 100; }; if (this.params.formatTooltip) { return this.params.formatTooltip.call(this, { index: currentIndex, value, label, color, percentage }); } const tooltipText = `${label ? `${label}: ` : ''}${round(value)} (${round(percentage)}%)`; return ` <div class="pie-chart-tooltip-label"> <span class="pie-chart-tooltip-color" style="background-color: ${color};"></span> ${tooltipText} </div> `; } setTooltip() { const self = this; const { currentIndex, el, app, params } = self; const { tooltip } = params; if (currentIndex === null && !self.f7Tooltip) return; if (!tooltip || !el) return; if (currentIndex !== null && !self.f7Tooltip) { self.f7Tooltip = app.tooltip.create({ trigger: 'manual', containerEl: el, targetEl: el.querySelector(`path[data-index="${currentIndex}"]`), text: self.formatTooltipText(), cssClass: 'pie-chart-tooltip' }); self.f7Tooltip.show(); return; } if (!self.f7Tooltip) return; if (currentIndex !== null) { self.f7Tooltip.setText(self.formatTooltipText()); self.f7Tooltip.setTargetEl(el.querySelector(`path[data-index="${currentIndex}"]`)); self.f7Tooltip.show(); } else { self.f7Tooltip.hide(); } } render() { const self = this; const size = self.params.size; const paths = self.getPaths(); return $jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, viewBox: `-${size / 3} -${size / 3} ${size * 2 / 3} ${size * 2 / 3}`, style: "transform: rotate(-90deg)" }, paths.map((path, index) => $jsx("path", { d: path.points, fill: path.color, "data-index": index }))); } update(newParams) { if (newParams === void 0) { newParams = {}; } const self = this; const { params } = self; Object.keys(newParams).forEach(param => { if (typeof newParams[param] !== 'undefined') { params[param] = newParams[param]; } }); if (self.$svgEl.length === 0) return self; self.$svgEl.remove(); delete self.$svgEl.f7PieChart; const $svgEl = $(self.render()).eq(0); $svgEl.f7PieChart = self; extend(self, { $svgEl, svgEl: $svgEl && $svgEl[0] }); self.$el.append($svgEl); return self; } setCurrentIndex(index) { const self = this; if (index === self.currentIndex) return; const { datasets } = self.params; self.currentIndex = index; self.$el.trigger('piechart:select', { index, dataset: datasets[index] }); self.emit('local::select pieChartSelect', self, index, datasets[index]); } showTooltip(e) { const newIndex = parseInt(e.target.getAttribute('data-index'), 10); this.setCurrentIndex(newIndex); this.$svgEl.find('path').removeClass('pie-chart-hidden').forEach((el, index) => { if (index !== this.currentIndex) $(el).addClass('pie-chart-hidden'); }); this.setTooltip(); } hideTooltip() { this.setCurrentIndex(null); this.$svgEl.find('path').removeClass('pie-chart-hidden'); this.setTooltip(); } init() { const self = this; const $svgEl = $(self.render()).eq(0); $svgEl.f7PieChart = self; extend(self, { $svgEl, svgEl: $svgEl && $svgEl[0] }); self.$el.append($svgEl); self.$el.on('click mouseenter', 'path', self.showTooltip, true); self.$el.on('mouseleave', 'path', self.hideTooltip, true); return self; } destroy() { const self = this; if (!self.$el || self.destroyed) return; self.$el.trigger('piechart:beforedestroy'); self.emit('local::beforeDestroy pieChartBeforeDestroy', self); self.$el.off('click mouseenter', 'path', self.showTooltip, true); self.$el.off('mouseleave', 'path', self.hideTooltip, true); self.$svgEl.remove(); if (self.f7Tooltip && self.f7Tooltip.destroy) { self.f7Tooltip.destroy(); } delete self.$el[0].f7PieChart; deleteProps(self); self.destroyed = true; } } export default PieChart;