SwipeTrap

A SwipeTrap is a component that traps swipe actions and triggers corresponding events.

Usage

Basic
Try swipe or drag on the container:

StartX:
StartY:
DiffX:
DiffY:
---
Triggered? False
Trigger direction:
Trigger diffX:
Trigger diffY:
<style>
    .basic-container {
        width: 100%;
        cursor: grab;
        height: 500px;
        display: flex;
        user-select: none;
        align-items: center;
        flex-direction: column;
        justify-content: center;
        border: 1px solid lightgray;
    }
</style>

<BitSwipeTrap Throttle="10"
              OnStart="HandleOnStartBasic"
              OnMove="HandleOnMoveBasic"
              OnEnd="HandleOnEndBasic"
              OnTrigger="HandleOnTriggerBasic">
    <div class="basic-container">
        <div>StartX: @swipeTrapEventArgs?.StartX</div>
        <div>StartY: @swipeTrapEventArgs?.StartY</div>
        <div>DiffX: @swipeTrapEventArgs?.DiffX</div>
        <div>DiffY: @swipeTrapEventArgs?.DiffY</div>
        <div>---</div>
        <div>Triggered? @isTriggered</div>
        <div>Trigger direction: <b>@swipeTrapTriggerArgs?.Direction</b></div>
        <div>Trigger diffX: @swipeTrapTriggerArgs?.DiffX</div>
        <div>Trigger diffY: @swipeTrapTriggerArgs?.DiffY</div>
    </div>
</BitSwipeTrap>
@code {
    private bool isTriggeredBasic;
    BitSwipeTrapEventArgs? swipeTrapEventArgsBasic;
    BitSwipeTrapTriggerArgs? swipeTrapTriggerArgsBasic;
    private void HandleOnStartBasic(BitSwipeTrapEventArgs args)
    {
        swipeTrapEventArgsBasic = args;
    }
    private void HandleOnMoveBasic(BitSwipeTrapEventArgs args)
    {
        swipeTrapEventArgsBasic = args;
    }
    private void HandleOnEndBasic(BitSwipeTrapEventArgs args)
    {
        swipeTrapEventArgsBasic = args;
    }
    private void HandleOnTriggerBasic(BitSwipeTrapTriggerArgs args)
    {
        isTriggeredBasic = true;
        swipeTrapTriggerArgsBasic = args;
        _ = Task.Delay(2000).ContinueWith(_ =>
        {
            isTriggeredBasic = false;
            swipeTrapEventArgsBasic = null;
            swipeTrapTriggerArgsBasic = null;
            InvokeAsync(StateHasChanged);
        });
    }
}
                    
Panel
Open the panel and try to close it by swiping it to the left:

Title

Item1
Item2
Item3
<style>
    .panel-container {
        width: 100%;
        height: 300px;
        overflow: hidden;
        user-select: none;
        position: relative;
        border: 1px solid lightgray;
    }

    .panel-container button {
        padding: 0.5rem;
    }

    .panel-container .panel {
        left: 0;
        color: black;
        width: 200px;
        cursor: grab;
        inset-block: 0;
        position: absolute;
        background-color: lightgray;
        transform: translateX(-100%);
    }

    .panel-container .panel.open {
        transform: translateX(0);
    }

    .panel-container .panel-trap {
        gap: 1rem;
        height: 100%;
        display: flex;
        flex-direction: column;
        background-color: gray;
    }
</style>

<div class="panel-container">
    <button @onclick="OpenPanel">
        Open
    </button>
    <div class="panel@(isPanelOpen ? " open": "")" style="@GetPanelStyle()">
        <button @onclick="ClosePanel" style="position:absolute;top:0;right:0">
            Close
        </button>
        <BitSwipeTrap Style="width:100%;height:100%"
                      OnMove="HandleOnMovePanel"
                      OnEnd="HandleOnEndPanel"
                      OnTrigger="HandleOnTriggerPanel">
            <div class="panel-trap">
                <h3>Title</h3>
                <div>Item1</div>
                <div>Item2</div>
                <div>Item3</div>
            </div>
        </BitSwipeTrap>
    </div>
</div>
@code {
    private decimal diffXPanel;
    private bool isPanelOpen;
    private void OpenPanel()
    {
        isPanelOpen = true;
    }
    private void ClosePanel()
    {
        isPanelOpen = false;
    }
    private void HandleOnMovePanel(BitSwipeTrapEventArgs args)
    {
        diffXPanel = args.DiffX;
    }
    private void HandleOnEndPanel(BitSwipeTrapEventArgs args)
    {
        diffXPanel = 0;
    }
    private void HandleOnTriggerPanel(BitSwipeTrapTriggerArgs args)
    {
        if (args.Direction == BitSwipeDirection.Left)
        {
            diffXPanel = 0;
            ClosePanel();
        }
    }
    private string GetPanelStyle()
    {
        return diffXPanel < 0 ? $"transform: translateX({diffXPanel}px)" : "";
    }
}
                    
List
Swipe each row to the right to trigger the delete action:

Delete
Item1
Delete
Item2
Delete
Item3
Delete
Item4
Delete
Item5
Delete
Item6
Delete
Item7
Delete
Item8
Delete
Item9
Delete
Item10

<style>
    .list-container {
        gap: 4px;
        width: 100%;
        color: black;
        height: 300px;
        display: flex;
        overflow-y: auto;
        user-select: none;
        overflow-x: hidden;
        position: relative;
        flex-direction: column;
        border: 1px solid lightgray;
    }

    .list-container .row {
        min-height: 40px;
        position: relative;
    }

    .list-container .delete {
        width: 60px;
        color: white;
        height: 100%;
        padding: 4px;
        position: absolute;
        background-color: red;
    }

    .list-container .row-trap {
        width: 100%;
        height: 100%;
        cursor: grab;
        padding: 4px;
        position: absolute;
        background-color: gray;
    }
</style>

<div class="list-container">
    @foreach (int idx in itemsList)
    {
        var i = idx;
        <div @key="@i" class="row">
            <div class="delete">Delete</div>
            <BitSwipeTrap Style="width:100%;height:100%"
                          Trigger="60m"
                          Threshold="10"
                          OnMove="args => HandleOnMoveList(args, i)"
                          OnEnd="args => HandleOnEndList(args, i)"
                          OnTrigger="args => HandleOnTriggerList(args, i)">
                <div class="row-trap" style="@GetRowStyle(i)">
                    <div>Item@(i + 1)</div>
                </div>
            </BitSwipeTrap>
        </div>
    }
</div>
<BitButton OnClick="ResetList">Reset</BitButton>
<BitDialog @bind-IsOpen="isListDialogOpen"
           Title="Delete item?"
           Message="Are you sure you want to delete this item?"
           OnOk="HandleOnOkList"
           OnCancel="HandleOnCancelList" />
@code {
    private int deletingIndex = -1;
    private bool isListDialogOpen;
    private TaskCompletionSource listTcs;
    private List<int> itemsList = Enumerable.Range(0, 10).ToList();
    private decimal[] diffXList = Enumerable.Repeat(0m, 10).ToArray();
    private void HandleOnMoveList(BitSwipeTrapEventArgs args, int index)
    {
        diffXList[index] = args.DiffX;
    }
    private void HandleOnEndList(BitSwipeTrapEventArgs args, int index)
    {
        if (diffXList[index] < 60)
        {
            diffXList[index] = 0;
        }
    }
    private async Task HandleOnTriggerList(BitSwipeTrapTriggerArgs args, int index)
    {
        if (args.Direction == BitSwipeDirection.Right)
        {
            deletingIndex = index;
            listTcs = new();
            isListDialogOpen = true;
            await listTcs.Task;
            isListDialogOpen = false;
            diffXList[index] = 0;
            deletingIndex = -1;
        }
    }
    private string GetRowStyle(int index)
    {
        var x = Math.Min(diffXList[index], 60);
        return x > 0 ? $"transform: translateX({x}px)" : "";
    }
    private void HandleOnOkList()
    {
        if (deletingIndex != -1)
        {
            itemsList.Remove(deletingIndex);
        }
        listTcs.SetResult();
    }
    private void HandleOnCancelList()
    {
        listTcs.SetResult();
    }
    private void ResetList()
    {
        itemsList = Enumerable.Range(0, 10).ToList();
    }
}
                    
Advanced
An illustrative example of integrating this component into a straightforward mobile application.

bit BlazorUI

Swipe left or right

Left Menu

Item1
Item2
Item3

Right Menu

Item1
Item2
Item3
<style>
    .mobile-frame {
        height: 666px;
        max-width: 375px;
        overflow: hidden;
        position: relative;
        border-radius: 36px;
        border: 16px solid #333;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        background-color: var(--bit-clr-fg-sec);
    }

    .mobile-frame .screen {
        width: 100%;
        height: 100%;
    }

    .mobile-frame .layout {
        height: 100%;
        display: flex;
        flex-direction: column;
    }

    .mobile-frame .header {
        gap: 1rem;
        width: 100%;
        height: 66px;
        display: flex;
        align-items: center;
        justify-content: center;
        background-color: var(--bit-clr-bg-sec);
    }

    .mobile-frame .main {
        flex-grow: 1;
        position: relative;
    }

    .mobile-frame .main-text {
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .mobile-frame .panel {
        color: black;
        cursor: grab;
        inset-block: 0;
        user-select: none;
        position: absolute;
        background-color: lightgray;

    .mobile-frame .panel.left {
        left: 0;
        width: 200px;
        transform: translateX(-100%);
    }

    .mobile-frame .panel.right {
        right: 0;
        width: 200px;
        transform: translateX(100%);
    }

    .mobile-frame .panel-trap {
        gap: 1rem;
        height: 100%;
        display: flex;
        padding-top: 0.2rem;
        padding-left: 0.8rem;
        flex-direction: column;
        background-color: gray;
    }
</style>

<div class="mobile-frame">
    <div class="screen">
        <div class="layout">
            <div class="header">
                <BitImage Src="/images/bit-logo.svg" Width="50" />
                <BitText Typography="BitTypography.H4" Color="BitColor.Info">
                    bit BlazorUI
                </BitText>
            </div>
            <div class="main">
                <BitSwipeTrap Style="width:100%;height:100%"
                              OnMove="HandleOnMovePanelAdvanced"
                              OnEnd="HandleOnEndPanelAdvanced"
                              OnTrigger="HandleOnTriggerPanelAdvanced">
                    <div class="main-text">
                        <BitText Style="user-select:none"
                                 Typography="BitTypography.H4"
                                 Color="BitColor.SecondaryBackground">
                            Swipe left or right
                        </BitText>
                    </div
                    <div class="panel left" style="@GetLeftPanelAdvancedStyle()">
                        <div class="panel-trap">
                            <h3>Left Menu</h3>
                            <div>Item1</div>
                            <div>Item2</div>
                            <div>Item3</div>
                        </div>
                    </div>
                    <div class="panel right" style="@GetRightPanelAdvancedStyle()">
                        <div class="panel-trap">
                            <h3>Right Menu</h3>
                            <div>Item1</div>
                            <div>Item2</div>
                            <div>Item3</div>
                        </div>
                    </div>
                </BitSwipeTrap>
            </div>
        </div>
    </div>
</div>
@code {
    private decimal? diffXPanelAdvanced;
    private BitSwipeDirection? direction;
    private BitSwipeDirection? panelOpen;
    private void OpenPanelAdvanced(BitSwipeDirection swipeDirection)
    {
        if (panelOpen == swipeDirection) return;
    
        direction = null;
        panelOpen = swipeDirection;
        diffXPanelAdvanced = 0;
    }
    private void ClosePanelAdvanced()
    {
        panelOpen = null;
        diffXPanelAdvanced = null;
    }
    private void HandleOnMovePanelAdvanced(BitSwipeTrapEventArgs args)
    {
        diffXPanelAdvanced = args.DiffX;
    
        if (Math.Abs(args.DiffX) > 2 || Math.Abs(args.DiffY) > 2)
        {
            direction = Math.Abs(args.DiffX) > Math.Abs(args.DiffY)
            ? args.DiffX > 0 ? BitSwipeDirection.Right : BitSwipeDirection.Left
            : args.DiffY > 0 ? BitSwipeDirection.Bottom : BitSwipeDirection.Top;
        }
        else
        {
            direction = null;
        }
    }
    private void HandleOnEndPanelAdvanced(BitSwipeTrapEventArgs args)
    {
        if (panelOpen.HasValue)
        {
            diffXPanelAdvanced = 0;
        }
        else
        {
            diffXPanelAdvanced = null;
        }
    }
    private void HandleOnTriggerPanelAdvanced(BitSwipeTrapTriggerArgs args)
    {
        if (args.Direction == BitSwipeDirection.Left)
        {
            if (panelOpen.HasValue is false || panelOpen == BitSwipeDirection.Right)
            {
                OpenPanelAdvanced(BitSwipeDirection.Right);
            }
            else if (panelOpen == BitSwipeDirection.Left)
            {
                ClosePanelAdvanced();
            }
        }
        else if (args.Direction == BitSwipeDirection.Right)
        {
            if (panelOpen.HasValue is false || panelOpen == BitSwipeDirection.Left)
            {
                OpenPanelAdvanced(BitSwipeDirection.Left);
            }
            else if (panelOpen == BitSwipeDirection.Right)
            {
                ClosePanelAdvanced();
            }
        }
    }
    private string GetLeftPanelAdvancedStyle()
    {
        if (panelOpen == BitSwipeDirection.Left && direction != BitSwipeDirection.Left)
        {
            return "transform: translateX(0px)";
        }
        else if((panelOpen.HasValue is false && direction == BitSwipeDirection.Right) || (panelOpen == BitSwipeDirection.Left && direction == BitSwipeDirection.Left))
        {
            return diffXPanelAdvanced switch
            {
                0 or > 200 => "transform: translateX(0px)",
                < 0 and < 200 => $"transform: translateX({diffXPanelAdvanced}px)",
                > 0 => $"transform: translateX(calc(-100% + {diffXPanelAdvanced}px))",
                _ => string.Empty
            };
        }
    
        return string.Empty;
    }
    private string GetRightPanelAdvancedStyle()
    {
        if (panelOpen == BitSwipeDirection.Right && direction != BitSwipeDirection.Right)
        {
            return "transform: translateX(0px)";
        }
        else if ((panelOpen.HasValue is false && direction == BitSwipeDirection.Left) || (panelOpen == BitSwipeDirection.Right && direction == BitSwipeDirection.Right))
        {
            return diffXPanelAdvanced switch
            {
                0 or < -200 => "transform: translateX(0px)",
                > 0 => $"transform: translateX({diffXPanelAdvanced}px)",
                < 0 => $"transform: translateX(calc(100% - {(-1 * diffXPanelAdvanced)}px))",
                _ => string.Empty
            };
        }
    
        return string.Empty;
    }
}
                    

API

BitSwipeTrap parameters
Name
Type
Default value
Description
ChildContent RenderFragment? null The content of the swipe trap.
OnStart EventCallback<BitSwipeTrapEventArgs> The event callback for when the swipe action starts on the container of the swipe trap.
OnMove EventCallback<BitSwipeTrapEventArgs> The event callback for when the swipe action moves on the container of the swipe trap.
OnEnd EventCallback<BitSwipeTrapEventArgs> The event callback for when the swipe action ends on the container of the swipe trap.
OnTrigger EventCallback<BitSwipeTrapTriggerArgs> The event callback for when the swipe action triggers based on the Trigger constraint.
OrientationLock BitSwipeOrientation? null Specifies the orientation lock in which the swipe trap allows to trap the swipe actions.
Threshold decimal? null The threshold in pixel for swiping distance that starts the swipe process process which stops the default behavior.
Throttle int? null The throttle time in milliseconds to apply a delay between periodic calls to raise the events (default is 10).
Trigger decimal? null The swiping point (fraction of element's width or an absolute value) to trigger and call the OnTrigger event (default is 0.25m).
BitComponentBase parameters
Name
Type
Default value
Description
AriaLabel string? null The aria-label of the control for the benefit of screen readers.
Class string? null Custom CSS class for the root element of the component.
Dir BitDir? null Determines the component direction.
HtmlAttributes Dictionary<string, object> new Dictionary<string, object>() Capture and render additional attributes in addition to the component's parameters.
Id string? null Custom id attribute for the root element. if null the UniqueId will be used instead.
IsEnabled bool true Whether or not the component is enabled.
Style string? null Custom CSS style for the root element of the component.
Visibility BitVisibility BitVisibility.Visible Whether the component is visible, hidden or collapsed.
BitComponentBase public members
Name
Type
Default value
Description
UniqueId Guid Guid.NewGuid() The readonly unique id of the root element. it will be assigned to a new Guid at component instance construction.
RootElement ElementReference The ElementReference of the root element.
BitSwipeTrapEventArgs properties
The event arguments of the SwipeTrap events.
Name
Type
Default value
Description
StartX decimal 0 The horizontal start point of the swipe action in pixels.
StartY decimal 0 The vertical start point of the swipe action in pixels.
DiffX decimal 0 The horizontal difference of swipe action in pixels.
DiffY decimal 0 The vertical difference of swipe action in pixels.
BitSwipeTrapTriggerArgs properties
The event arguments of the SwipeTrap trigger event.
Name
Type
Default value
Description
Direction BitSwipeDirection The swipe direction in which the action triggered.
DiffX decimal 0 The horizontal difference of swipe action in pixels.
DiffY decimal 0 The vertical difference of swipe action in pixels.
BitSwipeOrientation enum
Name
Value
Description
None 0 Not orientation lock for swipe trap.
Horizontal 1 Horizontal orientation lock of trapping the swipe action.
Vertical 2 Vertical orientation lock of trapping the swipe action.
BitSwipeDirection enum
Name
Value
Description
Right 0 Swipe to right direction.
Left 1 Swipe to left direction.
Top 2 Swipe to top direction.
Bottom 3 Swipe to bottom direction.
BitVisibility enum
Name
Value
Description
Visible 0 The content of the component is visible.
Hidden 1 The content of the component is hidden, but the space it takes on the page remains (visibility:hidden).
Collapsed 2 The component is hidden (display:none).
BitDir enum
Name
Value
Description
Ltr 0 Ltr (left to right) is to be used for languages that are written from the left to the right (like English).
Rtl 1 Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic).
Auto 2 Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element.

Feedback

You can give us your feedback through our GitHub repo by filing a new Issue or starting a new Discussion.

Or you can review / edit this page on GitHub.

Or you can review / edit this component on GitHub.
  • On this page