1
1
import * as React from 'react' ;
2
- import { VirtuosoGrid } from 'react-virtuoso' ;
3
2
import { styled } from '@mui/material/styles' ;
4
3
import MuiPaper from '@mui/material/Paper' ;
5
4
import copy from 'clipboard-copy' ;
@@ -94,21 +93,20 @@ function selectNode(node) {
94
93
selection . addRange ( range ) ;
95
94
}
96
95
96
+ const iconWidth = 35 ;
97
+
97
98
const StyledIcon = styled ( 'span' ) ( ( { theme } ) => ( {
98
99
display : 'inline-flex' ,
99
100
flexDirection : 'column' ,
100
101
color : theme . palette . text . secondary ,
101
102
margin : '0 4px' ,
102
103
'& > div' : {
103
- display : 'flex' ,
104
- } ,
105
- '& > div > *' : {
106
104
flexGrow : 1 ,
107
105
fontSize : '.6rem' ,
108
106
overflow : 'hidden' ,
109
107
textOverflow : 'ellipsis' ,
110
108
textAlign : 'center' ,
111
- width : 0 ,
109
+ width : `calc( ${ iconWidth } px + ${ theme . spacing ( 2 ) } * 2 + 2px)` ,
112
110
} ,
113
111
} ) ) ;
114
112
@@ -117,6 +115,7 @@ const StyledSvgIcon = styled(SvgIcon)(({ theme }) => ({
117
115
cursor : 'pointer' ,
118
116
color : theme . palette . text . primary ,
119
117
border : '1px solid transparent' ,
118
+ fontSize : iconWidth ,
120
119
borderRadius : '12px' ,
121
120
transition : theme . transitions . create ( [ 'background-color' , 'box-shadow' ] , {
122
121
duration : theme . transitions . duration . shortest ,
@@ -129,56 +128,64 @@ const StyledSvgIcon = styled(SvgIcon)(({ theme }) => ({
129
128
} ,
130
129
} ) ) ;
131
130
132
- const ListWrapper = React . forwardRef ( ( { style, children, ...props } , ref ) => {
131
+ const handleIconClick = ( event ) => {
132
+ const { iconName, iconTheme } = event . currentTarget . dataset ;
133
+
134
+ if ( Math . random ( ) < 0.1 ) {
135
+ window . gtag ( 'event' , 'material-icons' , {
136
+ eventAction : 'click' ,
137
+ eventLabel : iconName ,
138
+ } ) ;
139
+ window . gtag ( 'event' , 'material-icons-theme' , {
140
+ eventAction : 'click' ,
141
+ eventLabel : iconTheme ,
142
+ } ) ;
143
+ }
144
+ } ;
145
+
146
+ function handleLabelClick ( event ) {
147
+ selectNode ( event . currentTarget ) ;
148
+ }
149
+
150
+ function Icon ( props ) {
151
+ const { icon, onOpenClick } = props ;
152
+ /* eslint-disable jsx-a11y/click-events-have-key-events */
133
153
return (
134
- < div
135
- ref = { ref }
136
- { ...props }
137
- style = { { display : 'flex' , flexWrap : 'wrap' , ...style } }
154
+ < StyledIcon
155
+ key = { icon . importName }
156
+ onClick = { handleIconClick }
157
+ data-icon-theme = { icon . theme }
158
+ data-icon-name = { icon . name }
138
159
>
139
- { children }
140
- </ div >
160
+ < StyledSvgIcon
161
+ component = { icon . Component }
162
+ tabIndex = { - 1 }
163
+ onClick = { onOpenClick }
164
+ title = { icon . importName }
165
+ />
166
+ { /* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- TODO: a11y */ }
167
+ < div onClick = { handleLabelClick } > { icon . importName } </ div >
168
+ { /* eslint-enable jsx-a11y/click-events-have-key-events */ }
169
+ </ StyledIcon >
141
170
) ;
142
- } ) ;
171
+ }
143
172
144
- function Icon ( handleOpenClick ) {
145
- return function itemContent ( _ , icon ) {
146
- const handleIconClick = ( ) => {
147
- if ( Math . random ( ) < 0.1 ) {
148
- window . gtag ( 'event' , 'material-icons' , {
149
- eventAction : 'click' ,
150
- eventLabel : icon . name ,
151
- } ) ;
152
- window . gtag ( 'event' , 'material-icons-theme' , {
153
- eventAction : 'click' ,
154
- eventLabel : icon . theme ,
155
- } ) ;
156
- }
157
- } ;
173
+ const Icons = React . memo ( function Icons ( props ) {
174
+ const { icons, handleOpenClick } = props ;
158
175
159
- const handleLabelClick = ( event ) => {
160
- selectNode ( event . currentTarget ) ;
161
- } ;
176
+ return (
177
+ < div >
178
+ { icons . map ( ( icon ) => (
179
+ < Icon key = { icon . importName } icon = { icon } onOpenClick = { handleOpenClick } />
180
+ ) ) }
181
+ </ div >
182
+ ) ;
183
+ } ) ;
162
184
163
- return (
164
- /* eslint-disable jsx-a11y/click-events-have-key-events */
165
- < StyledIcon key = { icon . importName } onClick = { handleIconClick } >
166
- < StyledSvgIcon
167
- component = { icon . Component }
168
- fontSize = "large"
169
- tabIndex = { - 1 }
170
- onClick = { handleOpenClick }
171
- title = { icon . importName }
172
- />
173
- < div >
174
- { /* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- TODO: a11y */ }
175
- < div onClick = { handleLabelClick } > { icon . importName } </ div >
176
- </ div >
177
- { /* eslint-enable jsx-a11y/click-events-have-key-events */ }
178
- </ StyledIcon >
179
- ) ;
180
- } ;
181
- }
185
+ Icons . propTypes = {
186
+ handleOpenClick : PropTypes . func . isRequired ,
187
+ icons : PropTypes . array . isRequired ,
188
+ } ;
182
189
183
190
const ImportLink = styled ( Link ) ( ( { theme } ) => ( {
184
191
textAlign : 'right' ,
@@ -439,7 +446,14 @@ DialogDetails.propTypes = {
439
446
selectedIcon : PropTypes . object ,
440
447
} ;
441
448
449
+ const Form = styled ( 'form' ) ( {
450
+ position : 'sticky' ,
451
+ top : 80 ,
452
+ } ) ;
453
+
442
454
const Paper = styled ( MuiPaper ) ( ( { theme } ) => ( {
455
+ position : 'sticky' ,
456
+ top : 80 ,
443
457
display : 'flex' ,
444
458
alignItems : 'center' ,
445
459
marginBottom : theme . spacing ( 2 ) ,
@@ -525,30 +539,26 @@ export default function SearchIcons() {
525
539
setSelectedIcon ( '' ) ;
526
540
} , [ setSelectedIcon ] ) ;
527
541
528
- const deferredQuery = React . useDeferredValue ( query ) ;
529
- const deferredTheme = React . useDeferredValue ( theme ) ;
530
-
531
- const isPending = query !== deferredQuery || theme !== deferredTheme ;
532
-
533
542
const icons = React . useMemo ( ( ) => {
534
- const keys =
535
- deferredQuery === ''
536
- ? null
537
- : searchIndex . search ( deferredQuery , { limit : 3000 } ) ;
543
+ const keys = query === '' ? null : searchIndex . search ( query , { limit : 3000 } ) ;
538
544
return ( keys === null ? allIcons : keys . map ( ( key ) => allIconsMap [ key ] ) ) . filter (
539
- ( icon ) => deferredTheme === icon . theme ,
545
+ ( icon ) => theme === icon . theme ,
540
546
) ;
541
- } , [ deferredQuery , deferredTheme ] ) ;
547
+ } , [ query , theme ] ) ;
548
+
549
+ const deferredIcons = React . useDeferredValue ( icons ) ;
550
+
551
+ const isPending = deferredIcons !== icons ;
542
552
543
553
React . useEffect ( ( ) => {
544
554
// Keep track of the no results so we can add synonyms in the future.
545
- if ( deferredQuery . length >= 4 && icons . length === 0 ) {
555
+ if ( query . length >= 4 && icons . length === 0 ) {
546
556
window . gtag ( 'event' , 'material-icons' , {
547
557
eventAction : 'no-results' ,
548
- eventLabel : deferredQuery ,
558
+ eventLabel : query ,
549
559
} ) ;
550
560
}
551
- } , [ deferredQuery , icons . length ] ) ;
561
+ } , [ query , icons . length ] ) ;
552
562
553
563
const dialogSelectedIcon = useLatest (
554
564
selectedIcon ? allIconsMap [ selectedIcon ] : null ,
@@ -557,7 +567,7 @@ export default function SearchIcons() {
557
567
return (
558
568
< Grid container sx = { { minHeight : 500 } } >
559
569
< Grid item xs = { 12 } sm = { 3 } >
560
- < form >
570
+ < Form >
561
571
< Typography fontWeight = { 500 } sx = { { mb : 1 } } >
562
572
Filter the style
563
573
</ Typography >
@@ -578,7 +588,7 @@ export default function SearchIcons() {
578
588
} ,
579
589
) }
580
590
</ RadioGroup >
581
- </ form >
591
+ </ Form >
582
592
</ Grid >
583
593
< Grid item xs = { 12 } sm = { 9 } >
584
594
< Paper >
@@ -603,21 +613,13 @@ export default function SearchIcons() {
603
613
< Typography sx = { { mb : 1 } } > { `${ formatNumber (
604
614
icons . length ,
605
615
) } matching results`} </ Typography >
606
- < VirtuosoGrid
607
- style = { { height : 500 } }
608
- data = { icons }
609
- components = { { List : ListWrapper } }
610
- itemContent = { Icon ( handleOpenClick ) }
611
- />
616
+ < Icons icons = { deferredIcons } handleOpenClick = { handleOpenClick } />
612
617
</ Grid >
613
- { /* Temporary fix for Dialog not closing sometimes and Backdrop stuck at opacity 0 (see issue https://github.com/mui/material-ui/issues/32286). One disadvantage is that the closing animation is not applied. */ }
614
- { selectedIcon ? (
615
- < DialogDetails
616
- open = { ! ! selectedIcon }
617
- selectedIcon = { dialogSelectedIcon }
618
- handleClose = { handleClose }
619
- />
620
- ) : null }
618
+ < DialogDetails
619
+ open = { ! ! selectedIcon }
620
+ selectedIcon = { dialogSelectedIcon }
621
+ handleClose = { handleClose }
622
+ />
621
623
</ Grid >
622
624
) ;
623
625
}
0 commit comments