mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 23:16:21 +00:00
535fa71020
Run eslint --fix across src/ to clear ~1900 mechanical lint errors surfaced by the @typescript-eslint v8 + react-hooks v7 + react-compiler upgrade in the React 19 modernization PR. Issues fixed automatically: - brace-style (Allman): try/catch one-liners reformatted to multi-line - indent: tab-vs-space and depth corrections - semi: missing trailing semicolons - no-trailing-spaces No semantic changes. Remaining 701 errors are real-code issues (set-state-in-effect, rules-of-hooks, no-unsafe-* type checks) that need manual per-file review. https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
149 lines
4.6 KiB
TypeScript
149 lines
4.6 KiB
TypeScript
import * as RadixSlider from '@radix-ui/react-slider';
|
|
import { CSSProperties, FC, HTMLProps, ReactElement } from 'react';
|
|
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
|
|
import { Button } from './Button';
|
|
import { Flex } from './Flex';
|
|
|
|
export interface SliderThumbState
|
|
{
|
|
index: number;
|
|
value: number | number[];
|
|
valueNow: number;
|
|
}
|
|
|
|
export interface SliderProps
|
|
{
|
|
min?: number;
|
|
max?: number;
|
|
step?: number;
|
|
value?: number | number[];
|
|
defaultValue?: number | number[];
|
|
onChange?: (value: any, thumbIndex: number) => void;
|
|
disabled?: boolean;
|
|
disabledButton?: boolean;
|
|
invert?: boolean;
|
|
className?: string;
|
|
style?: CSSProperties;
|
|
trackClassName?: string;
|
|
thumbClassName?: string;
|
|
renderThumb?: (props: HTMLProps<HTMLDivElement>, state: SliderThumbState) => ReactElement;
|
|
}
|
|
|
|
const toArray = (value: number | number[] | undefined): number[] =>
|
|
{
|
|
if(Array.isArray(value)) return value;
|
|
if(typeof value === 'number') return [ value ];
|
|
|
|
return [ 0 ];
|
|
};
|
|
|
|
const cn = (...parts: (string | undefined | false)[]) => parts.filter(Boolean).join(' ');
|
|
|
|
export const Slider: FC<SliderProps> = props =>
|
|
{
|
|
const {
|
|
disabledButton,
|
|
disabled,
|
|
max = 100,
|
|
min = 0,
|
|
step = 1,
|
|
value,
|
|
defaultValue,
|
|
onChange,
|
|
invert,
|
|
className,
|
|
style,
|
|
trackClassName,
|
|
thumbClassName,
|
|
renderThumb
|
|
} = props;
|
|
|
|
const valueArr = toArray(value);
|
|
const currentValue = valueArr[0] ?? 0;
|
|
const minimum = (typeof min === 'number') ? min : 0;
|
|
const maximum = (typeof max === 'number') ? max : 0;
|
|
const buttonStep = ((typeof step === 'number') && (step > 0)) ? step : 1;
|
|
const isRange = valueArr.length > 1;
|
|
|
|
const roundToStep = (nextValue: number) =>
|
|
{
|
|
const decimalStep = buttonStep.toString();
|
|
const precision = decimalStep.includes('.') ? (decimalStep.length - decimalStep.indexOf('.') - 1) : 0;
|
|
|
|
return parseFloat(nextValue.toFixed(precision));
|
|
};
|
|
|
|
const emit = (next: number[]) =>
|
|
{
|
|
if(!onChange) return;
|
|
|
|
if(isRange) onChange(next, 0);
|
|
else onChange(next[0], 0);
|
|
};
|
|
|
|
const stepDown = () =>
|
|
{
|
|
const next = roundToStep(minimum < currentValue ? currentValue - buttonStep : minimum);
|
|
|
|
emit([ next, ...valueArr.slice(1) ]);
|
|
};
|
|
|
|
const stepUp = () =>
|
|
{
|
|
const next = roundToStep(maximum > currentValue ? currentValue + buttonStep : maximum);
|
|
|
|
emit([ next, ...valueArr.slice(1) ]);
|
|
};
|
|
|
|
const renderThumbElement = (i: number) =>
|
|
{
|
|
const baseProps: HTMLProps<HTMLDivElement> = {
|
|
key: i,
|
|
className: cn('thumb', `thumb-${ i }`, thumbClassName)
|
|
};
|
|
|
|
const state: SliderThumbState = {
|
|
index: i,
|
|
value: isRange ? valueArr : currentValue,
|
|
valueNow: valueArr[i] ?? 0
|
|
};
|
|
|
|
return (
|
|
<RadixSlider.Thumb key={ i } asChild>
|
|
{ renderThumb ? renderThumb(baseProps, state) : <div { ...baseProps } /> }
|
|
</RadixSlider.Thumb>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Flex fullWidth gap={ 1 } classNames={ [ 'nitro-slider-wrapper' ] }>
|
|
{ !disabledButton && (
|
|
<Button classNames={ [ 'nitro-slider-button', 'nitro-slider-button-left' ] } disabled={ disabled || (minimum >= currentValue) } onClick={ stepDown }>
|
|
<FaAngleLeft />
|
|
</Button>
|
|
) }
|
|
<RadixSlider.Root
|
|
inverted={ invert }
|
|
disabled={ disabled }
|
|
className={ cn('nitro-slider', 'relative', 'min-w-0', 'grow', className) }
|
|
style={ style }
|
|
max={ max }
|
|
min={ min }
|
|
step={ step }
|
|
value={ value !== undefined ? valueArr : undefined }
|
|
defaultValue={ defaultValue !== undefined ? toArray(defaultValue) : undefined }
|
|
onValueChange={ emit }>
|
|
<RadixSlider.Track className={ cn('track', 'track-1', 'grow', trackClassName) }>
|
|
<RadixSlider.Range className={ cn('track', 'track-0', trackClassName) } />
|
|
</RadixSlider.Track>
|
|
{ valueArr.map((_, i) => renderThumbElement(i)) }
|
|
</RadixSlider.Root>
|
|
{ !disabledButton && (
|
|
<Button classNames={ [ 'nitro-slider-button', 'nitro-slider-button-right' ] } disabled={ disabled || (maximum <= currentValue) } onClick={ stepUp }>
|
|
<FaAngleRight />
|
|
</Button>
|
|
) }
|
|
</Flex>
|
|
);
|
|
};
|