fix(module: tabs): update the style of ink after tab title is changed (#3978)

* fix(module: tabs): update the style of ink after tab title is changed

* fix test

* fix test

* fix test

* fix test

* fix tests
This commit is contained in:
James Yeung 2024-07-15 08:29:01 +08:00 коммит произвёл GitHub
Родитель 2695c105a1
Коммит 2286639039
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 128 добавлений и 36 удалений

Просмотреть файл

@ -48,5 +48,17 @@ namespace AntDesign.JsInterop
[JsonPropertyName("selectionStart")]
public decimal SelectionStart { get; set; }
[JsonPropertyName("marginTop")]
public decimal MarginTop { get; set; }
[JsonPropertyName("marginBottom")]
public decimal MarginBottom { get; set; }
[JsonPropertyName("marginLeft")]
public decimal MarginLeft { get; set; }
[JsonPropertyName("marginRight")]
public decimal MarginRight { get; set; }
}
}

Просмотреть файл

@ -28,6 +28,7 @@ export class infoHelper {
domElement = {};
}
const absolutePosition = this.getElementAbsolutePos(domElement);
const style = window.getComputedStyle(domElement);
const result: domTypes.domInfo = {
offsetTop: domElement.offsetTop || 0,
offsetLeft: domElement.offsetLeft || 0,
@ -43,7 +44,11 @@ export class infoHelper {
clientWidth: domElement.clientWidth || 0,
selectionStart: domElement.selectionStart || 0,
absoluteTop: Math.round(absolutePosition.y),
absoluteLeft: Math.round(absolutePosition.x)
absoluteLeft: Math.round(absolutePosition.x),
marginTop: parseFloat(style.marginTop),
marginBottom: parseFloat(style.marginBottom),
marginLeft: parseFloat(style.marginLeft),
marginRight: parseFloat(style.marginRight)
};
return result;
}

Просмотреть файл

@ -13,7 +13,11 @@
clientWidth: number,
selectionStart: number,
absoluteTop: number,
absoluteLeft: number
absoluteLeft: number,
marginTop: number,
marginBottom: number,
marginLeft: number,
marginRight: number,
}
export type position = {

Просмотреть файл

@ -71,7 +71,7 @@ namespace AntDesign
}
base.OnInitialized();
ReuseTabsService.Init(true);
ReuseTabsService.OnStateHasChanged += OnStateHasChanged;
ReuseTabsService.OnStateHasChanged += InvokeStateHasChanged;
if (RouteData != null)
{
@ -89,7 +89,7 @@ namespace AntDesign
protected override void Dispose(bool disposing)
{
ReuseTabsService.OnStateHasChanged -= OnStateHasChanged;
ReuseTabsService.OnStateHasChanged -= InvokeStateHasChanged;
Navmgr.LocationChanged -= OnLocationChanged;
base.Dispose(disposing);
}
@ -113,12 +113,7 @@ namespace AntDesign
ReuseTabsService.TrySetRouteData(ReuseTabsRouteData.RouteData, true);
}
StateHasChanged();
}
private void OnStateHasChanged()
{
_ = InvokeStateHasChangedAsync();
InvokeStateHasChanged();
}
}
}

Просмотреть файл

@ -20,7 +20,7 @@
@ondrop="@(_ => Parent.HandleDrop(this))"
class="@ClassMapper.Class"
draggable="@Parent.Draggable.ToString()"
id="@($"rc-tabs-{Id}-tab-{Key}")"
id="@TabId"
ondragover="event.preventDefault();">
<div @onkeydown="@HandleKeydown"
@ref="_tabBtnRef"

Просмотреть файл

@ -61,6 +61,8 @@ namespace AntDesign
internal ElementReference TabRef => _tabRef;
internal string TabId => $"rc-tabs-{Id}-tab-{Key}";
internal ElementReference TabBtnRef => _tabBtnRef;
private ClassMapper _tabPaneClassMapper = new();
@ -95,6 +97,16 @@ namespace AntDesign
}
}
public override async Task SetParametersAsync(ParameterView parameters)
{
if (parameters.IsParameterChanged(nameof(Tab), Tab))
{
Parent?.UpdateTabsPosition();
}
await base.SetParametersAsync(parameters);
}
private void SetClass()
{
ClassMapper

Просмотреть файл

@ -13,8 +13,8 @@
</div>
}
<div class="@_tabsNavWarpPingClassMapper.Class" @ref="@_navWarpRef">
<div class="ant-tabs-nav-list" style="@_navListStyle" @ref="@_navListRef">
<div class="@_tabsNavWarpPingClassMapper.Class" @ref="@_navWarpRef" id="@(Id)-nav-warpper">
<div class="ant-tabs-nav-list" style="@_navListStyle" @ref="@_navListRef" id="@(Id)-nav-list">
<CascadingValue Value="true" Name="IsTab" IsFixed="true">
@ChildContent
</CascadingValue>

Просмотреть файл

@ -161,6 +161,7 @@ namespace AntDesign
private TabPane _activePane;
private TabPane _activeTab;
private HtmlElement _activeTabElement;
private Dictionary<string, HtmlElement> _itemRefs;
private string _activeKey;
private TabPane _renderedActivePane;
@ -192,6 +193,7 @@ namespace AntDesign
private readonly List<TabPane> _tabs = new List<TabPane>();
private List<TabPane> _invisibleTabs = new List<TabPane>();
private bool NavWrapPingLeft => _scrollOffset > 0;
private bool NavWrapPingRight => _scrollListWidth - _wrapperWidth - _scrollOffset > 0;
@ -441,14 +443,11 @@ namespace AntDesign
}
_activeKey = _activePane.Key;
TryRenderInk();
}
Card?.SetBody(_activePane.ChildContent);
_needUpdateScrollListPosition = true;
_shouldRender = true;
InvokeAsync(StateHasChanged);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
@ -478,15 +477,18 @@ namespace AntDesign
private async Task ResetSizes()
{
var navList = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, _navListRef);
var navWarp = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, _navWarpRef);
_activeTabElement = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, _activeTab.TabRef);
ElementReference[] refs = [_navListRef, _navWarpRef, .. _tabs.Select(x => x.TabRef).ToArray()];
_itemRefs = await JsInvokeAsync<Dictionary<string, HtmlElement>>(JSInteropConstants.GetElementsDomInfo, refs);
var navList = _itemRefs[Id + "-nav-list"];
var navWarp = _itemRefs[Id + "-nav-warpper"];
_scrollListWidth = navList.ClientWidth;
_scrollListHeight = navList.ClientHeight;
_wrapperWidth = navWarp.ClientWidth;
_wrapperHeight = navWarp.ClientHeight;
_itemRefs.Remove(Id + "-nav-list");
_itemRefs.Remove(Id + "-nav-warpper");
}
private void UpdateScrollListPosition()
@ -571,6 +573,19 @@ namespace AntDesign
private void TryRenderInk()
{
if (!_afterFirstRender)
{
_needUpdateScrollListPosition = true;
StateHasChanged();
return;
}
if (_itemRefs is not { Count: > 0 })
{
return;
}
_activeTabElement = _itemRefs[_activeTab.TabId];
if (IsHorizontal)
{
_inkStyle = $"left: {_activeTabElement.OffsetLeft}px; width: {_activeTabElement.ClientWidth}px";
@ -608,6 +623,7 @@ namespace AntDesign
}
}
_shouldRender = true;
StateHasChanged();
_renderedActivePane = _activePane;
}
@ -617,6 +633,12 @@ namespace AntDesign
return _shouldRender || _renderedActivePane != _activePane;
}
internal void UpdateTabsPosition()
{
_needUpdateScrollListPosition = true;
_shouldRender = true;
}
private void OnVisibleChange(bool visible)
{
if (!visible)
@ -626,24 +648,37 @@ namespace AntDesign
}
int invisibleHeadCount;
decimal tabSize, visibleCount;
int visibleCount;
if (IsHorizontal)
{
tabSize = _scrollListWidth / _tabs.Count;
visibleCount = Math.Ceiling(_wrapperWidth / tabSize);
var tabWidths = _itemRefs.Values.Select(x => x.OffsetWidth + x.MarginLeft + x.MarginRight).ToArray();
invisibleHeadCount = GetOverflowCount(_scrollOffset, tabWidths);
visibleCount = GetOverflowCount(_scrollOffset + _wrapperWidth, tabWidths, true) - invisibleHeadCount;
}
else
{
tabSize = _scrollListHeight / _tabs.Count;
visibleCount = Math.Ceiling(_wrapperHeight / tabSize);
var tabHeights = _itemRefs.Values.Select(x => x.ClientHeight + x.MarginTop + x.MarginBottom).ToArray();
invisibleHeadCount = GetOverflowCount(_scrollOffset, tabHeights);
visibleCount = GetOverflowCount(_scrollOffset + _wrapperHeight, tabHeights, true) - invisibleHeadCount;
}
invisibleHeadCount = (int)Math.Ceiling(_scrollOffset / tabSize);
visibleCount = Math.Min(visibleCount, _tabs.Count - invisibleHeadCount);
_invisibleTabs = _tabs.ToList();
_invisibleTabs.RemoveRange(invisibleHeadCount, (int)visibleCount);
_invisibleTabs.RemoveRange(invisibleHeadCount, visibleCount);
}
private static int GetOverflowCount(decimal maxLength, decimal[] lengths, bool isRight = false)
{
var sum = 0m;
for (var i = 0; i < lengths.Length; i++)
{
sum += lengths[i];
if (sum - maxLength >= lengths[i] * (isRight ? 0.2m : 0.6m))
{
return i;
}
}
return lengths.Length;
}
#region DRAG & DROP

Просмотреть файл

@ -1,18 +1,32 @@
<Tabs @bind-ActiveKey="@activeKey" OnChange="OnTabChange" Animated>
<TabPane Tab="Tab 1" Key="1">
Content of Tab Pane 1
<TabPane Key="1">
<TabTemplate>
@tab
</TabTemplate>
<ChildContent>
Content of Tab Pane 1
</ChildContent>
</TabPane>
<TabPane Tab="Tab 2" Key="2">
Content of Tab Pane 2
Content of Tab Pane 2
</TabPane>
<TabPane Tab="Tab 3" Key="3">
Content of Tab Pane 3
</TabPane>
</Tabs>
<Button OnClick="OnClick">Click</Button>
@code{
string activeKey { get; set; } = "1";
string tab = "tab1";
void OnClick()
{
tab += tab;
}
void OnTabChange(string key)
{
Console.WriteLine($"tab change:{key}");

Просмотреть файл

@ -28,7 +28,11 @@ const domInfoDefaults: domInfo = {
clientWidth: 0,
selectionStart: 0,
absoluteTop: 0,
absoluteLeft: 0
absoluteLeft: 0,
marginTop: 0,
marginBottom: 0,
marginLeft: 0,
marginRight: 0,
}
type cooridnates = {

Просмотреть файл

@ -92,6 +92,7 @@
//Arrange
JSInterop.SetupVoid(JSInteropConstants.StyleHelper.AddCls, _ => true).SetVoidResult();
JSInterop.Setup<HtmlElement>(JSInteropConstants.DomInfoHelper.GetInfo, _ => true).SetResult(new());
JSInterop.Setup<Dictionary<string, HtmlElement>>("AntDesign.interop.domInfoHelper.getElementsInfo", _ => true);
var cut = Render<AntDesign.Card>(
@<Card Title=@("Card title")>
<CardTabs>

Просмотреть файл

@ -10,6 +10,7 @@
.SetResult(new AntDesign.JsInterop.Window());
JSInterop.SetupVoid("AntDesign.interop.styleHelper.addCls", _ => true);
JSInterop.Setup<HtmlElement>("AntDesign.interop.domInfoHelper.getInfo", _ => true);
JSInterop.Setup<Dictionary<string, HtmlElement>>("AntDesign.interop.domInfoHelper.getElementsInfo", _ => true);
var model = new Model
{
Text = "test"

Просмотреть файл

@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using AntDesign.JsInterop;
using Bunit;
using Microsoft.AspNetCore.Components;
@ -21,11 +22,18 @@ namespace AntDesign.Tests.Tabs
var jsRuntime = new Mock<IJSRuntime>();
jsRuntime.Setup(u => u.InvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, It.IsAny<object[]>()))
.ReturnsAsync(new HtmlElement());
jsRuntime.Setup(u => u.InvokeAsync<Dictionary<string, HtmlElement>>(JSInteropConstants.GetElementsDomInfo, It.IsAny<object[]>()))
.ReturnsAsync(new Dictionary<string, HtmlElement>() {
["1-nav-list"] = new HtmlElement() { },
["1-nav-warpper"] = new HtmlElement() { },
["rc-tabs-2-tab-2"] = new HtmlElement() { },
});
Context.Services.AddScoped(_ => jsRuntime.Object);
var cut = Context.RenderComponent<AntDesign.Tabs>(tabs => tabs
.Add(x => x.DefaultActiveKey, "2")
.Add(x => x.Id, "1")
.Add(b => b.ChildContent, b => childContent(b))
);
@ -36,6 +44,7 @@ namespace AntDesign.Tests.Tabs
{
var tabPane1Builder = new ComponentParameterCollectionBuilder<TabPane>()
.Add(x => x.Key, key)
.Add(x => x.Id, key)
.Add(x => x.Tab, $"Tab {key}")
.Add(x => x.ChildContent, $"Content {key}".ToRenderFragment());

Просмотреть файл

@ -4,7 +4,7 @@
{
JSInterop.Setup<HtmlElement>(JSInteropConstants.GetDomInfo, _ => true)
.SetResult(new HtmlElement());
JSInterop.Setup<Dictionary<string, HtmlElement>>("AntDesign.interop.domInfoHelper.getElementsInfo", _ => true);
JSInterop.SetupVoid(JSInteropConstants.StyleHelper.AddClsToFirstChild, _ => true);
JSInterop.SetupVoid(JSInteropConstants.StyleHelper.AddCls, _ => true);
}