/
ToggleButton.tsx
129 lines (111 loc) · 2.89 KB
/
ToggleButton.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { useCallback, useState } from 'react';
import Button, { ButtonProps } from './Button';
import {
BsPrefixAndClassNameOnlyProps,
BsPrefixComponentClass,
} from './helpers';
export interface ToggleButtonProps
extends ButtonProps,
React.PropsWithChildren<BsPrefixAndClassNameOnlyProps> {
type?: 'checkbox' | 'radio';
name?: string;
checked?: boolean;
disabled?: boolean;
onChange?: React.ChangeEventHandler<HTMLInputElement>;
value: unknown;
inputRef?: React.LegacyRef<'input'>;
}
type ToggleButton = BsPrefixComponentClass<'button', ToggleButtonProps>;
const noop = () => undefined;
const propTypes = {
/**
* The `<input>` element `type`
*/
type: PropTypes.oneOf(['checkbox', 'radio']),
/**
* The HTML input name, used to group like checkboxes or radio buttons together
* semantically
*/
name: PropTypes.string,
/**
* The checked state of the input, managed by `<ToggleButtonGroup>` automatically
*/
checked: PropTypes.bool,
/**
* The disabled state of both the label and input
*/
disabled: PropTypes.bool,
/**
* A callback fired when the underlying input element changes. This is passed
* directly to the `<input>` so shares the same signature as a native `onChange` event.
*/
onChange: PropTypes.func,
/**
* The value of the input, should be unique amongst it's siblings when nested in a
* `ToggleButtonGroup`.
*/
value: PropTypes.any.isRequired,
/**
* A ref attached to the `<input>` element
* @type {ReactRef}
*/
inputRef: PropTypes.any,
};
const ToggleButton = React.forwardRef<any, ToggleButtonProps>(
(
{
children,
name,
className,
checked,
type,
onChange,
value,
disabled,
inputRef,
...props
}: ToggleButtonProps,
ref,
) => {
const [focused, setFocused] = useState(false);
const handleFocus = useCallback((e) => {
if (e.target.tagName === 'INPUT') setFocused(true);
}, []);
const handleBlur = useCallback((e) => {
if (e.target.tagName === 'INPUT') setFocused(false);
}, []);
return (
<Button
{...props}
ref={ref}
className={classNames(
className,
focused && 'focus',
disabled && 'disabled',
)}
type={undefined}
active={!!checked}
as="label"
>
<input
name={name}
type={type}
value={value as any}
ref={inputRef as any}
autoComplete="off"
checked={!!checked}
disabled={!!disabled}
onFocus={handleFocus}
onBlur={handleBlur}
onChange={onChange || noop}
/>
{children}
</Button>
);
},
);
ToggleButton.propTypes = propTypes as any;
ToggleButton.displayName = 'ToggleButton';
export default ToggleButton;