@@ -303,6 +303,12 @@ export interface CubeListBoxProps<T>
303303 * Useful for implementing search/filter functionality.
304304 */
305305 filter ?: ( nodes : Iterable < any > ) => Iterable < any > ;
306+
307+ /**
308+ * Label to display when the list is empty (no items available).
309+ * Defaults to "No items".
310+ */
311+ emptyLabel ?: ReactNode ;
306312}
307313
308314const PROP_STYLES = [ ...BASE_STYLES , ...OUTER_STYLES , ...COLOR_STYLES ] ;
@@ -508,6 +514,7 @@ export const ListBox = forwardRef(function ListBox<T extends object>(
508514 selectAllLabel,
509515 allValueProps,
510516 filter,
517+ emptyLabel = 'No items' ,
511518 form,
512519 ...otherProps
513520 } = props ;
@@ -886,110 +893,121 @@ export const ListBox = forwardRef(function ListBox<T extends object>(
886893 ) }
887894 { /* Scroll container wrapper */ }
888895 < ListBoxScrollElement ref = { scrollRef } mods = { mods } { ...focusProps } >
889- < ListElement
890- qa = { qa || 'ListBox' }
891- { ...mergedListBoxProps }
892- ref = { listRef }
893- styles = { listStyles }
894- aria-disabled = { isDisabled || undefined }
895- mods = { { sections : hasSections } }
896- style = {
897- shouldVirtualize
898- ? {
899- position : 'relative' ,
900- height : `${ rowVirtualizer . getTotalSize ( ) + 3 } px` ,
901- }
902- : undefined
903- }
904- >
905- { shouldVirtualize
906- ? rowVirtualizer . getVirtualItems ( ) . map ( ( virtualItem ) => {
907- const item = itemsArray [ virtualItem . index ] ;
908-
909- return (
910- < Option
911- key = { virtualItem . key }
912- size = { size }
913- item = { item }
914- state = { listState }
915- styles = { optionStyles }
916- isParentDisabled = { isDisabled }
917- validationState = { validationState }
918- focusOnHover = { focusOnHover }
919- isCheckable = { isCheckable }
920- // We don't need to measure the element here, because the height is already set by the virtualizer
921- // This is a workaround to avoid glitches when selecting/deselecting items
922- virtualRef = { rowVirtualizer . measureElement as any }
923- virtualStyle = { {
924- position : 'absolute' ,
925- top : 0 ,
926- left : 0 ,
927- right : 0 ,
928- transform : `translateY(${ virtualItem . start } px)` ,
929- } }
930- virtualIndex = { virtualItem . index }
931- lastFocusSourceRef = { lastFocusSourceRef }
932- onClick = { onOptionClick }
933- />
934- ) ;
935- } )
936- : ( ( ) => {
937- const renderedItems : ReactNode [ ] = [ ] ;
938- let isFirstSection = true ;
939-
940- for ( const item of listState . collection ) {
941- if ( item . type === 'section' ) {
942- if ( ! isFirstSection ) {
896+ { listState . collection . size === 0 ? (
897+ < ItemBase
898+ preset = "t4"
899+ color = "#dark-03"
900+ size = { size }
901+ padding = "(.5x - 1bw)"
902+ >
903+ { emptyLabel }
904+ </ ItemBase >
905+ ) : (
906+ < ListElement
907+ qa = { qa || 'ListBox' }
908+ { ...mergedListBoxProps }
909+ ref = { listRef }
910+ styles = { listStyles }
911+ aria-disabled = { isDisabled || undefined }
912+ mods = { { sections : hasSections } }
913+ style = {
914+ shouldVirtualize
915+ ? {
916+ position : 'relative' ,
917+ height : `${ rowVirtualizer . getTotalSize ( ) + 3 } px` ,
918+ }
919+ : undefined
920+ }
921+ >
922+ { shouldVirtualize
923+ ? rowVirtualizer . getVirtualItems ( ) . map ( ( virtualItem ) => {
924+ const item = itemsArray [ virtualItem . index ] ;
925+
926+ return (
927+ < Option
928+ key = { virtualItem . key }
929+ size = { size }
930+ item = { item }
931+ state = { listState }
932+ styles = { optionStyles }
933+ isParentDisabled = { isDisabled }
934+ validationState = { validationState }
935+ focusOnHover = { focusOnHover }
936+ isCheckable = { isCheckable }
937+ // We don't need to measure the element here, because the height is already set by the virtualizer
938+ // This is a workaround to avoid glitches when selecting/deselecting items
939+ virtualRef = { rowVirtualizer . measureElement as any }
940+ virtualStyle = { {
941+ position : 'absolute' ,
942+ top : 0 ,
943+ left : 0 ,
944+ right : 0 ,
945+ transform : `translateY(${ virtualItem . start } px)` ,
946+ } }
947+ virtualIndex = { virtualItem . index }
948+ lastFocusSourceRef = { lastFocusSourceRef }
949+ onClick = { onOptionClick }
950+ />
951+ ) ;
952+ } )
953+ : ( ( ) => {
954+ const renderedItems : ReactNode [ ] = [ ] ;
955+ let isFirstSection = true ;
956+
957+ for ( const item of listState . collection ) {
958+ if ( item . type === 'section' ) {
959+ if ( ! isFirstSection ) {
960+ renderedItems . push (
961+ < StyledDivider
962+ key = { `divider-${ String ( item . key ) } ` }
963+ role = "separator"
964+ aria-orientation = "horizontal"
965+ /> ,
966+ ) ;
967+ }
968+
943969 renderedItems . push (
944- < StyledDivider
945- key = { `divider-${ String ( item . key ) } ` }
946- role = "separator"
947- aria-orientation = "horizontal"
970+ < ListBoxSection
971+ key = { item . key }
972+ item = { item }
973+ state = { listState }
974+ optionStyles = { optionStyles }
975+ headingStyles = { headingStyles }
976+ sectionStyles = { sectionStyles }
977+ isParentDisabled = { isDisabled }
978+ validationState = { validationState }
979+ focusOnHover = { focusOnHover }
980+ isCheckable = { isCheckable }
981+ size = { size }
982+ lastFocusSourceRef = { lastFocusSourceRef }
983+ onClick = { onOptionClick }
948984 /> ,
949985 ) ;
950- }
951986
952- renderedItems . push (
953- < ListBoxSection
954- key = { item . key }
955- item = { item }
956- state = { listState }
957- optionStyles = { optionStyles }
958- headingStyles = { headingStyles }
959- sectionStyles = { sectionStyles }
960- isParentDisabled = { isDisabled }
961- validationState = { validationState }
962- focusOnHover = { focusOnHover }
963- isCheckable = { isCheckable }
964- size = { size }
965- lastFocusSourceRef = { lastFocusSourceRef }
966- onClick = { onOptionClick }
967- /> ,
968- ) ;
969-
970- isFirstSection = false ;
971- } else {
972- renderedItems . push (
973- < Option
974- key = { item . key }
975- size = { size }
976- item = { item }
977- state = { listState }
978- styles = { optionStyles }
979- isParentDisabled = { isDisabled }
980- validationState = { validationState }
981- focusOnHover = { focusOnHover }
982- isCheckable = { isCheckable }
983- lastFocusSourceRef = { lastFocusSourceRef }
984- onClick = { onOptionClick }
985- /> ,
986- ) ;
987+ isFirstSection = false ;
988+ } else {
989+ renderedItems . push (
990+ < Option
991+ key = { item . key }
992+ size = { size }
993+ item = { item }
994+ state = { listState }
995+ styles = { optionStyles }
996+ isParentDisabled = { isDisabled }
997+ validationState = { validationState }
998+ focusOnHover = { focusOnHover }
999+ isCheckable = { isCheckable }
1000+ lastFocusSourceRef = { lastFocusSourceRef }
1001+ onClick = { onOptionClick }
1002+ /> ,
1003+ ) ;
1004+ }
9871005 }
988- }
9891006
990- return renderedItems ;
991- } ) ( ) }
992- </ ListElement >
1007+ return renderedItems ;
1008+ } ) ( ) }
1009+ </ ListElement >
1010+ ) }
9931011 </ ListBoxScrollElement >
9941012 { footer ? (
9951013 < StyledFooter styles = { footerStyles } data-size = { size } >
0 commit comments