UNPKG

vuetify

Version:

Vue Material Component Framework

151 lines (149 loc) 6.04 kB
// Utilities import { computed, nextTick, ref, useId, watch, createElementVNode as _createElementVNode } from 'vue'; import { makeLineProps } from "./util/line.js"; import { genPath as _genPath } from "./util/path.js"; import { genericComponent, getPropertyFromItem, propsFactory, useRender } from "../../util/index.js"; // Types export const makeVTrendlineProps = propsFactory({ fill: Boolean, ...makeLineProps() }, 'VTrendline'); export const VTrendline = genericComponent()({ name: 'VTrendline', props: makeVTrendlineProps(), setup(props, _ref) { let { slots } = _ref; const uid = useId(); const id = computed(() => props.id || `trendline-${uid}`); const autoDrawDuration = computed(() => Number(props.autoDrawDuration) || (props.fill ? 500 : 2000)); const lastLength = ref(0); const path = ref(null); function genPoints(values, boundary) { const { minX, maxX, minY, maxY } = boundary; const totalValues = values.length; const maxValue = props.max != null ? Number(props.max) : Math.max(...values); const minValue = props.min != null ? Number(props.min) : Math.min(...values); const gridX = (maxX - minX) / (totalValues - 1); const gridY = (maxY - minY) / (maxValue - minValue || 1); return values.map((value, index) => { return { x: minX + index * gridX, y: maxY - (value - minValue) * gridY, value }; }); } const hasLabels = computed(() => { return Boolean(props.showLabels || props.labels.length > 0 || !!slots?.label); }); const lineWidth = computed(() => { return parseFloat(props.lineWidth) || 4; }); const totalWidth = computed(() => Number(props.width)); const boundary = computed(() => { const padding = Number(props.padding); return { minX: padding, maxX: totalWidth.value - padding, minY: padding, maxY: parseInt(props.height, 10) - padding }; }); const items = computed(() => props.modelValue.map(item => getPropertyFromItem(item, props.itemValue, item))); const parsedLabels = computed(() => { const labels = []; const points = genPoints(items.value, boundary.value); const len = points.length; for (let i = 0; labels.length < len; i++) { const item = points[i]; let value = props.labels[i]; if (!value) { value = typeof item === 'object' ? item.value : item; } labels.push({ x: item.x, value: String(value) }); } return labels; }); watch(() => props.modelValue, async () => { await nextTick(); if (!props.autoDraw || !path.value) return; const pathRef = path.value; const length = pathRef.getTotalLength(); if (!props.fill) { // Initial setup to "hide" the line by using the stroke dash array pathRef.style.strokeDasharray = `${length}`; pathRef.style.strokeDashoffset = `${length}`; // Force reflow to ensure the transition starts from this state pathRef.getBoundingClientRect(); // Animate the stroke dash offset to "draw" the line pathRef.style.transition = `stroke-dashoffset ${autoDrawDuration.value}ms ${props.autoDrawEasing}`; pathRef.style.strokeDashoffset = '0'; } else { // Your existing logic for filled paths remains the same pathRef.style.transformOrigin = 'bottom center'; pathRef.style.transition = 'none'; pathRef.style.transform = `scaleY(0)`; pathRef.getBoundingClientRect(); pathRef.style.transition = `transform ${autoDrawDuration.value}ms ${props.autoDrawEasing}`; pathRef.style.transform = `scaleY(1)`; } lastLength.value = length; }, { immediate: true }); function genPath(fill) { const smoothValue = typeof props.smooth === 'boolean' ? props.smooth ? 8 : 0 : Number(props.smooth); return _genPath(genPoints(items.value, boundary.value), smoothValue, fill, parseInt(props.height, 10)); } useRender(() => { const gradientData = !props.gradient.slice().length ? [''] : props.gradient.slice().reverse(); return _createElementVNode("svg", { "display": "block", "stroke-width": parseFloat(props.lineWidth) ?? 4 }, [_createElementVNode("defs", null, [_createElementVNode("linearGradient", { "id": id.value, "gradientUnits": "userSpaceOnUse", "x1": props.gradientDirection === 'left' ? '100%' : '0', "y1": props.gradientDirection === 'top' ? '100%' : '0', "x2": props.gradientDirection === 'right' ? '100%' : '0', "y2": props.gradientDirection === 'bottom' ? '100%' : '0' }, [gradientData.map((color, index) => _createElementVNode("stop", { "offset": index / Math.max(gradientData.length - 1, 1), "stop-color": color || 'currentColor' }, null))])]), hasLabels.value && _createElementVNode("g", { "key": "labels", "style": { textAnchor: 'middle', dominantBaseline: 'mathematical', fill: 'currentColor' } }, [parsedLabels.value.map((item, i) => _createElementVNode("text", { "x": item.x + lineWidth.value / 2 + lineWidth.value / 2, "y": parseInt(props.height, 10) - 4 + (parseInt(props.labelSize, 10) || 7 * 0.75), "font-size": Number(props.labelSize) || 7 }, [slots.label?.({ index: i, value: item.value }) ?? item.value]))]), _createElementVNode("path", { "ref": path, "d": genPath(props.fill), "fill": props.fill ? `url(#${id.value})` : 'none', "stroke": props.fill ? 'none' : `url(#${id.value})` }, null), props.fill && _createElementVNode("path", { "d": genPath(false), "fill": "none", "stroke": props.color ?? props.gradient?.[0] }, null)]); }); } }); //# sourceMappingURL=VTrendline.js.map