import { Label } from '../label' ; import { LayoutBase } from '../layouts/layout-base' ; import { View , CSSType , CustomLayoutView , Template , KeyedTemplate } from '../core/view' ; import { Property } from '../core/properties' ; import { layout } from '../../utils' ; import { StackLayout } from '../layouts/stack-layout' ; import { ObservableArray , ChangedData } from '../../data/observable-array' ; import { addWeakEventListener , removeWeakEventListener } from '../core/weak-event-listener' ; import { Builder } from '../builder' ; import { profile } from '../../profiling' ; export interface ItemsSource { length : number ; getItem ( index ) : any ; } /**
 * Represents a UI Repeater component.
 */ @ CSSType ( 'Repeater' ) export class Repeater extends CustomLayoutView { public static knownFunctions = [ 'itemTemplateSelector' ] ; private _isDirty = false ; private _itemTemplateSelector : ( item : any , index : number , items : any ) => string ; private _itemTemplateSelectorBindable ; public ios ; public android ; constructor ( ) { super ( ) ; this . itemsLayout = new StackLayout ( ) ; } @ profile public onLoaded ( ) { if ( this . _isDirty ) { this . refresh ( ) ; } super . onLoaded ( ) ; } /**
     * Gets or set the items collection of the Repeater.
     * The items property can be set to an array or an object defining length and getItem(index) method.
     */ public items : any [ ] | ItemsSource ; /**
     * Gets or set the item template of the Repeater.
     */ public itemTemplate : string | Template ; /**
     * Gets or set the item templates of the Repeater.
     */ public itemTemplates : string | Array < KeyedTemplate > ; /**
     * Gets or set the items layout of the Repeater. Default value is StackLayout with orientation="vertical".
     */ public itemsLayout : LayoutBase ; get itemTemplateSelector ( ) : string | ( ( item : any , index : number , items : any ) => string ) { return this . _itemTemplateSelector ; } set itemTemplateSelector ( value ) { if ( typeof value === 'string' ) { if ( ! this . _itemTemplateSelectorBindable ) { this . _itemTemplateSelectorBindable = new Label ( ) ; } this . _itemTemplateSelectorBindable . bind ( { sourceProperty : null , targetProperty : 'templateKey' , expression : value , } ) ; this . _itemTemplateSelector = ( item , index , items ) => { item [ '$index' ] = index ; if ( this . _itemTemplateSelectorBindable . bindingContext === item ) { this . _itemTemplateSelectorBindable . bindingContext = null ; } this . _itemTemplateSelectorBindable . bindingContext = item ; return this . _itemTemplateSelectorBindable . get ( 'templateKey' ) ; } ; } else if ( typeof value === 'function' ) { this . _itemTemplateSelector = value ; } } public _requestRefresh ( ) { this . _isDirty = true ; if ( this . isLoaded ) { this . refresh ( ) ; } } /**
     * Forces the Repeater to reload all its items.
     */ public refresh ( ) { if ( this . itemsLayout ) { this . itemsLayout . removeChildren ( ) ; } if ( ! this . items ) { return ; } const length = this . items . length ; for ( let i = 0 ; i < length ; i ++ ) { const dataItem = this . _getDataItem ( i ) ; let viewToAdd = null ; if ( this . _itemTemplateSelector && this . itemTemplates ) { const key = this . _itemTemplateSelector ( dataItem , i , this . items ) ; const length2 = this . itemTemplates . length ; for ( let j = 0 ; j < length2 ; j ++ ) { const template = < KeyedTemplate > this . itemTemplates [ j ] ; if ( template . key === key ) { viewToAdd = template . createView ( ) ; break ; } } } if ( ! viewToAdd ) { if ( __UI_USE_EXTERNAL_RENDERER__ ) { viewToAdd = this . _getDefaultItemContent ( i ) ; } else { viewToAdd = this . itemTemplate ? Builder . parse ( this . itemTemplate , this ) : this . _getDefaultItemContent ( i ) ; } } viewToAdd . bindingContext = dataItem ; this . itemsLayout . addChild ( viewToAdd ) ; } this . _isDirty = false ; } public _onItemsChanged ( data ) { this . _requestRefresh ( ) ; } public _getDefaultItemContent ( index ) { const lbl = new Label ( ) ; lbl . bind ( { targetProperty : 'text' , sourceProperty : '$value' , } ) ; return lbl ; } private _getDataItem ( index ) { const items = < ItemsSource > this . items ; return items . getItem ? items . getItem ( index ) : this . items [ index ] ; } get _childrenCount ( ) : number { return this . itemsLayout ? 1 : 0 ; } public eachChildView ( callback ) { if ( this . itemsLayout ) { callback ( this . itemsLayout ) ; } } public onLayout ( left , top , right , bottom ) { const insets = this . getSafeAreaInsets ( ) ; const paddingLeft = this . effectiveBorderLeftWidth + this . effectivePaddingLeft + insets . left ; const paddingTop = this . effectiveBorderTopWidth + this . effectivePaddingTop + insets . top ; const paddingRight = this . effectiveBorderRightWidth + this . effectivePaddingRight + insets . right ; const paddingBottom = this . effectiveBorderBottomWidth + this . effectivePaddingBottom + insets . bottom ; const childLeft = paddingLeft ; const childTop = paddingTop ; const childRight = right - left - paddingRight ; const childBottom = bottom - top - paddingBottom ; View . layoutChild ( this , this . itemsLayout , childLeft , childTop , childRight , childBottom ) ; } public onMeasure ( widthMeasureSpec , heightMeasureSpec ) { const result = View . measureChild ( this , this . itemsLayout , widthMeasureSpec , heightMeasureSpec ) ; const width = layout . getMeasureSpecSize ( widthMeasureSpec ) ; const widthMode = layout . getMeasureSpecMode ( widthMeasureSpec ) ; const height = layout . getMeasureSpecSize ( heightMeasureSpec ) ; const heightMode = layout . getMeasureSpecMode ( heightMeasureSpec ) ; const widthAndState = View . resolveSizeAndState ( result . measuredWidth , width , widthMode , 0 ) ; const heightAndState = View . resolveSizeAndState ( result . measuredHeight , height , heightMode , 0 ) ; this . setMeasuredDimension ( widthAndState , heightAndState ) ; } } Repeater . prototype . recycleNativeView = 'auto' ; /**
 * Represents the item template property of each Repeater instance.
 */ export const itemTemplateProperty = new Property < Repeater , string | Template > ( { name : 'itemTemplate' , affectsLayout : true , valueChanged : ( target ) => { target . _requestRefresh ( ) ; } , } ) ; itemTemplateProperty . register ( Repeater ) ; /**
 * Represents the items template property of each Repeater instance.
 */ export const itemTemplatesProperty = new Property < Repeater , string | Array < KeyedTemplate > > ( { name : 'itemTemplates' , affectsLayout : true , valueConverter : ( value ) => { if ( typeof value === 'string' ) { if ( __UI_USE_XML_PARSER__ ) { return Builder . parseMultipleTemplates ( value , null ) ; } else { return null ; } } return value ; } , valueChanged : ( target ) => { target . _requestRefresh ( ) ; } , } ) ; itemTemplatesProperty . register ( Repeater ) ; /**
 * Represents the property backing the items property of each Repeater instance.
 */ export const itemsProperty = new Property < Repeater , any [ ] | ItemsSource > ( { name : 'items' , affectsLayout : true , valueChanged : ( target , oldValue , newValue ) => { if ( oldValue instanceof ObservableArray ) { removeWeakEventListener ( oldValue , ObservableArray . changeEvent , target . _onItemsChanged , target ) ; } if ( newValue instanceof ObservableArray ) { addWeakEventListener ( newValue , ObservableArray . changeEvent , target . _onItemsChanged , target ) ; } target . _requestRefresh ( ) ; } , } ) ; itemsProperty . register ( Repeater ) ; export const itemsLayoutProperty = new Property < Repeater , LayoutBase > ( { name : 'itemsLayout' , affectsLayout : true , valueChanged : ( target , oldValue , newValue ) => { if ( oldValue ) { target . _removeView ( oldValue ) ; oldValue . removeChildren ( ) ; } if ( newValue ) { target . _addView ( newValue ) ; } target . _requestRefresh ( ) ; } , } ) ; itemsLayoutProperty . register ( Repeater ) ;