import { computed , defineComponent , PropType , ref } from 'vue' ; import { getPrefixCls } from '../_utils/global-config' ; import Spin from '../spin' ; import Grid from '../grid' ; import Pagination , { PaginationProps } from '../pagination' ; import Empty from '../empty' ; import VirtualList from '../_components/virtual-list/virtual-list.vue' ; import { ScrollIntoViewOptions } from '../_components/virtual-list/interface' ; import { usePagination } from '../_hooks/use-pagination' ; import { omit } from '../_utils/omit' ; const LIST_SIZES = [ 'small' , 'medium' , 'large' ] as const ; type ListSize = typeof LIST_SIZES [ number ] ; export default defineComponent ( { name : 'List' , props : { zh en data : { type : Array as PropType < any [ ] > } , zh en values size : { type : String as PropType < ListSize > , default : 'medium' } , zh en bordered : { type : Boolean , default : true } , zh en split : { type : Boolean , default : true } , zh en loading : { type : Boolean , default : false } , zh en hoverable : { type : Boolean , default : false } , zh en paginationProps : { type : Object as PropType < PaginationProps > } , zh en gridProps : { type : Object } , zh en maxHeight : { type : Number , default : 0 } , zh en bottomOffset : { type : Number , default : 0 } , zh en type VirtualListProps virtualListProps : { type : Object } } , emits : [ 'scroll' , 'reachBottom' , 'pageChange' , 'pageSizeChange' , ] , setup ( props , { emit , slots } ) { const prefixCls = getPrefixCls ( 'list' ) ; const hasChildren = computed ( ( ) => Boolean ( slots . default ) ) ; const isVirtualList = computed ( ( ) => props . virtualListProps ) ; const handleScroll = ( e ) => { const { scrollTop , scrollHeight , offsetHeight } = e . target as HTMLElement ; const bottom = scrollHeight - ( scrollTop + offsetHeight ) ; if ( bottom <= props . bottomOffset ) { emit ( 'reachBottom' ) ; } emit ( 'scroll' ) ; } ; const { current , pageSize , handlePageChange , handlePageSizeChange } = usePagination ( props , { emit } ) ; const getCurrentPageItems = ( data ) => { if ( ! props . paginationProps ) { return data ; } const startIndex = ( current . value - 1 ) * pageSize . value ; return data . slice ( startIndex , startIndex + pageSize . value ) ; } ; const renderGridItems = ( data ) => { if ( ! props . gridProps ) { return null ; } const currentPageItems = getCurrentPageItems ( data ) ; if ( props . gridProps . span ) { const items = [ ] ; const rowSize = 24 / props . gridProps . span ; for ( let i = 0 ; i < currentPageItems . length ; i += rowSize ) { const nextIndex = i + rowSize ; const rowIndex = Math . floor ( i / rowSize ) ; items . push ( < Grid . Row key = { rowIndex } class = { `${ prefixCls }-row` } gutter = { props . gridProps . gutter } > { currentPageItems . slice ( i , nextIndex ) . map ( ( item , index ) => ( < Grid . Col key = { `${ rowIndex }-${ index }` } class = { `${ prefixCls }-col` } span = { props . gridProps ?. span } > { hasChildren . value ? item : slots . item ?. ( { item , index } ) } < /Grid.Col>))} < /Grid.Row>); } return items ; } return ( < Grid . Row class = { `${ prefixCls }-row` } gutter = { props . gridProps . gutter } > { currentPageItems . map ( ( item , index ) => ( < Grid . Col key = { index } class = { `${ prefixCls }-col` } > { hasChildren . value ? item : slots . item ?. ( { item , index } ) } < /Grid.Col>))} < /Grid.Row>); } ; const getListItemRenderMsg = ( data ) => { const currentPageItems = getCurrentPageItems ( data ) ; const currentPageItemRenderFunc = ( item , index ) => hasChildren . value ? currentPageItems [ index ] : slots . item ?. ( { item , index } ) ; return { currentPageItems , currentPageItemRenderFunc } ; } ; const renderListItems = ( data ) => { const { currentPageItems , currentPageItemRenderFunc } = getListItemRenderMsg ( data ) ; return currentPageItems . map ( currentPageItemRenderFunc ) ; } ; const renderItems = ( ) => { const data = slots . default ?. ( ) ?? props . data ; if ( data && data . length > 0 ) { return props . gridProps ? renderGridItems ( data ) : renderListItems ( data ) ; } return renderEmpty ( ) ; } ; const renderPagination = ( ) => { if ( ! props . paginationProps ) { return null ; } const paginationProps = omit ( props . paginationProps , [ 'current' , 'pageSize' , 'defaultCurrent' , 'defaultPageSize' , ] ) ; return ( < Pagination { ... paginationProps } class = { `${ prefixCls }-pagination` } current = { current . value } pageSize = { pageSize . value } onChange = { handlePageChange } onPageSizeChange = { handlePageSizeChange } / > ) ; } ; const cls = computed ( ( ) => [ prefixCls , `${ prefixCls }-${ props . size }` , { [ `${ prefixCls }-bordered` ] : props . bordered , [ `${ prefixCls }-split` ] : props . split , [ `${ prefixCls }-hover` ] : props . hoverable } , ] ) ; const contentStyle = computed ( ( ) => { if ( props . maxHeight > 0 ) { return { maxHeight : `${ props . maxHeight }px` } ; } return undefined ; } ) ; const contentCls = computed ( ( ) => [ `${ prefixCls }-content` , { [ `${ prefixCls }-virtual` ] : isVirtualList . value } , ] ) ; const virtualListRef = ref ( ) ; const renderVirtualList = ( ) => { const { currentPageItems , currentPageItemRenderFunc } = getListItemRenderMsg ( props . data || [ ] ) ; return currentPageItems . length ? ( < VirtualList ref = { virtualListRef } { ... props . virtualListProps } class = { contentCls . value } data = { currentPageItems } v - slots = { { item : ( { item , index } ) => currentPageItemRenderFunc ( item , index ) } } onScroll = { handleScroll } / > ) : ( renderEmpty ( ) ) ; } ; const renderEmpty = ( ) => { return < Empty />; } ; const renderList = ( ) => { return ( < div class = { `${ prefixCls }-wrapper` } > < div class = { cls . value } style = { contentStyle . value } onScroll = { handleScroll } > { slots . header && ( < div class = { `${ prefixCls }-header` } > { slots . header ( ) } < /div>)} { isVirtualList . value && ! props . gridProps ? ( renderVirtualList ( ) ) : ( < div class = { contentCls . value } > { renderItems ( ) } < /div>)} { slots . footer && ( < div class = { `${ prefixCls }-footer` } > { slots . footer ( ) } < /div>)} < /div> { renderPagination ( ) } < /div>); } ; return { virtualListRef , renderList } ; } , methods : { zh en param { { index ? : number ; key ? : number | string ; align : 'auto' | 'top' | 'bottom' } } options public scrollIntoView ( options ) { if ( this . virtualListRef ) { this . virtualListRef . scrollTo ( options ) ; } } } , render ( ) { const { loading , renderList } = this ; if ( loading ) { return ( < Spin style = { { display : 'block' } } loading > { renderList ( ) } < /Spin>); } return renderList ( ) ; } } ) ;