@nafr/echo-ui
Version:
A UI library born for WAA
63 lines (62 loc) • 2.7 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import * as d3 from 'd3';
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
import { useResizeObserver } from '../../../lib/hooks';
import { cn, validScaledNaN } from '../../../lib/utils';
import { useStyle } from './styles';
import { WIDTH, HEIGHT, AMPLITUDE_RANGE, LINE_COLOR, LINE_WIDTH } from './constants';
export const Oscilloscope = forwardRef((props, ref) => {
const { data: _data = [], amplitudeRange = AMPLITUDE_RANGE, lineColor = LINE_COLOR, lineWidth = LINE_WIDTH, ...restProps } = props;
useImperativeHandle(ref, () => oscilloscopeRef.current);
const oscilloscopeRef = useRef(null);
const svgRef = useRef(null);
const xScale = useRef(null);
const yScale = useRef(null);
const dataLength = useRef(0);
const dimensions = useResizeObserver(oscilloscopeRef, WIDTH, HEIGHT, () => {
generateScales();
generateLine();
});
useEffect(() => {
generateLine();
}, [_data]);
useEffect(() => {
dataLength.current = _data.length;
generateScales();
}, [_data.length]);
const generateScales = () => {
const { width, height } = dimensions.current;
xScale.current = d3.scaleLinear().domain([0, dataLength.current]).range([0, width]);
yScale.current = d3.scaleLinear().domain(amplitudeRange).range([height, 0]);
};
const generateLine = () => {
const svg = d3.select(svgRef.current);
svg.selectAll('g.echo-g-line').remove();
if (!_data.length)
return;
const { width, height } = dimensions.current;
const g = svg
.append('g')
.attr('class', 'echo-g-line')
.attr('width', width)
.attr('height', height);
// Update line generator
const lineGenerator = d3
.line()
.x((d) => validScaledNaN(xScale.current, d.index, -100))
.y((d) => validScaledNaN(yScale.current, d.amplitude, -100))
.curve(d3.curveNatural)
.curve(d3.curveCatmullRom.alpha(0.5));
// Bind new data and apply transitions
g.selectAll('path.echo-path-line')
.data([_data])
.join('path')
.attr('class', 'echo-path-line')
.attr('d', lineGenerator)
.attr('stroke', lineColor)
.attr('stroke-width', lineWidth)
.attr('fill', 'none');
};
const { base, svg } = useStyle();
return (_jsx("div", { ...restProps, ref: oscilloscopeRef, className: cn(base(), restProps.className), style: restProps.style, children: _jsx("svg", { ref: svgRef, className: cn(svg()) }) }));
});