Checkbox.tsx 3.7 KB
Newer Older
1
import React, {FunctionComponent, useCallback, useEffect, useState} from 'react';
2 3 4
import {
    WithStyled,
    backgroundColor,
5
    darken,
6
    ellipsis,
7 8 9
    em,
    half,
    lighten,
10
    math,
11 12 13 14 15 16 17
    position,
    primaryColor,
    sameBorder,
    size,
    textInvertColor,
    textLighterColor,
    transitionProps
18 19
} from '~/utils/style';

20 21
import styled from 'styled-components';

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
const height = em(20);
const checkSize = em(16);
const checkMark =
    // eslint-disable-next-line
    'data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjgiIHZpZXdCb3g9IjAgMCAxMSA4IiB3aWR0aD0iMTEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTkuNDc5NDI3MDggMTAuMTg3NWgtNS4yNXYtMS4zMTI1aDMuOTM3bC4wMDA1LTcuODc1aDEuMzEyNXoiIGZpbGw9IiNmNWY1ZjUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgdHJhbnNmb3JtPSJtYXRyaXgoLjcwNzEwNjc4IC43MDcxMDY3OCAtLjcwNzEwNjc4IC43MDcxMDY3OCA0Ljk2Mjk5NCAtNi4yMDg0NCkiLz48L3N2Zz4=';

const Wrapper = styled.label<{disabled?: boolean}>`
    position: relative;
    display: inline-flex;
    align-items: flex-start;
    cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};
`;

const Input = styled.input.attrs<{disabled?: boolean}>(props => ({
    type: 'checkbox',
    disabled: !!props.disabled
}))`
39 40
    ${size(0)}
    ${position('absolute', 0, null, null, 0)}
41 42 43 44 45 46 47 48
    opacity: 0;
    pointer-events: none;
`;

const Inner = styled.div<{checked?: boolean; size?: string; disabled?: boolean}>`
    color: ${props => (props.checked ? textInvertColor : 'transparent')};
    flex-shrink: 0;
    ${props => size(math(`${checkSize} * ${props.size === 'small' ? 0.875 : 1}`))}
49
    margin: ${half(`${height} - ${checkSize}`)} 0;
50
    margin-right: ${em(10)};
51
    ${props => sameBorder({color: props.disabled || !props.checked ? textLighterColor : primaryColor})};
52 53 54 55
    background-color: ${props =>
        props.disabled
            ? props.checked
                ? textLighterColor
56
                : lighten(1 / 3, textLighterColor)
57 58 59 60 61 62 63 64
            : props.checked
            ? primaryColor
            : backgroundColor};
    background-image: ${props => (props.checked ? `url("${checkMark}")` : 'none')};
    background-repeat: no-repeat;
    background-position: center center;
    background-size: ${em(10)} ${em(8)};
    position: relative;
65
    ${transitionProps(['border-color', 'background-color', 'color'])}
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

    ${Wrapper}:hover > & {
        border-color: ${props =>
            props.disabled ? textLighterColor : props.checked ? primaryColor : darken(0.1, textLighterColor)};
    }
`;

const Content = styled.div<{disabled?: boolean}>`
    line-height: ${height};
    flex-grow: 1;
    ${props => (props.disabled ? `color: ${textLighterColor};` : '')}
    ${ellipsis()}
`;

type CheckboxProps = {
    value?: boolean;
    onChange?: (value: boolean) => unknown;
    size?: 'small';
P
Peter Pan 已提交
84
    title?: string;
85 86 87 88 89 90 91 92 93
    disabled?: boolean;
};

const Checkbox: FunctionComponent<CheckboxProps & WithStyled> = ({
    value,
    children,
    size,
    disabled,
    className,
P
Peter Pan 已提交
94
    title,
95 96 97
    onChange
}) => {
    const [checked, setChecked] = useState(!!value);
98
    useEffect(() => setChecked(!!value), [setChecked, value]);
99 100 101 102 103 104 105 106 107 108
    const onChangeInput = useCallback(
        (e: React.ChangeEvent<HTMLInputElement>) => {
            if (disabled) {
                return;
            }
            setChecked(e.target.checked);
            onChange?.(e.target.checked);
        },
        [disabled, onChange]
    );
109 110

    return (
P
Peter Pan 已提交
111
        <Wrapper disabled={disabled} className={className} title={title}>
112 113 114 115 116 117 118 119
            <Input onChange={onChangeInput} checked={checked} disabled={disabled} />
            <Inner checked={checked} size={size} disabled={disabled} />
            <Content disabled={disabled}>{children}</Content>
        </Wrapper>
    );
};

export default Checkbox;