@@ -31,6 +31,9 @@ import { SearchInput } from '../../packages/circuit-ui/components/SearchInput/Se
31
31
import { Select } from '../../packages/circuit-ui/components/Select/Select.js' ;
32
32
import { SelectorGroup } from '../../packages/circuit-ui/components/SelectorGroup/SelectorGroup.js' ;
33
33
import { Tooltip } from '../../packages/circuit-ui/components/Tooltip/Tooltip.js' ;
34
+ import { IconButton } from '../../packages/circuit-ui/components/Button/IconButton.js' ;
35
+ import { ToastProvider } from '../../packages/circuit-ui/components/ToastContext/ToastContext.js' ;
36
+ import { useNotificationToast } from '../../packages/circuit-ui/components/NotificationToast/NotificationToast.js' ;
34
37
import { clsx } from '../../packages/circuit-ui/styles/clsx.js' ;
35
38
import { utilClasses } from '../../packages/circuit-ui/styles/utility.js' ;
36
39
import { slugify } from '../slugify.js' ;
@@ -77,7 +80,7 @@ export function Icons() {
77
80
78
81
const handleChange =
79
82
( setState : Dispatch < SetStateAction < string > > ) =>
80
- ( event : ChangeEvent < any > ) => {
83
+ ( event : ChangeEvent < HTMLInputElement | HTMLSelectElement > ) => {
81
84
setState ( event . target . value ) ;
82
85
} ;
83
86
@@ -113,100 +116,144 @@ export function Icons() {
113
116
114
117
return (
115
118
< Unstyled >
116
- < fieldset className = { classes . filters } >
117
- < legend className = { utilClasses . hideVisually } > Icon filters</ legend >
118
- < SearchInput
119
- label = "Search by name or keyword"
120
- placeholder = "Search..."
121
- value = { search }
122
- onChange = { handleChange ( setSearch ) }
123
- onClear = { ( ) => setSearch ( '' ) }
124
- clearLabel = "Clear"
125
- />
126
- < Select
127
- label = "Size"
128
- options = { sizeOptions }
129
- value = { size }
130
- onChange = { handleChange ( setSize ) }
131
- />
132
- < Select
133
- label = "Color"
134
- options = { colorOptions }
135
- value = { color }
136
- onChange = { handleChange ( setColor ) }
119
+ < ToastProvider >
120
+ < fieldset className = { classes . filters } >
121
+ < legend className = { utilClasses . hideVisually } > Icon filters</ legend >
122
+ < SearchInput
123
+ label = "Search by name or keyword"
124
+ placeholder = "Search..."
125
+ value = { search }
126
+ onChange = { handleChange ( setSearch ) }
127
+ onClear = { ( ) => setSearch ( '' ) }
128
+ clearLabel = "Clear"
129
+ />
130
+ < Select
131
+ label = "Size"
132
+ options = { sizeOptions }
133
+ value = { size }
134
+ onChange = { handleChange ( setSize ) }
135
+ />
136
+ < Select
137
+ label = "Color"
138
+ options = { colorOptions }
139
+ value = { color }
140
+ onChange = { handleChange ( setColor ) }
141
+ />
142
+ < SelectorGroup
143
+ label = "Scale"
144
+ options = { scaleOptions }
145
+ value = { scale }
146
+ onChange = { handleChange ( setScale ) }
147
+ />
148
+ </ fieldset >
149
+
150
+ { activeIcons . length <= 0 ? (
151
+ < Body > No icons found</ Body >
152
+ ) : (
153
+ Object . entries < IconsManifest [ 'icons' ] > (
154
+ groupBy ( activeIcons , 'category' ) ,
155
+ ) . map ( ( [ category , items ] ) => (
156
+ < section key = { category } className = { classes . category } >
157
+ < Headline as = "h2" size = "m" id = { slugify ( category ) } >
158
+ { category }
159
+ </ Headline >
160
+ < div className = { classes . list } >
161
+ { sortBy ( items , 'name' ) . map ( ( icon ) => (
162
+ < Icon
163
+ key = { `${ icon . name } -${ icon . size } ` }
164
+ icon = { icon }
165
+ scale = { scale }
166
+ color = { color }
167
+ />
168
+ ) ) }
169
+ </ div >
170
+ </ section >
171
+ ) )
172
+ ) }
173
+ </ ToastProvider >
174
+ </ Unstyled >
175
+ ) ;
176
+ }
177
+
178
+ function Icon ( {
179
+ icon,
180
+ scale,
181
+ color,
182
+ } : { icon : IconsManifest [ 'icons' ] [ number ] ; scale : string ; color : string } ) {
183
+ const { setToast } = useNotificationToast ( ) ;
184
+
185
+ const id = `${ icon . name } -${ icon . size } ` ;
186
+ const componentName = getComponentName (
187
+ icon . name ,
188
+ ) as keyof typeof iconComponents ;
189
+ const Icon = iconComponents [ componentName ] as IconComponentType ;
190
+
191
+ const copyIconURL = ( ) => {
192
+ const iconURL = `https://circuit.sumup.com/icons/v2/${ icon . name } _${ icon . size } .svg` ;
193
+ navigator . clipboard
194
+ . writeText ( iconURL )
195
+ . then ( ( ) => {
196
+ setToast ( {
197
+ variant : 'success' ,
198
+ body : `Copied the ${ componentName } (${ icon . size } ) icon URL to the clipboard.` ,
199
+ } ) ;
200
+ } )
201
+ . catch ( ( error ) => {
202
+ console . error ( error ) ;
203
+ setToast ( {
204
+ variant : 'danger' ,
205
+ body : `Failed to copy the ${ componentName } (${ icon . size } ) icon URL to the clipboard.` ,
206
+ } ) ;
207
+ } ) ;
208
+ } ;
209
+
210
+ return (
211
+ < div className = { classes . wrapper } >
212
+ < div className = { clsx ( classes [ 'icon-wrapper' ] , classes [ scale ] ) } >
213
+ < Icon
214
+ aria-labelledby = { id }
215
+ size = { icon . size }
216
+ className = { classes . icon }
217
+ style = { {
218
+ color,
219
+ backgroundColor :
220
+ color === 'var(--cui-fg-on-strong)'
221
+ ? 'var(--cui-bg-strong)'
222
+ : 'var(--cui-bg-normal)' ,
223
+ } }
137
224
/>
138
- < SelectorGroup
139
- label = "Scale"
140
- options = { scaleOptions }
141
- value = { scale }
142
- onChange = { handleChange ( setScale ) }
225
+ </ div >
226
+ < span id = { id } className = { classes . label } >
227
+ { componentName }
228
+ < span className = { classes . size } > { icon . size } </ span >
229
+ </ span >
230
+ { icon . deprecation && (
231
+ < Tooltip
232
+ type = "description"
233
+ label = { icon . deprecation }
234
+ component = { ( props ) => (
235
+ < Badge
236
+ { ...props }
237
+ tabIndex = { 0 }
238
+ variant = "warning"
239
+ className = { classes . badge }
240
+ >
241
+ Deprecated
242
+ </ Badge >
243
+ ) }
143
244
/>
144
- </ fieldset >
145
-
146
- { activeIcons . length <= 0 ? (
147
- < Body > No icons found</ Body >
148
- ) : (
149
- Object . entries < IconsManifest [ 'icons' ] > (
150
- groupBy ( activeIcons , 'category' ) ,
151
- ) . map ( ( [ category , items ] ) => (
152
- < section key = { category } className = { classes . category } >
153
- < Headline as = "h2" size = "m" id = { slugify ( category ) } >
154
- { category }
155
- </ Headline >
156
- < div className = { classes . list } >
157
- { sortBy ( items , 'name' ) . map ( ( icon ) => {
158
- const id = `${ icon . name } -${ icon . size } ` ;
159
- const componentName = getComponentName (
160
- icon . name ,
161
- ) as keyof typeof iconComponents ;
162
- const Icon = iconComponents [ componentName ] as IconComponentType ;
163
- return (
164
- < div key = { id } className = { classes . wrapper } >
165
- < div
166
- className = { clsx ( classes [ 'icon-wrapper' ] , classes [ scale ] ) }
167
- >
168
- < Icon
169
- aria-labelledby = { id }
170
- size = { icon . size }
171
- className = { classes . icon }
172
- style = { {
173
- color,
174
- backgroundColor :
175
- color === 'var(--cui-fg-on-strong)'
176
- ? 'var(--cui-bg-strong)'
177
- : 'var(--cui-bg-normal)' ,
178
- } }
179
- />
180
- </ div >
181
- < span id = { id } className = { classes . label } >
182
- { componentName }
183
- { size === 'all' && (
184
- < span className = { classes . size } > { icon . size } </ span >
185
- ) }
186
- </ span >
187
- { icon . deprecation && (
188
- < Tooltip
189
- type = "description"
190
- label = { icon . deprecation }
191
- component = { ( props ) => (
192
- < Badge
193
- { ...props }
194
- tabIndex = { 0 }
195
- variant = "warning"
196
- className = { classes . badge }
197
- >
198
- Deprecated
199
- </ Badge >
200
- ) }
201
- />
202
- ) }
203
- </ div >
204
- ) ;
205
- } ) }
206
- </ div >
207
- </ section >
208
- ) )
209
245
) }
210
- </ Unstyled >
246
+ { navigator . clipboard && (
247
+ < IconButton
248
+ variant = "tertiary"
249
+ size = "s"
250
+ icon = { iconComponents . Link }
251
+ className = { classes . copy }
252
+ onClick = { copyIconURL }
253
+ >
254
+ Copy URL
255
+ </ IconButton >
256
+ ) }
257
+ </ div >
211
258
) ;
212
259
}
0 commit comments