mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 14:14:47 +00:00
fix(bar): align subscription carousel and collapse stale blank space (#1600)
Closes #1599
This commit is contained in:
1 parent
7c635a1714
commit
62274dabfa
2 files changed
+25
-12
No files matched your search
@@ -5,11 +5,13 @@ import CCSBarCore
|
|||||||
// MARK: - Content height preference key
|
// MARK: - Content height preference key
|
||||||
|
|
||||||
/// Preference key used to bubble the measured content VStack height up to the
|
/// Preference key used to bubble the measured content VStack height up to the
|
||||||
/// ScrollView parent without a feedback loop. The reduction takes the MAX so
|
/// ScrollView parent. The reduction takes the MAX only to combine multiple
|
||||||
/// that intermediate layout passes that report a smaller size don't cause the
|
/// simultaneous reporters into one value (in practice a single background
|
||||||
/// frame to shrink, which would trigger another layout pass (the classic
|
/// GeometryReader reports here, so the max is just that reader's height). The
|
||||||
/// GeometryReader loop). A `@State` var is updated only when the value changes,
|
/// consumer (`onPreferenceChange`) then tracks the height in both directions so
|
||||||
/// keeping SwiftUI's diffing stable.
|
/// the popover frame can shrink back when content collapses. This is safe from
|
||||||
|
/// the classic GeometryReader feedback loop because the reader measures the
|
||||||
|
/// intrinsic content VStack, whose height does not depend on the consumer frame.
|
||||||
private struct ContentHeightKey: PreferenceKey {
|
private struct ContentHeightKey: PreferenceKey {
|
||||||
static let defaultValue: CGFloat = 0
|
static let defaultValue: CGFloat = 0
|
||||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
||||||
@@ -151,9 +153,14 @@ struct BarMenuView: View {
|
|||||||
// until the first preference fires.
|
// until the first preference fires.
|
||||||
.frame(height: scrollAreaHeight, alignment: .top)
|
.frame(height: scrollAreaHeight, alignment: .top)
|
||||||
.onPreferenceChange(ContentHeightKey.self) { measured in
|
.onPreferenceChange(ContentHeightKey.self) { measured in
|
||||||
// Only grow, never shrink — prevents a feedback loop where a smaller
|
// Track the measured content height in BOTH directions so the popover
|
||||||
// frame triggers a re-layout that reports an even smaller height.
|
// collapses back when content shrinks (e.g. alert / reauth rows clear)
|
||||||
if measured > contentHeight {
|
// instead of staying stuck at a past peak and rendering blank space
|
||||||
|
// below. The background GeometryReader measures the intrinsic content
|
||||||
|
// VStack, whose height does not depend on this frame, so updating in
|
||||||
|
// either direction cannot feed the classic GeometryReader layout loop.
|
||||||
|
// The 0.5pt deadband avoids churn on sub-pixel remeasures.
|
||||||
|
if abs(measured - contentHeight) > 0.5 {
|
||||||
contentHeight = measured
|
contentHeight = measured
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,9 +283,15 @@ struct BarMenuView: View {
|
|||||||
BarSubscriptionCard(
|
BarSubscriptionCard(
|
||||||
row: row, isParked: row.paused,
|
row: row, isParked: row.paused,
|
||||||
onRefresh: { viewModel.forceRefresh() })
|
onRefresh: { viewModel.forceRefresh() })
|
||||||
// 340 leaves 10px side padding inside the 360-wide popover.
|
// Each card spans exactly the horizontal scroll viewport so
|
||||||
.frame(width: 340, alignment: .leading)
|
// .paging snaps to one whole card per page. A fixed width
|
||||||
.padding(.horizontal, 2)
|
// wider than the viewport (the 360 popover minus the content
|
||||||
|
// VStack's 14pt padding leaves ~332pt) drifts a few points on
|
||||||
|
// every page and leaves the card stuck off-center after
|
||||||
|
// paging forward and back. containerRelativeFrame keeps the
|
||||||
|
// card width == viewport width, so paging stays aligned and
|
||||||
|
// the card is centered like the single-profile card.
|
||||||
|
.containerRelativeFrame(.horizontal)
|
||||||
.id(row.id)
|
.id(row.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1.9.0
|
1.9.1
|
||||||
Reference in new issue
Block a user