bm-core-ui 2.7.0 → 2.8.3

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.
@@ -18,7 +18,7 @@ const BM_AUTOMATIC_CELL_SIZE_MESSAGES = NO;
18
18
  /**
19
19
  * Constants representing the types of supplementary views that table layouts support.
20
20
  */
21
- export var BMCollectionViewTableLayoutSupplementaryView = Object.freeze({
21
+ export var BMCollectionViewTableLayoutSupplementaryView = Object.freeze({ // <enum>
22
22
  /**
23
23
  * Indicates that this supplementary view is a footer.
24
24
  */
@@ -1279,38 +1279,38 @@ BMExtend(BMCollectionViewTreeLayout.prototype, BMCollectionViewLayout.prototype,
1279
1279
  /**
1280
1280
  * Controls how cells are distributed within their row.
1281
1281
  */
1282
- export var BMCollectionViewFlowLayoutGravity = Object.freeze({
1282
+ export var BMCollectionViewFlowLayoutGravity = Object.freeze({ // <enum>
1283
1283
 
1284
1284
  /**
1285
1285
  * The cells will flow to the center of the row with no spacing between them.
1286
1286
  */
1287
- Center: {},
1287
+ Center: {}, // <enum>
1288
1288
 
1289
1289
  /**
1290
1290
  * The cells will be aligned to the start of the row.
1291
1291
  */
1292
- Start: {},
1292
+ Start: {}, // <enum>
1293
1293
 
1294
1294
  /**
1295
1295
  * The cells will be aligned to the end of the row.
1296
1296
  */
1297
- End: {},
1297
+ End: {}, // <enum>
1298
1298
 
1299
1299
  /**
1300
1300
  * The cells will flow to the edges of the row with equal spacing between them.
1301
1301
  */
1302
- Edge: {},
1302
+ Edge: {}, // <enum>
1303
1303
 
1304
1304
  /**
1305
1305
  * The cells will flow such that they have equal spacing between them and the row edges.
1306
1306
  */
1307
- Spaced: {},
1307
+ Spaced: {}, // <enum>
1308
1308
 
1309
1309
  /**
1310
1310
  * The cells will flow such that they will have no spacing between them.
1311
1311
  * If the cells in a row do not occupy the entire horizontal alrea of that row, they will be expanded proportionally until they do.
1312
1312
  */
1313
- Expand: {}
1313
+ Expand: {} // <enum>
1314
1314
 
1315
1315
  });
1316
1316
 
@@ -1325,39 +1325,39 @@ export var BMCollectionViewFlowLayoutAlignment = (function () {
1325
1325
  /**
1326
1326
  * Controls how cells will be aligned within their row.
1327
1327
  */
1328
- var BMCollectionViewFlowLayoutAlignment = Object.freeze({
1328
+ var BMCollectionViewFlowLayoutAlignment = Object.freeze({ // <enum>
1329
1329
 
1330
1330
  /**
1331
1331
  * @deprecated Use `Start`
1332
1332
  * The cells will be aligned to the starting edge of the row.
1333
1333
  */
1334
- Top: start,
1334
+ Top: start, // <enum>
1335
1335
 
1336
1336
  /**
1337
1337
  * The cells will be aligned to the start of the row.
1338
1338
  */
1339
- Start: start,
1339
+ Start: start, // <enum>
1340
1340
 
1341
1341
  /**
1342
1342
  * The cells will be aligned to the center of the row.
1343
1343
  */
1344
- Center: {},
1344
+ Center: {}, // <enum>
1345
1345
 
1346
1346
  /**
1347
1347
  * @deprecated Use `End`
1348
1348
  * The cells will be aligned to the end of the row.
1349
1349
  */
1350
- Bottom: end,
1350
+ Bottom: end, // <enum>
1351
1351
 
1352
1352
  /**
1353
1353
  * The cells will be aligned to the end of the row.
1354
1354
  */
1355
- End: end,
1355
+ End: end, // <enum>
1356
1356
 
1357
1357
  /**
1358
1358
  * The cells will expand to fit the entire height of the row.
1359
1359
  */
1360
- Expand: {}
1360
+ Expand: {} // <enum>
1361
1361
 
1362
1362
  });
1363
1363
 
@@ -1371,18 +1371,18 @@ export var BMCollectionViewFlowLayoutAlignment = (function () {
1371
1371
  /**
1372
1372
  * An enum containing the possible orientations that the flow layout can use.
1373
1373
  */
1374
- export var BMCollectionViewFlowLayoutOrientation = Object.freeze({
1374
+ export var BMCollectionViewFlowLayoutOrientation = Object.freeze({ // <enum>
1375
1375
  /**
1376
1376
  * Indicates that the flow layout will arrange cells primarily along the horizontal axis.
1377
1377
  * When the orientation is set to this value the flow layout will scroll horizontally.
1378
1378
  */
1379
- Horizontal: {},
1379
+ Horizontal: {}, // <enum>
1380
1380
 
1381
1381
  /**
1382
1382
  * Indicates that the flow layout will arrange cells primarily along the vertical axis.
1383
1383
  * When the orientation is set to this value the tile flow will scroll vertically.
1384
1384
  */
1385
- Vertical: {}
1385
+ Vertical: {} // <enum>
1386
1386
  });
1387
1387
 
1388
1388
  // @endtype
@@ -1699,9 +1699,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
1699
1699
  * When using automatic cell sizes, the generator will yield execution and only calculate portions of the layout on demand.
1700
1700
  * With the exeption of the first invocation, it is required to pass an index path to the iterator's `next` method. The layout process
1701
1701
  * will continue until it fully lays out the cell for that index path, at which point it will yield again.
1702
- * @param useOffset <Boolean> When set to `YES` the layout will take the scrollbar size into account.
1702
+ * @param useOffset <Boolean> When set to `YES` the layout will take the scrollbar size into account.
1703
+ * {
1704
+ * @param targetRect <BMRect, nullable> When computing a layout using automatic cell sizes, this is an optional rect up to which
1705
+ * to measure cells. This is reserved for when this is invoked as a delegate.
1706
+ * @param targetIndexPath <BMIndexPath, nullable> When computing a layout using automatic cell sizes, this is an optional index path up to which
1707
+ * to measure cells. This is reserved for when this is invoked as a delegate.
1708
+ * }
1703
1709
  */
1704
- *_prepareLayoutWithScrollbarOffsetGenerator(useOffset) {
1710
+ *_prepareLayoutWithScrollbarOffsetGenerator(useOffset, target) {
1705
1711
 
1706
1712
  // To standardize naming in order to support the orientation setting, the following terms are used:
1707
1713
  // Length - refers to the dimension across the axis on which collection view scrolls: height for vertical and width for horizontal
@@ -1803,7 +1809,6 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
1803
1809
  targetRect?: BMRect;
1804
1810
  }
1805
1811
  */
1806
- let target;
1807
1812
 
1808
1813
  // The sections that contain the preliminary layout
1809
1814
  const preliminarySections = [];
@@ -1981,8 +1986,11 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
1981
1986
  else {
1982
1987
  // If there is no minimum spacing required, then the number of columns is simply the available space divided
1983
1988
  // by the cell width
1984
- numberOfColumns = ((maximumLength / expectedSize) | 0) || 1;
1989
+ numberOfColumns = (maximumLength / expectedSize) | 0;
1985
1990
  }
1991
+
1992
+ // Ensure that there is at least one column
1993
+ numberOfColumns = Math.max(numberOfColumns, 1);
1986
1994
  }
1987
1995
 
1988
1996
  // Create the preliminary sections
@@ -2066,7 +2074,12 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2066
2074
 
2067
2075
  cachedLayout.size = BMSizeMake(this.collectionView.frame.size.width, height);
2068
2076
 
2069
- target = yield;
2077
+ // Yield after building the preliminary layout and wait for a target rect or index path up to which to measure
2078
+ if (!target) {
2079
+ // If a target is already supplied (as a parameter) this is a delegate generator and processing should continue
2080
+ // until this target
2081
+ target = yield;
2082
+ }
2070
2083
 
2071
2084
  // When requesting a rect initially, pre-measure the cells up to that rect
2072
2085
  if (target.rect) {
@@ -2082,7 +2095,13 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2082
2095
 
2083
2096
  cachedLayout.size = BMSizeMake(width, this.collectionView.frame.size.height);
2084
2097
 
2085
- target = yield;
2098
+ // Yield after building the preliminary layout and wait for a target rect or index path up to which to measure
2099
+ if (!target) {
2100
+ // If a target is already supplied (as a parameter) this is a delegate generator and processing should continue
2101
+ // until this target
2102
+ target = yield;
2103
+ }
2104
+
2086
2105
  // When requesting a rect initially, pre-measure the cells up to that rect
2087
2106
  if (target.rect) {
2088
2107
  measureCellsInRect(target.rect);
@@ -2217,7 +2236,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2217
2236
 
2218
2237
  // If the row height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2219
2238
  if (row[rowStartAttribute] + rowBreadth > frameSize) {
2220
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2239
+ if (this.collectionView.scrollBarSize && !useOffset) {
2240
+ if (target) {
2241
+ // With automatic cell size delegate iteration to the new invocation
2242
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2243
+ }
2244
+ else {
2245
+ return this._prepareLayoutWithScrollbarOffset(YES);
2246
+ }
2247
+ }
2221
2248
  }
2222
2249
 
2223
2250
  // if there is one
@@ -2380,7 +2407,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2380
2407
 
2381
2408
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2382
2409
  if (height > frameHeight) {
2383
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2410
+ if (this.collectionView.scrollBarSize && !useOffset) {
2411
+ if (target) {
2412
+ // With automatic cell size delegate iteration to the new invocation
2413
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2414
+ }
2415
+ else {
2416
+ return this._prepareLayoutWithScrollbarOffset(YES);
2417
+ }
2418
+ }
2384
2419
  }
2385
2420
  }
2386
2421
  else {
@@ -2401,7 +2436,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2401
2436
 
2402
2437
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2403
2438
  if (width > frameWidth) {
2404
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2439
+ if (this.collectionView.scrollBarSize && !useOffset) {
2440
+ if (target) {
2441
+ // With automatic cell size delegate iteration to the new invocation
2442
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2443
+ }
2444
+ else {
2445
+ return this._prepareLayoutWithScrollbarOffset(YES);
2446
+ }
2447
+ }
2405
2448
  }
2406
2449
  }
2407
2450
  }
@@ -2415,7 +2458,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2415
2458
 
2416
2459
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2417
2460
  if (height > frameHeight) {
2418
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2461
+ if (this.collectionView.scrollBarSize && !useOffset) {
2462
+ if (target) {
2463
+ // With automatic cell size delegate iteration to the new invocation
2464
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2465
+ }
2466
+ else {
2467
+ return this._prepareLayoutWithScrollbarOffset(YES);
2468
+ }
2469
+ }
2419
2470
  }
2420
2471
  }
2421
2472
  else {
@@ -2426,7 +2477,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2426
2477
 
2427
2478
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2428
2479
  if (width > frameWidth) {
2429
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2480
+ if (this.collectionView.scrollBarSize && !useOffset) {
2481
+ if (target) {
2482
+ // With automatic cell size delegate iteration to the new invocation
2483
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2484
+ }
2485
+ else {
2486
+ return this._prepareLayoutWithScrollbarOffset(YES);
2487
+ }
2488
+ }
2430
2489
  }
2431
2490
  }
2432
2491
 
@@ -2437,7 +2496,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2437
2496
 
2438
2497
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2439
2498
  if (height > frameHeight) {
2440
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2499
+ if (this.collectionView.scrollBarSize && !useOffset) {
2500
+ if (target) {
2501
+ // With automatic cell size delegate iteration to the new invocation
2502
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2503
+ }
2504
+ else {
2505
+ return this._prepareLayoutWithScrollbarOffset(YES);
2506
+ }
2507
+ }
2441
2508
  }
2442
2509
 
2443
2510
  // If the height is lower than the collection view's height, the content gravity must be applied to all cached attributes
@@ -2531,7 +2598,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2531
2598
  else {
2532
2599
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2533
2600
  if (width > frameWidth) {
2534
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2601
+ if (this.collectionView.scrollBarSize && !useOffset) {
2602
+ if (target) {
2603
+ // With automatic cell size delegate iteration to the new invocation
2604
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2605
+ }
2606
+ else {
2607
+ return this._prepareLayoutWithScrollbarOffset(YES);
2608
+ }
2609
+ }
2535
2610
  }
2536
2611
 
2537
2612
  // If the height is lower than the collection view's height, the content gravity must be applied to all cached attributes
@@ -2604,7 +2679,7 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2604
2679
  const scaledRowHeight = rowWidth * scaleFactor | 0;
2605
2680
  for (const attribute of row.attributes) {
2606
2681
  attribute.frame.origin.x += displacement;
2607
- attribute.frame.size.height = attribute.frame.size.height * scaleFactor | 0;
2682
+ attribute.frame.size.width = attribute.frame.size.width * scaleFactor | 0;
2608
2683
  }
2609
2684
 
2610
2685
  // Advance the displacement based on the row's new width then displace the row end
@@ -2711,8 +2786,10 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2711
2786
  else {
2712
2787
  // If there is no minimum spacing required, then the number of columns is simply the available space divided
2713
2788
  // by the cell width
2714
- numberOfColumns = ((length / cellSize) | 0) || 1;
2789
+ numberOfColumns = (length / cellSize) | 0;
2715
2790
  }
2791
+
2792
+ numberOfColumns = Math.max(numberOfColumns, 1);
2716
2793
  }
2717
2794
  cachedLayout.numberOfColumns = numberOfColumns;
2718
2795
 
@@ -4420,6 +4497,319 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
4420
4497
 
4421
4498
  return attributes;
4422
4499
  },
4500
+
4501
+ // @override - BMCollectionViewLayout
4502
+ indexPathToTheLeftOfIndexPath(indexPath) {
4503
+ // In a horizontal layout this function the same as above for vertical,
4504
+ // shifting to the previous row to a column with similar positioning
4505
+ if (this._orientation == BMCollectionViewFlowLayoutOrientation.Horizontal) {
4506
+ if (!this._cellSize || this._expectedCellSize) return this._cachedIndexPathAboveIndexPath(indexPath);
4507
+
4508
+ return this._computedIndexPathAboveIndexPath(indexPath);
4509
+ }
4510
+
4511
+ // Otherwise the base implementation is sufficient
4512
+ return BMCollectionViewLayout.prototype.indexPathToTheLeftOfIndexPath.apply(this, arguments);
4513
+ },
4514
+
4515
+ // @override - BMCollectionViewLayout
4516
+ indexPathAboveIndexPath(indexPath) {
4517
+ // In a horizontal layout the base implementation is sufficient
4518
+ if (this._orientation == BMCollectionViewFlowLayoutOrientation.Horizontal) {
4519
+ return BMCollectionViewLayout.prototype.indexPathToTheLeftOfIndexPath.apply(this, arguments);
4520
+ }
4521
+
4522
+ if (!this._cellSize || this._expectedCellSize) return this._cachedIndexPathAboveIndexPath(indexPath);
4523
+
4524
+ return this._computedIndexPathAboveIndexPath(indexPath);
4525
+ },
4526
+
4527
+ // @override - BMCollectionViewLayout
4528
+ indexPathToTheRightOfIndexPath(indexPath) {
4529
+ // In a horizontal layout this function the same as below for vertical,
4530
+ // shifting to the next row to a column with similar positioning
4531
+ if (this._orientation == BMCollectionViewFlowLayoutOrientation.Horizontal) {
4532
+ if (!this._cellSize || this._expectedCellSize) return this._cachedIndexPathBelowIndexPath(indexPath);
4533
+
4534
+ return this._computedIndexPathBelowIndexPath(indexPath);
4535
+ }
4536
+
4537
+ // Otherwise the base implementation is sufficient
4538
+ return BMCollectionViewLayout.prototype.indexPathToTheRightOfIndexPath.apply(this, arguments);
4539
+ },
4540
+
4541
+ // @override - BMCollectionViewLayout
4542
+ indexPathBelowIndexPath(indexPath) {
4543
+ // In a horizontal layout the base implementation is sufficient
4544
+ if (this._orientation == BMCollectionViewFlowLayoutOrientation.Horizontal) {
4545
+ return BMCollectionViewLayout.prototype.indexPathToTheRightOfIndexPath.apply(this, arguments);
4546
+ }
4547
+
4548
+ if (!this._cellSize || this._expectedCellSize) return this._cachedIndexPathBelowIndexPath(indexPath);
4549
+
4550
+ return this._computedIndexPathBelowIndexPath(indexPath);
4551
+ },
4552
+
4553
+ /**
4554
+ * Returns the index path that is above the given index path in a cached layout.
4555
+ * In a horizontal layout this returns the index path that is to the left of the given index path.
4556
+ * @param indexPath <BMIndexPath> The index path to start from.
4557
+ * @returns <BMIndexPath> The index path above the given one.
4558
+ */
4559
+ _cachedIndexPathAboveIndexPath(indexPath) {
4560
+ const section = indexPath.section;
4561
+ const row = indexPath.row;
4562
+
4563
+ // Find the row where the current index path is
4564
+ const cachedSection = this.cachedLayout.sections[section];
4565
+
4566
+ if (!cachedSection) return indexPath;
4567
+
4568
+ const sectionRows = cachedSection.rows;
4569
+
4570
+ let rowIndex = 0;
4571
+ const rowCount = sectionRows.length;
4572
+ for (rowIndex; rowIndex < rowCount; rowIndex++) {
4573
+ const sectionRow = sectionRows[rowIndex];
4574
+ if (sectionRow.startIndex <= row && sectionRow.endIndex >= row) {
4575
+ break;
4576
+ }
4577
+ }
4578
+
4579
+ const attributes = sectionRows[rowIndex].attributes.find(a => a.indexPath.row == row);
4580
+ const positionProperty = this._orientation == BMCollectionViewFlowLayoutOrientation.Vertical ? 'x' : 'y';
4581
+ const position = attributes.frame.center[positionProperty];
4582
+
4583
+ let previousRow;
4584
+
4585
+ // Find the next row, if any
4586
+ if (rowIndex >= 0) {
4587
+ const previousRowIndex = rowIndex - 1;
4588
+ previousRow = sectionRows[previousRowIndex];
4589
+ }
4590
+ else {
4591
+ // If the current row is the first one in its section, find the next section that has at least one row
4592
+ let sectionIndex = section - 1;
4593
+ while (sectionIndex >= 0) {
4594
+ if (this.collectionView.numberOfObjectsInSectionAtIndex(sectionIndex)) {
4595
+ previousRow = this.cachedLayout.sections[sectionIndex].sectionRows[this.cachedLayout.sections[sectionIndex].sectionRows.length - 1];
4596
+ break;
4597
+ }
4598
+ sectionIndex--;
4599
+ }
4600
+ }
4601
+
4602
+
4603
+ // If no viable row is found, the current row is the first one
4604
+ if (previousRow) {
4605
+ // In this row, return the index path for the attributes with
4606
+ // the closest horizontal distance from the source index path
4607
+ let minDistance = Number.MAX_SAFE_INTEGER;
4608
+ let minDistanceIndex = -1;
4609
+ for (let i = 0; i < previousRow.attributes.length; i++) {
4610
+ const attributes = previousRow.attributes[i];
4611
+ const distance = Math.abs(attributes.frame.center[positionProperty] - position);
4612
+
4613
+ if (distance < minDistance) {
4614
+ minDistance = distance;
4615
+ minDistanceIndex = i;
4616
+ }
4617
+ }
4618
+
4619
+ return previousRow.attributes[minDistanceIndex].indexPath;
4620
+ }
4621
+
4622
+ return indexPath;
4623
+ },
4624
+
4625
+ /**
4626
+ * Returns the index path that is above the given index path in a computed layout.
4627
+ * In a horizontal layout this returns the index path that is to the left of the given index path.
4628
+ * @param indexPath <BMIndexPath> The index path to start from.
4629
+ * @returns <BMIndexPath> The index path above the given one.
4630
+ */
4631
+ _computedIndexPathAboveIndexPath(indexPath) {
4632
+ const cache = this.cachedLayout;
4633
+
4634
+ var sectionIndex = indexPath.section;
4635
+
4636
+ const numberOfColumns = cache.numberOfColumns;
4637
+
4638
+ var objectIndex = indexPath.row;
4639
+
4640
+ // Find the row and column index within the section
4641
+ const cellRow = (objectIndex / numberOfColumns) | 0;
4642
+ const cellColumn = objectIndex % numberOfColumns;
4643
+
4644
+ let previousRow;
4645
+
4646
+ if (cellRow > 0) {
4647
+ previousRow = cellRow - 1;
4648
+ }
4649
+ else {
4650
+ // If the current row is the last one in its section, find the next section that has at least one row
4651
+ let previousSectionIndex = sectionIndex - 1;
4652
+ while (previousSectionIndex >= 0) {
4653
+ if (this.collectionView.numberOfObjectsInSectionAtIndex(previousSectionIndex)) {
4654
+ previousRow = cache.sections[previousSectionIndex].numberOfRows - 1;
4655
+ sectionIndex = previousSectionIndex;
4656
+ break;
4657
+ }
4658
+ previousSectionIndex--;
4659
+ }
4660
+ }
4661
+
4662
+ if (typeof previousRow != 'number') return indexPath;
4663
+
4664
+ const nextRowIndexStart = numberOfColumns * previousRow;
4665
+ const nextSectionNumberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(sectionIndex);
4666
+
4667
+
4668
+ // Find this cell's row prototype. In most cases this will be the prototype for the numberOfColumns columns,
4669
+ // unless this cell is in the last row - in those cases it is possible that there may be fewer columns
4670
+ if (previousRow == ((nextSectionNumberOfObjects / numberOfColumns) | 0)) {
4671
+ var numberOfColumnsInLastRow = (nextSectionNumberOfObjects % numberOfColumns) || numberOfColumns;
4672
+
4673
+ return this.collectionView.indexPathForObjectAtRow(nextRowIndexStart + Math.min(numberOfColumnsInLastRow, cellColumn), {inSectionAtIndex: sectionIndex})
4674
+ }
4675
+ else {
4676
+ return this.collectionView.indexPathForObjectAtRow(nextRowIndexStart + cellColumn, {inSectionAtIndex: sectionIndex});
4677
+ }
4678
+
4679
+ },
4680
+
4681
+ /**
4682
+ * Returns the index path that is below the given index path in a cached layout.
4683
+ * In a horizontal layout this returns the index path that is to the right of the given index path.
4684
+ * @param indexPath <BMIndexPath> The index path to start from.
4685
+ * @returns <BMIndexPath> The index path below the given one.
4686
+ */
4687
+ _cachedIndexPathBelowIndexPath(indexPath) {
4688
+ const sectionCount = this.collectionView.numberOfSections();
4689
+
4690
+ const section = indexPath.section;
4691
+ const row = indexPath.row;
4692
+
4693
+ // Find the row where the current index path is
4694
+ const cachedSection = this.cachedLayout.sections[section];
4695
+
4696
+ if (!cachedSection) return indexPath;
4697
+
4698
+ const sectionRows = cachedSection.rows;
4699
+
4700
+ let rowIndex = 0;
4701
+ const rowCount = sectionRows.length;
4702
+ for (rowIndex; rowIndex < rowCount; rowIndex++) {
4703
+ const sectionRow = sectionRows[rowIndex];
4704
+ if (sectionRow.startIndex <= row && sectionRow.endIndex >= row) {
4705
+ break;
4706
+ }
4707
+ }
4708
+
4709
+ const attributes = sectionRows[rowIndex].attributes.find(a => a.indexPath.row == row);
4710
+ const positionProperty = this._orientation == BMCollectionViewFlowLayoutOrientation.Vertical ? 'x' : 'y';
4711
+ const position = attributes.frame.center[positionProperty];
4712
+
4713
+ let nextRow;
4714
+
4715
+ // Find the next row, if any
4716
+ if (rowIndex < rowCount - 1) {
4717
+ const nextRowIndex = rowIndex + 1;
4718
+ nextRow = sectionRows[nextRowIndex];
4719
+ }
4720
+ else {
4721
+ // If the current row is the last one in its section, find the next section that has at least one row
4722
+ let sectionIndex = section + 1;
4723
+ while (sectionIndex < sectionCount) {
4724
+ if (this.collectionView.numberOfObjectsInSectionAtIndex(sectionIndex)) {
4725
+ nextRow = this.cachedLayout.sections[sectionIndex].sectionRows[0];
4726
+ break;
4727
+ }
4728
+ sectionIndex++;
4729
+ }
4730
+ }
4731
+
4732
+
4733
+ // If no viable row is found, the current row is the last one
4734
+ if (nextRow) {
4735
+ // In this row, return the index path for the attributes with
4736
+ // the closest horizontal distance from the source index path
4737
+ let minDistance = Number.MAX_SAFE_INTEGER;
4738
+ let minDistanceIndex = -1;
4739
+ for (let i = 0; i < nextRow.attributes.length; i++) {
4740
+ const attributes = nextRow.attributes[i];
4741
+ const distance = Math.abs(attributes.frame.center[positionProperty] - position);
4742
+
4743
+ if (distance < minDistance) {
4744
+ minDistance = distance;
4745
+ minDistanceIndex = i;
4746
+ }
4747
+ }
4748
+
4749
+ return nextRow.attributes[minDistanceIndex].indexPath;
4750
+ }
4751
+
4752
+ return indexPath;
4753
+ },
4754
+
4755
+ /**
4756
+ * Returns the index path that is below the given index path in a computed layout.
4757
+ * In a horizontal layout this returns the index path that is to the left of the given index path.
4758
+ * @param indexPath <BMIndexPath> The index path to start from.
4759
+ * @returns <BMIndexPath> The index path below the given one.
4760
+ */
4761
+ _computedIndexPathBelowIndexPath(indexPath) {
4762
+ const sectionCount = this.collectionView.numberOfSections();
4763
+
4764
+ const cache = this.cachedLayout;
4765
+
4766
+ var sectionIndex = indexPath.section;
4767
+ var section = cache.sections[sectionIndex];
4768
+
4769
+ const numberOfColumns = cache.numberOfColumns;
4770
+
4771
+ var objectIndex = indexPath.row;
4772
+
4773
+ // Find the row and column index within the section
4774
+ const cellRow = (objectIndex / numberOfColumns) | 0;
4775
+ const cellColumn = objectIndex % numberOfColumns;
4776
+
4777
+ let nextRow;
4778
+
4779
+ if (cellRow < section.numberOfRows - 1) {
4780
+ nextRow = cellRow + 1;
4781
+ }
4782
+ else {
4783
+ // If the current row is the last one in its section, find the next section that has at least one row
4784
+ let nextSectionIndex = sectionIndex + 1;
4785
+ while (nextSectionIndex < sectionCount) {
4786
+ if (this.collectionView.numberOfObjectsInSectionAtIndex(nextSectionIndex)) {
4787
+ nextRow = 0;
4788
+ sectionIndex = nextSectionIndex;
4789
+ break;
4790
+ }
4791
+ nextSectionIndex++;
4792
+ }
4793
+ }
4794
+
4795
+ if (typeof nextRow != 'number') return indexPath;
4796
+
4797
+ const nextRowIndexStart = numberOfColumns * nextRow;
4798
+ const nextSectionNumberOfObjects = this.collectionView.numberOfObjectsInSectionAtIndex(sectionIndex);
4799
+
4800
+
4801
+ // Find this cell's row prototype. In most cases this will be the prototype for the numberOfColumns columns,
4802
+ // unless this cell is in the last row - in those cases it is possible that there may be fewer columns
4803
+ if (nextRow == ((nextSectionNumberOfObjects / numberOfColumns) | 0)) {
4804
+ var numberOfColumnsInLastRow = (nextSectionNumberOfObjects % numberOfColumns) || numberOfColumns;
4805
+
4806
+ return this.collectionView.indexPathForObjectAtRow(nextRowIndexStart + Math.min(numberOfColumnsInLastRow, cellColumn), {inSectionAtIndex: sectionIndex})
4807
+ }
4808
+ else {
4809
+ return this.collectionView.indexPathForObjectAtRow(nextRowIndexStart + cellColumn, {inSectionAtIndex: sectionIndex});
4810
+ }
4811
+
4812
+ },
4423
4813
 
4424
4814
  // @override - BMCollectionViewLayout
4425
4815
  copy: function () {