import { Repeater as RepeaterDefinition, ItemsSource } from "."; import { Label } from "../label"; import { LayoutBase, CustomLayoutView, View, Template, Property, layout, CSSType } from "../layouts/layout-base"; 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 * from "../layouts/layout-base"; export module knownTemplates { export const itemTemplate = "itemTemplate"; } @CSSType("Repeater") export class Repeater extends CustomLayoutView implements RepeaterDefinition { private _isDirty = false; public ios; public android; constructor() { super(); // TODO: Do we need this as property? this.itemsLayout = new StackLayout(); } public items: any[] | ItemsSource; public itemTemplate: string | Template; public itemsLayout: LayoutBase; @profile public onLoaded() { if (this._isDirty) { this.refresh(); } super.onLoaded(); } public _requestRefresh() { this._isDirty = true; if (this.isLoaded) { this.refresh(); } } 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 viewToAdd = this.itemTemplate ? Builder.parse(this.itemTemplate, this) : this._getDefaultItemContent(i); const dataItem = this._getDataItem(i); viewToAdd.bindingContext = dataItem; this.itemsLayout.addChild(viewToAdd); } this._isDirty = false; } public _onItemsChanged(data: ChangedData) { // TODO: use the event args and optimize this code by remove/add single items instead of full rebuild. this._requestRefresh(); } public _getDefaultItemContent(index: number): View { const lbl = new Label(); lbl.bind({ targetProperty: "text", sourceProperty: "$value" }); return lbl; } private _getDataItem(index: number): any { let items = this.items; return items.getItem ? items.getItem(index) : this.items[index]; } get _childrenCount(): number { return this.itemsLayout ? 1 : 0; } public eachChildView(callback: (child: View) => boolean) { if (this.itemsLayout) { callback(this.itemsLayout); } } public onLayout(left: number, top: number, right: number, bottom: number): void { 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: number, heightMeasureSpec: number): void { 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 ListView instance. */ export const itemTemplateProperty = new Property({ name: "itemTemplate", affectsLayout: true, valueChanged: (target) => { target._requestRefresh(); } }); itemTemplateProperty.register(Repeater); /** * Represents the property backing the items property of each ListView instance. */ export const itemsProperty = new Property({ 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({ 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);