mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-12-05 21:47:38 +08:00
152a574577
* feat(module:overlay): OverlayTrigger not bound to a div * feat(module:overlay): OverlayTrigger not bound to a div * feat(module:overlay): Logic transfer to single Overlay * feat(module:overlay): remove obsolete duplication * feat(module:Tooltip): Add for unbounded oncontextmenu event handler * feat(module:tooltip): unbound js event listeners remove * docs(module:tooltip): unbound explanation * fix(module:button): attach Ref to top level html element @ref * feat(module:dropdown&tooltip&popconfirm&popover): Overlay not bound to a div * docs(module:dropdown&tooltip&popconfirm&popover): unbound explanation * feat(module:OverlayTrigger): common logic relocation * feat(module:overlaytrigger): Overlay not bound to a div * feat(module:DatePicker): Overlay not bound to a div * feat(module:select): Overlay not boud to div * fix(module:select): onclickarrow event relocation * fix(module:select): rename Show to OnArrowClick * feat(module:avatar): Overlay not bound to a div * docs(module:avatar): demo switch to unbound version * feat(module:autocomplete): partial OverlayTrigger not bound to a div * feat(module:slider): tooltip * docs(module:slider): tooltip * fix(module:overlay): add SetVisible method * feat: set Ref where missing, performance components register Ref when missing IsFixed flag for CascadeValue changed hard-code sequence numbers when using RenderTreeBuilder Rate component use Tooltip Unbound version Tabs test fix * fix: revert changes (accidental) * feat(module:upload): tooltip with unbound usage * feat(module:table): column use of unbound tooltip * feat(module:autocomplete):overlay unbound from div * fix(module:upload): missing div restore Co-authored-by: James Yeung <shunjiey@hotmail.com>
185 lines
5.8 KiB
C#
185 lines
5.8 KiB
C#
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using AntDesign.JsInterop;
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
namespace AntDesign
|
|
{
|
|
public partial class Affix : AntDomComponentBase
|
|
{
|
|
private const string PrefixCls = "ant-affix";
|
|
private const string RootScollSelector = "window";
|
|
private const string RootRectSelector = "app";
|
|
private bool _affixed;
|
|
private bool _rootListened;
|
|
private bool _targetListened;
|
|
|
|
private bool Affixed
|
|
{
|
|
get => _affixed;
|
|
set
|
|
{
|
|
if (_affixed != value)
|
|
{
|
|
_affixed = value;
|
|
StateHasChanged();
|
|
if (OnChange.HasDelegate)
|
|
{
|
|
OnChange.InvokeAsync(_affixed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private ElementReference _childRef;
|
|
private string _hiddenStyle;
|
|
private string _affixStyle;
|
|
|
|
[Inject]
|
|
private DomEventService DomEventService { get; set; }
|
|
|
|
#region Parameters
|
|
|
|
/// <summary>
|
|
/// Offset from the bottom of the viewport (in pixels)
|
|
/// </summary>
|
|
[Parameter]
|
|
public uint? OffsetBottom { get; set; }
|
|
|
|
/// <summary>
|
|
/// Offset from the top of the viewport (in pixels)
|
|
/// </summary>
|
|
[Parameter]
|
|
public uint? OffsetTop { get; set; } = 0;
|
|
|
|
/// <summary>
|
|
/// Specifies the scrollable area DOM node
|
|
/// </summary>
|
|
[Parameter]
|
|
public ElementReference Target { get; set; }
|
|
|
|
[Parameter]
|
|
public RenderFragment ChildContent { get; set; }
|
|
|
|
[Parameter]
|
|
public EventCallback<bool> OnChange { get; set; }
|
|
|
|
#endregion Parameters
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
base.OnInitialized();
|
|
|
|
SetClasses();
|
|
}
|
|
|
|
public async override Task SetParametersAsync(ParameterView parameters)
|
|
{
|
|
await base.SetParametersAsync(parameters);
|
|
|
|
if (!_targetListened && !string.IsNullOrEmpty(Target.Id))
|
|
{
|
|
DomEventService.AddEventListener(Target, "scroll", OnScroll);
|
|
DomEventService.AddEventListener(Target, "resize", OnWindowResize);
|
|
|
|
await RenderAffixAsync();
|
|
_targetListened = true;
|
|
}
|
|
}
|
|
|
|
protected async override Task OnFirstAfterRenderAsync()
|
|
{
|
|
await base.OnFirstAfterRenderAsync();
|
|
|
|
DomRect domRect = await JsInvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _childRef);
|
|
_hiddenStyle = $"width: {domRect.width}px; height: {domRect.height}px;";
|
|
|
|
if (_rootListened)
|
|
{
|
|
await RenderAffixAsync();
|
|
}
|
|
else if (!_rootListened && string.IsNullOrEmpty(Target.Id))
|
|
{
|
|
DomEventService.AddEventListener(RootScollSelector, "scroll", OnScroll, false);
|
|
DomEventService.AddEventListener(RootScollSelector, "resize", OnWindowResize, false);
|
|
_rootListened = true;
|
|
}
|
|
}
|
|
|
|
private async void OnScroll(JsonElement obj)
|
|
{
|
|
await RenderAffixAsync();
|
|
}
|
|
|
|
private async void OnWindowResize(JsonElement obj)
|
|
{
|
|
await RenderAffixAsync();
|
|
}
|
|
|
|
private void SetClasses()
|
|
{
|
|
ClassMapper.Clear()
|
|
.If(PrefixCls, () => _affixed);
|
|
}
|
|
|
|
private async Task RenderAffixAsync()
|
|
{
|
|
DomRect childRect = await JsInvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _childRef);
|
|
_hiddenStyle = $"width: {childRect.width}px; height: {childRect.height}px;";
|
|
|
|
DomRect domRect = await JsInvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, Ref);
|
|
DomRect appRect = await JsInvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, RootRectSelector);
|
|
// reset appRect.top / bottom, so its position is fixed.
|
|
appRect.top = 0;
|
|
appRect.bottom = appRect.height;
|
|
DomRect containerRect;
|
|
if (string.IsNullOrEmpty(Target.Id))
|
|
{
|
|
containerRect = appRect;
|
|
}
|
|
else
|
|
{
|
|
containerRect = await JsInvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, Target);
|
|
}
|
|
// become affixed
|
|
if (OffsetBottom.HasValue)
|
|
{
|
|
// domRect.bottom / domRect.top have the identical value here.
|
|
if (domRect.top > containerRect.height + containerRect.top)
|
|
{
|
|
_affixStyle = _hiddenStyle + $"bottom: { appRect.height - containerRect.bottom + OffsetBottom}px; position: fixed;";
|
|
Affixed = true;
|
|
}
|
|
else
|
|
{
|
|
_affixStyle = string.Empty;
|
|
Affixed = false;
|
|
}
|
|
}
|
|
else if (OffsetTop.HasValue)
|
|
{
|
|
if (domRect.top < containerRect.top + OffsetTop.Value)
|
|
{
|
|
_affixStyle = _hiddenStyle + $"top: {containerRect.top + OffsetTop}px; position: fixed;";
|
|
Affixed = true;
|
|
}
|
|
else
|
|
{
|
|
_affixStyle = string.Empty;
|
|
Affixed = false;
|
|
}
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
|
|
DomEventService.RemoveEventListerner<JsonElement>(RootScollSelector, "scroll", OnScroll);
|
|
DomEventService.RemoveEventListerner<JsonElement>(RootScollSelector, "resize", OnWindowResize);
|
|
}
|
|
}
|
|
}
|