@gitlab/ui
Version:
GitLab UI Components
284 lines (271 loc) • 8.53 kB
JavaScript
import * as echarts from 'echarts';
import { merge } from 'lodash-es';
import { validRenderers, toolboxHeight, defaultWidth, defaultHeight } from '../../../utils/charts/config';
import { themeName, createTheme } from '../../../utils/charts/theme';
import { resolveCssValues } from '../../../utils/charts/css_values';
import { onColorModeChange } from '../../../utils/charts/color_mode';
import { GlResizeObserverDirective } from '../../../directives/resize_observer/resize_observer';
import { debounceByAnimationFrame } from '../../../utils/utils';
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
/**
* Allowed values by eCharts
* https://echarts.apache.org/en/api.html#echartsInstance.resize
*/
const sizeValidator = size => Number.isFinite(size) || size === 'auto' || size == null;
var script = {
name: 'GlChart',
directives: {
resizeObserver: GlResizeObserverDirective
},
props: {
/**
* The ECharts configuration object.
* https://echarts.apache.org/en/option.html#title
*/
options: {
type: Object,
required: true
},
/**
* Width of the chart in pixels. Set to "auto" to use the width of the container.
*/
width: {
type: [Number, String],
required: false,
default: null,
validator: sizeValidator
},
/**
* Height of the chart in pixels. Set to "auto" to use the height of the container.
*/
height: {
type: [Number, String],
required: false,
default: null,
validator: sizeValidator
},
/**
* An ECharts group id. Used to connect multiple charts.
* https://echarts.apache.org/en/api.html#echarts.connect
*/
groupId: {
type: String,
required: false,
default: ''
},
/**
* How the chart should be rendered. Valid options are 'canvas' or 'svg'.
* https://echarts.apache.org/handbook/en/best-practices/canvas-vs-svg/
*/
renderer: {
type: String,
required: false,
default: 'svg',
validator(renderer) {
return validRenderers.includes(renderer);
}
},
/**
* When true, the chart automatically resizes to fit its container.
*/
responsive: {
type: Boolean,
required: false,
default: true
}
},
data() {
return {
chart: null,
debouncedHandleResize: debounceByAnimationFrame(this.handleResize)
};
},
computed: {
modifiedOptions() {
var _options$aria, _options$toolbox;
let options = {
...this.options
};
// Enable aria by default
if (((_options$aria = options.aria) === null || _options$aria === void 0 ? void 0 : _options$aria.enabled) === undefined) {
options = merge({}, options, {
aria: {
enabled: true
}
});
}
// Add space at the top to fit the toolbox
if (((_options$toolbox = options.toolbox) === null || _options$toolbox === void 0 ? void 0 : _options$toolbox.show) === true) {
var _options$grid;
const top = (((_options$grid = options.grid) === null || _options$grid === void 0 ? void 0 : _options$grid.top) || 0) + toolboxHeight;
options = merge({}, options, {
grid: {
top
}
});
}
return options;
}
},
watch: {
options() {
if (this.chart) {
this.draw();
}
},
width() {
this.setChartSize();
},
height() {
this.setChartSize();
}
},
created() {
echarts.registerTheme(themeName, createTheme(this.options));
},
async mounted() {
await this.$nextTick();
this.initChart();
this.observeColorMode();
},
beforeDestroy() {
var _this$unsubscribeColo;
if (!this.chart) return;
this.chart.off('click', this.handleClick);
(_this$unsubscribeColo = this.unsubscribeColorMode) === null || _this$unsubscribeColo === void 0 ? void 0 : _this$unsubscribeColo.call(this);
},
methods: {
initChart() {
const chart = echarts.init(this.$refs.chart, this.theme(), {
renderer: this.renderer,
width: defaultWidth,
height: defaultHeight
});
// FIXME: temporary workaround to ensure compatibility with @vue/compat
// eslint-disable-next-line no-underscore-dangle
chart.__v_skip = true;
this.chart = chart;
if (this.groupId.length) {
this.chart.group = this.groupId;
echarts.connect(this.groupId);
}
this.chart.on('click', this.handleClick);
/**
* Emitted after calling `echarts.init`
*/
this.$emit('created', this.chart);
this.draw();
this.setChartSize();
},
/**
* The SVG renderer consumes the registered theme as-is, leaving its `var(--token)` colours for
* the browser to resolve. Canvas cannot, so it gets a copy resolved against this element.
*/
theme() {
if (this.renderer !== 'canvas') {
return themeName;
}
return resolveCssValues(createTheme(this.options), this.$refs.chart);
},
/**
* Rebuilding the chart discards its interaction state, so the legend selection and zoom range
* are carried over. Only these stateful fields are copied: the full option from getOption()
* holds colours resolved against the old colour mode.
*/
preservedState() {
const _this$chart$getOption = this.chart.getOption(),
_this$chart$getOption2 = _this$chart$getOption.legend,
legend = _this$chart$getOption2 === void 0 ? [] : _this$chart$getOption2,
_this$chart$getOption3 = _this$chart$getOption.dataZoom,
dataZoom = _this$chart$getOption3 === void 0 ? [] : _this$chart$getOption3;
return {
legend: legend.map(_ref => {
let selected = _ref.selected;
return {
selected
};
}),
dataZoom: dataZoom.map(_ref2 => {
let start = _ref2.start,
end = _ref2.end;
return {
start,
end
};
})
};
},
/**
* Colours resolved for canvas are frozen at init, so the chart has to be rebuilt when the
* colour mode changes. SVG re-resolves on its own and needs no subscription.
*/
observeColorMode() {
if (this.renderer !== 'canvas') {
return;
}
this.unsubscribeColorMode = onColorModeChange(this.$refs.chart, () => {
const state = this.preservedState();
this.chart.dispose();
this.initChart();
this.chart.setOption(state);
});
},
draw() {
this.chart.setOption(this.modifiedOptions);
/**
* Emitted after calling `echarts.setOption`
*/
this.$emit('updated', this.chart);
},
setChartSize() {
this.chart.resize({
width: this.width || 'auto',
height: this.height || defaultHeight
});
},
handleClick(params) {
/**
* Emitted when clicked on a data item in the chart (e.g., a bar/column).
*
* @property {object} chart The chart instance
* @property {object} params A params object, see also https://echarts.apache.org/en/api.html#events.Mouse%20events
*/
this.$emit('chart-item-clicked', {
chart: this.chart,
params
});
},
handleResize() {
this.chart.resize();
}
}
};
/* script */
const __vue_script__ = script;
/* template */
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{directives:[{name:"resize-observer",rawName:"v-resize-observer:[responsive]",value:(_vm.debouncedHandleResize),expression:"debouncedHandleResize",arg:_vm.responsive}],ref:"chart"})};
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 };