Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 114 additions & 63 deletions maui/src/BottomSheet/SfBottomSheet.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Controls.Shapes;
using Syncfusion.Maui.Toolkit.Internals;
using Syncfusion.Maui.Toolkit.Themes;
using Syncfusion.Maui.Toolkit.Helper;
Expand Down Expand Up @@ -74,11 +74,16 @@ public partial class SfBottomSheet : SfView, IParentThemeElement
/// </summary>
bool _isPointerPressed;

// Touch tracking
/// <summary>
/// The initial Y-coordinate of a touch event on the bottom sheet.
/// </summary>
double _initialTouchY;
/// <summary>
/// Indicates whether the overlay grid is currently added to the view hierarchy.
/// </summary>
bool _isOverlayAdded;

// Touch tracking
/// <summary>
/// The initial Y-coordinate of a touch event on the bottom sheet.
/// </summary>
double _initialTouchY;

/// <summary>
/// The starting Y-coordinate of a swipe gesture on the bottom sheet.
Expand Down Expand Up @@ -1242,7 +1247,7 @@ internal Color OverlayBackgroundColor
/// </exception>
public void Show()
{
if (_bottomSheet is null || _overlayGrid is null)
if (_bottomSheet is null)
{
return;
}
Expand All @@ -1267,15 +1272,15 @@ public void Show()
/// </exception>
public void Close()
{
if(_bottomSheet is null || _overlayGrid is null)
if(_bottomSheet is null)
{
return;
}

AnimateBottomSheet(Height, onFinish: () =>
{
_bottomSheet.IsVisible = false;
_overlayGrid.IsVisible = false;
RemoveOverlayFromView();
});

if (_isSheetOpen)
Expand Down Expand Up @@ -1356,9 +1361,8 @@ void InitializeLayout()
InitializeBottomSheetBorder();
InitializeContentBorder();

if (_bottomSheet is not null && _overlayGrid is not null)
if (_bottomSheet is not null)
{
Children.Add(_overlayGrid);
Children.Add(_bottomSheet);
_bottomSheet.IsVisible = false;
}
Expand All @@ -1370,6 +1374,7 @@ void InitializeLayout()
void UpdateContentView()
{
Children.Clear();
_isOverlayAdded = false; // Reset overlay state
UpdateAllChild();
}

Expand All @@ -1379,7 +1384,6 @@ void UpdateContentView()
void UpdateAllChild()
{
AddChild(Content);
AddChild(_overlayGrid);
AddChild(_bottomSheet);
}

Expand All @@ -1404,7 +1408,7 @@ void InitializeOverlayGrid()
{
BackgroundColor = OverlayBackgroundColor,
Opacity = DefaultOverlayOpacity,
IsVisible = false
IsVisible = true
};

var tapGestureRecognizer = new TapGestureRecognizer();
Expand Down Expand Up @@ -1485,6 +1489,36 @@ void InitializeBottomSheetBorder()
};
}

/// <summary>
/// Adds the overlay grid to the view hierarchy if it's not already added and modal is enabled.
/// </summary>
void AddOverlayToView()
{
if (_overlayGrid is not null && IsModal && !_isOverlayAdded)
{
if (!Children.Contains(_overlayGrid))
{
Children.Insert(Children.Count - 1, _overlayGrid); // Insert before bottom sheet
}
_isOverlayAdded = true;
}
}

/// <summary>
/// Removes the overlay grid from the view hierarchy.
/// </summary>
void RemoveOverlayFromView()
{
if (_overlayGrid is not null && _isOverlayAdded)
{
if (Children.Contains(_overlayGrid))
{
Children.Remove(_overlayGrid);
}
_isOverlayAdded = false;
}
}

/// <summary>
/// Calculates the initial height for the half-expanded state.
/// </summary>
Expand Down Expand Up @@ -2040,16 +2074,20 @@ void RegisterSizeChangedEvent()
/// </summary>
void SetupBottomSheetForShow()
{
if (_isSheetOpen || _bottomSheet is null || _overlayGrid is null)
if (_isSheetOpen || _bottomSheet is null)
{
return;
}

// Position the bottom sheet just below the visible area
_bottomSheet.TranslationY = Height;
_bottomSheet.IsVisible = true;
_overlayGrid.IsVisible = IsModal;
_overlayGrid.Opacity = 0;

// Add overlay to view if modal
if (IsModal)
{
AddOverlayToView();
}
}


Expand Down Expand Up @@ -2133,11 +2171,6 @@ void AnimateBottomSheet(double targetPosition, Action? onFinish = null)
_bottomSheet.AbortAnimation("bottomSheetAnimation");
}

if (_overlayGrid.AnimationIsRunning("overlayGridAnimation"))
{
_overlayGrid.AbortAnimation("overlayGridAnimation");
}

int animationDuration = this.GetClampedAnimationDuration();
const int topPadding = 2;
_isSheetOpen = true;
Expand All @@ -2159,46 +2192,45 @@ void AnimateBottomSheet(double targetPosition, Action? onFinish = null)
/// </summary>
void AnimateOverlay(int animationDuration)
{
if (_overlayGrid is not null)
if (_overlayGrid is null || !IsModal)
{
double startValue = 0;
double endValue = 0;
_overlayGrid.IsVisible = IsModal;
return;
}

if (IsModal)
// Ensure overlay is added to view when needed
bool shouldShowOverlay = State is not (BottomSheetState.Collapsed or BottomSheetState.Hidden);

if (shouldShowOverlay)
{
AddOverlayToView();
}

if (_overlayGrid.AnimationIsRunning("overlayGridAnimation"))
{
_overlayGrid.AbortAnimation("overlayGridAnimation");
}

double startValue = _overlayGrid.Opacity;
double endValue = shouldShowOverlay ? DefaultOverlayOpacity : 0;

var overlayGridAnimation = new Animation(d =>
{
if (!double.IsNaN(d))
{
if (State is BottomSheetState.Collapsed || State is BottomSheetState.Hidden)
{
startValue = _overlayGrid.Opacity;
endValue = 0;
}
else
{
startValue = _overlayGrid.Opacity;
endValue = DefaultOverlayOpacity;
}
_overlayGrid.Opacity = d;
}
}, startValue, endValue);

var overlayGridAnimation = new Animation(d =>
_overlayGrid.Animate("overlayGridAnimation", overlayGridAnimation,
length: (uint)animationDuration,
easing: Easing.Linear,
finished: (v, c) =>
{
if (!shouldShowOverlay)
{
// Ensure the opacity is only updated with valid numeric values to avoid rendering issues.
if (!double.IsNaN(d))
{
_overlayGrid.Opacity = d;
}
RemoveOverlayFromView();
}
, startValue, endValue);
_overlayGrid.Animate("overlayGridAnimation", overlayGridAnimation,
length: (uint)animationDuration,
easing: Easing.Linear,
finished: (e, v) =>
{
if (State is BottomSheetState.Collapsed || State is BottomSheetState.Hidden)
{
_overlayGrid.IsVisible = false;
}
});
}
}
});
}

/// <summary>
Expand Down Expand Up @@ -2342,16 +2374,29 @@ AllowedState is BottomSheetAllowedState.HalfExpanded &&
/// <param name="touchY">The current Y coordinate of the touch point.</param>
void UpdateBottomSheetPosition(double newTranslationY, double touchY)
{
if (_bottomSheet is null || _overlayGrid is null)
if (_bottomSheet is null)
{
return;
}

_bottomSheet.TranslationY = newTranslationY;
_initialTouchY = touchY;
_bottomSheet.HeightRequest = Height - newTranslationY;
_overlayGrid.IsVisible = IsModal && (_bottomSheet.HeightRequest > CollapsedHeight);
_overlayGrid.Opacity = CalculateOverlayOpacity(_bottomSheet.HeightRequest);
// Manage overlay visibility during touch
bool shouldShowOverlay = IsModal && (_bottomSheet.HeightRequest > CollapsedHeight);

if (shouldShowOverlay)
{
AddOverlayToView();
if (_overlayGrid is not null)
{
_overlayGrid.Opacity = CalculateOverlayOpacity(_bottomSheet.HeightRequest);
}
}
else
{
RemoveOverlayFromView();
}
}

/// <summary>
Expand Down Expand Up @@ -2517,12 +2562,18 @@ static void OnIsModalPropertyChanged(BindableObject bindable, object oldValue, o
{
if (bindable is SfBottomSheet sheet)
{
if (sheet._overlayGrid is not null && (sheet.State is BottomSheetState.FullExpanded || sheet.State is BottomSheetState.HalfExpanded))
{
sheet._overlayGrid.IsVisible = sheet.IsModal;
bool isModal = (bool)newValue;

if (isModal && (sheet.State is BottomSheetState.FullExpanded or BottomSheetState.HalfExpanded))
{
sheet.AddOverlayToView();
sheet.AnimateOverlay(150);
}
}
}
else if (!isModal)
{
sheet.RemoveOverlayFromView();
}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void Constructor_InitializesDefaultsCorrectly()
Assert.Equal(4d, _bottomSheet.GrabberHeight);
Assert.Equal(32d, _bottomSheet.GrabberWidth);
Assert.Equal(12d, _bottomSheet.GrabberCornerRadius);
Assert.Equal(150d, _bottomSheet.AnimationDuration);
Assert.Equal(150d,_bottomSheet.AnimationDuration);
if (_bottomSheet.GrabberBackground is SolidColorBrush grabberBrush)
{
var grabberColor = grabberBrush.Color;
Expand Down Expand Up @@ -564,7 +564,7 @@ public void InitializeLayout()
var bottomSheet = GetPrivateField(_bottomSheet, "_bottomSheet");
var overlayGrid = GetPrivateField(_bottomSheet, "_overlayGrid");
Assert.True(_bottomSheet.Children?.Contains(bottomSheet));
Assert.True(_bottomSheet.Children?.Contains(overlayGrid));
Assert.False(_bottomSheet.Children?.Contains(overlayGrid));
}

[Fact]
Expand Down Expand Up @@ -592,7 +592,7 @@ public void InitializeOverlayGrid()
Grid? overlayGrid = (Grid?)GetPrivateField(_bottomSheet, "_overlayGrid");
Assert.Equal(overlayGrid?.BackgroundColor, Color.FromArgb("#80000000"));
Assert.Equal(overlayGrid?.Opacity, 0.5);
Assert.Equal(overlayGrid?.IsVisible, false);
Assert.Equal(overlayGrid?.IsVisible, true);
}

[Fact]
Expand Down Expand Up @@ -852,24 +852,24 @@ public void UpdateStateChanged(BottomSheetState oldState, BottomSheetState newSt

[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
[InlineData(false, true)]
public void SetupBottomSheetForShow(bool input, bool expected)
{
_bottomSheet.IsModal = input;
InvokePrivateMethod(_bottomSheet, "SetupBottomSheetForShow");
SfGrid? overlay = (SfGrid?)GetPrivateField(_bottomSheet, "_overlayGrid");
SfBorder? bottomsheet = (SfBorder?)GetPrivateField(_bottomSheet, "_bottomSheet");
Assert.True(bottomsheet?.IsVisible);
Assert.Equal(0, overlay?.Opacity);
Assert.Equal(0.5, overlay?.Opacity);
Assert.Equal(expected, overlay?.IsVisible);
}

[Fact]
public void GetCollapsedPosition()
{
InvokePrivateMethod(_bottomSheet, "GetCollapsedPosition");
SfGrid? overlay = (SfGrid?)GetPrivateField(_bottomSheet, "_overlayGrid");
Assert.False(overlay?.IsVisible);
var overlay = GetPrivateField(_bottomSheet, "_overlayGrid");
Assert.False(_bottomSheet.Children?.Contains(overlay));
}

[Fact]
Expand Down