2021-07-02 01:16:53 +08:00
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
2020-04-16 12:05:59 +08:00
using System.Collections.Generic ;
using System.Linq ;
2021-07-09 10:24:27 +08:00
using System.Text.Json ;
2020-04-16 12:05:59 +08:00
using System.Threading.Tasks ;
2020-08-11 22:26:49 +08:00
using AntDesign.JsInterop ;
using Microsoft.AspNetCore.Components ;
using Microsoft.AspNetCore.Components.Web ;
2020-04-16 12:05:59 +08:00
2020-05-29 00:33:49 +08:00
namespace AntDesign
2020-04-16 12:05:59 +08:00
{
2020-06-07 19:41:00 +08:00
public partial class Tabs : AntDomComponentBase
2020-04-16 12:05:59 +08:00
{
private const string PrefixCls = "ant-tabs" ;
2020-06-07 19:41:00 +08:00
private bool IsHorizontal { get = > TabPosition = = AntDesign . TabPosition . Top | | TabPosition = = AntDesign . TabPosition . Bottom ; }
2020-06-10 13:34:51 +08:00
//private ClassMapper _barClassMapper = new ClassMapper();
//private ClassMapper _prevClassMapper = new ClassMapper();
//private ClassMapper _nextClassMapper = new ClassMapper();
//private ClassMapper _navClassMapper = new ClassMapper();
2020-06-07 19:41:00 +08:00
private TabPane _activePane ;
2020-06-10 13:34:51 +08:00
2020-06-07 19:41:00 +08:00
private TabPane _renderedActivePane ;
2020-06-10 13:34:51 +08:00
2020-04-16 12:05:59 +08:00
private ElementReference _scrollTabBar ;
private ElementReference _tabBars ;
2020-06-10 13:34:51 +08:00
2020-04-16 12:05:59 +08:00
private string _inkStyle ;
2020-06-10 13:34:51 +08:00
2020-04-16 12:05:59 +08:00
private string _navStyle ;
2020-06-10 13:34:51 +08:00
2021-07-09 10:24:27 +08:00
private bool _wheelDisabled ;
2020-06-10 13:34:51 +08:00
//private string _contentStyle;
//private bool? _prevIconEnabled;
//private bool? _nextIconEnabled;
private string _operationClass ;
private string _tabsNavWarpPingClass ;
private string _operationStyle ;
private int _scrollOffset ;
2021-07-09 10:24:27 +08:00
private int _listWidth ;
private int _listHeight ;
private int _navWidth ;
private int _navHeight ;
2020-04-16 12:05:59 +08:00
private bool _needRefresh ;
2020-12-03 13:44:49 +08:00
private bool _afterFirstRender ;
2020-11-30 10:53:41 +08:00
2020-06-07 19:41:00 +08:00
internal List < TabPane > _panes = new List < TabPane > ( ) ;
2020-04-16 12:05:59 +08:00
2021-07-09 10:24:27 +08:00
[Inject]
public DomEventService DomEventService { get ; set ; }
2020-04-16 12:05:59 +08:00
#region Parameters
[Parameter]
public RenderFragment ChildContent { get ; set ; }
private string _activeKey ;
/// <summary>
2020-06-07 19:41:00 +08:00
/// Current <see cref="TabPane"/>'s <see cref="TabPane.Key"/>
2020-04-16 12:05:59 +08:00
/// </summary>
[Parameter]
public string ActiveKey
{
get
{
return _activeKey ;
}
set
{
if ( _activeKey ! = value )
{
2020-11-30 10:53:41 +08:00
_activeKey = value ;
2020-08-11 22:26:49 +08:00
if ( _panes . Count = = 0 )
return ;
2020-11-30 10:53:41 +08:00
var tabPane = _panes . Find ( p = > p . Key = = value ) ;
2020-08-11 22:26:49 +08:00
if ( tabPane = = null )
return ;
2020-07-08 23:01:00 +08:00
ActivatePane ( tabPane ) ;
2020-04-16 12:05:59 +08:00
}
}
}
2020-07-22 00:30:31 +08:00
[Parameter]
public EventCallback < string > ActiveKeyChanged { get ; set ; }
2020-04-16 12:05:59 +08:00
/// <summary>
2020-06-07 19:41:00 +08:00
/// Whether to change tabs with animation. Only works while <see cref="TabPosition"/> = <see cref="TabPosition.Top"/> or <see cref="TabPosition.Bottom"/>
2020-04-16 12:05:59 +08:00
/// </summary>
[Parameter]
2021-07-15 22:34:05 +08:00
public bool Animated { get ; set ; }
2020-04-16 12:05:59 +08:00
/// <summary>
/// Replace the TabBar
/// </summary>
[Parameter]
public object RenderTabBar { get ; set ; }
/// <summary>
2020-06-07 19:41:00 +08:00
/// Initial active <see cref="TabPane"/>'s <see cref="TabPane.Key"/>, if <see cref="ActiveKey"/> is not set
2020-04-16 12:05:59 +08:00
/// </summary>
[Parameter]
public string DefaultActiveKey { get ; set ; }
/// <summary>
2020-06-07 19:41:00 +08:00
/// Hide plus icon or not. Only works while <see cref="Type"/> = <see cref="TabType.EditableCard"/>
2020-04-16 12:05:59 +08:00
/// </summary>
[Parameter]
public bool HideAdd { get ; set ; } = false ;
/// <summary>
/// Preset tab bar size
/// </summary>
[Parameter]
2020-06-07 19:41:00 +08:00
public string Size { get ; set ; } = TabSize . Default ;
2020-04-16 12:05:59 +08:00
/// <summary>
/// Extra content in tab bar
/// </summary>
[Parameter]
public RenderFragment TabBarExtraContent { get ; set ; }
/// <summary>
/// The gap between tabs
/// </summary>
[Parameter]
public int TabBarGutter { get ; set ; }
/// <summary>
/// Tab bar style object
/// </summary>
[Parameter]
public string TabBarStyle { get ; set ; }
/// <summary>
/// Position of tabs
/// </summary>
[Parameter]
2020-06-07 19:41:00 +08:00
public string TabPosition { get ; set ; } = AntDesign . TabPosition . Top ;
2020-04-16 12:05:59 +08:00
/// <summary>
/// Basic style of tabs
/// </summary>
[Parameter]
2020-06-07 19:41:00 +08:00
public string Type { get ; set ; } = TabType . Line ;
2020-04-16 12:05:59 +08:00
/// <summary>
/// Callback executed when active tab is changed
/// </summary>
[Parameter]
2020-08-11 22:26:49 +08:00
public EventCallback < string > OnChange { get ; set ; }
2020-04-16 12:05:59 +08:00
/// <summary>
2020-06-07 19:41:00 +08:00
/// Callback executed when tab is added or removed. Only works while <see cref="Type"/> = <see cref="TabType.EditableCard"/>
2020-04-16 12:05:59 +08:00
/// </summary>
[Parameter]
2020-11-30 10:53:41 +08:00
public Func < string , string , Task < bool > > OnEdit { get ; set ; } = ( key , action ) = > Task . FromResult ( true ) ;
2021-07-02 01:16:53 +08:00
/// <summary>
/// Callback when tab is closed
/// </summary>
[Parameter]
public EventCallback < string > OnClose { get ; set ; }
2020-11-30 10:53:41 +08:00
[Parameter]
public EventCallback OnAddClick { get ; set ; }
[Parameter]
public EventCallback < string > AfterTabCreated { get ; set ; }
2020-04-16 12:05:59 +08:00
/// <summary>
/// Callback executed when next button is clicked
/// </summary>
[Parameter]
public EventCallback OnNextClick { get ; set ; }
/// <summary>
/// Callback executed when prev button is clicked
/// </summary>
[Parameter]
public EventCallback OnPrevClick { get ; set ; }
/// <summary>
/// Callback executed when tab is clicked
/// </summary>
[Parameter]
2020-08-11 22:26:49 +08:00
public EventCallback < string > OnTabClick { get ; set ; }
2020-04-16 12:05:59 +08:00
/// <summary>
/// Whether to turn on keyboard navigation
/// </summary>
[Parameter]
public bool Keyboard { get ; set ; } = true ;
2020-04-16 20:53:03 +08:00
[Parameter]
public bool Draggable { get ; set ; }
2020-09-20 10:43:40 +08:00
[CascadingParameter]
public Card Card { get ; set ; }
2020-04-16 12:05:59 +08:00
#endregion Parameters
2020-12-03 13:44:49 +08:00
protected override void OnInitialized ( )
{
base . OnInitialized ( ) ;
ClassMapper . Clear ( )
. Add ( PrefixCls )
2020-12-18 15:59:25 +08:00
. GetIf ( ( ) = > $"{PrefixCls}-{TabPosition}" , ( ) = > TabPosition . IsIn ( AntDesign . TabPosition . Top , AntDesign . TabPosition . Bottom , AntDesign . TabPosition . Left , AntDesign . TabPosition . Right ) )
. GetIf ( ( ) = > $"{PrefixCls}-{Type}" , ( ) = > Type . IsIn ( TabType . Card , TabType . EditableCard , TabType . Line ) )
2020-12-03 13:44:49 +08:00
. If ( $"{PrefixCls}-large" , ( ) = > Size = = TabSize . Large | | Card ! = null )
. If ( $"{PrefixCls}-head-tabs" , ( ) = > Card ! = null )
. If ( $"{PrefixCls}-small" , ( ) = > Size = = TabSize . Small )
. GetIf ( ( ) = > $"{PrefixCls}-{TabType.Card}" , ( ) = > Type = = TabType . EditableCard )
2021-03-12 17:02:11 +08:00
. If ( $"{PrefixCls}-no-animation" , ( ) = > ! Animated )
. If ( $"{PrefixCls}-rtl" , ( ) = > RTL ) ;
2020-12-03 13:44:49 +08:00
}
2020-04-16 12:05:59 +08:00
protected override void OnParametersSet ( )
{
base . OnParametersSet ( ) ;
_needRefresh = true ;
_renderedActivePane = null ;
2021-07-15 22:34:05 +08:00
}
2020-04-16 12:05:59 +08:00
2021-07-15 22:34:05 +08:00
public override Task SetParametersAsync ( ParameterView parameters )
{
2020-04-16 12:05:59 +08:00
string type = parameters . GetValueOrDefault < string > ( nameof ( Type ) ) ;
2021-07-15 22:34:05 +08:00
// according to ant design documents,
// Animated default to false when type="card"
Animated = type ! = TabType . Card ;
2020-04-16 12:05:59 +08:00
return base . SetParametersAsync ( parameters ) ;
}
/// <summary>
2020-12-03 13:44:49 +08:00
/// Add <see cref="TabPane"/> to <see cref="Tabs"/>
2020-04-16 12:05:59 +08:00
/// </summary>
/// <param name="tabPane">The AntTabPane to be added</param>
/// <exception cref="ArgumentNullException">Key is null</exception>
/// <exception cref="ArgumentException">An AntTabPane with the same key already exists</exception>
2020-06-07 19:41:00 +08:00
internal void AddTabPane ( TabPane tabPane )
2020-04-16 12:05:59 +08:00
{
if ( string . IsNullOrEmpty ( tabPane . Key ) )
{
2020-04-24 18:32:50 +08:00
throw new ArgumentNullException ( nameof ( tabPane ) , "Key is null" ) ;
2020-04-16 12:05:59 +08:00
}
2020-04-24 18:32:50 +08:00
if ( _panes . Select ( p = > p . Key ) . Contains ( tabPane . Key ) )
2020-04-16 12:05:59 +08:00
{
throw new ArgumentException ( "An AntTabPane with the same key already exists" ) ;
}
2020-04-24 18:32:50 +08:00
_panes . Add ( tabPane ) ;
2020-11-30 10:53:41 +08:00
}
2020-12-03 13:44:49 +08:00
internal void Complete ( TabPane content )
2020-11-30 10:53:41 +08:00
{
var pane = _panes . FirstOrDefault ( x = > x . Key = = content . Key ) ;
if ( pane ! = null & & pane . IsComplete ( ) )
2020-04-16 12:05:59 +08:00
{
2020-12-03 13:44:49 +08:00
if ( _panes . Any ( x = > ! x . IsComplete ( ) ) )
2020-06-10 21:44:21 +08:00
{
2020-12-03 13:44:49 +08:00
return ;
2020-06-10 21:44:21 +08:00
}
2020-12-03 13:44:49 +08:00
if ( ! string . IsNullOrWhiteSpace ( ActiveKey ) )
2020-08-11 22:26:49 +08:00
{
2020-11-30 10:53:41 +08:00
var activedPane = _panes . Find ( x = > x . Key = = ActiveKey ) ;
if ( activedPane ? . IsActive = = false )
2020-08-11 22:26:49 +08:00
{
2020-11-30 10:53:41 +08:00
ActivatePane ( activedPane ) ;
}
}
2020-12-03 13:44:49 +08:00
else if ( ! string . IsNullOrWhiteSpace ( DefaultActiveKey ) )
{
var defaultPane = _panes . FirstOrDefault ( x = > x . Key = = DefaultActiveKey ) ;
if ( defaultPane ! = null )
{
ActivatePane ( defaultPane ) ;
}
}
if ( _activePane = = null | | _panes . All ( x = > ! x . IsActive ) )
{
ActivatePane ( _panes . FirstOrDefault ( ) ) ;
}
2020-11-30 10:53:41 +08:00
if ( AfterTabCreated . HasDelegate )
{
AfterTabCreated . InvokeAsync ( pane . Key ) ;
2020-08-11 22:26:49 +08:00
}
}
}
public async Task RemoveTabPane ( TabPane pane )
{
2020-11-30 10:53:41 +08:00
if ( await OnEdit . Invoke ( pane . Key , "remove" ) )
2020-08-11 22:26:49 +08:00
{
2020-11-30 10:53:41 +08:00
var index = _panes . IndexOf ( pane ) ;
2020-08-11 22:26:49 +08:00
_panes . Remove ( pane ) ;
if ( pane ! = null & & pane . IsActive & & _panes . Count > 0 )
2020-04-24 18:32:50 +08:00
{
2020-11-30 10:53:41 +08:00
ActivatePane ( index > 1 ? _panes [ index - 1 ] : _panes [ 0 ] ) ;
2020-08-11 22:26:49 +08:00
}
2020-11-30 10:53:41 +08:00
_needRefresh = true ;
2021-07-02 01:16:53 +08:00
if ( OnClose . HasDelegate )
{
await OnClose . InvokeAsync ( pane . Key ) ;
}
2020-11-30 10:53:41 +08:00
StateHasChanged ( ) ;
2020-04-16 12:05:59 +08:00
}
}
2020-12-03 13:44:49 +08:00
internal void HandleTabClick ( TabPane tabPane )
2020-04-16 12:05:59 +08:00
{
2020-11-24 18:47:52 +08:00
if ( tabPane . IsActive )
2020-11-30 10:53:41 +08:00
return ;
2020-11-24 18:47:52 +08:00
2020-08-11 22:26:49 +08:00
if ( OnTabClick . HasDelegate )
2020-04-16 12:05:59 +08:00
{
2020-08-11 22:26:49 +08:00
OnTabClick . InvokeAsync ( tabPane . Key ) ;
2020-04-16 12:05:59 +08:00
}
2020-08-11 22:26:49 +08:00
ActivatePane ( tabPane ) ;
2020-04-16 12:05:59 +08:00
}
2020-06-07 19:41:00 +08:00
private void ActivatePane ( TabPane tabPane )
2020-04-16 12:05:59 +08:00
{
2020-04-24 18:32:50 +08:00
if ( ! tabPane . Disabled & & _panes . Contains ( tabPane ) )
2020-04-16 12:05:59 +08:00
{
if ( _activePane ! = null )
{
_activePane . IsActive = false ;
}
tabPane . IsActive = true ;
_activePane = tabPane ;
2020-08-11 22:26:49 +08:00
if ( _activeKey ! = _activePane . Key )
2020-07-22 00:30:31 +08:00
{
2020-08-11 22:26:49 +08:00
if ( ! string . IsNullOrEmpty ( _activeKey ) )
2020-07-22 00:30:31 +08:00
{
2020-08-11 22:26:49 +08:00
if ( ActiveKeyChanged . HasDelegate )
{
ActiveKeyChanged . InvokeAsync ( _activePane . Key ) ;
}
if ( OnChange . HasDelegate )
{
OnChange . InvokeAsync ( _activePane . Key ) ;
}
2020-07-22 00:30:31 +08:00
}
2020-08-11 22:26:49 +08:00
_activeKey = _activePane . Key ;
2020-07-22 00:30:31 +08:00
}
2020-12-03 13:44:49 +08:00
_needRefresh = true ;
2020-09-20 10:43:40 +08:00
Card ? . SetBody ( _activePane . ChildContent ) ;
2020-04-16 12:05:59 +08:00
StateHasChanged ( ) ;
}
}
2020-07-22 00:30:31 +08:00
protected override async Task OnAfterRenderAsync ( bool firstRender )
2020-04-16 12:05:59 +08:00
{
await base . OnAfterRenderAsync ( firstRender ) ;
2020-12-03 13:44:49 +08:00
if ( firstRender )
{
_afterFirstRender = true ;
}
2020-04-16 12:05:59 +08:00
2021-07-09 10:24:27 +08:00
var element = await JsInvokeAsync < HtmlElement > ( JSInteropConstants . GetDomInfo , _scrollTabBar ) ;
_listWidth = element . ClientWidth ;
_listHeight = element . ClientHeight ;
var navSection = await JsInvokeAsync < HtmlElement > ( JSInteropConstants . GetDomInfo , _tabBars ) ;
_navWidth = navSection . ClientWidth ;
_navHeight = navSection . ClientHeight ;
if ( IsHorizontal & & ! _wheelDisabled )
{
DomEventService . AddEventListener < string > ( _scrollTabBar , "wheel" , OnWheel , true , true ) ;
_wheelDisabled = true ;
}
if ( ! IsHorizontal & & _wheelDisabled )
{
DomEventService . RemoveEventListerner < string > ( _scrollTabBar , "wheel" , OnWheel ) ;
_wheelDisabled = false ;
}
2020-12-03 13:44:49 +08:00
if ( _afterFirstRender & & _activePane ! = null )
2020-04-16 12:05:59 +08:00
{
2020-11-30 10:53:41 +08:00
await TryRenderInk ( ) ;
await TryRenderNavOperation ( ) ;
}
2020-04-16 12:05:59 +08:00
_needRefresh = false ;
}
2021-07-09 10:24:27 +08:00
private void OnWheel ( string json )
{
int maxOffset ;
if ( IsHorizontal )
{
maxOffset = _listWidth - _navWidth ;
}
else
{
maxOffset = _listHeight - _navHeight ;
}
int delta = JsonDocument . Parse ( json ) . RootElement . GetProperty ( "wheelDelta" ) . GetInt32 ( ) ;
if ( delta > = 0 )
{
_scrollOffset - = 100 ;
}
else
{
_scrollOffset + = 100 ;
}
_scrollOffset = Math . Max ( 0 , _scrollOffset ) ;
_scrollOffset = Math . Min ( maxOffset , _scrollOffset ) ;
_renderedActivePane = null ;
if ( IsHorizontal )
{
_navStyle = $"transform: translate(-{_scrollOffset}px, 0px);" ;
}
else
{
_navStyle = $"transform: translate(0px, -{_scrollOffset}px);" ;
}
StateHasChanged ( ) ;
_renderedActivePane = _activePane ;
}
2020-06-10 13:34:51 +08:00
private async Task TryRenderNavOperation ( )
2020-04-16 12:05:59 +08:00
{
2021-04-17 22:14:01 +08:00
int navWidth = ( await JsInvokeAsync < HtmlElement > ( JSInteropConstants . GetDomInfo , _tabBars ) ) . ClientWidth ;
int navTotalWidth = ( await JsInvokeAsync < HtmlElement > ( JSInteropConstants . GetDomInfo , _scrollTabBar ) ) . ClientWidth ;
2020-06-10 13:34:51 +08:00
if ( navTotalWidth < navWidth )
2020-04-16 12:05:59 +08:00
{
2020-06-10 13:34:51 +08:00
_operationClass = "ant-tabs-nav-operations ant-tabs-nav-operations-hidden" ;
_operationStyle = "visibility: hidden; order: 1;" ;
_tabsNavWarpPingClass = string . Empty ;
}
else
{
_operationClass = "ant-tabs-nav-operations" ;
_tabsNavWarpPingClass = "ant-tabs-nav-wrap-ping-right" ;
_operationStyle = string . Empty ;
2020-04-16 12:05:59 +08:00
}
2020-06-10 13:34:51 +08:00
StateHasChanged ( ) ;
2020-04-16 12:05:59 +08:00
}
private async Task TryRenderInk ( )
{
2020-12-03 13:44:49 +08:00
if ( _renderedActivePane = = _activePane )
2020-04-16 12:05:59 +08:00
{
2020-12-03 13:44:49 +08:00
return ;
}
2020-11-24 18:47:52 +08:00
2020-12-03 13:44:49 +08:00
// TODO: slide to activated tab
// animate Active Ink
// ink bar
2021-04-17 22:14:01 +08:00
var element = await JsInvokeAsync < HtmlElement > ( JSInteropConstants . GetDomInfo , _activePane . TabBar ) ;
var navSection = await JsInvokeAsync < HtmlElement > ( JSInteropConstants . GetDomInfo , _tabBars ) ;
2020-12-03 13:44:49 +08:00
if ( IsHorizontal )
{
//_inkStyle = "left: 0px; width: 0px;";
2021-04-17 22:14:01 +08:00
_inkStyle = $"left: {element.OffsetLeft}px; width: {element.ClientWidth}px" ;
if ( element . OffsetLeft > _scrollOffset + navSection . ClientWidth
| | element . OffsetLeft < _scrollOffset )
2020-04-16 12:05:59 +08:00
{
2020-12-03 13:44:49 +08:00
// need to scroll tab bars
2021-04-17 22:14:01 +08:00
_scrollOffset = element . OffsetLeft ;
2020-12-03 13:44:49 +08:00
_navStyle = $"transform: translate(-{_scrollOffset}px, 0px);" ;
2020-04-16 12:05:59 +08:00
}
2020-12-03 13:44:49 +08:00
}
else
{
2021-04-17 22:14:01 +08:00
_inkStyle = $"top: {element.OffsetTop}px; height: {element.ClientHeight}px;" ;
if ( element . OffsetTop > _scrollOffset + navSection . ClientHeight
| | element . OffsetTop < _scrollOffset )
2020-04-16 12:05:59 +08:00
{
2020-12-03 13:44:49 +08:00
// need to scroll tab bars
2021-04-17 22:14:01 +08:00
_scrollOffset = element . OffsetTop ;
2020-12-03 13:44:49 +08:00
_navStyle = $"transform: translate(0px, -{_scrollOffset}px);" ;
2020-04-16 12:05:59 +08:00
}
}
2020-12-03 13:44:49 +08:00
StateHasChanged ( ) ;
_renderedActivePane = _activePane ;
2020-04-16 12:05:59 +08:00
}
protected override bool ShouldRender ( )
{
return _needRefresh | | _renderedActivePane ! = _activePane ;
}
2020-04-16 20:53:03 +08:00
2021-07-09 10:24:27 +08:00
private IEnumerable < TabPane > GetInvisibleTabs ( )
{
double average ;
int invisibleHeadCount , visibleCount ;
if ( IsHorizontal )
{
average = 1.0 * _listWidth / _panes . Count ;
visibleCount = ( int ) ( _navWidth / average ) ;
}
else
{
average = 1.0 * _listHeight / _panes . Count ;
visibleCount = ( int ) ( _navHeight / average ) ;
}
invisibleHeadCount = ( int ) Math . Ceiling ( _scrollOffset / average ) ;
IEnumerable < TabPane > invisibleTabs = _panes . Take ( invisibleHeadCount ) . Concat ( _panes . Skip ( invisibleHeadCount + visibleCount ) ) ;
return invisibleTabs ;
}
2020-04-16 20:53:03 +08:00
#region DRAG & DROP
2020-06-07 19:41:00 +08:00
private TabPane _draggingPane ;
2020-04-16 20:53:03 +08:00
2020-11-30 10:53:41 +08:00
internal void HandleDragStart ( DragEventArgs args , TabPane pane )
2020-04-16 20:53:03 +08:00
{
if ( Draggable )
{
args . DataTransfer . DropEffect = "move" ;
args . DataTransfer . EffectAllowed = "move" ;
_draggingPane = pane ;
}
}
2020-11-30 10:53:41 +08:00
internal void HandleDrop ( TabPane pane )
2020-04-16 20:53:03 +08:00
{
if ( Draggable & & _draggingPane ! = null )
{
2020-04-24 18:32:50 +08:00
int newIndex = _panes . IndexOf ( pane ) ;
_panes . Remove ( _draggingPane ) ;
_panes . Insert ( newIndex , _draggingPane ) ;
2020-04-16 20:53:03 +08:00
_draggingPane = null ;
_needRefresh = true ;
_renderedActivePane = null ;
StateHasChanged ( ) ;
}
}
2020-04-24 18:32:50 +08:00
#endregion DRAG & DROP
2020-04-16 12:05:59 +08:00
}
2020-04-24 18:32:50 +08:00
}