UNPKG

juijs-chart

Version:

SVG-based JUI chart that can be used in the browser and Node.js. Support many types of charts. (Dashboard, Map, Topology, Full 3D)

291 lines (238 loc) 11.1 kB
import jui from '../main.js'; export default { name: "chart.widget.tooltip", extend: "chart.widget.core", component: function() { var _ = jui.include("util.base"); var ColorUtil = jui.include("util.color"); var PADDING = 7, ANCHOR = 7, RATIO = 1.2; var TooltipWidget = function(chart, axis, widget) { var self = this, tooltips = {}, lineHeight = 0; function getFormat(k, d) { var key = null, value = null; if(_.typeCheck("function", widget.format)) { var obj = self.format(d, k); if(_.typeCheck("object", obj)) { key = obj.key; value = obj.value; } else { value = obj; } } else { if(k && !d) { value = k; } if(k && d) { key = k; value = self.format(d[k]); } } return { key: key, value: value } } function printTooltip(obj) { var tooltip = tooltips[obj.brush.index], texts = tooltip.get(1).get(1), width = 0, height = 0, onlyValue = false; if(obj.dataKey && widget.all === false) { setTextInTooltip([ obj.dataKey ]); } else { setTextInTooltip(obj.brush.target); } function setTextInTooltip(targets) { for(var i = 0; i < targets.length; i++) { var key = targets[i], msg = getFormat(key, obj.data); texts.get(i).attr({ x: PADDING }); if(msg.key) { texts.get(i).get(0).text(msg.key); } else { texts.get(i).get(1).attr({ "text-anchor": "middle" }); onlyValue = true; } if(!_.typeCheck([ "null", "undefined" ], msg.value)) { texts.get(i).get(1).attr({ x: 0 }).text(msg.value); } width = Math.max(width, texts.get(i).size().width); } height = targets.length * lineHeight; } return { width: width + PADDING * 3, height: height + PADDING, onlyValue: onlyValue }; } function existBrush(index) { var list = self.getIndexArray(self.widget.brush); return (_.inArray(index, list) == -1) ? false : true; } function getColorByKey(obj) { var targets = obj.brush.target; for(var i = 0; i < targets.length; i++) { if(targets[i] == obj.dataKey) { return ColorUtil.lighten(self.chart.color(i, obj.brush.colors)); } } return null; } function getTooltipXY(e, size, orient) { var x = e.bgX - (size.width / 2), y = e.bgY - size.height - ANCHOR - (PADDING / 2), lineX = 2; if(orient == "left" || orient == "right") { y = e.bgY - (size.height / 2) - (PADDING / 2); } if(orient == "left") { x = e.bgX - size.width - ANCHOR; } else if(orient == "right") { x = e.bgX + ANCHOR; lineX = -2; } else if(orient == "bottom") { y = e.bgY + (ANCHOR * 2); } return { x: x, y: y, c: lineX } } function setTooltipEvent() { var isActive = false, size = null, orient = null, axis = null; self.on("mouseover", function(obj, e) { if(isActive || !existBrush(obj.brush.index)) return; if(!obj.dataKey && !obj.data) return; // 툴팁 크기 가져오기 size = printTooltip(obj); orient = widget.orient; axis = chart.axis(obj.brush.axis); // 툴팁 좌표 가져오기 var xy = getTooltipXY(e, size, orient), x = xy.x - chart.padding("left"), y = xy.y - chart.padding("top"); // 엑시스 범위를 넘었을 경우 처리 if(widget.flip) { if (orient == "left" && x < 0) { orient = "right"; } else if (orient == "right" && x + size.width > axis.area("width")) { orient = "left"; } else if (orient == "top" && y < 0) { orient = "bottom"; } else if (orient == "bottom" && y + size.height > axis.area("height")) { orient = "top"; } } // 툴팁 엘리먼트 가져오기 var tooltip = tooltips[obj.brush.index], line = tooltip.get(0), target = tooltip.get(1), rect = tooltip.get(1).get(0), text = tooltip.get(1).get(1).translate(0, (orient != "bottom") ? lineHeight : lineHeight + ANCHOR), borderColor = chart.theme("tooltipBorderColor") || getColorByKey(obj), lineColor = chart.theme("tooltipLineColor") || getColorByKey(obj); rect.attr({ points: self.balloonPoints(orient, size.width, size.height, (widget.anchor) ? ANCHOR : null), stroke: borderColor }); line.attr({ stroke: lineColor }); text.each(function(i, elem) { elem.get(1).attr({ x: (size.onlyValue) ? size.width / 2 : size.width - PADDING }); }); tooltip.attr({ visibility: "visible" }); target.translate(xy.x, xy.y); isActive = true; }); self.on("mousemove", function(obj, e) { if(!isActive) return; var tooltip = tooltips[obj.brush.index], line = tooltip.get(0), target = tooltip.get(1), xy = getTooltipXY(e, size, orient); line.attr({ x1: e.bgX + xy.c, y1: chart.padding("top") + axis.area("y"), x2: e.bgX + xy.c, y2: chart.padding("top") + axis.area("y2") }); target.translate(xy.x, xy.y); }); self.on("mouseout", function(obj, e) { if(!isActive) return; var tooltip = tooltips[obj.brush.index]; tooltip.attr({ visibility: "hidden" }); isActive = false; }); } this.drawBefore = function() { lineHeight = chart.theme("tooltipFontSize") * RATIO; } this.draw = function() { var group = chart.svg.group(), list = this.getIndexArray(this.widget.brush); for(var i = 0; i < list.length; i++) { var brush = chart.get("brush", list[i]), words = [ "" ]; // 모든 타겟을 툴팁에 보여주는 옵션일 경우 if(widget.all && brush.target.length > 1) { for (var j = 1; j < brush.target.length; j++) { words.push(""); } } tooltips[brush.index] = chart.svg.group({ visibility: "hidden" }, function() { chart.svg.line({ "stroke-width": chart.theme("tooltipLineWidth"), visibility: (widget.line) ? "visible" : "hidden" }); chart.svg.group({}, function () { chart.svg.polygon({ fill: chart.theme("tooltipBackgroundColor"), "fill-opacity": chart.theme("tooltipBackgroundOpacity"), "stroke-width": chart.theme("tooltipBorderWidth") }); var text = chart.texts({ "font-size": chart.theme("tooltipFontSize"), "fill": chart.theme("tooltipFontColor") }, words, RATIO); for(var i = 0; i < words.length; i++) { text.get(i).append(chart.svg.tspan({ "text-anchor": "start", "font-weight": "bold", "x": PADDING })); text.get(i).append(chart.svg.tspan({ "text-anchor": "end" })); } }); }); group.append(tooltips[brush.index]); } setTooltipEvent(); return group; } } TooltipWidget.setup = function() { return { /** @cfg {"bottom"/"top"/"left"/"right"} Determines the side on which the tool tip is displayed (top, bottom, left, right). */ orient: "top", /** @cfg {Boolean} [anchor=true] Remove tooltip's anchor */ anchor: true, /** @cfg {Boolean} [all=false] Determines whether to show all values of row data.*/ all: false, /** @cfg {Boolean} [line=false] Visible Guidelines. */ line: false, /** @cfg {Boolean} [flip=false] When I went out of the area, reversing the tooltip. */ flip: false, /** @cfg {Function} [format=null] Sets the format of the value that is displayed on the tool tip. */ format: null, /** @cfg {Number} [brush=0] Specifies a brush index for which a widget is used. */ brush: 0 }; } return TooltipWidget; } }