import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useCallback, useEffect, useRef, useState, forwardRef } from 'react';
import { NumberInput } from '../Number';
import { useConsolidatedRef, useDirection, useTestIds, useUID } from '../../hooks';
import FormField from '../FormField';
import Flex from '../Flex';
import Grid from '../Grid';
import { cap, withTestIds } from '../../utils';
import { StyledSlider, StyledTrack, StyledThumb, StyledSliderTrackWrapper, StyledBar, StyledPreview } from './Slider.styles';
import { calculateValueLength, getNearestValue } from './utils';
import SliderTicks from './SliderTicks';
import { getSliderTestIds } from './Slider.test-ids';
const Slider = forwardRef(function Slider(props, ref) {
    const uid = useUID();
    const { testId, id = uid, required, status, info, label, labelHidden, min = 0, max = 100, step = 1, value = min, orientation = 'horizontal', preview, disabled = false, readOnly = false, showInput = true, showProgress, ticks = {}, additionalInfo, onChange, autoFocus, onDragStart, onDragEnd: onDragEndProp, ...restProps } = props;
    const testIds = useTestIds(testId, getSliderTestIds);
    const thumbRef = useConsolidatedRef(ref);
    const trackRef = useRef(null);
    const [inDrag, setInDrag] = useState(false);
    const [inputValue, setInputValue] = useState(value.toString());
    const setValue = useCallback((newValue) => {
        if (disabled || readOnly)
            return;
        const normalizedValue = Math.min(Math.max(newValue, min), max);
        setInputValue(normalizedValue.toString());
        onChange(normalizedValue);
    }, [disabled, readOnly, onChange]);
    const { start, end } = useDirection();
    const moveThumb = useCallback((e) => {
        if (disabled || readOnly)
            return;
        if (trackRef.current) {
            const track = trackRef.current.getBoundingClientRect();
            const { clientX, clientY } = 'touches' in e ? e.touches[0] : e;
            const newPosition = orientation === 'horizontal'
                ? (clientX - track[start]) / (track[end] - track[start])
                : (clientY - track.bottom) / (track.top - track.bottom);
            const normalizedValue = Math.min(Math.max(min + (max - min) * newPosition, min), max);
            setValue(getNearestValue(normalizedValue, min, max, step));
        }
        thumbRef.current?.focus();
    }, [disabled, readOnly, step, min, max, setValue, start, end]);
    const onThumbKeyDown = useCallback((e) => {
        if ([
            'ArrowDown',
            'ArrowUp',
            'ArrowLeft',
            'ArrowRight',
            'PageUp',
            'PageDown',
            'Home',
            'End'
        ].includes(e.key))
            e.preventDefault();
        switch (e.key) {
            case 'ArrowDown':
            case `Arrow${cap(start)}`:
                setValue(value - step);
                break;
            case 'ArrowUp':
            case `Arrow${cap(end)}`:
                setValue(value + step);
                break;
            case 'PageUp':
                setValue(value + 10 * step);
                break;
            case 'PageDown':
                setValue(value - 10 * step);
                break;
            case 'Home':
                setValue(min);
                break;
            case 'End':
                setValue(max);
                break;
            default:
        }
    }, [setValue, value, step, start, end]);
    useEffect(() => {
        const onDragEnd = () => {
            setInDrag(false);
            if (inDrag)
                onDragEndProp?.();
        };
        document.addEventListener('mouseup', onDragEnd);
        document.addEventListener('touchend', onDragEnd);
        document.addEventListener('touchcancel', onDragEnd);
        if (inDrag) {
            document.addEventListener('mousemove', moveThumb);
            document.addEventListener('touchmove', moveThumb);
        }
        return () => {
            document.removeEventListener('mouseup', onDragEnd);
            document.removeEventListener('touchend', onDragEnd);
            document.removeEventListener('touchcancel', onDragEnd);
            document.removeEventListener('mousemove', moveThumb);
            document.removeEventListener('touchmove', moveThumb);
        };
    }, [inDrag, moveThumb, onDragEndProp]);
    useEffect(() => {
        if (autoFocus)
            thumbRef.current?.focus();
    }, [autoFocus]);
    const percentage = ((Number(value) - min) / (max - min)) * 100;
    const size = calculateValueLength(min, max, step);
    const Comp = (_jsxs(Flex, { "data-testid": testIds.root, ...restProps, as: StyledSlider, container: {
            alignItems: 'start',
            direction: orientation === 'horizontal' ? 'row' : 'column'
        }, orientation: orientation, status: status, disabled: disabled, readOnly: readOnly, size: size, style: {
            '--slider-value': `${percentage}%`,
            userSelect: inDrag ? 'none' : 'auto'
        }, children: [_jsxs(Grid, { as: StyledSliderTrackWrapper, container: true, children: [_jsxs(Flex, { as: StyledTrack, ref: trackRef, onMouseDown: moveThumb, container: { alignItems: 'center', justify: 'center' }, children: [_jsx(StyledBar, { showProgress: showProgress }), _jsx(StyledThumb, { ref: thumbRef, role: 'slider', tabIndex: 0, onKeyDown: onThumbKeyDown, onMouseDown: () => {
                                    setInDrag(true);
                                    onDragStart?.();
                                }, onTouchStart: (e) => {
                                    setInDrag(true);
                                    e.preventDefault(); // Prevent dispatching mouse events as some browser may do that
                                    onDragStart?.();
                                }, "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": value, "aria-orientation": orientation, "aria-readonly": readOnly || undefined, "aria-disabled": disabled || undefined, id: id, "aria-label": label?.toString(), "aria-describedby": `${id}-info`, "aria-controls": `${id}-value` })] }), Object.keys(ticks).length > 0 && (_jsx(SliderTicks, { ticks: ticks, min: min, max: max, step: step, orientation: orientation, onClick: setValue }))] }), showInput && !preview && (_jsx(NumberInput, { "data-testid": testIds.control, id: `${id}-value`, status: status, min: min, max: max, step: step, value: inputValue, showGroupSeparators: false, onChange: setInputValue, onBlur: v => setValue(getNearestValue(Number(v), min, max, step)), size: size, readOnly: readOnly, disabled: disabled, "aria-label": label?.toString() })), preview && _jsx(StyledPreview, { id: `${id}-value`, value: value, size: size })] }));
    return label ? (_jsx(FormField, { testId: testIds, label: label, labelHidden: labelHidden, id: id, info: info, status: status, required: required, disabled: disabled, additionalInfo: additionalInfo, children: Comp })) : (Comp);
});
export default withTestIds(Slider, getSliderTestIds);
//# sourceMappingURL=Slider.js.map