/
Switch.tsx
155 lines (138 loc) · 3.79 KB
/
Switch.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import type { CSSProperties, InputHTMLAttributes, ReactNode } from "react";
import { forwardRef } from "react";
import cn from "classnames";
import { TextIconSpacing } from "@react-md/icon";
import { Label } from "../label/Label";
import { ToggleContainer } from "./ToggleContainer";
import { SwitchTrack } from "./SwitchTrack";
export interface SwitchProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "readOnly"> {
/**
* The id for the radio or checkbox. This is required for a11y and will be
* used as the `for` attribute if the `label` prop is provided.
*/
id: string;
/**
* An optional style to apply to the switch's ball.
*/
ballStyle?: CSSProperties;
/**
* An optional className to apply to the switch's ball.
*/
ballClassName?: string;
/**
* An optional style to apply to the switch's track (this is the
* `<input type="checkbox">` used behind the scenes).
*/
trackStyle?: CSSProperties;
/**
* An optional className to apply to the switch's track (this is the
* `<input type="checkbox">` used behind the scenes).
*/
trackClassName?: string;
/**
* Boolean if the input toggle is currently errored. This will update the
* label and the input to gain error colors.
*/
error?: boolean;
/**
* Boolean if the container element should be rendered as `inline-flex`
* instead of `flex`.
*/
inline?: boolean;
/**
* Boolean if the label should be stacked above/below the input toggle instead
* of inline.
*/
stacked?: boolean;
/**
* An optional label to display with the input. If this prop is omitted and
* you aren't adding a custom `<label>` anywhere else, you **should** apply an
* `aria-label` or `aria-labelledby` for a11y.
*/
label?: ReactNode;
/**
* An optional style to apply to the `<label>` when the `label` prop is used.
*/
labelStyle?: CSSProperties;
/**
* An optional className to apply to the `<label>` when the `label` prop is
* used.
*/
labelClassName?: string;
/**
* An optional boolean if the label should gain the disabled style. When this
* is `undefined`, the `disabled` prop will be used instead. This is really
* just useful when you want to disable the switch from being toggled while
* some async action is being called, but not changing styles during the wait.
*/
labelDisabled?: boolean;
/**
* Boolean if the input toggle should appear after the label instead of
* before.
*/
iconAfter?: boolean;
/**
* Any optional children that should be displayed within the switch's ball.
*/
children?: ReactNode;
}
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
{
style,
className,
ballStyle,
ballClassName,
trackStyle,
trackClassName,
label,
labelStyle,
labelClassName,
labelDisabled,
error = false,
disabled = false,
stacked = false,
inline = false,
iconAfter = false,
children,
...props
},
ref
) {
const { id } = props;
return (
<ToggleContainer
style={style}
className={cn("rmd-switch-container", className)}
inline={inline}
stacked={stacked}
>
<TextIconSpacing
icon={
<Label
style={labelStyle}
className={labelClassName}
htmlFor={id}
error={error}
disabled={labelDisabled ?? disabled}
>
{label}
</Label>
}
iconAfter={!iconAfter}
>
<SwitchTrack
{...props}
ref={ref}
style={trackStyle}
className={trackClassName}
disabled={disabled}
ballStyle={ballStyle}
ballClassName={ballClassName}
>
{children}
</SwitchTrack>
</TextIconSpacing>
</ToggleContainer>
);
});