Skip to content

Commit d9278b3

Browse files
committed
refactor(form): Created SwitchTrack and InputToggleIcon components
1 parent 517f199 commit d9278b3

File tree

5 files changed

+210
-34
lines changed

5 files changed

+210
-34
lines changed

packages/form/src/toggle/InputToggle.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { bem } from "@react-md/utils";
1414
import { Label } from "../label/Label";
1515
import { useFocusState } from "../useFocusState";
1616
import { ToggleContainer } from "./ToggleContainer";
17+
import { InputToggleIcon } from "./InputToggleIcon";
1718

1819
/**
1920
* The props for a checkbox or radio input element.
@@ -261,20 +262,16 @@ export const InputToggle = forwardRef<HTMLInputElement, Props>(
261262
onBlur={onBlur}
262263
className={cn(block("input"), inputClassName)}
263264
/>
264-
<span
265+
<InputToggleIcon
266+
circle={!disableIconOverlay && type === "radio"}
267+
disabled={disabled}
268+
overlay={!disableIconOverlay}
269+
indeterminate={indeterminate}
265270
style={iconStyle}
266-
className={cn(
267-
block("icon", {
268-
circle: !disableIconOverlay && type === "radio",
269-
disabled,
270-
overlay: !disableIconOverlay,
271-
indeterminate,
272-
}),
273-
iconClassName
274-
)}
271+
className={iconClassName}
275272
>
276273
{icon}
277-
</span>
274+
</InputToggleIcon>
278275
{ripples}
279276
{children}
280277
</span>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React, { forwardRef, HTMLAttributes, ReactElement } from "react";
2+
import cn from "classnames";
3+
import { bem } from "@react-md/utils";
4+
5+
/**
6+
* @remarks \@since 2.8.0
7+
*/
8+
export interface InputToggleIconProps extends HTMLAttributes<HTMLSpanElement> {
9+
/**
10+
* Boolean if the icon should use circle styles. This should normally be
11+
* enabled for radio input types.
12+
*/
13+
circle?: boolean;
14+
15+
/**
16+
* Boolean if the disabled styles should be applied.
17+
*/
18+
disabled?: boolean;
19+
20+
/**
21+
* Boolean if using an overlay for the different icon states.
22+
*/
23+
overlay?: boolean;
24+
25+
/**
26+
* Boolean if the icon should gain the checked state.
27+
*/
28+
checked?: boolean;
29+
30+
/**
31+
* Boolean if using the indeterminate checkbox state.
32+
*/
33+
indeterminate?: boolean;
34+
}
35+
36+
const styles = bem("rmd-toggle");
37+
38+
/**
39+
* @remarks \@since 2.8.0
40+
*/
41+
export const InputToggleIcon = forwardRef<
42+
HTMLSpanElement,
43+
InputToggleIconProps
44+
>(function InputToggleIcon(
45+
{
46+
circle = false,
47+
disabled = false,
48+
overlay = false,
49+
checked = false,
50+
indeterminate = false,
51+
className,
52+
children,
53+
...props
54+
}: InputToggleIconProps,
55+
ref
56+
): ReactElement | null {
57+
return (
58+
<span
59+
{...props}
60+
ref={ref}
61+
className={cn(
62+
styles("icon", {
63+
circle,
64+
disabled,
65+
overlay,
66+
checked,
67+
indeterminate,
68+
}),
69+
className
70+
)}
71+
>
72+
{children}
73+
</span>
74+
);
75+
});

packages/form/src/toggle/Switch.tsx

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import React, {
66
} from "react";
77
import cn from "classnames";
88
import { TextIconSpacing } from "@react-md/icon";
9-
import { bem } from "@react-md/utils";
109

1110
import { Label } from "../label/Label";
1211
import { ToggleContainer } from "./ToggleContainer";
12+
import { SwitchTrack } from "./SwitchTrack";
1313

1414
export interface SwitchProps
1515
extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "readOnly"> {
@@ -97,8 +97,6 @@ export interface SwitchProps
9797
children?: ReactNode;
9898
}
9999

100-
const block = bem("rmd-switch");
101-
102100
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
103101
{
104102
style,
@@ -137,32 +135,24 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
137135
className={labelClassName}
138136
htmlFor={id}
139137
error={error}
140-
disabled={
141-
typeof labelDisabled === "boolean" ? labelDisabled : disabled
142-
}
138+
disabled={labelDisabled ?? disabled}
143139
>
144140
{label}
145141
</Label>
146142
}
147143
iconAfter={!iconAfter}
148144
>
149-
<span style={trackStyle} className={cn(block(), trackClassName)}>
150-
<input
151-
{...props}
152-
ref={ref}
153-
type="checkbox"
154-
className={cn(block("input"))}
155-
disabled={disabled}
156-
/>
157-
<label
158-
htmlFor={id}
159-
aria-hidden
160-
style={ballStyle}
161-
className={cn(block("ball"), ballClassName)}
162-
>
163-
{children}
164-
</label>
165-
</span>
145+
<SwitchTrack
146+
{...props}
147+
ref={ref}
148+
style={trackStyle}
149+
className={trackClassName}
150+
disabled={disabled}
151+
ballStyle={ballStyle}
152+
ballClassName={ballClassName}
153+
>
154+
{children}
155+
</SwitchTrack>
166156
</TextIconSpacing>
167157
</ToggleContainer>
168158
);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import React, {
2+
CSSProperties,
3+
forwardRef,
4+
HTMLAttributes,
5+
InputHTMLAttributes,
6+
} from "react";
7+
import cn from "classnames";
8+
import { bem, PropsWithRef } from "@react-md/utils";
9+
10+
const styles = bem("rmd-switch");
11+
12+
/** @remarks \@since 2.8.0 */
13+
export interface SwitchTrackProps
14+
extends InputHTMLAttributes<HTMLInputElement> {
15+
/**
16+
* If an `id` is provided, the track will contain a checkbox input element and
17+
* render the "ball" as a `<label>`. If the `id` is omitted, no input element
18+
* will be rendered and the "ball" will be rendered as a `<span>`.
19+
*
20+
* Basically only omit the `id` if this is used in another accessible widget
21+
* like `menuitemcheckbox`.
22+
*/
23+
id?: string;
24+
25+
/**
26+
* An optional style object to provide to the ball.
27+
*/
28+
ballStyle?: CSSProperties;
29+
30+
/**
31+
* An optional class name to provide to the ball.
32+
*/
33+
ballClassName?: string;
34+
35+
/**
36+
* Any additional props and optional ref to provide to the track itself since
37+
* all the props are normally provided to the `<input>` element instead.
38+
*/
39+
containerProps?: PropsWithRef<
40+
HTMLAttributes<HTMLSpanElement>,
41+
HTMLSpanElement
42+
>;
43+
}
44+
45+
/**
46+
* This is most likely an internal only component that is used to render the
47+
* switch element either as a checkbox or in the `MenuItemSwitch` component.
48+
*
49+
* @remarks \@since 2.8.0
50+
*/
51+
export const SwitchTrack = forwardRef<HTMLInputElement, SwitchTrackProps>(
52+
function SwitchTrack(
53+
{
54+
id,
55+
disabled = false,
56+
checked = false,
57+
className,
58+
ballStyle,
59+
ballClassName,
60+
containerProps,
61+
children,
62+
...props
63+
},
64+
ref
65+
) {
66+
return (
67+
<span
68+
{...containerProps}
69+
className={cn(styles(), containerProps?.className)}
70+
>
71+
{id && (
72+
<>
73+
<input
74+
{...props}
75+
id={id}
76+
ref={ref}
77+
type="checkbox"
78+
className={cn(styles("input"), className)}
79+
disabled={disabled}
80+
/>
81+
<label
82+
htmlFor={id}
83+
aria-hidden
84+
style={ballStyle}
85+
className={cn(styles("ball"), ballClassName)}
86+
>
87+
{children}
88+
</label>
89+
</>
90+
)}
91+
{!id && (
92+
<span
93+
style={ballStyle}
94+
className={cn(styles("ball", { checked }), ballClassName)}
95+
/>
96+
)}
97+
</span>
98+
);
99+
}
100+
);

packages/form/src/toggle/_mixins.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,16 @@
158158
@include rmd-form-theme(top, toggle-dense-inset);
159159
@include rmd-form-theme(height, indeterminate-dense-height);
160160
}
161+
162+
&--checked {
163+
@if $rmd-toggle-active-color != $rmd-toggle-inactive-color {
164+
@include rmd-icon-theme-update-var(color, $rmd-toggle-active-color);
165+
}
166+
167+
&::before {
168+
opacity: 0;
169+
}
170+
}
161171
}
162172

163173
/// Updates the checkbox and radio components to have a dense theme by updating
@@ -325,6 +335,10 @@
325335
width: $rmd-switch-ball-size;
326336
z-index: 1;
327337
}
338+
339+
&--checked {
340+
@include rmd-switch-ball-checked;
341+
}
328342
}
329343

330344
/// @access private

0 commit comments

Comments
 (0)