UNPKG

8.26 kBJavaScriptView Raw
1import { Label } from '../label';
2import { View, CSSType, CustomLayoutView } from '../core/view';
3import { Property } from '../core/properties';
4import { layout } from '../../utils';
5import { StackLayout } from '../layouts/stack-layout';
6import { ObservableArray } from '../../data/observable-array';
7import { addWeakEventListener, removeWeakEventListener } from '../core/weak-event-listener';
8import { Builder } from '../builder';
9import { profile } from '../../profiling';
10import { isFunction } from '../../utils/types';
11/**
12 * Represents a UI Repeater component.
13 */
14let Repeater = class Repeater extends CustomLayoutView {
15 constructor() {
16 super();
17 this._isDirty = false;
18 // TODO: Do we need this as property?
19 this.itemsLayout = new StackLayout();
20 }
21 onLoaded() {
22 if (this._isDirty) {
23 this.refresh();
24 }
25 super.onLoaded();
26 }
27 get itemTemplateSelector() {
28 return this._itemTemplateSelector;
29 }
30 set itemTemplateSelector(value) {
31 if (typeof value === 'string') {
32 if (!this._itemTemplateSelectorBindable) {
33 this._itemTemplateSelectorBindable = new Label();
34 }
35 this._itemTemplateSelectorBindable.bind({
36 sourceProperty: null,
37 targetProperty: 'templateKey',
38 expression: value,
39 });
40 this._itemTemplateSelector = (item, index, items) => {
41 item['$index'] = index;
42 if (this._itemTemplateSelectorBindable.bindingContext === item) {
43 this._itemTemplateSelectorBindable.bindingContext = null;
44 }
45 this._itemTemplateSelectorBindable.bindingContext = item;
46 return this._itemTemplateSelectorBindable.get('templateKey');
47 };
48 }
49 else if (typeof value === 'function') {
50 this._itemTemplateSelector = value;
51 }
52 }
53 _requestRefresh() {
54 this._isDirty = true;
55 if (this.isLoaded) {
56 this.refresh();
57 }
58 }
59 /**
60 * Forces the Repeater to reload all its items.
61 */
62 refresh() {
63 if (this.itemsLayout) {
64 this.itemsLayout.removeChildren();
65 }
66 if (!this.items) {
67 return;
68 }
69 const length = this.items.length;
70 for (let i = 0; i < length; i++) {
71 const dataItem = this._getDataItem(i);
72 let viewToAdd = null;
73 if (this._itemTemplateSelector && this.itemTemplates) {
74 const key = this._itemTemplateSelector(dataItem, i, this.items);
75 const length2 = this.itemTemplates.length;
76 for (let j = 0; j < length2; j++) {
77 const template = this.itemTemplates[j];
78 if (template.key === key) {
79 viewToAdd = template.createView();
80 break;
81 }
82 }
83 }
84 if (!viewToAdd) {
85 if (__UI_USE_EXTERNAL_RENDERER__) {
86 viewToAdd = isFunction(this.itemTemplate) ? this.itemTemplate() : this._getDefaultItemContent(i);
87 }
88 else {
89 viewToAdd = this.itemTemplate ? Builder.parse(this.itemTemplate, this) : this._getDefaultItemContent(i);
90 }
91 }
92 viewToAdd.bindingContext = dataItem;
93 this.itemsLayout.addChild(viewToAdd);
94 }
95 this._isDirty = false;
96 }
97 _onItemsChanged(data) {
98 // TODO: use the event args and optimize this code by remove/add single items instead of full rebuild.
99 this._requestRefresh();
100 }
101 _getDefaultItemContent(index) {
102 const lbl = new Label();
103 lbl.bind({
104 targetProperty: 'text',
105 sourceProperty: '$value',
106 });
107 return lbl;
108 }
109 _getDataItem(index) {
110 const items = this.items;
111 return items.getItem ? items.getItem(index) : this.items[index];
112 }
113 get _childrenCount() {
114 return this.itemsLayout ? 1 : 0;
115 }
116 eachChildView(callback) {
117 if (this.itemsLayout) {
118 callback(this.itemsLayout);
119 }
120 }
121 onLayout(left, top, right, bottom) {
122 const insets = this.getSafeAreaInsets();
123 const paddingLeft = this.effectiveBorderLeftWidth + this.effectivePaddingLeft + insets.left;
124 const paddingTop = this.effectiveBorderTopWidth + this.effectivePaddingTop + insets.top;
125 const paddingRight = this.effectiveBorderRightWidth + this.effectivePaddingRight + insets.right;
126 const paddingBottom = this.effectiveBorderBottomWidth + this.effectivePaddingBottom + insets.bottom;
127 const childLeft = paddingLeft;
128 const childTop = paddingTop;
129 const childRight = right - left - paddingRight;
130 const childBottom = bottom - top - paddingBottom;
131 View.layoutChild(this, this.itemsLayout, childLeft, childTop, childRight, childBottom);
132 }
133 onMeasure(widthMeasureSpec, heightMeasureSpec) {
134 const result = View.measureChild(this, this.itemsLayout, widthMeasureSpec, heightMeasureSpec);
135 const width = layout.getMeasureSpecSize(widthMeasureSpec);
136 const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
137 const height = layout.getMeasureSpecSize(heightMeasureSpec);
138 const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
139 const widthAndState = View.resolveSizeAndState(result.measuredWidth, width, widthMode, 0);
140 const heightAndState = View.resolveSizeAndState(result.measuredHeight, height, heightMode, 0);
141 this.setMeasuredDimension(widthAndState, heightAndState);
142 }
143};
144// TODO: get rid of such hacks.
145Repeater.knownFunctions = ['itemTemplateSelector']; // See component-builder.ts isKnownFunction
146__decorate([
147 profile,
148 __metadata("design:type", Function),
149 __metadata("design:paramtypes", []),
150 __metadata("design:returntype", void 0)
151], Repeater.prototype, "onLoaded", null);
152Repeater = __decorate([
153 CSSType('Repeater'),
154 __metadata("design:paramtypes", [])
155], Repeater);
156export { Repeater };
157Repeater.prototype.recycleNativeView = 'auto';
158/**
159 * Represents the item template property of each Repeater instance.
160 */
161export const itemTemplateProperty = new Property({
162 name: 'itemTemplate',
163 affectsLayout: true,
164 valueChanged: (target) => {
165 target._requestRefresh();
166 },
167});
168itemTemplateProperty.register(Repeater);
169/**
170 * Represents the items template property of each Repeater instance.
171 */
172export const itemTemplatesProperty = new Property({
173 name: 'itemTemplates',
174 affectsLayout: true,
175 valueConverter: (value) => {
176 if (typeof value === 'string') {
177 if (__UI_USE_XML_PARSER__) {
178 return Builder.parseMultipleTemplates(value, null);
179 }
180 else {
181 return null;
182 }
183 }
184 return value;
185 },
186 valueChanged: (target) => {
187 target._requestRefresh();
188 },
189});
190itemTemplatesProperty.register(Repeater);
191/**
192 * Represents the property backing the items property of each Repeater instance.
193 */
194export const itemsProperty = new Property({
195 name: 'items',
196 affectsLayout: true,
197 valueChanged: (target, oldValue, newValue) => {
198 if (oldValue instanceof ObservableArray) {
199 removeWeakEventListener(oldValue, ObservableArray.changeEvent, target._onItemsChanged, target);
200 }
201 if (newValue instanceof ObservableArray) {
202 addWeakEventListener(newValue, ObservableArray.changeEvent, target._onItemsChanged, target);
203 }
204 target._requestRefresh();
205 },
206});
207itemsProperty.register(Repeater);
208export const itemsLayoutProperty = new Property({
209 name: 'itemsLayout',
210 affectsLayout: true,
211 valueChanged: (target, oldValue, newValue) => {
212 if (oldValue) {
213 target._removeView(oldValue);
214 oldValue.removeChildren();
215 }
216 if (newValue) {
217 target._addView(newValue);
218 }
219 target._requestRefresh();
220 },
221});
222itemsLayoutProperty.register(Repeater);
223//# sourceMappingURL=index.js.map
\No newline at end of file