UNPKG

@gitlab/ui

Version:
372 lines (357 loc) • 11.1 kB
import * as echarts from 'echarts'; import { uid, debounceByAnimationFrame } from '../../../../utils/utils'; import GlPopover from '../../../base/popover/popover'; import { popoverPlacements } from '../../../../utils/constants'; import { TOOLTIP_LEFT_OFFSET, TOOLTIP_TOP_OFFSET } from '../../../../utils/charts/constants'; import { getTooltipAxisConfig, getTooltipTitle, getTooltipContent } from '../../../../utils/charts/config'; import TooltipDefaultFormat from './tooltip_default_format/tooltip_default_format'; import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js'; // var script = { name: 'GlChartTooltip', components: { GlPopover, TooltipDefaultFormat }, inheritAttrs: false, props: { chart: { type: Object, required: true, validator(chart) { return Object.is(chart, echarts.getInstanceByDom(chart.getDom())); } }, id: { type: String, required: false, default: () => uid() }, /** * Position of the popover respective to the chart. * Sets the `top` style property. */ top: { type: String, required: false, default: null }, /** * Position of the popover respective to the chart. * Sets the `bottom` style property. */ bottom: { type: String, required: false, default: null }, /** * Position of the popover respective to the chart. * Sets the `left` style property. */ left: { type: String, required: false, default: null }, /** * Position of the popover respective to the chart. * Sets the `right` style property. */ right: { type: String, required: false, default: null }, /** * Set to `true` to show, set to `false` to not show. * Set to `null` to show only when the mouse is in the chart. */ show: { type: Boolean, required: false, default: null }, /** * Popover placement */ placement: { type: String, required: false, default: popoverPlacements.right }, /** * Distance between the popover and the pointer when * no position is defined */ xOffset: { type: Number, required: false, default: TOOLTIP_LEFT_OFFSET, validator(value) { // popover target must have a size of at least 1 return value >= 1; } }, /** * Distance between the popover and the pointer when * no position is defined */ yOffset: { type: Number, required: false, default: TOOLTIP_TOP_OFFSET, validator(value) { // popover target must have a size of at least 1 return value >= 1; } }, /** * Set to true to use the default tooltip formatter. */ useDefaultTooltipFormatter: { type: Boolean, required: false, default: false }, /** * Set to true to enable click-to-pin functionality. * When enabled, clicking on a chart point will keep the tooltip open, so the user can interact with the tooltip's content. * The tooltip will unpin when clicking again on the chart and not on the tooltip. */ clickToPin: { type: Boolean, required: false, default: false }, /** * Specifies which axis contains the dimensional data used in the tooltip. * When `xAxis`, x-axis value becomes the tooltip's title and * y-axis values become the tooltip's values. * When `yAxis`, roles are reversed. */ dimensionAxis: { type: String, required: false, default: 'xAxis', validator(value) { return value === 'xAxis' || value === 'yAxis'; } } }, data() { return { pointerCoords: null, isPointerInChart: false, pinnedPosition: null, debouncedMouseHandler: debounceByAnimationFrame(this.mouseHandler), title: null, content: {}, params: null }; }, computed: { targetId() { // if multiple tooltips are used in a chart component, // `this.id` can be used to uniquely identify them return `${this.chart.getDom().getAttribute('_echarts_instance_')}-tooltip-${this.id}`; }, targetStyle() { // the target is a rectangular space between cursor and popover return { marginTop: `${-this.yOffset}px`, height: `${this.yOffset * 2}px`, marginLeft: `${-this.xOffset}px`, width: `${this.xOffset * 2}px`, pointerEvents: this.isPinned ? 'auto' : 'none', ...this.tooltipPosition }; }, fixedPosition() { const { top, left, bottom, right } = this; if (top || left || bottom || right) { return { top, left, bottom, right }; } return null; }, tooltipPosition() { if (this.fixedPosition) { return this.fixedPosition; } if (this.pinnedPosition) { return this.pinnedPosition; } return this.pointerPosition; }, shouldShowPopover() { if (this.show !== null) { return this.show; } return this.isPinned || this.isPointerInChart; }, isPinned() { return this.pinnedPosition !== null; }, pointerPosition() { if (!this.pointerCoords) { return null; } return { left: `${this.pointerCoords.x}px`, top: `${this.pointerCoords.y}px` }; } }, created() { this.chart.getZr().on('mousemove', this.debouncedMouseHandler); this.chart.getZr().on('mouseout', this.debouncedMouseHandler); if (this.clickToPin) { document.addEventListener('keydown', this.keyDownHandler); this.chart.getZr().on('click', this.clickHandler); } if (this.useDefaultTooltipFormatter) { this.chart.setOption({ [this.dimensionAxis]: { axisPointer: { show: true, label: { formatter: params => { var _options$this$dimensi, _options$this$dimensi2, _options$valueAxis, _options$valueAxis$; const options = this.chart.getOption(); const { dimensionIndex, metricIndex, valueAxis } = getTooltipAxisConfig(this.dimensionAxis); const titleAxisName = (_options$this$dimensi = options[this.dimensionAxis]) === null || _options$this$dimensi === void 0 ? void 0 : (_options$this$dimensi2 = _options$this$dimensi[0]) === null || _options$this$dimensi2 === void 0 ? void 0 : _options$this$dimensi2.name; const valueAxisName = (_options$valueAxis = options[valueAxis]) === null || _options$valueAxis === void 0 ? void 0 : (_options$valueAxis$ = _options$valueAxis[0]) === null || _options$valueAxis$ === void 0 ? void 0 : _options$valueAxis$.name; this.title = getTooltipTitle(params, titleAxisName, dimensionIndex); this.content = getTooltipContent(params, valueAxisName, metricIndex); this.params = params; } } } } }); } }, beforeDestroy() { this.chart.getZr().off('mousemove', this.debouncedMouseHandler); this.chart.getZr().off('mouseout', this.debouncedMouseHandler); if (this.clickToPin) { this.chart.getZr().off('click', this.clickHandler); document.removeEventListener('keydown', this.keyDownHandler); } }, methods: { getEventCoordsWithinChart(_ref) { let { event } = _ref; const { zrX, zrY } = event; const x = Math.round(zrX); const y = Math.round(zrY); if (!Number.isFinite(x) || !Number.isFinite(y)) { return null; } if (!this.chart.containPixel('grid', [x, y])) { return null; } return { x, y }; }, mouseHandler(event) { const coords = this.getEventCoordsWithinChart(event); if (!coords) { this.isPointerInChart = false; return; } this.pointerCoords = coords; this.isPointerInChart = true; }, clickHandler(event) { if (!this.getEventCoordsWithinChart(event)) { return; } if (!this.isPinned) { this.pinTooltip(); } else { this.unpinTooltip(event.event); } }, pinTooltip() { this.pinnedPosition = this.pointerPosition; // prevents any axis pointers from being moved to a new position, they should also be pinned this.chart.setOption({ tooltip: { triggerOn: 'none' } }); }, unpinTooltip() { this.pinnedPosition = null; // restores the axis pointers to be unpinned and be triggered by mousemove this.chart.setOption({ tooltip: { triggerOn: 'mousemove', show: false } }); // shows the tooltip and axis pointer at the position of the click this.chart.dispatchAction({ type: 'showTip', x: this.pointerCoords.x, y: this.pointerCoords.y }); }, keyDownHandler(event) { if (event.key === 'Escape' && this.isPinned) { this.unpinTooltip(); } } } }; /* script */ const __vue_script__ = script; /* template */ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.chart)?_c('div',[_c('div',{staticClass:"gl-chart-tooltip",style:(_vm.targetStyle),attrs:{"id":_vm.targetId}}),_vm._v(" "),_c('gl-popover',_vm._b({attrs:{"show":_vm.shouldShowPopover,"target":_vm.targetId,"container":_vm.targetId,"placement":_vm.placement,"triggers":""},scopedSlots:_vm._u([{key:"title",fn:function(){return [_vm._t("title",function(){return [_vm._v(_vm._s(_vm.title))]},null,{ title: _vm.title, params: _vm.params })]},proxy:true}],null,true)},'gl-popover',_vm.$attrs,false),[_vm._v(" "),_vm._t("default",function(){return [_c('tooltip-default-format',{attrs:{"tooltip-content":_vm.content},scopedSlots:_vm._u([(_vm.$scopedSlots['tooltip-value'])?{key:"tooltip-value",fn:function(scope){return [_vm._t("tooltip-value",null,null,scope)]}}:null],null,true)})]},null,{ content: _vm.content, params: _vm.params })],2)],1):_vm._e()}; var __vue_staticRenderFns__ = []; /* style */ const __vue_inject_styles__ = undefined; /* scoped */ const __vue_scope_id__ = undefined; /* module identifier */ const __vue_module_identifier__ = undefined; /* functional template */ const __vue_is_functional_template__ = false; /* style inject */ /* style inject SSR */ /* style inject shadow dom */ const __vue_component__ = /*#__PURE__*/__vue_normalize__( { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined ); export { __vue_component__ as default };