1 | import { Label } from '../label';
|
2 | import { View, CSSType, CustomLayoutView } from '../core/view';
|
3 | import { Property } from '../core/properties';
|
4 | import { layout } from '../../utils';
|
5 | import { StackLayout } from '../layouts/stack-layout';
|
6 | import { ObservableArray } from '../../data/observable-array';
|
7 | import { addWeakEventListener, removeWeakEventListener } from '../core/weak-event-listener';
|
8 | import { Builder } from '../builder';
|
9 | import { profile } from '../../profiling';
|
10 | import { isFunction } from '../../utils/types';
|
11 |
|
12 |
|
13 |
|
14 | let Repeater = class Repeater extends CustomLayoutView {
|
15 | constructor() {
|
16 | super();
|
17 | this._isDirty = false;
|
18 |
|
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 |
|
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 |
|
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 |
|
145 | Repeater.knownFunctions = ['itemTemplateSelector'];
|
146 | __decorate([
|
147 | profile,
|
148 | __metadata("design:type", Function),
|
149 | __metadata("design:paramtypes", []),
|
150 | __metadata("design:returntype", void 0)
|
151 | ], Repeater.prototype, "onLoaded", null);
|
152 | Repeater = __decorate([
|
153 | CSSType('Repeater'),
|
154 | __metadata("design:paramtypes", [])
|
155 | ], Repeater);
|
156 | export { Repeater };
|
157 | Repeater.prototype.recycleNativeView = 'auto';
|
158 |
|
159 |
|
160 |
|
161 | export const itemTemplateProperty = new Property({
|
162 | name: 'itemTemplate',
|
163 | affectsLayout: true,
|
164 | valueChanged: (target) => {
|
165 | target._requestRefresh();
|
166 | },
|
167 | });
|
168 | itemTemplateProperty.register(Repeater);
|
169 |
|
170 |
|
171 |
|
172 | export 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 | });
|
190 | itemTemplatesProperty.register(Repeater);
|
191 |
|
192 |
|
193 |
|
194 | export 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 | });
|
207 | itemsProperty.register(Repeater);
|
208 | export 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 | });
|
222 | itemsLayoutProperty.register(Repeater);
|
223 |
|
\ | No newline at end of file |