RangeSlider.tsx 3.4 KB
Newer Older
1
import InputRange, {Range} from 'react-input-range';
2
import React, {FunctionComponent, useCallback} from 'react';
3 4 5 6 7 8
import {
    WithStyled,
    backgroundColor,
    em,
    half,
    position,
P
Peter Pan 已提交
9
    primaryActiveColor,
10
    primaryColor,
P
Peter Pan 已提交
11
    primaryFocusedColor,
12 13
    sameBorder,
    size,
P
Peter Pan 已提交
14
    textLighterColor
15 16
} from '~/utils/style';

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
import styled from 'styled-components';

const height = em(20);
const railHeight = em(4);
const thumbSize = em(12);
const railColor = '#DBDEEB';

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

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

        &__label {
            display: none;
        }

P
Peter Pan 已提交
35 36 37 38 39 40 41 42 43 44
        --color: ${primaryColor};

        &:hover {
            --color: ${primaryFocusedColor};
        }

        &:active {
            --color: ${primaryActiveColor};
        }

45 46 47 48
        &__track {
            cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};

            &--background {
49 50
                ${size(railHeight, '100%')}
                ${position('absolute', '50%', null, null, null)}
51 52 53 54 55 56 57 58
                margin-top: -${half(railHeight)};
                background-color: ${railColor};
                border-radius: ${half(railHeight)};
            }

            &--active {
                height: ${railHeight};
                position: absolute;
P
Peter Pan 已提交
59
                background-color: ${props => (props.disabled ? textLighterColor : 'var(--color)')};
60 61 62 63 64 65
                border-radius: ${half(railHeight)};
                outline: none;
            }
        }

        &__slider-container {
66
            top: -${half(`${thumbSize} - ${railHeight}`)};
67 68 69 70 71
            margin-left: -${half(thumbSize)};
        }

        &__slider {
            ${size(thumbSize)}
72 73 74
            ${props =>
                sameBorder({
                    width: em(3),
P
Peter Pan 已提交
75
                    color: props.disabled ? textLighterColor : 'var(--color)',
76 77
                    radius: half(thumbSize)
                })}
78 79 80 81 82 83 84 85 86 87 88 89
            background-color: ${backgroundColor};
        }
    }
`;

type RangeSliderProps = {
    min?: number;
    max?: number;
    step?: number;
    value?: number;
    disabled?: boolean;
    onChange?: (value: number) => unknown;
P
Peter Pan 已提交
90
    onChangeStart?: () => unknown;
91 92 93 94 95
    onChangeComplete?: () => unknown;
};

const RangeSlider: FunctionComponent<RangeSliderProps & WithStyled> = ({
    onChange,
P
Peter Pan 已提交
96
    onChangeStart,
97 98 99 100 101 102 103 104
    onChangeComplete,
    className,
    min,
    max,
    step,
    value,
    disabled
}) => {
105
    const onChangeRange = useCallback((range: number | Range) => onChange?.(range as number), [onChange]);
106 107 108 109 110 111 112 113 114 115 116 117 118 119

    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 已提交
120
                onChangeStart={() => onChangeStart?.()}
121 122 123 124 125 126 127 128 129 130 131 132 133 134
                onChangeComplete={() => onChangeComplete?.()}
            />
        </Wrapper>
    );
};

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

export default RangeSlider;