import { ObservableArray } from "data/observable-array" ; import { parse , parseMultipleTemplates } from "ui/builder" ; import { EventData , makeParser , makeValidator } from "ui/content-view" ; import { CSSType , CoercibleProperty , ContainerView , KeyedTemplate , Length , PercentLength , Property , Template , View } from "ui/core/view" ; import { addWeakEventListener , removeWeakEventListener } from "ui/core/weak-event-listener" ; import { Label } from "ui/label" ; import { ItemsSource } from "ui/list-view" ; import { GridItemEventData , GridView as GridViewDefinition , Orientation } from "." ; const autoEffectiveRowHeight = 100 ; const autoEffectiveColWidth = 100 ; export * from "ui/core/view" ; export module knownTemplates { export const itemTemplate = "itemTemplate" ; } export module knownMultiTemplates { export const itemTemplates = "itemTemplates" ; } @ CSSType ( "GridView" ) export abstract class GridViewBase extends ContainerView implements GridViewDefinition { public static itemLoadingEvent = "itemLoading" ; public static itemTapEvent = "itemTap" ; public static loadMoreItemsEvent = "loadMoreItems" ; public static scrollEvent = "scroll" ; public static knownFunctions = [ "itemTemplateSelector" , "itemIdGenerator" ] ; public _defaultTemplate : KeyedTemplate = { key : "default" , createView : ( ) => { if ( this . itemTemplate ) { return parse ( this . itemTemplate , this ) ; } return undefined ; } } ; public _itemTemplatesInternal = new Array < KeyedTemplate > ( this . _defaultTemplate ) ; public orientation : Orientation ; public itemTemplate : string | Template ; public itemTemplates : string | KeyedTemplate [ ] ; public items : any [ ] | ItemsSource ; public isItemsSourceIn : boolean ; public rowHeight : PercentLength ; public colWidth : PercentLength ; public verticalSpacing : Length ; public horizontalSpacing : Length ; public _innerWidth : number = 0 ; public _innerHeight : number = 0 ; public _effectiveRowHeight : number ; public _effectiveColWidth : number ; private _itemTemplateSelectorBindable = new Label ( ) ; private _itemTemplateSelector : ( item : any , index : number , items : any ) => string ; get itemTemplateSelector ( ) : string | ( ( item : any , index : number , items : any ) => string ) { return this . _itemTemplateSelector ; } set itemTemplateSelector ( value ) { if ( typeof value === "string" ) { this . _itemTemplateSelectorBindable . bind ( { sourceProperty : null , targetProperty : "templateKey" , expression : value } ) ; this . _itemTemplateSelector = ( item , index , items ) => { item [ "$index" ] = index ; this . _itemTemplateSelectorBindable . bindingContext = item ; return this . _itemTemplateSelectorBindable . get ( "templateKey" ) ; } ; } else if ( typeof value === "function" ) { this . _itemTemplateSelector = value ; } } public itemIdGenerator : ( item : any , index : number , items : any ) => number = ( _item , index ) => index ; public abstract refresh ( ) ; public abstract scrollToIndex ( index , animated ) ; public onLayout ( left , top , right , bottom ) { super . onLayout ( left , top , right , bottom ) ; this . _innerWidth = right - left - this . effectivePaddingLeft - this . effectivePaddingRight ; this . _effectiveColWidth = PercentLength . toDevicePixels ( this . colWidth , autoEffectiveColWidth , this . _innerWidth ) ; this . _innerHeight = bottom - top - this . effectivePaddingTop - this . effectivePaddingBottom ; this . _effectiveRowHeight = PercentLength . toDevicePixels ( this . rowHeight , autoEffectiveRowHeight , this . _innerHeight ) ; } public _getItemTemplate ( index ) { let templateKey = "default" ; if ( this . itemTemplateSelector ) { const dataItem = this . _getDataItem ( index ) ; templateKey = this . _itemTemplateSelector ( dataItem , index , this . items ) ; } for ( const template of this . _itemTemplatesInternal ) { if ( template . key === templateKey ) { return template ; } } return this . _itemTemplatesInternal [ 0 ] ; } public _prepareItem ( item , index ) { if ( item ) { item . bindingContext = this . _getDataItem ( index ) ; } } public _getDataItem ( index ) { return this . isItemsSourceIn ? ( this . items as ItemsSource ) . getItem ( index ) : this . items [ index ] ; } protected _updateColWidthProperty ( ) { colWidthProperty . coerce ( this ) ; } protected _updateRowHeightProperty ( ) { rowHeightProperty . coerce ( this ) ; } } export interface GridViewBase { on ( eventNames , callback , thisArg ? ) ; on ( event , callback , thisArg ? ) ; on ( event , callback , thisArg ? ) ; on ( event , callback , thisArg ? ) ; } export const itemsProperty = new Property < GridViewBase , any [ ] | ItemsSource > ( { name : "items" , valueChanged : ( target , oldValue , newValue ) => { const getItem = newValue && ( newValue as ItemsSource ) . getItem ; target . isItemsSourceIn = typeof getItem === "function" ; if ( oldValue instanceof ObservableArray ) { removeWeakEventListener ( oldValue , ObservableArray . changeEvent , target . refresh , target ) ; } if ( newValue instanceof ObservableArray ) { addWeakEventListener ( newValue , ObservableArray . changeEvent , target . refresh , target ) ; } target . refresh ( ) ; } } ) ; itemsProperty . register ( GridViewBase ) ; export const itemTemplateProperty = new Property < GridViewBase , string | Template > ( { name : "itemTemplate" , valueChanged : ( target ) => { target . refresh ( ) ; } } ) ; itemTemplateProperty . register ( GridViewBase ) ; export const itemTemplatesProperty = new Property < GridViewBase , string | KeyedTemplate [ ] > ( { name : "itemTemplates" , valueConverter : ( value ) => { if ( typeof value === "string" ) { return parseMultipleTemplates ( value ) ; } return value ; } } ) ; itemTemplatesProperty . register ( GridViewBase ) ; const defaultRowHeight = "auto" ; export const rowHeightProperty = new CoercibleProperty < GridViewBase , PercentLength > ( { name : "rowHeight" , defaultValue : defaultRowHeight , affectsLayout : true , equalityComparer : PercentLength . equals , valueConverter : PercentLength . parse , coerceValue : ( target , value ) => { return target . nativeView ? value : defaultRowHeight ; } , valueChanged : ( target , oldValue , newValue ) => { target . _effectiveRowHeight = PercentLength . toDevicePixels ( newValue , autoEffectiveRowHeight , target . _innerHeight ) ; target . refresh ( ) ; } } ) ; rowHeightProperty . register ( GridViewBase ) ; const defaultColWidth = "auto" ; export const colWidthProperty = new CoercibleProperty < GridViewBase , PercentLength > ( { name : "colWidth" , defaultValue : PercentLength . parse ( "100" ) , affectsLayout : true , equalityComparer : PercentLength . equals , valueConverter : PercentLength . parse , coerceValue : ( target , value ) => { return target . nativeView ? value : defaultColWidth ; } , valueChanged : ( target , oldValue , newValue ) => { target . _effectiveColWidth = PercentLength . toDevicePixels ( newValue , autoEffectiveColWidth , target . _innerWidth ) ; target . refresh ( ) ; } } ) ; colWidthProperty . register ( GridViewBase ) ; const converter = makeParser < Orientation > ( makeValidator ( "horizontal" , "vertical" ) ) ; export const orientationProperty = new Property < GridViewBase , Orientation > ( { name : "orientation" , defaultValue : "vertical" , affectsLayout : true , valueChanged : ( target , oldValue , newValue ) => { target . refresh ( ) ; } , valueConverter : converter } ) ; orientationProperty . register ( GridViewBase ) ;