350 lines
13 KiB
JavaScript
350 lines
13 KiB
JavaScript
|
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||
|
|
||
|
// Mixins
|
||
|
import Colorable from '../../mixins/colorable';
|
||
|
// Utilities
|
||
|
import mixins from '../../util/mixins';
|
||
|
import { genPoints } from './helpers/core';
|
||
|
import { genPath as _genPath } from './helpers/path';
|
||
|
export default mixins(Colorable).extend({
|
||
|
name: 'VSparkline',
|
||
|
props: {
|
||
|
autoDraw: Boolean,
|
||
|
autoDrawDuration: {
|
||
|
type: Number,
|
||
|
default: 2000
|
||
|
},
|
||
|
autoDrawEasing: {
|
||
|
type: String,
|
||
|
default: 'ease'
|
||
|
},
|
||
|
autoLineWidth: {
|
||
|
type: Boolean,
|
||
|
default: false
|
||
|
},
|
||
|
color: {
|
||
|
type: String,
|
||
|
default: 'primary'
|
||
|
},
|
||
|
fill: {
|
||
|
type: Boolean,
|
||
|
default: false
|
||
|
},
|
||
|
gradient: {
|
||
|
type: Array,
|
||
|
default: function _default() {
|
||
|
return [];
|
||
|
}
|
||
|
},
|
||
|
gradientDirection: {
|
||
|
type: String,
|
||
|
validator: function validator(val) {
|
||
|
return ['top', 'bottom', 'left', 'right'].includes(val);
|
||
|
},
|
||
|
default: 'top'
|
||
|
},
|
||
|
height: {
|
||
|
type: [String, Number],
|
||
|
default: 75
|
||
|
},
|
||
|
labels: {
|
||
|
type: Array,
|
||
|
default: function _default() {
|
||
|
return [];
|
||
|
}
|
||
|
},
|
||
|
lineWidth: {
|
||
|
type: [String, Number],
|
||
|
default: 4
|
||
|
},
|
||
|
padding: {
|
||
|
type: [String, Number],
|
||
|
default: 8
|
||
|
},
|
||
|
smooth: {
|
||
|
type: [Boolean, Number, String],
|
||
|
default: false
|
||
|
},
|
||
|
showLabels: Boolean,
|
||
|
type: {
|
||
|
type: String,
|
||
|
default: 'trend',
|
||
|
validator: function validator(val) {
|
||
|
return ['trend', 'bar'].includes(val);
|
||
|
}
|
||
|
},
|
||
|
value: {
|
||
|
type: Array,
|
||
|
default: function _default() {
|
||
|
return [];
|
||
|
}
|
||
|
},
|
||
|
width: {
|
||
|
type: [Number, String],
|
||
|
default: 300
|
||
|
},
|
||
|
labelSize: {
|
||
|
type: [Number, String],
|
||
|
default: 7
|
||
|
}
|
||
|
},
|
||
|
data: function data() {
|
||
|
return {
|
||
|
lastLength: 0
|
||
|
};
|
||
|
},
|
||
|
computed: {
|
||
|
parsedPadding: function parsedPadding() {
|
||
|
return Number(this.padding);
|
||
|
},
|
||
|
parsedWidth: function parsedWidth() {
|
||
|
return Number(this.width);
|
||
|
},
|
||
|
totalBars: function totalBars() {
|
||
|
return this.value.length;
|
||
|
},
|
||
|
_lineWidth: function _lineWidth() {
|
||
|
if (this.autoLineWidth && this.type !== 'trend') {
|
||
|
var totalPadding = this.parsedPadding * (this.totalBars + 1);
|
||
|
return (this.parsedWidth - totalPadding) / this.totalBars;
|
||
|
} else {
|
||
|
return Number(this.lineWidth) || 4;
|
||
|
}
|
||
|
},
|
||
|
boundary: function boundary() {
|
||
|
var height = Number(this.height);
|
||
|
return {
|
||
|
minX: this.parsedPadding,
|
||
|
minY: this.parsedPadding,
|
||
|
maxX: this.parsedWidth - this.parsedPadding,
|
||
|
maxY: height - this.parsedPadding
|
||
|
};
|
||
|
},
|
||
|
hasLabels: function hasLabels() {
|
||
|
return Boolean(this.showLabels || this.labels.length > 0 || this.$scopedSlots.label);
|
||
|
},
|
||
|
parsedLabels: function parsedLabels() {
|
||
|
var labels = [];
|
||
|
var points = this.points;
|
||
|
var len = points.length;
|
||
|
for (var i = 0; labels.length < len; i++) {
|
||
|
var item = points[i];
|
||
|
var value = this.labels[i];
|
||
|
if (!value) {
|
||
|
value = item === Object(item) ? item.value : item;
|
||
|
}
|
||
|
labels.push(_extends({}, item, {
|
||
|
value: String(value)
|
||
|
}));
|
||
|
}
|
||
|
return labels;
|
||
|
},
|
||
|
points: function points() {
|
||
|
return genPoints(this.value.slice(), this.boundary, this.type);
|
||
|
},
|
||
|
textY: function textY() {
|
||
|
return this.boundary.maxY + 6;
|
||
|
}
|
||
|
},
|
||
|
watch: {
|
||
|
value: {
|
||
|
immediate: true,
|
||
|
handler: function handler() {
|
||
|
var _this = this;
|
||
|
|
||
|
this.$nextTick(function () {
|
||
|
if (!_this.autoDraw || _this.type === 'bar') return;
|
||
|
var path = _this.$refs.path;
|
||
|
var length = path.getTotalLength();
|
||
|
if (!_this.fill) {
|
||
|
path.style.transition = 'none';
|
||
|
path.style.strokeDasharray = length + ' ' + length;
|
||
|
path.style.strokeDashoffset = Math.abs(length - (_this.lastLength || 0)).toString();
|
||
|
path.getBoundingClientRect();
|
||
|
path.style.transition = 'stroke-dashoffset ' + _this.autoDrawDuration + 'ms ' + _this.autoDrawEasing;
|
||
|
path.style.strokeDashoffset = '0';
|
||
|
} else {
|
||
|
path.style.transformOrigin = 'bottom center';
|
||
|
path.style.transition = 'none';
|
||
|
path.style.transform = 'scaleY(0)';
|
||
|
path.getBoundingClientRect();
|
||
|
path.style.transition = 'transform ' + _this.autoDrawDuration + 'ms ' + _this.autoDrawEasing;
|
||
|
path.style.transform = 'scaleY(1)';
|
||
|
}
|
||
|
_this.lastLength = length;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
genGradient: function genGradient() {
|
||
|
var _this2 = this;
|
||
|
|
||
|
var gradientDirection = this.gradientDirection;
|
||
|
var gradient = this.gradient.slice();
|
||
|
// Pushes empty string to force
|
||
|
// a fallback to currentColor
|
||
|
if (!gradient.length) gradient.push('');
|
||
|
var len = Math.max(gradient.length - 1, 1);
|
||
|
var stops = gradient.reverse().map(function (color, index) {
|
||
|
return _this2.$createElement('stop', {
|
||
|
attrs: {
|
||
|
offset: index / len,
|
||
|
'stop-color': color || _this2.color || 'currentColor'
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
return this.$createElement('defs', [this.$createElement('linearGradient', {
|
||
|
attrs: {
|
||
|
id: this._uid,
|
||
|
x1: +(gradientDirection === 'left'),
|
||
|
y1: +(gradientDirection === 'top'),
|
||
|
x2: +(gradientDirection === 'right'),
|
||
|
y2: +(gradientDirection === 'bottom')
|
||
|
}
|
||
|
}, stops)]);
|
||
|
},
|
||
|
genG: function genG(children) {
|
||
|
return this.$createElement('g', {
|
||
|
style: {
|
||
|
fontSize: '8',
|
||
|
textAnchor: 'middle',
|
||
|
dominantBaseline: 'mathematical',
|
||
|
fill: this.color || 'currentColor'
|
||
|
}
|
||
|
}, children);
|
||
|
},
|
||
|
genLabels: function genLabels() {
|
||
|
if (!this.hasLabels) return undefined;
|
||
|
return this.genG(this.parsedLabels.map(this.genText));
|
||
|
},
|
||
|
genPath: function genPath() {
|
||
|
var radius = this.smooth === true ? 8 : Number(this.smooth);
|
||
|
return this.$createElement('path', {
|
||
|
attrs: {
|
||
|
id: this._uid,
|
||
|
d: _genPath(this.points.slice(), radius, this.fill, Number(this.height)),
|
||
|
fill: this.fill ? 'url(#' + this._uid + ')' : 'none',
|
||
|
stroke: this.fill ? 'none' : 'url(#' + this._uid + ')'
|
||
|
},
|
||
|
ref: 'path'
|
||
|
});
|
||
|
},
|
||
|
genText: function genText(item, index) {
|
||
|
var children = this.$scopedSlots.label ? this.$scopedSlots.label({ index: index, value: item.value }) : item.value;
|
||
|
return this.$createElement('text', {
|
||
|
attrs: {
|
||
|
x: item.x,
|
||
|
y: this.textY
|
||
|
}
|
||
|
}, [children]);
|
||
|
},
|
||
|
genBar: function genBar() {
|
||
|
if (!this.value || this.totalBars < 2) return undefined;
|
||
|
var width = this.width,
|
||
|
height = this.height,
|
||
|
parsedPadding = this.parsedPadding,
|
||
|
_lineWidth = this._lineWidth;
|
||
|
|
||
|
var viewWidth = width || this.totalBars * parsedPadding * 2;
|
||
|
var viewHeight = height || 75;
|
||
|
var boundary = {
|
||
|
minX: parsedPadding,
|
||
|
minY: parsedPadding,
|
||
|
maxX: Number(viewWidth) - parsedPadding,
|
||
|
maxY: Number(viewHeight) - parsedPadding
|
||
|
};
|
||
|
var props = _extends({}, this.$props);
|
||
|
props.points = genPoints(this.value, boundary, this.type);
|
||
|
var totalWidth = boundary.maxX / (props.points.length - 1);
|
||
|
props.boundary = boundary;
|
||
|
props.lineWidth = _lineWidth || totalWidth - Number(parsedPadding || 5);
|
||
|
props.offsetX = 0;
|
||
|
if (!this.autoLineWidth) {
|
||
|
props.offsetX = boundary.maxX / this.totalBars / 2 - boundary.minX;
|
||
|
}
|
||
|
return this.$createElement('svg', {
|
||
|
attrs: {
|
||
|
width: '100%',
|
||
|
height: '25%',
|
||
|
viewBox: '0 0 ' + viewWidth + ' ' + viewHeight
|
||
|
}
|
||
|
}, [this.genGradient(), this.genClipPath(props.offsetX, props.lineWidth, 'sparkline-bar-' + this._uid), this.hasLabels ? this.genBarLabels(props) : undefined, this.$createElement('g', {
|
||
|
attrs: {
|
||
|
transform: 'scale(1,-1) translate(0,-' + boundary.maxY + ')',
|
||
|
'clip-path': 'url(#sparkline-bar-' + this._uid + '-clip)',
|
||
|
fill: 'url(#' + this._uid + ')'
|
||
|
}
|
||
|
}, [this.$createElement('rect', {
|
||
|
attrs: {
|
||
|
x: 0,
|
||
|
y: 0,
|
||
|
width: viewWidth,
|
||
|
height: viewHeight
|
||
|
}
|
||
|
})])]);
|
||
|
},
|
||
|
genClipPath: function genClipPath(offsetX, lineWidth, id) {
|
||
|
var _this3 = this;
|
||
|
|
||
|
var maxY = this.boundary.maxY;
|
||
|
|
||
|
var rounding = typeof this.smooth === 'number' ? this.smooth : this.smooth ? 2 : 0;
|
||
|
return this.$createElement('clipPath', {
|
||
|
attrs: {
|
||
|
id: id + '-clip'
|
||
|
}
|
||
|
}, this.points.map(function (item) {
|
||
|
return _this3.$createElement('rect', {
|
||
|
attrs: {
|
||
|
x: item.x + offsetX,
|
||
|
y: 0,
|
||
|
width: lineWidth,
|
||
|
height: Math.max(maxY - item.y, 0),
|
||
|
rx: rounding,
|
||
|
ry: rounding
|
||
|
}
|
||
|
}, [_this3.autoDraw ? _this3.$createElement('animate', {
|
||
|
attrs: {
|
||
|
attributeName: 'height',
|
||
|
from: 0,
|
||
|
to: maxY - item.y,
|
||
|
dur: _this3.autoDrawDuration + 'ms',
|
||
|
fill: 'freeze'
|
||
|
}
|
||
|
}) : undefined]);
|
||
|
}));
|
||
|
},
|
||
|
genBarLabels: function genBarLabels(props) {
|
||
|
var _this4 = this;
|
||
|
|
||
|
var offsetX = props.offsetX || 0;
|
||
|
var children = props.points.map(function (item) {
|
||
|
return _this4.$createElement('text', {
|
||
|
attrs: {
|
||
|
x: item.x + offsetX + _this4._lineWidth / 2,
|
||
|
y: props.boundary.maxY + (Number(_this4.labelSize) || 7),
|
||
|
'font-size': Number(_this4.labelSize) || 7
|
||
|
}
|
||
|
}, item.value.toString());
|
||
|
});
|
||
|
return this.genG(children);
|
||
|
},
|
||
|
genTrend: function genTrend() {
|
||
|
return this.$createElement('svg', this.setTextColor(this.color, {
|
||
|
attrs: {
|
||
|
'stroke-width': this._lineWidth || 1,
|
||
|
width: '100%',
|
||
|
height: '25%',
|
||
|
viewBox: '0 0 ' + this.width + ' ' + this.height
|
||
|
}
|
||
|
}), [this.genGradient(), this.genLabels(), this.genPath()]);
|
||
|
}
|
||
|
},
|
||
|
render: function render(h) {
|
||
|
if (this.totalBars < 2) return undefined;
|
||
|
return this.type === 'trend' ? this.genTrend() : this.genBar();
|
||
|
}
|
||
|
});
|
||
|
//# sourceMappingURL=VSparkline.js.map
|