@nafr/echo-ui
Version:
A UI library born for WAA
83 lines (82 loc) • 4.24 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { scaleLinear } from 'd3';
import { cn } from '../../../lib/utils';
import { VuMeterContext, VuMeterContextProvider } from './context';
import { checkPropsIsValid } from './utils';
import { useStyle } from './styles';
import { DEFAULT_LUMPS_QUANTITY, MIN, MAX, MIN_THRESHOLD, MAX_THRESHOLD } from './constants';
import { Axis } from '../Axis';
export const VuMeter = forwardRef((props, ref) => {
const { value, horizontal = false, compact = false, lumpsQuantity = DEFAULT_LUMPS_QUANTITY, hideAxis = false, axisProps, classNames, styles, onChange, ...restProps } = props;
useImperativeHandle(ref, () => vuMeterRef.current);
const monoRef = useRef(null);
const stereoRef = useRef(null);
useEffect(() => {
checkPropsIsValid(props);
}, []);
const vuMeterRef = useRef(null);
const isStereo = Array.isArray(value);
const [lumps, setLumps] = useState(Array(lumpsQuantity).fill(0));
const [stereoLumps, setStereoLumps] = useState([
Array(lumpsQuantity).fill(0),
Array(lumpsQuantity).fill(0),
]);
const scale = scaleLinear().domain([MIN, MAX]).range([0, lumps.length]);
const updateLumps = () => {
if (isStereo) {
const leftDBValue = scale(value[0]);
const rightDBValue = scale(value[1]);
const newLumps = [
stereoLumps[0].map((_, index) => (index < leftDBValue ? 1 : 0)),
stereoLumps[1].map((_, index) => (index < rightDBValue ? 1 : 0)),
];
setStereoLumps(newLumps);
}
else {
const dBValue = scale(value);
const newLumps = lumps.map((_, index) => (index < dBValue ? 1 : 0));
setLumps(newLumps);
}
};
useEffect(() => {
onChange && onChange(value);
updateLumps();
}, [value, onChange]);
const { base, lumps: _lumps, lump, axis } = useStyle({ horizontal, isStereo, compact });
const contextValue = {
horizontal,
styles,
classNames,
minThresholdValue: scale(MIN_THRESHOLD),
maxThresholdValue: scale(MAX_THRESHOLD),
_lumps,
lump,
};
return (_jsx(VuMeterContextProvider, { value: contextValue, children: _jsx("div", { ...restProps, ref: vuMeterRef, className: cn(restProps.className), style: {
...restProps.style,
display: 'flex',
}, children: _jsxs("div", { className: cn(base()), children: [isStereo ? (_jsx(StereoVuMeter, { ref: stereoRef, stereoLumps: stereoLumps })) : (_jsx(MonoVuMeter, { ref: monoRef, lumps: lumps })), !hideAxis && (_jsx(Axis, { vertical: !horizontal, tickSize: 0, relatedRef: isStereo ? stereoRef : monoRef, ...axisProps, className: cn(axis(), classNames?.axis), style: styles?.axis, min: MIN, max: MAX }))] }) }) }));
});
const StereoVuMeter = forwardRef((props, ref) => {
const { stereoLumps } = props;
const { horizontal } = useContext(VuMeterContext);
return (_jsx("div", { ref: ref, className: cn('flex gap-0.5', horizontal && 'flex-col'), children: stereoLumps.map((lumps, index) => (_jsx(MonoVuMeter, { lumps: lumps }, index))) }));
});
const MonoVuMeter = forwardRef((props, ref) => {
const { lumps } = props;
const { horizontal, styles, classNames, minThresholdValue, maxThresholdValue, _lumps, lump } = useContext(VuMeterContext);
const dataValue = (lumpValue, index) => {
if (lumpValue === 1 && index <= minThresholdValue)
return 'low';
if (lumpValue === 1 && index > maxThresholdValue)
return 'high';
if (lumpValue === 1 && index <= maxThresholdValue)
return 'medium';
return 'none';
};
return (_jsx("div", { ref: ref, className: cn(_lumps(), classNames?.lumps), style: {
...styles?.lumps,
flexDirection: horizontal ? 'row' : 'column-reverse',
}, children: lumps.map((lumpValue, index) => (_jsx("span", { "data-active": dataValue(lumpValue, index), className: cn(lump(), classNames?.lump), style: styles?.lump }, index))) }));
});