@cc-component/cc-ex-component 1.1.6 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/assets/core/BaseReference.ts +40 -0
  2. package/assets/{video/VideoComponent.ts.meta → core/BaseReference.ts.meta} +1 -1
  3. package/assets/core/BaseViewModelData.ts +12 -0
  4. package/assets/{video/Interface.ts.meta → core/BaseViewModelData.ts.meta} +1 -1
  5. package/assets/core/ReferenceComponent.ts +317 -0
  6. package/assets/core/ViewModel.ts +542 -0
  7. package/assets/{video/IVideo.ts.meta → core/ViewModel.ts.meta} +9 -9
  8. package/assets/ex/EXButton.ts +191 -0
  9. package/assets/ex/EXButton.ts.meta +9 -0
  10. package/assets/ex/ExCommon.ts +6 -4
  11. package/assets/ex/ExTool.ts +116 -0
  12. package/assets/ex/ExTool.ts.meta +9 -0
  13. package/assets/lib/collectView/lib-ext/custom-grid-flow-layout.ts +105 -0
  14. package/assets/lib/collectView/lib-ext/custom-grid-flow-layout.ts.meta +9 -0
  15. package/assets/lib/collectView/lib-ext/horizontal-center-layout.ts +84 -0
  16. package/assets/lib/collectView/lib-ext/horizontal-center-layout.ts.meta +9 -0
  17. package/assets/lib/collectView/lib-ext/yx-card-page-layout.ts +132 -0
  18. package/assets/lib/collectView/lib-ext/yx-card-page-layout.ts.meta +9 -0
  19. package/assets/lib/collectView/lib-ext/yx-carousel-layout.ts +156 -0
  20. package/assets/lib/collectView/lib-ext/yx-carousel-layout.ts.meta +9 -0
  21. package/assets/lib/collectView/lib-ext/yx-cover-layout.ts +405 -0
  22. package/assets/lib/collectView/lib-ext/yx-cover-layout.ts.meta +9 -0
  23. package/assets/lib/collectView/lib-ext/yx-masonry-flow-layout.ts +194 -0
  24. package/assets/lib/collectView/lib-ext/yx-masonry-flow-layout.ts.meta +9 -0
  25. package/assets/lib/collectView/lib-ext/yx-page-view.ts +232 -0
  26. package/assets/lib/collectView/lib-ext/yx-page-view.ts.meta +9 -0
  27. package/assets/lib/collectView/lib-ext/yx-table-view.ts +159 -0
  28. package/assets/lib/collectView/lib-ext/yx-table-view.ts.meta +9 -0
  29. package/assets/lib/collectView/lib-ext.meta +9 -0
  30. package/assets/lib/collectView/lib_collect/yx-collection-view.ts +1549 -0
  31. package/assets/lib/collectView/lib_collect/yx-collection-view.ts.meta +9 -0
  32. package/assets/lib/collectView/lib_collect/yx-compact-flow-layout.ts +364 -0
  33. package/assets/lib/collectView/lib_collect/yx-compact-flow-layout.ts.meta +9 -0
  34. package/assets/lib/collectView/lib_collect/yx-flow-layout.ts +909 -0
  35. package/assets/lib/collectView/lib_collect/yx-flow-layout.ts.meta +9 -0
  36. package/assets/lib/collectView/lib_collect/yx-table-layout.ts +352 -0
  37. package/assets/lib/collectView/lib_collect/yx-table-layout.ts.meta +9 -0
  38. package/assets/lib/collectView/lib_collect.meta +9 -0
  39. package/assets/{video/list.meta → lib/collectView.meta} +9 -9
  40. package/assets/lib/tableView/IListView.ts +17 -0
  41. package/assets/lib/tableView/IListView.ts.meta +9 -0
  42. package/assets/lib/tableView/ListView.ts +197 -0
  43. package/assets/lib/tableView/ListView.ts.meta +9 -0
  44. package/assets/lib/tableView/ListViewPage.ts +1048 -0
  45. package/assets/lib/tableView/ListViewPage.ts.meta +9 -0
  46. package/assets/lib/tableView/ListViewPageLoop.ts +922 -0
  47. package/assets/lib/tableView/ListViewPageLoop.ts.meta +1 -0
  48. package/assets/lib/tableView/TableView.ts +82 -0
  49. package/assets/lib/tableView/TableView.ts.meta +9 -0
  50. package/assets/lib/tableView.meta +9 -0
  51. package/assets/{video.meta → lib.meta} +1 -1
  52. package/assets/platform/Interface.ts +15 -10
  53. package/assets/platform/android/AndroidModule.ts +12 -0
  54. package/assets/platform/android/AndroidModule.ts.meta +9 -0
  55. package/assets/platform/android/AndroidSDK.ts +1 -2
  56. package/assets/platform/base/PlatfprmModule.ts +44 -29
  57. package/assets/platform/base/SDKBase.ts +2 -2
  58. package/assets/platform/base/TTSDK.ts +21 -10
  59. package/assets/platform/base/WXSDK.ts +15 -16
  60. package/assets/platform/wx/MiniSDK.ts +41 -3
  61. package/assets/platform/wx/wxmini.d.ts +2 -2
  62. package/index.ts +0 -1
  63. package/package.json +1 -1
  64. package/assets/core/ReferenceCollector.ts +0 -172
  65. package/assets/video/IVideo.ts +0 -73
  66. package/assets/video/Interface.ts +0 -25
  67. package/assets/video/VideoComponent.prefab +0 -614
  68. package/assets/video/VideoComponent.prefab.meta +0 -13
  69. package/assets/video/VideoComponent.ts +0 -33
  70. package/assets/video/VideoManager.ts +0 -399
  71. package/assets/video/VideoManager.ts.meta +0 -9
  72. package/assets/video/VideoModule.ts +0 -137
  73. package/assets/video/VideoModule.ts.meta +0 -9
  74. package/assets/video/VideoPlayTT.ts +0 -338
  75. package/assets/video/VideoPlayTT.ts.meta +0 -9
  76. package/assets/video/VideoPlayWX.ts +0 -274
  77. package/assets/video/VideoPlayWX.ts.meta +0 -9
  78. package/assets/video/VideoPlayWeb.ts +0 -228
  79. package/assets/video/VideoPlayWeb.ts.meta +0 -9
  80. /package/assets/core/{ReferenceCollector.ts.meta → ReferenceComponent.ts.meta} +0 -0
@@ -0,0 +1,909 @@
1
+ import { _decorator, math, UITransform } from 'cc';
2
+ import { YXBinaryLayout, YXCollectionView, YXEdgeInsets, YXIndexPath, YXLayoutAttributes } from './yx-collection-view';
3
+
4
+ const _rectOut = new math.Rect()
5
+
6
+ enum _yx_flow_layout_alignment {
7
+
8
+ /**
9
+ * 节点在所处行/列内居中
10
+ */
11
+ CENTER,
12
+
13
+ /**
14
+ * 这个配置表示向前对齐,节点会向排列开始的方向对齐,具体表现为:
15
+ * 当列表为垂直方向列表时,如果节点排列方向是由上至下排列的,这个值就代表上对齐,如果节点排列方向是由下至上排列的,这个值就代表下对齐
16
+ * 当列表为水平方向列表时,如果节点排列方向是由左至右排列的,这个值就代表左对齐,如果节点排列方向是由右至左排列的,这个值就代表右对齐
17
+ */
18
+ FORWARD,
19
+
20
+ /**
21
+ * 这个配置表示向后对齐,具体表现跟 FORWARD 相反
22
+ */
23
+ BACKWARD,
24
+ }
25
+
26
+
27
+ enum _yx_flow_layout_priority {
28
+
29
+ /**
30
+ * 以 spacing 配置为准,弱化 sectionInset 配置
31
+ */
32
+ CENTER,
33
+
34
+ /**
35
+ * 以 sectionInset 配置为准,弱化 spacing 配置
36
+ */
37
+ SIDE,
38
+
39
+ /**
40
+ * 以 sectionInset.left 为准,弱化 right 配置
41
+ * 仅支持垂直滚动列表
42
+ */
43
+ LEFT,
44
+
45
+ /**
46
+ * 以 sectionInset.right 为准,弱化 left 配置
47
+ * 仅支持垂直滚动列表
48
+ */
49
+ RIGHT,
50
+
51
+ /**
52
+ * 以 sectionInset.top 为准,弱化 bottom 配置
53
+ * 仅支持水平滚动列表
54
+ */
55
+ TOP,
56
+
57
+ /**
58
+ * 以 sectionInset.bottom 为准,弱化 top 配置
59
+ * 仅支持水平滚动列表
60
+ */
61
+ BOTTOM,
62
+ }
63
+
64
+ enum _yx_flow_layout_reverse_mode {
65
+
66
+ /**
67
+ * 默认方案
68
+ * 水平排列方向为从左至右排列
69
+ * 垂直排列方向为从上至下排列
70
+ */
71
+ NONE = 0,
72
+
73
+ /**
74
+ * 这个标识会将水平排列方向变为从右至左排列
75
+ */
76
+ HORIZONTAL = 1 << 0,
77
+
78
+ /**
79
+ * 这个标识会将垂直排列方向变为从下至上排列
80
+ */
81
+ VERTICAL = 1 << 1,
82
+ }
83
+
84
+ /**
85
+ * flow-layout 约定了每个区允许配置一个区头和一个区尾
86
+ * 设置 sectionHeaderSize 开启区头
87
+ * 设置 sectionFooterSize 开启区尾
88
+ * 通过 YXCollectionView 的 supplementaryForItemAt 返回区头/区尾对应的节点
89
+ *
90
+ * 在使用列表的补充视图 (supplementary) 相关的接口时,可以通过这个 kinds 枚举判断是区头还是区尾
91
+ */
92
+ enum _yx_flow_layout_section_kinds {
93
+ HEADER = 'flow-layout-HEADER',
94
+ FOOTER = 'flow-layout-FOOTER',
95
+ }
96
+
97
+ /**
98
+ * 流式布局
99
+ * 支持水平/垂直方向排列滚动
100
+ */
101
+ export class YXFlowLayout extends YXBinaryLayout {
102
+
103
+ /**
104
+ * 是否开启分页滚动效果
105
+ */
106
+ pagingEnabled: boolean = false
107
+
108
+ /**
109
+ * 分页吸附动画时间
110
+ */
111
+ pagingAnimationDuration: number = 0.5
112
+
113
+ /**
114
+ * 节点在行(垂直列表)或者列(水平列表)内的对齐方式
115
+ */
116
+ alignment: _yx_flow_layout_alignment = YXFlowLayout.Alignment.CENTER
117
+ static Alignment = _yx_flow_layout_alignment
118
+
119
+ /**
120
+ * 约束优先级
121
+ */
122
+ priority: _yx_flow_layout_priority = YXFlowLayout.Priority.LEFT
123
+ static Priority = _yx_flow_layout_priority
124
+
125
+ /**
126
+ * 当 priority 设置为 SIDE 模式,并且最后一行或者最后一列只有一个单元 cell 时,这一个 cell 如何对齐
127
+ * 默认是 null,也就是垂直列表时靠左,水平列表时靠上
128
+ * 正常情况下,一般不需要关心这个配置,除非列表是 SIDE 模式并且需要调整最后一个单元 cell 的对齐方式时才需要调整这个配置
129
+ */
130
+ lastItemPriority: _yx_flow_layout_priority = null
131
+
132
+ /**
133
+ * 反向排列节点
134
+ * @example
135
+ * // 水平反向排列
136
+ * layout.reverse = YXFlowLayout.Reverse.HORIZONTAL
137
+ *
138
+ * // 垂直反向排列
139
+ * layout.reverse = YXFlowLayout.Reverse.VERTICAL
140
+ *
141
+ * // 水平/垂直都反向排列
142
+ * layout.reverse = YXFlowLayout.Reverse.HORIZONTAL | YXFlowLayout.Reverse.VERTICAL
143
+ */
144
+ reverse: _yx_flow_layout_reverse_mode = YXFlowLayout.Reverse.NONE
145
+ static Reverse = _yx_flow_layout_reverse_mode
146
+
147
+ /**
148
+ * 元素大小
149
+ */
150
+ itemSize: math.Size | ((indexPath: YXIndexPath, layout: YXFlowLayout, collectionView: YXCollectionView) => math.Size) = new math.Size(100, 100)
151
+ getItemSize(): math.Size {
152
+ if (this.itemSize instanceof Function == false) {
153
+ return this.itemSize
154
+ }
155
+ throw new Error("YXFlowLayout: 动态配置的布局参数不支持直接获取,请检查自己的布局逻辑并谨慎的通过动态配置自己获取,注意避免死循环");
156
+ }
157
+
158
+ /**
159
+ * 元素之间垂直间距
160
+ */
161
+ verticalSpacing: number | ((section: number, layout: YXFlowLayout, collectionView: YXCollectionView) => number) = 0
162
+ getVerticalSpacing(): number {
163
+ if (this.verticalSpacing instanceof Function == false) {
164
+ return this.verticalSpacing
165
+ }
166
+ throw new Error("YXFlowLayout: 动态配置的布局参数不支持直接获取,请检查自己的布局逻辑并谨慎的通过动态配置自己获取,注意避免死循环");
167
+ }
168
+
169
+ /**
170
+ * 元素之间水平间距
171
+ */
172
+ horizontalSpacing: number | ((section: number, layout: YXFlowLayout, collectionView: YXCollectionView) => number) = 0
173
+ getHorizontalSpacing(): number {
174
+ if (this.horizontalSpacing instanceof Function == false) {
175
+ return this.horizontalSpacing
176
+ }
177
+ throw new Error("YXFlowLayout: 动态配置的布局参数不支持直接获取,请检查自己的布局逻辑并谨慎的通过动态配置自己获取,注意避免死循环");
178
+ }
179
+
180
+ /**
181
+ * 边距
182
+ */
183
+ sectionInset: YXEdgeInsets | ((section: number, layout: YXFlowLayout, collectionView: YXCollectionView) => YXEdgeInsets) = YXEdgeInsets.ZERO
184
+ getSectionInset(): YXEdgeInsets {
185
+ if (this.sectionInset instanceof Function == false) {
186
+ return this.sectionInset
187
+ }
188
+ throw new Error("YXFlowLayout: 动态配置的布局参数不支持直接获取,请检查自己的布局逻辑并谨慎的通过动态配置自己获取,注意避免死循环");
189
+ }
190
+
191
+ /**
192
+ * 区头大小
193
+ */
194
+ sectionHeaderSize: math.Size | ((section: number, layout: YXFlowLayout, collectionView: YXCollectionView) => math.Size) = null
195
+ getSectionHeaderSize(): math.Size {
196
+ if (this.sectionHeaderSize instanceof Function == false) {
197
+ return this.sectionHeaderSize
198
+ }
199
+ throw new Error("YXFlowLayout: 动态配置的布局参数不支持直接获取,请检查自己的布局逻辑并谨慎的通过动态配置自己获取,注意避免死循环");
200
+ }
201
+
202
+ /**
203
+ * 区尾大小
204
+ */
205
+ sectionFooterSize: math.Size | ((section: number, layout: YXFlowLayout, collectionView: YXCollectionView) => math.Size) = null
206
+ getSectionFooterSize(): math.Size {
207
+ if (this.sectionFooterSize instanceof Function == false) {
208
+ return this.sectionFooterSize
209
+ }
210
+ throw new Error("YXFlowLayout: 动态配置的布局参数不支持直接获取,请检查自己的布局逻辑并谨慎的通过动态配置自己获取,注意避免死循环");
211
+ }
212
+
213
+ /**
214
+ * 访问定义的区头/区尾标识
215
+ */
216
+ static SupplementaryKinds = _yx_flow_layout_section_kinds
217
+
218
+ /**
219
+ * 钉住 header 的位置 ( header 吸附在列表可见范围内 )
220
+ * 垂直列表设置了垂直反向排列 或者 水平列表设置了水平反向排列时,此配置不会生效
221
+ */
222
+ sectionHeadersPinToVisibleBounds: boolean = false
223
+
224
+ /**
225
+ * 钉住 footer 的位置 ( footer 吸附在列表可见范围内 )
226
+ * 垂直列表设置了垂直反向排列 或者 水平列表设置了水平反向排列时,此配置不会生效
227
+ */
228
+ sectionFootersPinToVisibleBounds: boolean = false
229
+
230
+ protected sectionPinToVisibleBoundsSupported = false // 是否支持 Pin 区头/区尾业务
231
+ protected originalHeaderRect: Map<number, math.Rect> = new Map() // 保存所有 header 的原始位置
232
+ protected originalFooterRect: Map<number, math.Rect> = new Map() // 保存所有 footer 的原始位置
233
+
234
+ prepare(collectionView: YXCollectionView): void {
235
+ this.originalHeaderRect.clear()
236
+ this.originalFooterRect.clear()
237
+ this.sectionPinToVisibleBoundsSupported = (this.sectionHeadersPinToVisibleBounds || this.sectionFootersPinToVisibleBounds)
238
+ if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.HORIZONTAL) {
239
+ if (this.reverse & _yx_flow_layout_reverse_mode.HORIZONTAL) { this.sectionPinToVisibleBoundsSupported = false }
240
+ this._horizontal(collectionView)
241
+ return
242
+ }
243
+ if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.VERTICAL) {
244
+ if (this.reverse & _yx_flow_layout_reverse_mode.VERTICAL) { this.sectionPinToVisibleBoundsSupported = false }
245
+ this._vertical(collectionView)
246
+ return
247
+ }
248
+ }
249
+
250
+ initOffset(collectionView: YXCollectionView): void {
251
+ if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.HORIZONTAL) {
252
+ collectionView.scrollView.scrollToLeft(0)
253
+ return
254
+ }
255
+ if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.VERTICAL) {
256
+ collectionView.scrollView.scrollToTop(0)
257
+ return
258
+ }
259
+ }
260
+
261
+ targetOffset(collectionView: YXCollectionView, touchMoveVelocity: math.Vec3, startOffset: math.Vec2, originTargetOffset: math.Vec2, originScrollDuration: number): { offset: math.Vec2; time?: number; attenuated?: boolean; } | null {
262
+ if (this.pagingEnabled == false) {
263
+ return null
264
+ }
265
+ let offset = collectionView.scrollView.getScrollOffset()
266
+ offset.x = - offset.x
267
+ let threshold = 0.2
268
+ if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.HORIZONTAL) {
269
+ let idx = Math.round(offset.x / collectionView.scrollView.view.width)
270
+ let r = touchMoveVelocity.x / collectionView.scrollView.view.width
271
+ if (startOffset && Math.abs(r) >= threshold) {
272
+ idx = Math.round(startOffset.x / collectionView.scrollView.view.width) + (r > 0 ? -1 : 1)
273
+ }
274
+ offset.x = idx * collectionView.scrollView.view.width
275
+ }
276
+ if (collectionView.scrollDirection == YXCollectionView.ScrollDirection.VERTICAL) {
277
+ let idx = Math.round(offset.y / collectionView.scrollView.view.height)
278
+ let r = touchMoveVelocity.y / collectionView.scrollView.view.height
279
+ if (startOffset && Math.abs(r) >= threshold) {
280
+ idx = Math.round(startOffset.y / collectionView.scrollView.view.height) + (r > 0 ? 1 : -1)
281
+ }
282
+ offset.y = idx * collectionView.scrollView.view.height
283
+ }
284
+ return { offset: offset, time: this.pagingAnimationDuration }
285
+ }
286
+
287
+ protected _horizontal(collectionView: YXCollectionView) {
288
+ collectionView.scrollView.horizontal = true
289
+ collectionView.scrollView.vertical = false
290
+ let contentSize = collectionView.node.getComponent(UITransform).contentSize.clone()
291
+ let allAttributes: YXLayoutAttributes[] = []
292
+
293
+ let numberOfSections = collectionView.getNumberOfSections()
294
+
295
+ let sectionMaxX = 0
296
+ for (let section = 0; section < numberOfSections; section++) {
297
+ let numberOfItems = collectionView.getNumberOfItems(section)
298
+ let verticalSpacing = this.verticalSpacing instanceof Function ? this.verticalSpacing(section, this, collectionView) : this.verticalSpacing
299
+ let horizontalSpacing = this.horizontalSpacing instanceof Function ? this.horizontalSpacing(section, this, collectionView) : this.horizontalSpacing
300
+ let sectionInset = this.sectionInset instanceof Function ? this.sectionInset(section, this, collectionView) : this.sectionInset
301
+
302
+ // header
303
+ let sectionHeaderSize = this.sectionHeaderSize instanceof Function ? this.sectionHeaderSize(section, this, collectionView) : this.sectionHeaderSize
304
+ if (sectionHeaderSize && sectionHeaderSize.width > 0 && sectionHeaderSize.height > 0) {
305
+ let indexPath = new YXIndexPath(section, 0)
306
+ let attr = YXLayoutAttributes.layoutAttributesForSupplementary(indexPath, _yx_flow_layout_section_kinds.HEADER)
307
+ attr.zIndex = 1
308
+ attr.frame.y = sectionInset.top
309
+ attr.frame.x = sectionMaxX
310
+ attr.frame.size = sectionHeaderSize
311
+ allAttributes.push(attr)
312
+ sectionMaxX = attr.frame.xMax + sectionInset.left
313
+ this.originalHeaderRect.set(attr.indexPath.section, attr.frame.clone())
314
+ } else {
315
+ sectionMaxX += sectionInset.left
316
+ }
317
+
318
+ // cells
319
+ let whole = new _yx_flow_layout_whole()
320
+ whole.verticalSpacing = verticalSpacing
321
+ whole.horizontalSpacing = horizontalSpacing
322
+ whole.sectionInset = sectionInset
323
+ whole.offset = sectionMaxX
324
+ whole.attrs = []
325
+ whole.containerWidth = 0
326
+ whole.containerHeight = contentSize.height
327
+ whole.alignment = this.alignment
328
+ whole.priority = this.priority
329
+ whole.lastItemPriority = this.lastItemPriority
330
+ whole.reverse = this.reverse
331
+
332
+ for (let item = 0; item < numberOfItems; item++) {
333
+ let indexPath = new YXIndexPath(section, item)
334
+ let itemSize = this.itemSize instanceof Function ? this.itemSize(indexPath, this, collectionView) : this.itemSize
335
+
336
+ let attr = whole.layout_horizontal_item(indexPath, itemSize)
337
+ if (attr == null) {
338
+ // 返回 null 表示摆不下了,需要换列排列
339
+ whole.layout_horizontal_complet()
340
+ whole.offset = whole.offset + whole.containerWidth + horizontalSpacing
341
+ whole.containerWidth = 0
342
+ whole.attrs = []
343
+ attr = whole.layout_horizontal_item(indexPath, itemSize)
344
+ }
345
+ if (attr) {
346
+ allAttributes.push(attr)
347
+ }
348
+ sectionMaxX = Math.max(sectionMaxX, whole.offset + whole.containerWidth)
349
+ }
350
+
351
+ whole.layout_horizontal_complet()
352
+ sectionMaxX += sectionInset.right
353
+
354
+ // footer
355
+ let sectionFooterSize = this.sectionFooterSize instanceof Function ? this.sectionFooterSize(section, this, collectionView) : this.sectionFooterSize
356
+ if (sectionFooterSize && sectionFooterSize.width > 0 && sectionFooterSize.height > 0) {
357
+ let indexPath = new YXIndexPath(section, 0)
358
+ let attr = YXLayoutAttributes.layoutAttributesForSupplementary(indexPath, _yx_flow_layout_section_kinds.FOOTER)
359
+ attr.zIndex = 1
360
+ attr.frame.y = sectionInset.top
361
+ attr.frame.x = sectionMaxX
362
+ attr.frame.size = sectionFooterSize
363
+ allAttributes.push(attr)
364
+ sectionMaxX = attr.frame.xMax + sectionInset.left
365
+ this.originalFooterRect.set(attr.indexPath.section, attr.frame.clone())
366
+ }
367
+ }
368
+
369
+ this._reverse_horizontal(allAttributes, sectionMaxX)
370
+ this.attributes = allAttributes
371
+ contentSize.width = Math.max(contentSize.width, sectionMaxX)
372
+ this.contentSize = contentSize
373
+ }
374
+
375
+ protected _reverse_horizontal(attributes: YXLayoutAttributes[], contentWidth: number) {
376
+ if (this.reverse & _yx_flow_layout_reverse_mode.HORIZONTAL) {
377
+ for (let index = 0; index < attributes.length; index++) {
378
+ const element = attributes[index];
379
+ element.frame.x = contentWidth - element.frame.x - element.frame.width;
380
+ }
381
+ }
382
+ }
383
+
384
+ protected _vertical(collectionView: YXCollectionView) {
385
+ collectionView.scrollView.horizontal = false
386
+ collectionView.scrollView.vertical = true
387
+ let contentSize = collectionView.node.getComponent(UITransform).contentSize.clone()
388
+ let allAttributes: YXLayoutAttributes[] = []
389
+
390
+ let numberOfSections = collectionView.getNumberOfSections()
391
+
392
+ let sectionMaxY = 0
393
+ for (let section = 0; section < numberOfSections; section++) {
394
+ let numberOfItems = collectionView.getNumberOfItems(section)
395
+ let verticalSpacing = this.verticalSpacing instanceof Function ? this.verticalSpacing(section, this, collectionView) : this.verticalSpacing
396
+ let horizontalSpacing = this.horizontalSpacing instanceof Function ? this.horizontalSpacing(section, this, collectionView) : this.horizontalSpacing
397
+ let sectionInset = this.sectionInset instanceof Function ? this.sectionInset(section, this, collectionView) : this.sectionInset
398
+
399
+ // header
400
+ let sectionHeaderSize = this.sectionHeaderSize instanceof Function ? this.sectionHeaderSize(section, this, collectionView) : this.sectionHeaderSize
401
+ if (sectionHeaderSize && sectionHeaderSize.width > 0 && sectionHeaderSize.height > 0) {
402
+ let indexPath = new YXIndexPath(section, 0)
403
+ let attr = YXLayoutAttributes.layoutAttributesForSupplementary(indexPath, _yx_flow_layout_section_kinds.HEADER)
404
+ attr.zIndex = 1
405
+ attr.frame.y = sectionMaxY
406
+ attr.frame.x = sectionInset.left
407
+ attr.frame.size = sectionHeaderSize
408
+ allAttributes.push(attr)
409
+ sectionMaxY = attr.frame.yMax + sectionInset.top
410
+ this.originalHeaderRect.set(attr.indexPath.section, attr.frame.clone())
411
+ } else {
412
+ sectionMaxY += sectionInset.top
413
+ }
414
+
415
+ // cells
416
+ let whole = new _yx_flow_layout_whole()
417
+ whole.verticalSpacing = verticalSpacing
418
+ whole.horizontalSpacing = horizontalSpacing
419
+ whole.sectionInset = sectionInset
420
+ whole.offset = sectionMaxY
421
+ whole.attrs = []
422
+ whole.containerWidth = contentSize.width
423
+ whole.containerHeight = 0
424
+ whole.alignment = this.alignment
425
+ whole.priority = this.priority
426
+ whole.lastItemPriority = this.lastItemPriority
427
+ whole.reverse = this.reverse
428
+
429
+ for (let item = 0; item < numberOfItems; item++) {
430
+ let indexPath = new YXIndexPath(section, item)
431
+ let itemSize = this.itemSize instanceof Function ? this.itemSize(indexPath, this, collectionView) : this.itemSize
432
+
433
+ let attr = whole.layout_vertical_item(indexPath, itemSize)
434
+ if (attr == null) {
435
+ // 返回 null 表示摆不下了,需要换行排列
436
+ whole.layout_vertical_complet()
437
+ whole.offset = whole.offset + whole.containerHeight + verticalSpacing
438
+ whole.containerHeight = 0
439
+ whole.attrs = []
440
+ attr = whole.layout_vertical_item(indexPath, itemSize)
441
+ }
442
+ if (attr) {
443
+ allAttributes.push(attr)
444
+ }
445
+ sectionMaxY = Math.max(sectionMaxY, whole.offset + whole.containerHeight)
446
+ }
447
+
448
+ whole.layout_vertical_complet()
449
+ sectionMaxY += sectionInset.bottom
450
+
451
+ // footer
452
+ let sectionFooterSize = this.sectionFooterSize instanceof Function ? this.sectionFooterSize(section, this, collectionView) : this.sectionFooterSize
453
+ if (sectionFooterSize && sectionFooterSize.width > 0 && sectionFooterSize.height > 0) {
454
+ let indexPath = new YXIndexPath(section, 0)
455
+ let attr = YXLayoutAttributes.layoutAttributesForSupplementary(indexPath, _yx_flow_layout_section_kinds.FOOTER)
456
+ attr.zIndex = 1
457
+ attr.frame.y = sectionMaxY
458
+ attr.frame.x = sectionInset.left
459
+ attr.frame.size = sectionFooterSize
460
+ allAttributes.push(attr)
461
+ sectionMaxY = attr.frame.yMax
462
+ this.originalFooterRect.set(attr.indexPath.section, attr.frame.clone())
463
+ }
464
+ }
465
+
466
+ this._reverse_vertical(allAttributes, sectionMaxY)
467
+ this.attributes = allAttributes
468
+ contentSize.height = Math.max(contentSize.height, sectionMaxY)
469
+ this.contentSize = contentSize
470
+ }
471
+
472
+ protected _reverse_vertical(attributes: YXLayoutAttributes[], contentHeight: number) {
473
+ if (this.reverse & _yx_flow_layout_reverse_mode.VERTICAL) {
474
+ for (let index = 0; index < attributes.length; index++) {
475
+ const element = attributes[index];
476
+ element.frame.y = contentHeight - element.frame.y - element.frame.height;
477
+ }
478
+ }
479
+ }
480
+
481
+ layoutAttributesForElementsInRect(rect: math.Rect, collectionView: YXCollectionView): YXLayoutAttributes[] {
482
+ if (!this.sectionPinToVisibleBoundsSupported) {
483
+ return super.layoutAttributesForElementsInRect(rect, collectionView)
484
+ }
485
+ if (collectionView.scrollDirection === YXCollectionView.ScrollDirection.HORIZONTAL) {
486
+ return this.layoutAttributesForElementsInRect_horizontal(rect, collectionView)
487
+ }
488
+ if (collectionView.scrollDirection === YXCollectionView.ScrollDirection.VERTICAL) {
489
+ return this.layoutAttributesForElementsInRect_vertical(rect, collectionView)
490
+ }
491
+ return this.attributes
492
+ }
493
+
494
+ protected layoutAttributesForElementsInRect_horizontal(rect: math.Rect, collectionView: YXCollectionView): YXLayoutAttributes[] {
495
+ let numberOfSections = collectionView.getNumberOfSections()
496
+ let scrollOffset = collectionView.scrollView.getScrollOffset()
497
+ scrollOffset.x = - scrollOffset.x
498
+ for (let index = 0; index < this.attributes.length; index++) {
499
+ const element = this.attributes[index];
500
+ if (element.elementCategory === 'Supplementary') {
501
+
502
+ if (this.sectionHeadersPinToVisibleBounds && element.supplementaryKinds === _yx_flow_layout_section_kinds.HEADER) {
503
+ const originalFrame = this.originalHeaderRect.get(element.indexPath.section)
504
+ element.frame.x = originalFrame.x
505
+ if (scrollOffset.x > originalFrame.x) {
506
+ element.frame.x = scrollOffset.x
507
+ }
508
+ const nextOriginalFrame = this.getNextOriginalFrame(element.indexPath.section, _yx_flow_layout_section_kinds.FOOTER, numberOfSections)
509
+ if (nextOriginalFrame) {
510
+ if (element.frame.xMax > nextOriginalFrame.x) {
511
+ element.frame.x = nextOriginalFrame.x - element.frame.width
512
+ }
513
+ }
514
+ }
515
+
516
+ if (this.sectionFootersPinToVisibleBounds && element.supplementaryKinds === _yx_flow_layout_section_kinds.FOOTER) {
517
+ let bottom = scrollOffset.x + collectionView.scrollView.view.width
518
+ const originalFrame = this.originalFooterRect.get(element.indexPath.section)
519
+ const previousOriginalFrame = this.getPreviousOriginalFrame(element.indexPath.section, _yx_flow_layout_section_kinds.HEADER)
520
+ element.frame.x = originalFrame.x
521
+ if (bottom < originalFrame.xMax) {
522
+ element.frame.x = bottom - element.frame.width
523
+ if (previousOriginalFrame) {
524
+ if (element.frame.x < previousOriginalFrame.xMax) {
525
+ element.frame.x = previousOriginalFrame.xMax
526
+ }
527
+ }
528
+ }
529
+ }
530
+ }
531
+ }
532
+ return this.attributes
533
+ }
534
+
535
+ protected layoutAttributesForElementsInRect_vertical(rect: math.Rect, collectionView: YXCollectionView): YXLayoutAttributes[] {
536
+ let numberOfSections = collectionView.getNumberOfSections()
537
+ let scrollOffset = collectionView.scrollView.getScrollOffset()
538
+ for (let index = 0; index < this.attributes.length; index++) {
539
+ const element = this.attributes[index];
540
+ if (element.elementCategory === 'Supplementary') {
541
+
542
+ if (this.sectionHeadersPinToVisibleBounds && element.supplementaryKinds === _yx_flow_layout_section_kinds.HEADER) {
543
+ const originalFrame = this.originalHeaderRect.get(element.indexPath.section)
544
+ element.frame.y = originalFrame.y
545
+ if (scrollOffset.y > originalFrame.y) {
546
+ element.frame.y = scrollOffset.y
547
+ }
548
+ const nextOriginalFrame = this.getNextOriginalFrame(element.indexPath.section, _yx_flow_layout_section_kinds.FOOTER, numberOfSections)
549
+ if (nextOriginalFrame) {
550
+ if (element.frame.yMax > nextOriginalFrame.y) {
551
+ element.frame.y = nextOriginalFrame.y - element.frame.height
552
+ }
553
+ }
554
+ }
555
+
556
+ if (this.sectionFootersPinToVisibleBounds && element.supplementaryKinds === _yx_flow_layout_section_kinds.FOOTER) {
557
+ let bottom = scrollOffset.y + collectionView.scrollView.view.height
558
+ const originalFrame = this.originalFooterRect.get(element.indexPath.section)
559
+ const previousOriginalFrame = this.getPreviousOriginalFrame(element.indexPath.section, _yx_flow_layout_section_kinds.HEADER)
560
+ element.frame.y = originalFrame.y
561
+ if (bottom < originalFrame.yMax) {
562
+ element.frame.y = bottom - element.frame.height
563
+ if (previousOriginalFrame) {
564
+ if (element.frame.y < previousOriginalFrame.yMax) {
565
+ element.frame.y = previousOriginalFrame.yMax
566
+ }
567
+ }
568
+ }
569
+ }
570
+ }
571
+ }
572
+ return this.attributes
573
+ }
574
+
575
+ protected getNextOriginalFrame(section: number, kinds: _yx_flow_layout_section_kinds, total: number) {
576
+ if (section >= total) { return null }
577
+ if (kinds === _yx_flow_layout_section_kinds.HEADER) {
578
+ let result = this.originalHeaderRect.get(section)
579
+ if (result) { return result }
580
+ return this.getNextOriginalFrame(section, _yx_flow_layout_section_kinds.FOOTER, total)
581
+ }
582
+ if (kinds === _yx_flow_layout_section_kinds.FOOTER) {
583
+ let result = this.originalFooterRect.get(section)
584
+ if (result) { return result }
585
+ return this.getNextOriginalFrame(section + 1, _yx_flow_layout_section_kinds.HEADER, total)
586
+ }
587
+ return null
588
+ }
589
+
590
+ protected getPreviousOriginalFrame(section: number, kinds: _yx_flow_layout_section_kinds) {
591
+ if (section < 0) { return null }
592
+ if (kinds === _yx_flow_layout_section_kinds.HEADER) {
593
+ let result = this.originalHeaderRect.get(section)
594
+ if (result) { return result }
595
+ return this.getPreviousOriginalFrame(section - 1, _yx_flow_layout_section_kinds.FOOTER)
596
+ }
597
+ if (kinds === _yx_flow_layout_section_kinds.FOOTER) {
598
+ let result = this.originalFooterRect.get(section)
599
+ if (result) { return result }
600
+ return this.getPreviousOriginalFrame(section, _yx_flow_layout_section_kinds.HEADER)
601
+ }
602
+ return null
603
+ }
604
+
605
+ shouldUpdateAttributesForBoundsChange(): boolean {
606
+ return this.sectionPinToVisibleBoundsSupported
607
+ }
608
+ shouldUpdateAttributesZIndex(): boolean {
609
+ return this.sectionPinToVisibleBoundsSupported
610
+ }
611
+
612
+ onDestroy(): void {
613
+ this.attributes = []
614
+ this.attributes = null
615
+
616
+ this.originalHeaderRect.clear()
617
+ this.originalHeaderRect = null
618
+
619
+ this.originalFooterRect.clear()
620
+ this.originalHeaderRect = null
621
+ }
622
+ }
623
+
624
+ export namespace YXFlowLayout {
625
+ export type Alignment = _yx_flow_layout_alignment
626
+ export type Priority = _yx_flow_layout_priority
627
+ export type Reverse = _yx_flow_layout_reverse_mode
628
+ }
629
+
630
+ /**
631
+ * 实现按行/列排列节点 && 调整对齐方式/约束优先级
632
+ * 一个 whole 对象管理一行或者一列的节点
633
+ */
634
+ class _yx_flow_layout_whole {
635
+
636
+ /**
637
+ * 当前这块内容的起始位置
638
+ * 可能是 x,可能是 y,取决于排列方向
639
+ */
640
+ offset: number
641
+
642
+ /**
643
+ * flow layout 部分参数
644
+ */
645
+ verticalSpacing: number
646
+ horizontalSpacing: number
647
+ sectionInset: YXEdgeInsets
648
+ alignment: _yx_flow_layout_alignment
649
+ priority: _yx_flow_layout_priority
650
+ lastItemPriority: _yx_flow_layout_priority
651
+ reverse: _yx_flow_layout_reverse_mode
652
+
653
+ /**
654
+ * 容器相关的参数
655
+ */
656
+ containerWidth: number
657
+ containerHeight: number
658
+
659
+ /**
660
+ * 这块内容区域目前已经摆放的节点
661
+ */
662
+ attrs: YXLayoutAttributes[] = []
663
+
664
+ /**
665
+ * 检查传进来的位置是否跟当前这块内容的其他节点重叠
666
+ */
667
+ intersects(rect: math.Rect) {
668
+ for (let index = 0; index < this.attrs.length; index++) {
669
+ const element = this.attrs[index];
670
+ math.Rect.intersection(_rectOut, element.frame, rect)
671
+ if (_rectOut.width > 0 && _rectOut.height > 0) {
672
+ return true
673
+ }
674
+ }
675
+ return false
676
+ }
677
+
678
+ /**
679
+ * 垂直方向列表的节点的排列规则
680
+ */
681
+ layout_vertical_item(indexPath: YXIndexPath, itemSize: math.Size) {
682
+ if (this.attrs.length <= 0) {
683
+ let attributes = YXLayoutAttributes.layoutAttributesForCell(indexPath)
684
+ attributes.frame.set(this.sectionInset.left, this.offset, itemSize.width, itemSize.height)
685
+ this.attrs.push(attributes)
686
+ this.containerHeight = Math.max(this.containerHeight, attributes.frame.height)
687
+ return attributes
688
+ }
689
+
690
+ const frame = new math.Rect()
691
+ frame.size = itemSize
692
+
693
+ // 检查所有节点的右边
694
+ for (let index = 0; index < this.attrs.length; index++) {
695
+ const element = this.attrs[index];
696
+ frame.x = element.frame.xMax + this.horizontalSpacing
697
+ frame.y = element.frame.y
698
+ if (frame.xMax <= (this.containerWidth - this.sectionInset.right)) {
699
+ if (this.intersects(frame) == false) {
700
+ let attributes = YXLayoutAttributes.layoutAttributesForCell(indexPath)
701
+ attributes.frame.set(frame)
702
+ this.attrs.push(attributes)
703
+ this.containerHeight = Math.max(this.containerHeight, attributes.frame.yMax - this.offset)
704
+ return attributes
705
+ }
706
+ }
707
+ }
708
+
709
+ // 走到这里表示这块内容区域已经摆不下了,需要换行处理
710
+ return null
711
+ }
712
+
713
+ layout_vertical_complet() {
714
+
715
+ // 反转水平方向 (如果需要的话)
716
+ if (this.reverse & _yx_flow_layout_reverse_mode.HORIZONTAL) {
717
+ this.attrs = this.attrs.reverse()
718
+ }
719
+
720
+ // 调整对齐方式
721
+ if (this.alignment == _yx_flow_layout_alignment.CENTER) {
722
+ for (let index = 0; index < this.attrs.length; index++) {
723
+ const element = this.attrs[index];
724
+ element.frame.y = this.offset + (this.containerHeight - element.frame.height) * 0.5
725
+ }
726
+ }
727
+ if (this.alignment == _yx_flow_layout_alignment.BACKWARD) {
728
+ for (let index = 0; index < this.attrs.length; index++) {
729
+ const element = this.attrs[index];
730
+ element.frame.y = this.offset + (this.containerHeight - element.frame.height)
731
+ }
732
+ }
733
+
734
+ // 按优先级调整节点位置
735
+ let priority = this.priority
736
+ if (priority == _yx_flow_layout_priority.SIDE && this.attrs.length == 1) {
737
+ priority = this.lastItemPriority
738
+ }
739
+ if (priority == _yx_flow_layout_priority.TOP || priority == _yx_flow_layout_priority.BOTTOM) {
740
+ throw new Error("flow layout priority/lastItemPriority 配置错误: 不支持的优先级配置");
741
+ }
742
+ if (priority == _yx_flow_layout_priority.CENTER) {
743
+ let totalWidth = 0
744
+ for (let index = 0; index < this.attrs.length; index++) {
745
+ const element = this.attrs[index];
746
+ totalWidth += element.frame.width
747
+ }
748
+ totalWidth = totalWidth + (this.attrs.length - 1) * this.horizontalSpacing
749
+ let left = (this.containerWidth - totalWidth) * 0.5
750
+ for (let index = 0; index < this.attrs.length; index++) {
751
+ const element = this.attrs[index];
752
+ element.frame.x = left
753
+ left = element.frame.xMax + this.horizontalSpacing
754
+ }
755
+ }
756
+ if (priority == _yx_flow_layout_priority.LEFT && this.reverse & _yx_flow_layout_reverse_mode.HORIZONTAL) {
757
+ let left = this.sectionInset.left
758
+ for (let index = 0; index < this.attrs.length; index++) {
759
+ const element = this.attrs[index];
760
+ element.frame.x = left
761
+ left = element.frame.xMax + this.horizontalSpacing
762
+ }
763
+ }
764
+ if (priority == _yx_flow_layout_priority.RIGHT) {
765
+ let totalWidth = 0
766
+ for (let index = 0; index < this.attrs.length; index++) {
767
+ const element = this.attrs[index];
768
+ totalWidth += element.frame.width
769
+ }
770
+ totalWidth = totalWidth + (this.attrs.length - 1) * this.horizontalSpacing
771
+ let left = (this.containerWidth - totalWidth) - this.sectionInset.right
772
+ for (let index = 0; index < this.attrs.length; index++) {
773
+ const element = this.attrs[index];
774
+ element.frame.x = left
775
+ left = element.frame.xMax + this.horizontalSpacing
776
+ }
777
+ }
778
+ if (priority == _yx_flow_layout_priority.SIDE) {
779
+ let totalWidth = 0
780
+ for (let index = 0; index < this.attrs.length; index++) {
781
+ const element = this.attrs[index];
782
+ totalWidth += element.frame.width
783
+ }
784
+ let horizontalSpacing = (this.containerWidth - this.sectionInset.left - this.sectionInset.right - totalWidth) / (this.attrs.length - 1)
785
+ let offset = this.sectionInset.left
786
+ for (let index = 0; index < this.attrs.length; index++) {
787
+ const element = this.attrs[index];
788
+ element.frame.x = offset
789
+ offset = element.frame.xMax + horizontalSpacing
790
+ }
791
+ }
792
+ }
793
+
794
+ /**
795
+ * 水平方向列表的节点的排列规则
796
+ */
797
+ layout_horizontal_item(indexPath: YXIndexPath, itemSize: math.Size) {
798
+ if (this.attrs.length <= 0) {
799
+ let attributes = YXLayoutAttributes.layoutAttributesForCell(indexPath)
800
+ attributes.frame.set(this.offset, this.sectionInset.top, itemSize.width, itemSize.height)
801
+ this.attrs.push(attributes)
802
+ this.containerWidth = Math.max(this.containerWidth, attributes.frame.width)
803
+ return attributes
804
+ }
805
+
806
+ const frame = new math.Rect()
807
+ frame.size = itemSize
808
+
809
+ // 检测所有节点的下边
810
+ for (let index = 0; index < this.attrs.length; index++) {
811
+ const element = this.attrs[index];
812
+ frame.x = element.frame.x
813
+ frame.y = element.frame.yMax + this.verticalSpacing
814
+ if (frame.yMax <= (this.containerHeight - this.sectionInset.bottom)) {
815
+ if (this.intersects(frame) == false) {
816
+ let attributes = YXLayoutAttributes.layoutAttributesForCell(indexPath)
817
+ attributes.frame.set(frame)
818
+ this.attrs.push(attributes)
819
+ this.containerWidth = Math.max(this.containerWidth, attributes.frame.xMax - this.offset)
820
+ return attributes
821
+ }
822
+ }
823
+ }
824
+
825
+ // 走到这里表示这块内容区域已经摆不下了
826
+ return null
827
+ }
828
+
829
+ layout_horizontal_complet() {
830
+
831
+ // 反转垂直方向 (如果需要的话)
832
+ if (this.reverse & _yx_flow_layout_reverse_mode.VERTICAL) {
833
+ this.attrs = this.attrs.reverse()
834
+ }
835
+
836
+ // 调整对齐方式
837
+ if (this.alignment == _yx_flow_layout_alignment.CENTER) {
838
+ for (let index = 0; index < this.attrs.length; index++) {
839
+ const element = this.attrs[index];
840
+ element.frame.x = this.offset + (this.containerWidth - element.frame.width) * 0.5
841
+ }
842
+ }
843
+ if (this.alignment == _yx_flow_layout_alignment.BACKWARD) {
844
+ for (let index = 0; index < this.attrs.length; index++) {
845
+ const element = this.attrs[index];
846
+ element.frame.x = this.offset + (this.containerWidth - element.frame.width)
847
+ }
848
+ }
849
+
850
+ // 按优先级调整节点位置
851
+ let priority = this.priority
852
+ if (priority == _yx_flow_layout_priority.SIDE && this.attrs.length == 1) {
853
+ priority = this.lastItemPriority
854
+ }
855
+ if (priority == _yx_flow_layout_priority.LEFT || priority == _yx_flow_layout_priority.RIGHT) {
856
+ throw new Error("flow layout priority/lastItemPriority 配置错误: 不支持的优先级配置");
857
+ }
858
+ if (priority == _yx_flow_layout_priority.CENTER) {
859
+ let totalHeight = 0
860
+ for (let index = 0; index < this.attrs.length; index++) {
861
+ const element = this.attrs[index];
862
+ totalHeight += element.frame.height
863
+ }
864
+ totalHeight = totalHeight + (this.attrs.length - 1) * this.verticalSpacing
865
+ let top = (this.containerHeight - totalHeight) * 0.5
866
+ for (let index = 0; index < this.attrs.length; index++) {
867
+ const element = this.attrs[index];
868
+ element.frame.y = top
869
+ top = element.frame.yMax + this.verticalSpacing
870
+ }
871
+ }
872
+ if (priority == _yx_flow_layout_priority.TOP && this.reverse & _yx_flow_layout_reverse_mode.VERTICAL) {
873
+ let top = this.sectionInset.top
874
+ for (let index = 0; index < this.attrs.length; index++) {
875
+ const element = this.attrs[index];
876
+ element.frame.y = top
877
+ top = element.frame.yMax + this.verticalSpacing
878
+ }
879
+ }
880
+ if (priority == _yx_flow_layout_priority.BOTTOM) {
881
+ let totalHeight = 0
882
+ for (let index = 0; index < this.attrs.length; index++) {
883
+ const element = this.attrs[index];
884
+ totalHeight += element.frame.height
885
+ }
886
+ totalHeight = totalHeight + (this.attrs.length - 1) * this.verticalSpacing
887
+ let top = (this.containerHeight - totalHeight) - this.sectionInset.bottom
888
+ for (let index = 0; index < this.attrs.length; index++) {
889
+ const element = this.attrs[index];
890
+ element.frame.y = top
891
+ top = element.frame.yMax + this.verticalSpacing
892
+ }
893
+ }
894
+ if (priority == _yx_flow_layout_priority.SIDE) {
895
+ let totalHeight = 0
896
+ for (let index = 0; index < this.attrs.length; index++) {
897
+ const element = this.attrs[index];
898
+ totalHeight += element.frame.height
899
+ }
900
+ let verticalSpacing = (this.containerHeight - this.sectionInset.top - this.sectionInset.bottom - totalHeight) / (this.attrs.length - 1)
901
+ let offset = this.sectionInset.top
902
+ for (let index = 0; index < this.attrs.length; index++) {
903
+ const element = this.attrs[index];
904
+ element.frame.y = offset
905
+ offset = element.frame.yMax + verticalSpacing
906
+ }
907
+ }
908
+ }
909
+ }