22'use client' ;
33
44import { Autocomplete , GridLayout , ListBox , ListBoxItem , Size , useFilter , Virtualizer } from 'react-aria-components' ;
5- import { Content , Heading , IllustratedMessage , pressScale , SearchField } from '@react-spectrum/s2' ;
5+ import Close from '@react-spectrum/s2/icons/Close' ;
6+ import { Content , Heading , IllustratedMessage , pressScale , SearchField , Skeleton , Text } from '@react-spectrum/s2' ;
67import { focusRing , iconStyle , style } from '@react-spectrum/s2/style' with { type : 'macro' } ;
78import { iconAliases } from './iconAliases.js' ;
89// @ts -ignore
910import icons from '/packages/@react-spectrum/s2/s2wf-icons/*.svg' ;
1011// eslint-disable-next-line monorepo/no-internal-import
1112import NoSearchResults from '@react-spectrum/s2/illustrations/linear/NoSearchResults' ;
12- import React , { useCallback , useRef } from 'react' ;
13+ import React , { useCallback , useMemo , useRef } from 'react' ;
1314
14- const iconList = Object . keys ( icons ) . map ( name => ( { id : name . replace ( / ^ S 2 _ I c o n _ ( .* ?) ( S i z e \d + ) ? _ 2 .* / , '$1' ) , icon : icons [ name ] . default } ) ) ;
15+ export const iconList = Object . keys ( icons ) . map ( name => ( { id : name . replace ( / ^ S 2 _ I c o n _ ( .* ?) ( S i z e \d + ) ? _ 2 .* / , '$1' ) , icon : icons [ name ] . default } ) ) ;
1516
1617const itemStyle = style ( {
1718 ...focusRing ( ) ,
@@ -37,7 +38,8 @@ const itemStyle = style({
3738 gap : 4 ,
3839 alignItems : 'center' ,
3940 justifyContent : 'center' ,
40- paddingX : 4
41+ paddingX : 4 ,
42+ cursor : 'default'
4143} ) ;
4244
4345let handleCopyImport = ( id : string ) => {
@@ -48,6 +50,119 @@ let handleCopyImport = (id: string) => {
4850 } ) ;
4951} ;
5052
53+ interface IconSearchViewProps {
54+ filteredItems : typeof iconList
55+ }
56+
57+ export function IconSearchView ( { filteredItems} : IconSearchViewProps ) {
58+ return (
59+ < Virtualizer layout = { GridLayout } layoutOptions = { { minItemSize : new Size ( 64 , 64 ) , maxItemSize : new Size ( 64 , 64 ) , minSpace : new Size ( 12 , 12 ) , preserveAspectRatio : true } } >
60+ < ListBox
61+ onAction = { ( item ) => handleCopyImport ( item . toString ( ) ) }
62+ items = { filteredItems }
63+ layout = "grid"
64+ className = { style ( { width : '100%' , scrollPaddingY : 4 } ) }
65+ renderEmptyState = { ( ) => (
66+ < IllustratedMessage styles = { style ( { marginX : 'auto' , marginY : 32 } ) } >
67+ < NoSearchResults />
68+ < Heading > No results</ Heading >
69+ < Content > Try a different search term.</ Content >
70+ </ IllustratedMessage >
71+ ) } >
72+ { item => < IconItem item = { item } /> }
73+ </ ListBox >
74+ </ Virtualizer >
75+ ) ;
76+ }
77+
78+ function IconItem ( { item} ) {
79+ let Icon = item . icon ;
80+ let ref = useRef ( null ) ;
81+ return (
82+ < ListBoxItem id = { item . id } value = { item } textValue = { item . id } className = { itemStyle } ref = { ref } style = { pressScale ( ref ) } >
83+ < Icon styles = { iconStyle ( { size : 'XL' } ) } />
84+ < div
85+ className = { style ( {
86+ maxWidth : '100%' ,
87+ textOverflow : 'ellipsis' ,
88+ overflow : 'hidden' ,
89+ whiteSpace : 'nowrap'
90+ } ) } >
91+ { item . id }
92+ </ div >
93+ </ ListBoxItem >
94+ ) ;
95+ }
96+
97+ export function SkeletonIconItem ( { item} : { item : { id : string } } ) {
98+ const PlaceholderIcon = Close ;
99+ const ref = useRef ( null ) ;
100+
101+ const itemStyle = style ( {
102+ ...focusRing ( ) ,
103+ size : 'full' ,
104+ backgroundColor : {
105+ default : 'gray-50' ,
106+ isHovered : 'gray-100' ,
107+ isFocused : 'gray-100' ,
108+ isSelected : 'neutral'
109+ } ,
110+ '--iconPrimary' : {
111+ type : 'color' ,
112+ value : {
113+ default : 'neutral' ,
114+ isSelected : 'gray-25'
115+ }
116+ } ,
117+ font : 'ui-sm' ,
118+ borderRadius : 'default' ,
119+ transition : 'default' ,
120+ display : 'flex' ,
121+ flexDirection : 'column' ,
122+ gap : 4 ,
123+ alignItems : 'center' ,
124+ justifyContent : 'center' ,
125+ paddingX : 4
126+ } ) ;
127+
128+ return (
129+ < ListBoxItem
130+ id = { item . id }
131+ value = { item }
132+ textValue = "skeleton"
133+ className = { itemStyle }
134+ ref = { ref } >
135+ < PlaceholderIcon styles = { iconStyle ( { size : 'XL' } ) } />
136+ < div
137+ className = { style ( {
138+ maxWidth : '100%' ,
139+ textOverflow : 'ellipsis' ,
140+ overflow : 'hidden' ,
141+ whiteSpace : 'nowrap'
142+ } ) } >
143+ < Text styles = { style ( { font : 'ui-sm' } ) } > Name</ Text >
144+ </ div >
145+ </ ListBoxItem >
146+ ) ;
147+ }
148+
149+ export function IconSearchSkeleton ( ) {
150+ const mockItems = useMemo ( ( ) => Array . from ( { length : 140 } , ( _ , i ) => ( { id : `skeleton-${ i } ` } ) ) , [ ] ) ;
151+
152+ return (
153+ < Skeleton isLoading >
154+ < Virtualizer layout = { GridLayout } layoutOptions = { { minItemSize : new Size ( 64 , 64 ) , maxItemSize : new Size ( 64 , 64 ) , minSpace : new Size ( 12 , 12 ) , preserveAspectRatio : true } } >
155+ < ListBox
156+ items = { mockItems }
157+ layout = "grid"
158+ className = { style ( { width : '100%' , scrollPaddingY : 4 } ) } >
159+ { ( item ) => < SkeletonIconItem item = { item } /> }
160+ </ ListBox >
161+ </ Virtualizer >
162+ </ Skeleton >
163+ ) ;
164+ }
165+
51166export function IconCards ( ) {
52167 let { contains} = useFilter ( { sensitivity : 'base' } ) ;
53168 let filter = useCallback ( ( textValue , inputValue ) => {
@@ -84,22 +199,3 @@ export function IconCards() {
84199 </ Autocomplete >
85200 ) ;
86201}
87-
88- function IconItem ( { item} ) {
89- let Icon = item . icon ;
90- let ref = useRef ( null ) ;
91- return (
92- < ListBoxItem id = { item . id } value = { item } textValue = { item . id } className = { itemStyle } ref = { ref } style = { pressScale ( ref ) } >
93- < Icon styles = { iconStyle ( { size : 'XL' } ) } />
94- < div
95- className = { style ( {
96- maxWidth : '100%' ,
97- textOverflow : 'ellipsis' ,
98- overflow : 'hidden' ,
99- whiteSpace : 'nowrap'
100- } ) } >
101- { item . id }
102- </ div >
103- </ ListBoxItem >
104- ) ;
105- }
0 commit comments