Skip to content

Commit

Permalink
⚡ (Tabs): use js interop to calculate and set the position of slider (#…
Browse files Browse the repository at this point in the history
…2040)

* ⚡ (Tabs): use js interop to calculate and set the position of slider

* update
  • Loading branch information
capdiem authored Jul 16, 2024
1 parent 4ed328b commit bea53c0
Show file tree
Hide file tree
Showing 14 changed files with 109 additions and 117 deletions.
35 changes: 35 additions & 0 deletions src/Masa.Blazor.JS/src/interop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1592,3 +1592,38 @@ export function unregisterTableScrollEvent(wrapper: HTMLElement) {
delete wrapper["_m_table_scroll_event"]
}
}

export function updateTabSlider(
sliderWrapper: HTMLElement,
tab: HTMLElement,
sliderSize: number,
vertical: boolean,
isReversed: boolean
) {
if (!sliderWrapper) {
console.warn('[MTab] the element of slider wrapper is not found')
return;
}

if (!tab) {
console.warn('[MTab] the element of tab to be activated is not found')
return;
}

const height = !vertical ? sliderSize : tab.scrollHeight;
const left = vertical ? 0 : tab.offsetLeft;
const right = vertical ? 0 : tab.offsetLeft + tab.offsetWidth;
const top = tab.offsetTop;
const width = vertical ? sliderSize : tab.clientWidth;
sliderWrapper.style.width = `${width}px`;
sliderWrapper.style.height = `${height}px`;
if (!isReversed) {
sliderWrapper.style.left = `${left}px`;
}
if (isReversed) {
sliderWrapper.style.right = `${right}px`;
}
if (vertical) {
sliderWrapper.style.top = `${top}px`;
}
}
Empty file.
Empty file.
6 changes: 3 additions & 3 deletions src/Masa.Blazor/Components/ItemGroup/MGroupable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ protected override async Task OnInitializedAsync()

if (this is IGroupable item)
{
ItemGroup!.Register(item);
await ItemGroup!.Register(item);
}

await base.OnInitializedAsync();
Expand Down Expand Up @@ -132,10 +132,9 @@ public async Task RefreshState()
if (!Matched || HasRoutableAncestor) return;

await SetInternalIsActive(ValueMatched);
StateHasChanged();
}

protected virtual async Task ToggleAsync()
protected async Task ToggleAsync()
{
if (!Matched) return;

Expand All @@ -149,6 +148,7 @@ protected async Task SetInternalIsActive(bool val, bool force = false)
if (InternalIsActive != val || force)
{
InternalIsActive = val;
StateHasChanged();
}
}
else
Expand Down
39 changes: 19 additions & 20 deletions src/Masa.Blazor/Components/ItemGroup/MItemGroupBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ internal List<StringNumber?> InternalValues
set => SetValue(value);
}

protected StringNumber? InternalValue => InternalValues?.LastOrDefault();
protected StringNumber? InternalValue => InternalValues.LastOrDefault();

private HashSet<StringNumber?> _prevInternalValues = [];
private CancellationTokenSource? _cts;

protected override void OnParametersSet()
{
Expand All @@ -64,10 +67,14 @@ protected override void OnParametersSet()
GroupType = TargetGroup.Value;
}

RefreshItemsState();
if (!_prevInternalValues.SetEquals(InternalValues))
{
_prevInternalValues = [..InternalValues];
RefreshItemsState();
}
}

private void RefreshItemsState()
protected virtual void RefreshItemsState()
{
Items.ForEach(item => item.RefreshState());
}
Expand All @@ -77,7 +84,7 @@ protected StringNumber InitDefaultItemValue()
return _registeredItemsIndex++;
}

internal virtual void Register(IGroupable item)
internal virtual async Task Register(IGroupable item)
{
item.Value ??= InitDefaultItemValue();

Expand All @@ -89,26 +96,22 @@ internal virtual void Register(IGroupable item)
{
if (InternalValues.Count == 0)
{
InternalValues = new List<StringNumber?>() { item.Value };
InternalValues = [item.Value];

if (Multiple)
{
if (ValuesChanged.HasDelegate)
{
ValuesChanged.InvokeAsync(InternalValues.ToList());
}
await ValuesChanged.InvokeAsync(InternalValues.ToList());
}
else
{
if (ValueChanged.HasDelegate)
{
ValueChanged.InvokeAsync(item.Value);
}
await ValueChanged.InvokeAsync(item.Value);
}
}
}

RefreshItemsState();
_cts?.Cancel();
_cts = new CancellationTokenSource();
await RunTaskInMicrosecondsAsync(RefreshItemsState, 16, _cts.Token);
}

public virtual void Unregister(IGroupable item)
Expand Down Expand Up @@ -153,8 +156,6 @@ public async Task ToggleAsync(StringNumber? key)
{
InternalValues = Values.ToList();
}

StateHasChanged();
}
}
else
Expand All @@ -170,8 +171,6 @@ public async Task ToggleAsync(StringNumber? key)
{
InternalValues = new List<StringNumber?>() { Value };
}

StateHasChanged();
}
}

Expand All @@ -184,13 +183,13 @@ private void InitOrUpdateInternalValues()
{
if (!IsDirtyParameter(nameof(Values))) return;

InternalValues = Values == null ? new List<StringNumber?>() : Values.ToList();
InternalValues = Values == null ? [] : Values.ToList();
}
else
{
if (!IsDirtyParameter(nameof(Value))) return;

InternalValues = Value == null ? new List<StringNumber?>() : new List<StringNumber?>() { Value };
InternalValues = Value == null ? [] : [Value];
}
}

Expand Down
16 changes: 9 additions & 7 deletions src/Masa.Blazor/Components/Tabs/MTab.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
@inherits MRoutableGroupItem<MItemGroupBase>

<MElement Tag="@Tag"
Class="@GetClass()"
Style="@GetStyle()"
ReferenceCaptureAction="r => Ref = r"
id="@Id"
@onclick="HandleOnClick"
@attributes="@Attributes">
@ChildContent
Class="@GetClass()"
Style="@GetStyle()"
ReferenceCaptureAction="r => Ref = r"
id="@Id"
@onclick="HandleOnClick"
@attributes="@Attributes">
<MShouldRender Value="InternalIsActive">
@ChildContent
</MShouldRender>
</MElement>
12 changes: 2 additions & 10 deletions src/Masa.Blazor/Components/Tabs/MTabs.razor
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
PrevIcon="@PrevIcon"
ShowArrows="@ShowArrows"
Value="@Value"
ValueChanged="@ValueChanged"
ValueChanged="@OnValueChanged"
Color="@Color"
ActiveClass="@ActiveClass"
CenterActive="@CenterActive"
Expand Down Expand Up @@ -54,16 +54,8 @@

private RenderFragment GenSlider() => __builder =>
{
var style = StyleBuilder.Create()
.AddWidth(Slider.width)
.AddHeight(Slider.height)
.AddIf("left", Slider.left.ToUnit(), !IsReversed)
.AddIf("right", Slider.right.ToUnit(), IsReversed)
.AddIf("top", Slider.top.ToUnit(), Vertical)
.AddIf("transition", "none", Slider.left == null);

<div class="m-tabs-slider-wrapper"
style="@style">
@ref="@_sliderWrapperRef">
<div class="@GetClass("m-tabs-slider", CssClassUtils.GetBackgroundColor(SliderColor))"
style="@(StyleBuilder.Create().AddBackgroundColor(SliderColor))">
</div>
Expand Down
94 changes: 27 additions & 67 deletions src/Masa.Blazor/Components/Tabs/MTabs.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,9 @@ public partial class MTabs : MasaComponentBase, IThemeable
[Parameter] public string? SliderColor { get; set; }

[Parameter] [MasaApiParameter(2)] public StringNumber SliderSize { get; set; } = 2;

[Parameter] public StringNumber? Value { get; set; }

private EventCallback<StringNumber>? _valueChanged;

[Parameter]
public EventCallback<StringNumber> ValueChanged
{
get
{
if (_valueChanged.HasValue)
{
return _valueChanged.Value;
}

return EventCallback.Factory.Create<StringNumber>(this, (v) => Value = v);
}
set => _valueChanged = value;
}
[Parameter] public EventCallback<StringNumber?> ValueChanged { get; set; }

[Parameter] public bool Vertical { get; set; }

Expand All @@ -80,10 +64,10 @@ public EventCallback<StringNumber> ValueChanged
[Parameter] public bool Dark { get; set; }

[Parameter] public bool Light { get; set; }
private StringNumber? _prevValue;
private int _registeredTabItemsIndex;
private bool _callSliderOnAfterRender;
private CancellationTokenSource? _callSliderCts;
private ElementReference _sliderWrapperRef;
private bool _isFirstRender = true;

private List<ITabItem> TabItems { get; set; } = new();

Expand Down Expand Up @@ -132,17 +116,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
{
await ResizeJSModule.ObserverAsync(Ref, OnResize);
await IntersectJSModule.ObserverAsync(Ref, OnIntersectAsync);
_callSliderOnAfterRender = true;
}
else if (_prevValue != Value)
{
_prevValue = Value;
_callSliderOnAfterRender = true;
}

if (_callSliderOnAfterRender)
{
_callSliderOnAfterRender = false;
_isFirstRender = false;
await CallSlider();
}
}
Expand All @@ -158,7 +132,7 @@ protected override void OnParametersSet()
if (MasaBlazor.IsSsr && !IndependentTheme)
{
CascadingIsDark = MasaBlazor.Theme.Dark;
}
}
}
#endif

Expand Down Expand Up @@ -187,9 +161,19 @@ private async Task OnIntersectAsync(IntersectEventArgs e)
}
}

private async Task OnValueChanged(StringNumber? val)
{
if (Value == val)
{
return;
}
Value = val;
await ValueChanged.InvokeAsync(val);
}

public bool IsReversed => RTL && Vertical;

public MSlideGroup? Instance => TabsBarRef as MSlideGroup;
public MItemGroup? Instance => TabsBarRef as MItemGroup;

public void RegisterTabItem(ITabItem tabItem)
{
Expand All @@ -212,40 +196,16 @@ public void UnregisterTabItem(ITabItem tabItem)
[MasaApiPublicMethod]
public async Task CallSlider()
{
if (HideSlider) return;

_callSliderCts?.Cancel();
_callSliderCts = new();

try
{
await Task.Delay(16, _callSliderCts.Token);

var item = Instance?.Items?.FirstOrDefault(item => item.Value == Instance.Value);
if (item?.Ref.Context == null)
{
Slider = (0, 0, 0, 0, 0);
}
else
{
var el = await Js.InvokeAsync<Masa.Blazor.JSInterop.Element>(JsInteropConstants.GetDomInfo, item.Ref);
var height = !Vertical ? SliderSize.TryGetNumber().number : el.ScrollHeight;
var left = Vertical ? 0 : el.OffsetLeft;
var right = Vertical ? 0 : el.OffsetLeft + el.OffsetWidth;
var top = el.OffsetTop;
var width = Vertical
? SliderSize.TryGetNumber().number
: el.ClientWidth; // REVIEW: el.ScrollWidth was used in Vuetify2

Slider = (height, left, right, top, width);
}

StateHasChanged();
}
catch (TaskCanceledException)
{
// ignored
}
if (HideSlider || _isFirstRender) return;

var item = Instance?.Items.FirstOrDefault(item => item.Value == Value);
await Js.InvokeVoidAsync(
JsInteropConstants.UpdateTabSlider,
_sliderWrapperRef,
item?.Ref,
SliderSize.TryGetNumber().number,
Vertical,
IsReversed);
}

private async Task OnResize()
Expand All @@ -264,7 +224,7 @@ private async Task OnResize()
[MasaApiPublicMethod]
public void CallSliderAfterRender()
{
_callSliderOnAfterRender = true;
NextTick(CallSlider);
}

protected override async ValueTask DisposeAsyncCore()
Expand Down
7 changes: 7 additions & 0 deletions src/Masa.Blazor/Components/Tabs/MTabsBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ protected override IEnumerable<string> BuildContentClass()
return base.BuildContentClass().Concat(new[] { _block.Element("content").Name });
}

protected override void RefreshItemsState()
{
base.RefreshItemsState();

Tabs?.CallSlider();
}

public override void Unregister(IGroupable item)
{
base.Unregister(item);
Expand Down
7 changes: 0 additions & 7 deletions src/Masa.Blazor/Components/Window/MWindow.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,6 @@ protected override void RegisterWatchers(PropertyWatcher watcher)
watcher.Watch<List<StringNumber?>>(nameof(InternalValues), UpdateInternalIndex);
}

internal override void Register(IGroupable item)
{
base.Register(item);

StateHasChanged();
}

public void RenderState()
{
StateHasChanged();
Expand Down
Loading

0 comments on commit bea53c0

Please sign in to comment.