11import styled from '@emotion/styled' ;
22
3- import * as ChonkSwitch from './switch. chonk' ;
3+ import { chonkFor } from 'sentry/components/core/ chonk' ;
44
55export interface SwitchProps
66 extends Omit < React . InputHTMLAttributes < HTMLInputElement > , 'size' | 'type' | 'onClick' > {
77 ref ?: React . Ref < HTMLInputElement > ;
88 size ?: 'sm' | 'lg' ;
99}
1010
11+ const toggleWrapperSize = {
12+ sm : { width : 36 , height : 20 } ,
13+ lg : { width : 40 , height : 24 } ,
14+ } ;
15+
16+ const toggleButtonSize = {
17+ sm : { width : 20 , height : 20 , icon : 14 , iconOffset : 2 } ,
18+ lg : { width : 24 , height : 24 , icon : 16 , iconOffset : 3 } ,
19+ } ;
20+
21+ /** We inject hex colors as background image, which requires escaping the hex characters */
22+ function urlEscapeHex ( hex : string ) {
23+ return hex . replace ( / # / g, '%23' ) ;
24+ }
25+
26+ const NativeHiddenCheckbox = styled ( 'input' ) < {
27+ nativeSize : NonNullable < SwitchProps [ 'size' ] > ;
28+ } > `
29+ position: absolute;
30+ opacity: 0;
31+ top: 0;
32+ left: 0;
33+ width: 100%;
34+ height: 100%;
35+ cursor: pointer;
36+
37+ &:focus-visible + div {
38+ ${ p => p . theme . focusRing ( ) } ;
39+ }
40+
41+ + div {
42+ border-radius: ${ p => p . theme . radius . sm } ;
43+ pointer-events: none;
44+
45+ background: ${ p => p . theme . colors . surface200 } ;
46+ border-top: 3px solid ${ p => p . theme . colors . surface100 } ;
47+ border-right: 1px solid ${ p => p . theme . colors . surface100 } ;
48+ border-bottom: 1px solid ${ p => p . theme . colors . surface100 } ;
49+ border-left: 1px solid ${ p => p . theme . colors . surface100 } ;
50+ transition: all 100ms ease-in-out;
51+
52+ > div {
53+ border-radius: ${ p => p . theme . radius . sm } ;
54+ background: ${ p => p . theme . colors . surface500 } ;
55+ border: 1px solid ${ p => p . theme . colors . surface100 } ;
56+
57+ width: ${ p => toggleButtonSize [ p . nativeSize ] . width } px;
58+ height: ${ p => toggleButtonSize [ p . nativeSize ] . height } px;
59+ position: absolute;
60+ top: 0;
61+ left: 0;
62+ transform: translateY(-1px);
63+ transition:
64+ all ${ p => p . theme . motion . smooth . moderate } ,
65+ transform ${ p => p . theme . motion . exit . slow } ;
66+
67+ &:after {
68+ /** The icon is not clickable */
69+ pointer-events: none;
70+ position: absolute;
71+ content: '';
72+ display: block;
73+ width: ${ p => toggleButtonSize [ p . nativeSize ] . icon } px;
74+ height: ${ p => toggleButtonSize [ p . nativeSize ] . icon } px;
75+ top: ${ p => toggleButtonSize [ p . nativeSize ] . iconOffset } px;
76+ left: ${ p => toggleButtonSize [ p . nativeSize ] . iconOffset } px;
77+ background-repeat: no-repeat;
78+ background-size: ${ p => toggleButtonSize [ p . nativeSize ] . icon } px
79+ ${ p => toggleButtonSize [ p . nativeSize ] . icon } px;
80+ transition: transform ${ p => p . theme . motion . snap . slow } ;
81+ /* stylelint-disable */
82+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path fill="${ p =>
83+ urlEscapeHex (
84+ p . theme . tokens . content . muted
85+ ) } " d="M5.03 3.97a.75.75 0 0 0-1.06 1.06L6.94 8l-2.97 2.97a.75.75 0 1 0 1.06 1.06L8 9.06l2.97 2.97a.75.75 0 1 0 1.06-1.06L9.06 8l2.97-2.97a.75.75 0 0 0-1.06-1.06L8 6.94 5.03 3.97Z" clip-rule="evenodd"/></svg>');
86+ /* stylelint-enable */
87+ }
88+ }
89+ }
90+
91+ &:checked + div {
92+ background: ${ p => p . theme . colors . chonk . blue400 } ;
93+ border-top: 3px solid ${ p => chonkFor ( p . theme , p . theme . colors . chonk . blue400 ) } ;
94+ border-right: 1px solid ${ p => chonkFor ( p . theme , p . theme . colors . chonk . blue400 ) } ;
95+ border-bottom: 1px solid ${ p => chonkFor ( p . theme , p . theme . colors . chonk . blue400 ) } ;
96+ border-left: 1px solid ${ p => chonkFor ( p . theme , p . theme . colors . chonk . blue400 ) } ;
97+
98+ > div {
99+ background: ${ p => p . theme . colors . surface500 } ;
100+ border: 1px solid ${ p => chonkFor ( p . theme , p . theme . colors . chonk . blue400 ) } ;
101+ transform: translateY(-1px)
102+ translateX(
103+ ${ p =>
104+ toggleWrapperSize [ p . nativeSize ] . width -
105+ toggleButtonSize [ p . nativeSize ] . width } px
106+ );
107+
108+ &:after {
109+ /* stylelint-disable */
110+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path fill="${ p =>
111+ urlEscapeHex (
112+ p . theme . tokens . content . accent
113+ ) } " fill-rule="evenodd" d="M13.53 4.22c.3.3.3.77 0 1.06l-6.5 6.5a.75.75 0 0 1-1.08-.02l-3.5-3.75A.75.75 0 0 1 3.55 7l2.97 3.18 5.95-5.95c.3-.3.77-.3 1.06 0Z" clip-rule="evenodd"/></svg>');
114+ /* stylelint-enable */
115+ }
116+ }
117+ }
118+
119+ &:disabled {
120+ cursor: not-allowed;
121+
122+ + div {
123+ opacity: 0.6;
124+
125+ > div {
126+ transform: translateY(0px) translateX(0px);
127+ }
128+ }
129+ }
130+
131+ &:checked:disabled + div > div {
132+ transform: translateY(0px) translateX(16px);
133+ }
134+ ` ;
135+
136+ const FakeCheckbox = styled ( 'div' ) < {
137+ size : NonNullable < SwitchProps [ 'size' ] > ;
138+ } > `
139+ width: ${ p => toggleWrapperSize [ p . size ] . width } px;
140+ height: ${ p => toggleWrapperSize [ p . size ] . height } px;
141+ ` ;
142+
143+ const FakeCheckboxButton = styled ( 'div' ) `` ;
144+
11145export function Switch ( { ref, size = 'sm' , ...props } : SwitchProps ) {
12146 return (
13147 < SwitchWrapper >
@@ -28,9 +162,3 @@ const SwitchWrapper = styled('div')`
28162 display: inline-flex;
29163 justify-content: flex-start;
30164` ;
31-
32- const NativeHiddenCheckbox = ChonkSwitch . ChonkNativeHiddenCheckbox ;
33-
34- const FakeCheckbox = ChonkSwitch . ChonkFakeCheckbox ;
35-
36- const FakeCheckboxButton = ChonkSwitch . ChonkFakeCheckboxButton ;
0 commit comments