@craft-native/ios 0.0.497 → 0.0.500

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.
@@ -316,7 +316,8 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
316
316
  rows += (pending + columns - 1) / columns
317
317
  content = CGFloat(rows) * 44 + CGFloat(max(0, rows - 1)) * flowLayout.minimumLineSpacing
318
318
  }
319
- return max(0, ceil(content + contentInset.top + contentInset.bottom))
319
+ // Its rows and its own padding: not the room left for what floats over it.
320
+ return max(0, ceil(content + contentPadding.top + contentPadding.bottom))
320
321
  }
321
322
 
322
323
  /// The width a row gets: the list's own width inside its content padding,
@@ -406,6 +407,24 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
406
407
  bottom: nonNegative(raw?["paddingBottom"]) ?? vertical,
407
408
  right: nonNegative(raw?["paddingRight"]) ?? horizontal
408
409
  )
410
+ guard contentPadding != inset else { return }
411
+ contentPadding = inset
412
+ applyContentInsets()
413
+ }
414
+
415
+ /// `contentContainerStyle`'s padding, which the list draws as its insets.
416
+ private var contentPadding: UIEdgeInsets = .zero
417
+
418
+ /// How far the compact tab actions reach over the end of this list. The
419
+ /// rows get that much more room at the end, so the last one can be
420
+ /// brought clear of them (#523).
421
+ var floatingCover: CGFloat = 0 {
422
+ didSet { if abs(oldValue - floatingCover) > 0.5 { applyContentInsets() } }
423
+ }
424
+
425
+ private func applyContentInsets() {
426
+ var inset = contentPadding
427
+ if flowLayout.scrollDirection == .vertical { inset.bottom += floatingCover }
409
428
  guard contentInset != inset else { return }
410
429
  contentInset = inset
411
430
  scrollIndicatorInsets = inset
@@ -507,11 +526,10 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
507
526
  let oldItems = itemsByIdentity
508
527
  let viewportAnchor = currentViewportAnchor()
509
528
  var seen: [String: Int] = [:]
529
+ var taken = Set<String>()
510
530
  var next = nodes.enumerated().map { index, node -> Item in
511
531
  let base = Self.identity(for: node) ?? "index:\(index)"
512
- let occurrence = seen[base, default: 0]
513
- seen[base] = occurrence + 1
514
- let identity = occurrence == 0 ? base : "\(base)#\(occurrence)"
532
+ let identity = Self.uniqueIdentity(base, seen: &seen, taken: &taken)
515
533
  let props = node["props"] as? [String: Any] ?? [:]
516
534
  let role = props["listRole"] as? String
517
535
  let signature = (try? JSONSerialization.data(withJSONObject: node, options: [.sortedKeys])) ?? Data()
@@ -532,7 +550,7 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
532
550
  return dataItemCount - 1
533
551
  }
534
552
  items = next
535
- itemsByIdentity = Dictionary(uniqueKeysWithValues: next.map { ($0.identity, $0) })
553
+ itemsByIdentity = Dictionary(next.map { ($0.identity, $0) }, uniquingKeysWith: { first, _ in first })
536
554
 
537
555
  flowLayout.scrollDirection = horizontal ? .horizontal : .vertical
538
556
  alwaysBounceHorizontal = horizontal
@@ -680,7 +698,7 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
680
698
  let lastDataOrdinal = dataOrdinals[lastVisible]
681
699
  guard lastDataOrdinal >= 0 else { return }
682
700
  let remaining = dataItemCount - lastDataOrdinal - 1
683
- let thresholdItems = max(1, Int(ceil(Double(dataItemCount) * endReachedThreshold)))
701
+ let thresholdItems = max(1, craftNativeInt(ceil(Double(dataItemCount) * endReachedThreshold)))
684
702
  guard remaining <= thresholdItems else { return }
685
703
  let signature = dataContentSignature
686
704
  guard endReachedSignature != signature else { return }
@@ -864,6 +882,25 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
864
882
  setContentOffset(clamped(offset), animated: false)
865
883
  }
866
884
 
885
+ /// A row's identity, its own in this list.
886
+ ///
887
+ /// Rows that share a key are told apart by a suffix: `run`, then `run#1`.
888
+ /// A row whose own key was `run#1` then had the identity of the second
889
+ /// `run`, and the list's table and its snapshot both trap on a repeated
890
+ /// identity: three tags keyed `run`, `run` and `run#1` ended the process.
891
+ /// The suffix counts on until the identity is one no row has taken. A
892
+ /// list with no such key has the identities it always had.
893
+ static func uniqueIdentity(_ base: String, seen: inout [String: Int], taken: inout Set<String>) -> String {
894
+ var occurrence = seen[base, default: 0]
895
+ var identity = occurrence == 0 ? base : "\(base)#\(occurrence)"
896
+ while !taken.insert(identity).inserted {
897
+ occurrence += 1
898
+ identity = "\(base)#\(occurrence)"
899
+ }
900
+ seen[base] = occurrence + 1
901
+ return identity
902
+ }
903
+
867
904
  private static func identity(for node: [String: Any]) -> String? {
868
905
  let props = node["props"] as? [String: Any] ?? [:]
869
906
  return [node["key"], props["key"], props["testID"], node["id"]]