1+ <script >
2+ // based on suggestions from:
3+ // Inclusive Components by Heydon Pickering https://inclusive-components.design/toggle-button/
4+ // On Designing and Building Toggle Switches by Sara Soueidan https://www.sarasoueidan.com/blog/toggle-switch-design/
5+ // and this example by Scott O'hara https://codepen.io/scottohara/pen/zLZwNv
6+
7+
8+ export let label;
9+ export let design = ' inner label'
10+ export let options = [];
11+ export let fontSize = 16 ;
12+ export let value = ' off' ;
13+
14+ let checked = value === ' on' ? true : false ;
15+
16+
17+ const uniqueID = Math .floor (Math .random () * 100 )
18+
19+ function handleClick (event ){
20+ const target = event .target
21+
22+ const state = target .getAttribute (' aria-checked' )
23+
24+ checked = state === ' true' ? false : true
25+
26+ value = checked === true ? ' on' : ' off'
27+ }
28+
29+ const slugify = (str = " " ) =>
30+ str .toLowerCase ().replace (/ / g , " -" ).replace (/ \. / g , " " );
31+
32+ </script >
33+
34+ {#if design == ' inner' }
35+ <div class =" s s--inner" >
36+ <span id ={` switch-${uniqueID } ` }>{label }</span >
37+ <button
38+ role =" switch"
39+ aria-checked ={checked }
40+ aria-labelledby ={` switch-${uniqueID } ` }
41+ on:click ={handleClick }>
42+ <span >on</span >
43+ <span >off</span >
44+ </button >
45+ </div >
46+ {:else if design == ' slider' }
47+ <div class ="s s--slider" style ="font-size: {fontSize }px" >
48+ <span id ={` switch-${uniqueID } ` }>{label }</span >
49+ <button
50+ role =" switch"
51+ aria-checked ={checked }
52+ aria-labelledby ={` switch-${uniqueID } ` }
53+ on:click ={handleClick }>
54+ </button >
55+ </div >
56+ {:else }
57+ <div class =" s s--multi" >
58+ <div role =' radiogroup'
59+ class =" group-container"
60+ aria-labelledby ={` label-${uniqueID } ` }
61+ style ="font-size: {fontSize }px"
62+ id ={` group-${uniqueID } ` }>
63+ <div class ='legend' id ={` label-${uniqueID } ` }>{label }</div >
64+ {#each options as option }
65+ <input type ="radio" id ={` ${option }-${uniqueID } ` } value ={option } bind:group ={value }>
66+ <label for ={` ${option }-${uniqueID } ` }>
67+ {option }
68+ </label >
69+ {/each }
70+ </div >
71+ </div >
72+
73+ {/if }
74+
75+ <style >
76+ /* Inner Design Option */
77+ .s--inner button {
78+ padding : 0.5em ;
79+ background-color : #fff ;
80+ border : 1px solid #ccc ;
81+ }
82+ [role = ' switch' ][aria-checked = ' true' ] :first-child ,
83+ [role = ' switch' ][aria-checked = ' false' ] :last-child {
84+ display : none ;
85+ color : #fff ;
86+ }
87+
88+ .s--inner button span {
89+ user-select : none ;
90+ pointer-events :none ;
91+ padding : 0.25em ;
92+ }
93+
94+ .s--inner button :focus {
95+ outline : #0bb489 solid 1px ;
96+ }
97+
98+ /* Slider Design Option */
99+
100+ .s--slider {
101+ display : flex ;
102+ align-items : center ;
103+ }
104+
105+ .s--slider button {
106+ width : 3em ;
107+ height : 1.6em ;
108+ position : relative ;
109+ margin : 0 0 0 0.5em ;
110+ background : #ccc ;
111+ border : none ;
112+ }
113+
114+ .s--slider button ::before {
115+ content : ' ' ;
116+ position : absolute ;
117+ width : 1.3em ;
118+ height : 1.3em ;
119+ background : #fff ;
120+ top : 0.13em ;
121+ right : 1.5em ;
122+ transition : transform 0.3s ;
123+ }
124+
125+ .s--slider button [aria-checked = ' true' ]{
126+ background-color : #0bb489
127+ }
128+
129+ .s--slider button [aria-checked = ' true' ]::before {
130+ transform : translateX (1.3em );
131+ transition : transform 0.3s ;
132+ }
133+
134+ .s--slider button :focus {
135+ box-shadow : 0 0px 0px 1px #0bb489 ;
136+ }
137+
138+ /* Multi Design Option */
139+
140+ /* Based on suggestions from Sara Soueidan https://www.sarasoueidan.com/blog/toggle-switch-design/
141+ and this example from Scott O'hara https://codepen.io/scottohara/pen/zLZwNv */
142+
143+ .s--multi .group-container {
144+ border : none ;
145+ padding : 0 ;
146+ white-space : nowrap ;
147+ }
148+
149+ /* .s--multi legend {
150+ font-size: 2px;
151+ opacity: 0;
152+ position: absolute;
153+ } */
154+
155+ .s--multi label {
156+ display : inline-block ;
157+ line-height : 1.6 ;
158+ position : relative ;
159+ z-index : 2 ;
160+ }
161+
162+ .s--multi input {
163+ opacity : 0 ;
164+ position : absolute ;
165+ }
166+
167+ .s--multi label :first-of-type {
168+ padding-right : 5em ;
169+ }
170+
171+ .s--multi label :last-child {
172+ margin-left : -5em ;
173+ padding-left : 5em ;
174+ }
175+
176+ .s--multi :focus-within label :first-of-type :after {
177+ box-shadow : 0 0px 8px #0bb489 ;
178+ border-radius : 1.5em ;
179+ }
180+
181+
182+
183+ /* making the switch UI. */
184+ .s--multi label :first-of-type :before ,
185+ .s--multi label :first-of-type :after {
186+ content : " " ;
187+ height : 1.25em ;
188+ overflow : hidden ;
189+ pointer-events : none ;
190+ position : absolute ;
191+ vertical-align : middle ;
192+ }
193+
194+ .s--multi label :first-of-type :before {
195+ border-radius : 100% ;
196+ z-index : 2 ;
197+ position : absolute ;
198+ width : 1.2em ;
199+ height : 1.2em ;
200+ background : #fff ;
201+ top : 0.2em ;
202+ right : 1.2em ;
203+ transition : transform 0.3s ;
204+ }
205+
206+ .s--multi label :first-of-type :after {
207+ background : #0bb489 ;
208+ border-radius : 1em ;
209+ margin : 0 1em ;
210+ transition : background .2s ease-in-out ;
211+ width : 3em ;
212+ height : 1.6em ;
213+ }
214+
215+ .s--multi input :first-of-type:checked ~ label :first-of-type :after {
216+ background : #ccc ;
217+ }
218+
219+ .s--multi input :first-of-type:checked ~ label :first-of-type :before {
220+ transform : translateX (-1.4em );
221+ }
222+
223+ .s--multi input :last-of-type:checked ~ label :last-of-type {
224+ z-index : 1 ;
225+ }
226+
227+ .s--multi input :focus {
228+ box-shadow : 0 0px 8px #0bb489 ;
229+ border-radius : 1.5em ;
230+ }
231+
232+ /* gravy */
233+
234+ /* Inner Design Option */
235+ [role = ' switch' ][aria-checked = ' true' ] :first-child ,
236+ [role = ' switch' ][aria-checked = ' false' ] :last-child {
237+ border-radius : 0.25em ;
238+ background : #0bb489 ;
239+ display : inline-block ;
240+ }
241+
242+ .s--inner button :focus {
243+ box-shadow : 0 0px 8px #0bb489 ;
244+ border-radius : 0.1em ;
245+ }
246+
247+ /* Slider Design Option */
248+ .s--slider button {
249+ border-radius : 1.5em ;
250+ }
251+
252+ .s--slider button ::before {
253+ border-radius : 100% ;
254+ }
255+
256+ .s--slider button :focus {
257+ box-shadow : 0 0px 8px #0bb489 ;
258+ border-radius : 1.5em ;
259+ }
260+
261+
262+ </style >
0 commit comments