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
18 changes: 16 additions & 2 deletions maui/src/BottomSheet/BottomSheetBorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ internal class BottomSheetBorder : SfBorder, ITouchListener
// To store the child count of bottom sheet
double _childLoopCount;

// To handle bottom sheet swipe based on initial touch point.
bool _canHandleTouch = true;

#endif

#endregion
#endregion

#region Constructor

Expand Down Expand Up @@ -123,10 +126,21 @@ public void OnTouch(Toolkit.Internals.PointerEventArgs e)
}

var firstDescendant = Content.GetVisualTreeDescendants().FirstOrDefault();
if (firstDescendant is not null && IsChildElementScrolled(firstDescendant, e.TouchPoint))
if (firstDescendant is not null && _canHandleTouch && IsChildElementScrolled(firstDescendant, e.TouchPoint))
{
return;
}
else
{
if (e is not null && e.Action == PointerActions.Pressed)
{
_canHandleTouch = false;
}
else if (e is not null && e.Action == PointerActions.Released)
{
_canHandleTouch = true;
}
}

#endif
if (e is not null && _bottomSheetRef is not null)
Expand Down
162 changes: 126 additions & 36 deletions maui/src/BottomSheet/SfBottomSheet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public partial class SfBottomSheet : SfView, IParentThemeElement
/// <summary>
/// The shape used to provide corner radius for the grabber.
/// </summary>
RoundRectangle? _grabberStrokeShape;
RoundRectangle? _grabberStrokeShape;

// Shape
/// <summary>
Expand Down Expand Up @@ -823,7 +823,6 @@ public void Show()
}

SetupBottomSheetForShow();
_isSheetOpen = true;
AnimateBottomSheet(GetTargetPosition());
IsOpen = true;
}
Expand Down Expand Up @@ -1028,7 +1027,8 @@ void InitializeBottomSheetBorder()
HeightRequest = CalculateInitialHeight(),
IsVisible = false,
StrokeShape = _bottomSheetStrokeShape,
Content = _bottomSheetContent ?? throw new InvalidOperationException("Bottom sheet content is not initialized.")
Content = _bottomSheetContent ?? throw new InvalidOperationException("Bottom sheet content is not initialized."),
Padding = ContentPadding
};
}

Expand All @@ -1048,8 +1048,7 @@ void InitializeContentBorder()
{
_contentBorder = new SfBorder()
{
StrokeThickness = 0,
Padding = ContentPadding
StrokeThickness = 0
};
}

Expand Down Expand Up @@ -1298,9 +1297,9 @@ CornerRadius EnsureValidCornerRadius(CornerRadius cornerRadius)
/// <param name="padding">The new padding to be applied.</param>
void UpdatePadding(Thickness padding)
{
if (_contentBorder is not null && !_contentBorder.Padding.Equals(padding))
if (_bottomSheet is not null && !_bottomSheet.Padding.Equals(padding))
{
_contentBorder.Padding = padding;
_bottomSheet.Padding = padding;
OnPropertyChanged(nameof(ContentPadding));
}
}
Expand All @@ -1316,7 +1315,7 @@ void UpdateGrabberHeightProperty(double newValue)
return;
}

_grabber.HeightRequest = (newValue<0) ? (double)(GrabberHeightProperty.DefaultValue) : newValue;
_grabber.HeightRequest = (newValue<=0) ? (double)(GrabberHeightProperty.DefaultValue) : newValue;
}

/// <summary>
Expand All @@ -1330,7 +1329,7 @@ void UpdateGrabberWidthProperty(double newValue)
return;
}

_grabber.WidthRequest = (newValue<0) ? (double)GrabberWidthProperty.DefaultValue : newValue;
_grabber.WidthRequest = (newValue<=0) ? (double)GrabberWidthProperty.DefaultValue : newValue;
}


Expand Down Expand Up @@ -1495,15 +1494,29 @@ void UpdateStateBasedOnNearestPoint()

if (nearestPoint == fullExpandedHeight)
{
State = BottomSheetState.FullExpanded;
if(State is not BottomSheetState.FullExpanded)
{
State = BottomSheetState.FullExpanded;
}
else
{
Show();
}
}
else if (nearestPoint == halfExpandedHeight)
{
State = BottomSheetState.HalfExpanded;
}
else
{
State = BottomSheetState.Collapsed;
if (State is not BottomSheetState.Collapsed)
{
State = BottomSheetState.Collapsed;
}
else
{
Show();
}
}
}

Expand All @@ -1519,12 +1532,7 @@ void UpdateStateChanged(BottomSheetState oldState, BottomSheetState newState)
{
_stateChangedEventArgs.OldState = oldState;
_stateChangedEventArgs.NewState = newState;
if (_overlayGrid is not null)
{
_overlayGrid.IsVisible = (State is BottomSheetState.Collapsed) ? false : IsModal;
}

OnStateChanged(_stateChangedEventArgs);
OnStateChanged(_stateChangedEventArgs);
}
}

Expand Down Expand Up @@ -1611,12 +1619,7 @@ double GetFullExpandedPosition()
double GetCollapsedPosition()
{
double targetPosition = Height - CollapsedHeight;
if (_overlayGrid is not null)
{
_overlayGrid.IsVisible = false;
}

return targetPosition;
return targetPosition;
}

/// <summary>
Expand Down Expand Up @@ -1645,23 +1648,68 @@ double GetHalfExpandedPosition()
/// <param name="onFinish">Optional action to be executed when the animation finishes.</param>
void AnimateBottomSheet(double targetPosition, Action? onFinish = null)
{
const int AnimationDuration = 150;
const int topPadding = 2;
if (_bottomSheet.AnimationIsRunning("bottomSheetAnimation"))
{
_bottomSheet.AbortAnimation("bottomSheetAnimation");
}

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

const int animationDuration = 150;
const int topPadding = 2;
_isSheetOpen = true;
if (_bottomSheet is not null)
{
var bottomSheetAnimation = new Animation(d => _bottomSheet.TranslationY = d, _bottomSheet.TranslationY, targetPosition + topPadding);
_bottomSheet?.Animate("bottomSheetAnimation", bottomSheetAnimation, length: AnimationDuration, easing: Easing.Linear, finished: (v, e) =>
_bottomSheet?.Animate("bottomSheetAnimation", bottomSheetAnimation, length: animationDuration, easing: Easing.Linear, finished: (v, e) =>
{
UpdateBottomSheetHeight();
onFinish?.Invoke();
});
}

AnimateOverlay(animationDuration);
}

/// <summary>
/// Animates the overlay of the bottom sheet based on state transitions.
/// </summary>
void AnimateOverlay(int animationDuration)
{
if (_overlayGrid is not null)
{
var overlayGridAnimation = new Animation(d => _overlayGrid.Opacity = d, _overlayGrid.Opacity, _isSheetOpen ? DefaultOverlayOpacity : 0);
_overlayGrid?.Animate("overlayGridAnimation", overlayGridAnimation, length: AnimationDuration, easing: Easing.Linear);
double startValue = 0;
double endValue = 0;
_overlayGrid.IsVisible = IsModal;

if (IsModal)
{
if (State is BottomSheetState.Collapsed || State is BottomSheetState.Hidden)
{
startValue = _overlayGrid.Opacity;
endValue = 0;
}
else
{
startValue = _overlayGrid.Opacity;
endValue = DefaultOverlayOpacity;
}

var overlayGridAnimation = new Animation(d => _overlayGrid.Opacity = d, 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;
}
});
}
}
}

Expand Down Expand Up @@ -1767,14 +1815,31 @@ bool ShouldRestrictMovement(double newTranslationY, double diffY)
return false;
}

bool isHalfExpandedAndRestricted = State is BottomSheetState.HalfExpanded &&
double endPosition = 0;
double updatedHeight = Height - newTranslationY;
switch (State)
{
case BottomSheetState.FullExpanded:
endPosition = Height * FullExpandedRatio;
break;

case BottomSheetState.HalfExpanded:
endPosition = Height * HalfExpandedRatio;
break;

case BottomSheetState.Collapsed:
endPosition = CollapsedHeight;
break;
}

bool isHalfExpandedAndRestricted = State is BottomSheetState.HalfExpanded &&
AllowedState is BottomSheetAllowedState.HalfExpanded &&
_bottomSheet.TranslationY > newTranslationY;
updatedHeight > endPosition;

bool isCollapsedAndMovingDown = State is BottomSheetState.Collapsed && diffY > 0;
bool isCollapsedAndMovingDown = State is BottomSheetState.Collapsed && updatedHeight < endPosition;

bool isFullExpandedRestricted = State is BottomSheetState.FullExpanded &&
_bottomSheet.TranslationY > newTranslationY;
updatedHeight > endPosition;

bool isBehind = (newTranslationY > Height - CollapsedHeight) || (newTranslationY < Height * (1 - FullExpandedRatio));

Expand All @@ -1789,14 +1854,35 @@ 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)
if (_bottomSheet is null || _overlayGrid is null)
{
return;
}

_bottomSheet.TranslationY = newTranslationY;
_initialTouchY = touchY;
_bottomSheet.HeightRequest = Height - newTranslationY;
_overlayGrid.IsVisible = IsModal && (_bottomSheet.HeightRequest > CollapsedHeight);
_overlayGrid.Opacity = CalculateOverlayOpacity(_bottomSheet.HeightRequest);
}

/// <summary>
/// Calculates the overlay opacity based on the current height of the bottom sheet.
/// </summary>
/// <param name="currentHeight">The current height of the bottom sheet.</param>
/// <returns>The calculated opacity value ranging from 0 to 0.5</returns>
double CalculateOverlayOpacity(double currentHeight)
{
const double maxOpacity = 0.5;

// Calculate how far along the transition from collapsed to half-expanded.
double transitionProgress = (currentHeight - CollapsedHeight) / ((HalfExpandedRatio * Height) - CollapsedHeight);

// Clamp the transition progress to between 0 and 1.
transitionProgress = Math.Clamp(transitionProgress, 0, 1);

// Calculate and return the opacity based on the transition progress.
return transitionProgress * maxOpacity;
}

/// <summary>
Expand All @@ -1809,7 +1895,10 @@ void HandleTouchReleased(double touchY)
_initialTouchY = 0;
_isPointerPressed = false;

UpdatePosition();
if(_bottomSheet is not null && touchY >= _bottomSheet.TranslationY)
{
UpdatePosition();
}
}


Expand Down Expand Up @@ -1940,9 +2029,10 @@ static void OnIsModalPropertyChanged(BindableObject bindable, object oldValue, o
{
if (bindable is SfBottomSheet sheet)
{
if (sheet._overlayGrid is not null)
if (sheet._overlayGrid is not null && (sheet.State is BottomSheetState.FullExpanded || sheet.State is BottomSheetState.HalfExpanded))
{
sheet._overlayGrid.IsVisible = sheet.IsModal;
sheet.AnimateOverlay(150);
}
}
}
Expand Down Expand Up @@ -2035,7 +2125,7 @@ static void OnStatePropertyChanged(BindableObject bindable, object oldValue, obj

if (newState == BottomSheetState.Hidden)
{
sheet._isHalfExpanded = true;
sheet._isHalfExpanded = (sheet.AllowedState != BottomSheetAllowedState.FullExpanded);
if (sheet._isSheetOpen)
{
sheet._isSheetOpen = false;
Expand Down
Loading