RangeSlider.tsx 3.4 KB
Newer Older
1
import InputRange, {Range} from 'react-input-range';
2
import React, {FunctionComponent, useCallback} from 'react';
P
Peter Pan 已提交
3
import {WithStyled, em, half, position, sameBorder, size, transitionProps} from '~/utils/style';
4

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
import styled from 'styled-components';

const height = em(20);
const railHeight = em(4);
const thumbSize = em(12);

const Wrapper = styled.div<{disabled?: boolean}>`
    height: ${height};

    .input-range {
        height: 100%;
        position: relative;

        &__label {
            display: none;
        }

P
Peter Pan 已提交
22
        --color: var(--primary-color);
P
Peter Pan 已提交
23 24

        &:hover {
P
Peter Pan 已提交
25
            --color: var(--primary-focused-color);
P
Peter Pan 已提交
26 27 28
        }

        &:active {
P
Peter Pan 已提交
29
            --color: var(--primary-active-color);
P
Peter Pan 已提交
30 31
        }

32 33 34 35
        &__track {
            cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};

            &--background {
36 37
                ${size(railHeight, '100%')}
                ${position('absolute', '50%', null, null, null)}
38
                margin-top: -${half(railHeight)};
P
Peter Pan 已提交
39
                background-color: var(--slider-rail-color);
40
                border-radius: ${half(railHeight)};
P
Peter Pan 已提交
41
                ${transitionProps('background-color')}
42 43 44 45 46
            }

            &--active {
                height: ${railHeight};
                position: absolute;
P
Peter Pan 已提交
47
                background-color: ${props => (props.disabled ? 'var(--text-lighter-color)' : 'var(--color)')};
48 49
                border-radius: ${half(railHeight)};
                outline: none;
P
Peter Pan 已提交
50
                ${transitionProps('background-color')}
51 52 53 54
            }
        }

        &__slider-container {
55
            top: -${half(`${thumbSize} - ${railHeight}`)};
56 57 58 59 60
            margin-left: -${half(thumbSize)};
        }

        &__slider {
            ${size(thumbSize)}
61 62 63
            ${props =>
                sameBorder({
                    width: em(3),
P
Peter Pan 已提交
64
                    color: props.disabled ? 'var(--text-lighter-color)' : 'var(--color)',
65 66
                    radius: half(thumbSize)
                })}
P
Peter Pan 已提交
67 68
            background-color: var(--slider-gripper-color);
            ${transitionProps(['border-color', 'background-color'])}
69 70 71 72 73 74 75 76 77 78 79
        }
    }
`;

type RangeSliderProps = {
    min?: number;
    max?: number;
    step?: number;
    value?: number;
    disabled?: boolean;
    onChange?: (value: number) => unknown;
P
Peter Pan 已提交
80
    onChangeStart?: () => unknown;
81 82 83 84 85
    onChangeComplete?: () => unknown;
};

const RangeSlider: FunctionComponent<RangeSliderProps & WithStyled> = ({
    onChange,
P
Peter Pan 已提交
86
    onChangeStart,
87 88 89 90 91 92 93 94
    onChangeComplete,
    className,
    min,
    max,
    step,
    value,
    disabled
}) => {
95
    const onChangeRange = useCallback((range: number | Range) => onChange?.(range as number), [onChange]);
96 97 98 99 100 101 102 103 104 105 106 107 108 109

    return (
        <Wrapper className={className} disabled={disabled}>
            <InputRange
                minValue={min}
                maxValue={max}
                // there may be a warning when `minValue` equals `maxValue` though `allSameValue` is set to TRUE
                // this is a bug of react-input-range
                // ignore for now
                allowSameValues
                step={step}
                disabled={disabled}
                value={value as number}
                onChange={onChangeRange}
P
Peter Pan 已提交
110
                onChangeStart={() => onChangeStart?.()}
111 112 113 114 115 116 117 118 119 120 121 122 123 124
                onChangeComplete={() => onChangeComplete?.()}
            />
        </Wrapper>
    );
};

RangeSlider.defaultProps = {
    min: 0,
    max: 100,
    step: 1,
    value: 50
};

export default RangeSlider;