uicore-ts 1.1.88 → 1.1.102

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.
@@ -56,8 +56,16 @@ export class UITableView extends UINativeScrollView {
56
56
  _isDrawVisibleRowsScheduled = NO
57
57
  _shouldAnimateNextLayout?: boolean
58
58
 
59
+ override usesVirtualLayoutingForIntrinsicSizing = NO
60
+
59
61
  override animationDuration = 0.25
60
62
 
63
+ // Viewport scrolling properties
64
+ private _useViewportScrolling = NO
65
+ private _windowScrollHandler?: () => void
66
+ private _resizeHandler?: () => void
67
+ private _intersectionObserver?: IntersectionObserver
68
+
61
69
 
62
70
  constructor(elementID?: string) {
63
71
 
@@ -70,6 +78,260 @@ export class UITableView extends UINativeScrollView {
70
78
 
71
79
  this.scrollsX = NO
72
80
 
81
+ // Automatically detect if we should use viewport scrolling
82
+ this._autoDetectScrollMode()
83
+
84
+ }
85
+
86
+
87
+ /**
88
+ * Automatically detect if this table should use viewport scrolling
89
+ * If the table's bounds height is >= the total content height,
90
+ * then it doesn't need internal scrolling and should use viewport scrolling
91
+ */
92
+ private _autoDetectScrollMode() {
93
+ // Run detection after the view is added to the tree and layout has occurred
94
+ const checkScrollMode = () => {
95
+ if (!this.isMemberOfViewTree) {
96
+ return
97
+ }
98
+
99
+ this._calculateAllPositions()
100
+
101
+ const totalContentHeight = this._rowPositions.length > 0
102
+ ? this._rowPositions[this._rowPositions.length - 1].bottomY
103
+ : 0
104
+
105
+ const tableBoundsHeight = this.bounds.height
106
+
107
+ // If the table's height can contain all content, use viewport scrolling
108
+ if (tableBoundsHeight >= totalContentHeight) {
109
+ this.enableViewportBasedVirtualScrolling()
110
+ }
111
+ }
112
+
113
+ // Check after first layout
114
+ UIView.runFunctionBeforeNextFrame(checkScrollMode)
115
+ }
116
+
117
+
118
+ /**
119
+ * Enable viewport-based virtual scrolling for full-height tables
120
+ * This allows the table to be part of the page flow while still
121
+ * benefiting from virtual scrolling performance
122
+ */
123
+ enableViewportBasedVirtualScrolling() {
124
+ if (this._useViewportScrolling) {
125
+ return // Already enabled
126
+ }
127
+
128
+ this._useViewportScrolling = YES
129
+ this.scrollsX = NO
130
+ this.scrollsY = NO
131
+
132
+ // No style changes - respect the absolute positioning system
133
+
134
+ this._setupViewportScrollListeners()
135
+ }
136
+
137
+
138
+ /**
139
+ * Disable viewport scrolling and return to normal scroll behavior
140
+ */
141
+ disableViewportBasedVirtualScrolling() {
142
+ if (!this._useViewportScrolling) {
143
+ return
144
+ }
145
+
146
+ this._useViewportScrolling = NO
147
+ this.scrollsY = YES
148
+
149
+ this._cleanupViewportScrollListeners()
150
+ }
151
+
152
+
153
+ private _setupViewportScrollListeners() {
154
+ this._windowScrollHandler = () => {
155
+ this._scheduleDrawVisibleRowsInViewport()
156
+ }
157
+
158
+ this._resizeHandler = () => {
159
+ // Invalidate all row positions on resize as widths may have changed
160
+ this._rowPositions.forEach(pos => pos.isValid = NO)
161
+ this._highestValidRowPositionIndex = -1
162
+ this._scheduleDrawVisibleRowsInViewport()
163
+ }
164
+
165
+ window.addEventListener('scroll', this._windowScrollHandler, { passive: true })
166
+ window.addEventListener('resize', this._resizeHandler, { passive: true })
167
+
168
+ // Use IntersectionObserver to detect when table enters/exits viewport
169
+ this._intersectionObserver = new IntersectionObserver(
170
+ (entries) => {
171
+ entries.forEach(entry => {
172
+ if (entry.isIntersecting) {
173
+ this._scheduleDrawVisibleRowsInViewport()
174
+ }
175
+ })
176
+ },
177
+ {
178
+ root: null,
179
+ rootMargin: '100% 0px', // Load rows 100% viewport height before/after
180
+ threshold: 0
181
+ }
182
+ )
183
+
184
+ this._intersectionObserver.observe(this.viewHTMLElement)
185
+ }
186
+
187
+
188
+ private _cleanupViewportScrollListeners() {
189
+ if (this._windowScrollHandler) {
190
+ window.removeEventListener('scroll', this._windowScrollHandler)
191
+ this._windowScrollHandler = undefined
192
+ }
193
+
194
+ if (this._resizeHandler) {
195
+ window.removeEventListener('resize', this._resizeHandler)
196
+ this._resizeHandler = undefined
197
+ }
198
+
199
+ if (this._intersectionObserver) {
200
+ this._intersectionObserver.disconnect()
201
+ this._intersectionObserver = undefined
202
+ }
203
+ }
204
+
205
+
206
+ private _scheduleDrawVisibleRowsInViewport() {
207
+ if (!this._isDrawVisibleRowsScheduled) {
208
+ this._isDrawVisibleRowsScheduled = YES
209
+
210
+ UIView.runFunctionBeforeNextFrame(() => {
211
+ this._calculateAllPositions()
212
+ this._drawVisibleRowsInViewport()
213
+ this.setNeedsLayout()
214
+ this._isDrawVisibleRowsScheduled = NO
215
+ })
216
+ }
217
+ }
218
+
219
+
220
+ /**
221
+ * Calculate which rows are visible in the browser viewport
222
+ * rather than in the scrollview's content area
223
+ */
224
+ indexesForVisibleRowsInViewport(paddingRatio = 0.5): number[] {
225
+ const tableRect = this.viewHTMLElement.getBoundingClientRect()
226
+ const viewportHeight = window.innerHeight
227
+ const pageScale = UIView.pageScale
228
+
229
+ // Calculate which part of the table is visible in viewport
230
+ // Account for page scale when converting from screen to content coordinates
231
+ const visibleTop = Math.max(0, -tableRect.top / pageScale)
232
+ const visibleBottom = Math.min(tableRect.height / pageScale, (viewportHeight - tableRect.top) / pageScale)
233
+
234
+ // Add padding to render rows slightly before they enter viewport
235
+ const paddingPx = (viewportHeight / pageScale) * paddingRatio
236
+ const firstVisibleY = Math.max(0, visibleTop - paddingPx)
237
+ const lastVisibleY = Math.min(tableRect.height / pageScale, visibleBottom + paddingPx)
238
+
239
+ const numberOfRows = this.numberOfRows()
240
+
241
+ if (this.allRowsHaveEqualHeight) {
242
+ const rowHeight = this.heightForRowWithIndex(0)
243
+
244
+ let firstIndex = Math.floor(firstVisibleY / rowHeight)
245
+ let lastIndex = Math.floor(lastVisibleY / rowHeight)
246
+
247
+ firstIndex = Math.max(firstIndex, 0)
248
+ lastIndex = Math.min(lastIndex, numberOfRows - 1)
249
+
250
+ const result = []
251
+ for (let i = firstIndex; i <= lastIndex; i++) {
252
+ result.push(i)
253
+ }
254
+ return result
255
+ }
256
+
257
+ // Variable height rows
258
+ this._calculateAllPositions()
259
+
260
+ const rowPositions = this._rowPositions
261
+ const result = []
262
+
263
+ for (let i = 0; i < numberOfRows; i++) {
264
+ const position = rowPositions[i]
265
+ if (!position) break
266
+
267
+ const rowTop = position.topY
268
+ const rowBottom = position.bottomY
269
+
270
+ // Check if row intersects with visible area
271
+ if (rowBottom >= firstVisibleY && rowTop <= lastVisibleY) {
272
+ result.push(i)
273
+ }
274
+
275
+ // Early exit if we've passed the visible area
276
+ if (rowTop > lastVisibleY) {
277
+ break
278
+ }
279
+ }
280
+
281
+ return result
282
+ }
283
+
284
+
285
+ /**
286
+ * Draw visible rows based on viewport position
287
+ */
288
+ private _drawVisibleRowsInViewport() {
289
+ if (!this.isMemberOfViewTree) {
290
+ return
291
+ }
292
+
293
+ const visibleIndexes = this.indexesForVisibleRowsInViewport()
294
+
295
+ const minIndex = visibleIndexes[0]
296
+ const maxIndex = visibleIndexes[visibleIndexes.length - 1]
297
+
298
+ const removedViews: UITableViewRowView[] = []
299
+
300
+ const visibleRows: UITableViewRowView[] = []
301
+ this._visibleRows.forEach((row) => {
302
+ if (IS_DEFINED(row._UITableViewRowIndex) &&
303
+ (row._UITableViewRowIndex < minIndex || row._UITableViewRowIndex > maxIndex)) {
304
+
305
+ this._persistedData[row._UITableViewRowIndex] = this.persistenceDataItemForRowWithIndex(
306
+ row._UITableViewRowIndex,
307
+ row
308
+ )
309
+
310
+ this._removedReusableViews[row._UITableViewReusabilityIdentifier].push(row)
311
+ removedViews.push(row)
312
+ }
313
+ else {
314
+ visibleRows.push(row)
315
+ }
316
+ })
317
+ this._visibleRows = visibleRows
318
+
319
+ visibleIndexes.forEach((rowIndex: number) => {
320
+ if (this.isRowWithIndexVisible(rowIndex)) {
321
+ return
322
+ }
323
+ const view: UITableViewRowView = this.viewForRowWithIndex(rowIndex)
324
+ this._firstLayoutVisibleRows.push(view)
325
+ this._visibleRows.push(view)
326
+ this.addSubview(view)
327
+ })
328
+
329
+ for (let i = 0; i < removedViews.length; i++) {
330
+ const view = removedViews[i]
331
+ if (this._visibleRows.indexOf(view) == -1) {
332
+ view.removeFromSuperview()
333
+ }
334
+ }
73
335
  }
74
336
 
75
337
 
@@ -126,8 +388,8 @@ export class UITableView extends UINativeScrollView {
126
388
 
127
389
 
128
390
  highlightRowAsNew(row: UIView) {
129
-
130
-
391
+
392
+
131
393
  }
132
394
 
133
395
 
@@ -139,14 +401,6 @@ export class UITableView extends UINativeScrollView {
139
401
 
140
402
  this._highestValidRowPositionIndex = Math.min(this._highestValidRowPositionIndex, index - 1)
141
403
 
142
- // if (index == 0) {
143
-
144
- // this._highestValidRowPositionIndex = 0;
145
-
146
- // this._rowPositions = [];
147
-
148
- // }
149
-
150
404
  this._needsDrawingOfVisibleRowsBeforeLayout = YES
151
405
 
152
406
  this._shouldAnimateNextLayout = animateChange
@@ -160,7 +414,7 @@ export class UITableView extends UINativeScrollView {
160
414
 
161
415
  _calculatePositionsUntilIndex(maxIndex: number) {
162
416
 
163
- var validPositionObject = this._rowPositions[this._highestValidRowPositionIndex]
417
+ let validPositionObject = this._rowPositions[this._highestValidRowPositionIndex]
164
418
  if (!IS(validPositionObject)) {
165
419
  validPositionObject = {
166
420
  bottomY: 0,
@@ -169,7 +423,7 @@ export class UITableView extends UINativeScrollView {
169
423
  }
170
424
  }
171
425
 
172
- var previousBottomY = validPositionObject.bottomY
426
+ let previousBottomY = validPositionObject.bottomY
173
427
 
174
428
  if (!this._rowPositions.length) {
175
429
 
@@ -177,9 +431,9 @@ export class UITableView extends UINativeScrollView {
177
431
 
178
432
  }
179
433
 
180
- for (var i = this._highestValidRowPositionIndex + 1; i <= maxIndex; i++) {
434
+ for (let i = this._highestValidRowPositionIndex + 1; i <= maxIndex; i++) {
181
435
 
182
- var height: number
436
+ let height: number
183
437
 
184
438
  const rowPositionObject = this._rowPositions[i]
185
439
 
@@ -217,6 +471,12 @@ export class UITableView extends UINativeScrollView {
217
471
 
218
472
  indexesForVisibleRows(paddingRatio = 0.5): number[] {
219
473
 
474
+ // If using viewport scrolling, delegate to viewport method
475
+ if (this._useViewportScrolling) {
476
+ return this.indexesForVisibleRowsInViewport(paddingRatio)
477
+ }
478
+
479
+ // Original scroll-based logic
220
480
  const firstVisibleY = this.contentOffset.y - this.bounds.height * paddingRatio
221
481
  const lastVisibleY = firstVisibleY + this.bounds.height * (1 + paddingRatio)
222
482
 
@@ -226,8 +486,8 @@ export class UITableView extends UINativeScrollView {
226
486
 
227
487
  const rowHeight = this.heightForRowWithIndex(0)
228
488
 
229
- var firstIndex = firstVisibleY / rowHeight
230
- var lastIndex = lastVisibleY / rowHeight
489
+ let firstIndex = firstVisibleY / rowHeight
490
+ let lastIndex = lastVisibleY / rowHeight
231
491
 
232
492
  firstIndex = Math.trunc(firstIndex)
233
493
  lastIndex = Math.trunc(lastIndex) + 1
@@ -235,23 +495,23 @@ export class UITableView extends UINativeScrollView {
235
495
  firstIndex = Math.max(firstIndex, 0)
236
496
  lastIndex = Math.min(lastIndex, numberOfRows - 1)
237
497
 
238
- var result = []
239
- for (var i = firstIndex; i < lastIndex + 1; i++) {
498
+ const result = []
499
+ for (let i = firstIndex; i < lastIndex + 1; i++) {
240
500
  result.push(i)
241
501
  }
242
502
  return result
243
503
  }
244
504
 
245
- var accumulatedHeight = 0
246
- var result = []
505
+ let accumulatedHeight = 0
506
+ const result = []
247
507
 
248
508
  this._calculateAllPositions()
249
509
 
250
510
  const rowPositions = this._rowPositions
251
511
 
252
- for (var i = 0; i < numberOfRows; i++) {
512
+ for (let i = 0; i < numberOfRows; i++) {
253
513
 
254
- const height = rowPositions[i].bottomY - rowPositions[i].topY // this.heightForRowWithIndex(i)
514
+ const height = rowPositions[i].bottomY - rowPositions[i].topY
255
515
 
256
516
  accumulatedHeight = accumulatedHeight + height
257
517
  if (accumulatedHeight >= firstVisibleY) {
@@ -327,8 +587,6 @@ export class UITableView extends UINativeScrollView {
327
587
  this._visibleRows.forEach((row) => {
328
588
  if (IS_DEFINED(row._UITableViewRowIndex) && (row._UITableViewRowIndex < minIndex || row._UITableViewRowIndex > maxIndex)) {
329
589
 
330
- //row.removeFromSuperview();
331
-
332
590
  this._persistedData[row._UITableViewRowIndex] = this.persistenceDataItemForRowWithIndex(
333
591
  row._UITableViewRowIndex,
334
592
  row
@@ -351,7 +609,6 @@ export class UITableView extends UINativeScrollView {
351
609
  return
352
610
  }
353
611
  const view: UITableViewRowView = this.viewForRowWithIndex(rowIndex)
354
- //view._UITableViewRowIndex = rowIndex;
355
612
  this._firstLayoutVisibleRows.push(view)
356
613
  this._visibleRows.push(view)
357
614
  this.addSubview(view)
@@ -363,22 +620,17 @@ export class UITableView extends UINativeScrollView {
363
620
  const view = removedViews[i]
364
621
  if (this._visibleRows.indexOf(view) == -1) {
365
622
 
366
- //this._persistedData[view._UITableViewRowIndex] = this.persistenceDataItemForRowWithIndex(view._UITableViewRowIndex, view);
367
623
  view.removeFromSuperview()
368
624
 
369
- //this._removedReusableViews[view._UITableViewReusabilityIdentifier].push(view);
370
-
371
625
  }
372
626
 
373
627
  }
374
628
 
375
- //this.setNeedsLayout();
376
-
377
629
  }
378
630
 
379
631
 
380
632
  visibleRowWithIndex(rowIndex: number | undefined): UIView {
381
- for (var i = 0; i < this._visibleRows.length; i++) {
633
+ for (let i = 0; i < this._visibleRows.length; i++) {
382
634
  const row = this._visibleRows[i]
383
635
  if (row._UITableViewRowIndex == rowIndex) {
384
636
  return row
@@ -447,13 +699,13 @@ export class UITableView extends UINativeScrollView {
447
699
  }
448
700
 
449
701
  defaultRowPersistenceDataItem(): any {
450
-
451
-
702
+
703
+
452
704
  }
453
705
 
454
706
  persistenceDataItemForRowWithIndex(rowIndex: number, row: UIView): any {
455
-
456
-
707
+
708
+
457
709
  }
458
710
 
459
711
  viewForRowWithIndex(rowIndex: number): UITableViewRowView {
@@ -471,6 +723,11 @@ export class UITableView extends UINativeScrollView {
471
723
 
472
724
  super.didScrollToPosition(offsetPosition)
473
725
 
726
+ // Skip if using viewport scrolling
727
+ if (this._useViewportScrolling) {
728
+ return
729
+ }
730
+
474
731
  this.forEachViewInSubtree(function (view: UIView) {
475
732
 
476
733
  view._isPointerValid = NO
@@ -481,7 +738,7 @@ export class UITableView extends UINativeScrollView {
481
738
 
482
739
  this._isDrawVisibleRowsScheduled = YES
483
740
 
484
- UIView.runFunctionBeforeNextFrame(function (this: UITableView) {
741
+ UIView.runFunctionBeforeNextFrame(() => {
485
742
 
486
743
  this._calculateAllPositions()
487
744
 
@@ -491,14 +748,25 @@ export class UITableView extends UINativeScrollView {
491
748
 
492
749
  this._isDrawVisibleRowsScheduled = NO
493
750
 
494
- }.bind(this))
751
+ })
495
752
 
496
753
  }
497
754
 
498
755
  }
499
756
 
500
757
  override wasAddedToViewTree() {
758
+ super.wasAddedToViewTree()
501
759
  this.loadData()
760
+
761
+ // Re-check scroll mode in case CSS was applied after construction
762
+ if (!this._useViewportScrolling) {
763
+ this._autoDetectScrollMode()
764
+ }
765
+ }
766
+
767
+ override wasRemovedFromViewTree() {
768
+ super.wasRemovedFromViewTree()
769
+ this._cleanupViewportScrollListeners()
502
770
  }
503
771
 
504
772
  override setFrame(rectangle: UIRectangle, zIndex?: number, performUncheckedLayout?: boolean) {
@@ -550,8 +818,17 @@ export class UITableView extends UINativeScrollView {
550
818
 
551
819
  })
552
820
 
553
- this._fullHeightView.frame = bounds.rectangleWithHeight((positions.lastElement ||
554
- nil).bottomY).rectangleWithWidth(bounds.width * 0.5)
821
+ // For viewport scrolling, the full height view needs to establish the total height
822
+ if (this._useViewportScrolling) {
823
+ this._fullHeightView.hidden = NO
824
+ this._fullHeightView.style.position = 'absolute'
825
+ this._fullHeightView.style.pointerEvents = 'none'
826
+ this._fullHeightView.frame = bounds.rectangleWithHeight((positions.lastElement || nil).bottomY)
827
+ .rectangleWithWidth(1) // Minimal width
828
+ } else {
829
+ this._fullHeightView.frame = bounds.rectangleWithHeight((positions.lastElement || nil).bottomY)
830
+ .rectangleWithWidth(bounds.width * 0.5)
831
+ }
555
832
 
556
833
  this._firstLayoutVisibleRows = []
557
834
 
@@ -564,17 +841,15 @@ export class UITableView extends UINativeScrollView {
564
841
  this.animationDuration,
565
842
  0,
566
843
  undefined,
567
- function (this: UITableView) {
844
+ () => {
568
845
 
569
846
  this._layoutAllRows()
570
847
 
571
- }.bind(this),
572
- function (this: UITableView) {
573
-
574
- // this._calculateAllPositions()
575
- // this._layoutAllRows()
576
-
577
- }.bind(this)
848
+ },
849
+ () => {
850
+
851
+
852
+ }
578
853
  )
579
854
 
580
855
  }
@@ -588,9 +863,11 @@ export class UITableView extends UINativeScrollView {
588
863
 
589
864
  if (this._needsDrawingOfVisibleRowsBeforeLayout) {
590
865
 
591
- //this._calculateAllPositions()
592
-
593
- this._drawVisibleRows()
866
+ if (this._useViewportScrolling) {
867
+ this._drawVisibleRowsInViewport()
868
+ } else {
869
+ this._drawVisibleRows()
870
+ }
594
871
 
595
872
  this._needsDrawingOfVisibleRowsBeforeLayout = NO
596
873
 
@@ -618,11 +895,11 @@ export class UITableView extends UINativeScrollView {
618
895
  if (previousVisibleRowsLength < this._visibleRows.length) {
619
896
 
620
897
 
621
- UIView.runFunctionBeforeNextFrame(function (this: UITableView) {
898
+ UIView.runFunctionBeforeNextFrame(() => {
622
899
 
623
900
  this._animateLayoutAllRows()
624
901
 
625
- }.bind(this))
902
+ })
626
903
 
627
904
  }
628
905
  else {
@@ -637,14 +914,6 @@ export class UITableView extends UINativeScrollView {
637
914
  }
638
915
  else {
639
916
 
640
- // if (this._needsDrawingOfVisibleRowsBeforeLayout) {
641
-
642
- // this._drawVisibleRows();
643
-
644
- // this._needsDrawingOfVisibleRowsBeforeLayout = NO;
645
-
646
- // }
647
-
648
917
  this._calculateAllPositions()
649
918
 
650
919
  this._layoutAllRows()
@@ -659,7 +928,7 @@ export class UITableView extends UINativeScrollView {
659
928
  override intrinsicContentHeight(constrainingWidth = 0) {
660
929
 
661
930
 
662
- var result = 0
931
+ let result = 0
663
932
 
664
933
  this._calculateAllPositions()
665
934
 
@@ -675,35 +944,3 @@ export class UITableView extends UINativeScrollView {
675
944
 
676
945
 
677
946
  }
678
-
679
-
680
-
681
-
682
-
683
-
684
-
685
-
686
-
687
-
688
-
689
-
690
-
691
-
692
-
693
-
694
-
695
-
696
-
697
-
698
-
699
-
700
-
701
-
702
-
703
-
704
-
705
-
706
-
707
-
708
-
709
-