mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-12-03 12:37:40 +08:00
Merge 0.9.4 to feature (#1942)
* fix(module: form): remove `FormItem` from `Form` when it was disposed (#1901) * pref(module: table): put fixed column style into js (#1897) * pref(module: table): put fixed column style into js * fix resize listener * docs: Add building demo assets to the Readme (#1904) * feat(module: table): add `CellData` for `CellRender` (#1907) * docs: add dynamic table demo (#1908) * feat(module: table): add dynamic data demo * add scroll x * fix(module: select): value no longer reset on datasource set (#1906) * fix(module:select): value no longer reset on datasource set * fix(module:select): dataSource change detection * fix: improve datasource detection add item & set value in SelectOption * tests: scenario from issue #1207 * fix: DataSourceEqualityComparer default * docs: Fixed typo (#1915) * fix(module: input): Add stop propagation (#1917) * docs(module: select): update coordinate demo (#1914) Co-authored-by: James Yeung <shunjiey@hotmail.com> * fix(module: textarea): add rows parameter (#1920) doc: adjust to match antD tests: sizing tests * test(module: select): Add some unit tests for Select (#1891) * Add Select clear tests * Add Select DataSource to null test * Change the implement of Throw tests * Change int to int? * fix(module: button): loading icon styles (#1902) * Fix loading icon styles * Fix button render test * feat(module: InputNumber): Add inputmode for mobile keyboard (#1923) * perf: avoid memory leak issue of event listener (#1857) * perf: avoid memory leak #1834 Avoid memory leak by remove the exclusive parameter and logic in the code block on AddEventListener method in DomEventService class. The following are the components affected: components/affix/Affix.razor.cs components/anchor/Anchor.razor.cs components/carousel/Carousel.razor.cs components/core/Component/Overlay/Overlay.razor.cs components/core/Component/Overlay/OverlayTrigger.razor.cs components/core/JsInterop/DomEventService.cs components/descriptions/Descriptions.razor.cs components/dropdown/DropdownButton.cs components/grid/Row.razor.cs components/input/Input.cs components/input/TextArea.razor.cs components/layout/Sider.razor.cs components/list/ListItem.razor.cs components/select/Select.razor.cs components/select/internal/SelectContent.razor.cs components/slider/Slider.razor.cs components/table/Table.razor.cs components/tabs/Tabs.razor.cs * fix override AddEventListener method in AntDesign.TestKit project * add register/remove event listerner for exclusive use in DomEventService class * move _dotNetObjects to DomEventListerner class/service, so that users not required to maintain it in each component. * * move share/reuse dom event listerner methods to DomEventListerner class * remove method 'AddEventListener' that no longer exists in DomEventService class in AntDesign.TestKit project * * change the component referring to an IDomEventListerner interface instead of a concrete class, so that the component can be tested via a mock TestDomEventListerner. * introduce DisposeShared and Dispose method in DomEventListerner to ease user remove callback from DomEventListerner * register IDomEventListerner into DI container instead of create manually * fix FormatKey * fix FormatKey * fix tests * fix test * fix test Co-authored-by: James Yeung <shunjiey@hotmail.com> * fix(module: textarea): replace wrong event (#1927) * tests(module: textarea): include js function mock to avoid bunit exceptions (#1930) * perf(module: overlay): positioning moved to js (#1848) * fix(module:overlay): move postion calculation to js fixes #1836 * docs: TriggerBoundaryAdjustMode explanation * docs: select & datepicker got BoundaryAdjustmetMode cleanup * test: fixes optimization & cleanup * fix(module:overlay): recalculate overlay position when trigger resizes * Minor clean-up * fix(module:overlay): wait for Show to finish in Hide * fix: prevent vertical scrollbar on overlay adding * fix: extract waiting function * fix: overlay not to repostion when trigger vanishes (menu issue) * fix: scroll adjustment for position: fixed * fix: on menu mode change, keep * merge conflict fix * fix: nominal calculation reset on failed adjustment * fix: bugs * test: exclude log method from test coverage in overlay.ts * fix(module: table): It would be load twice at first time if pagesize is not 10 (#1933) * fix(module: menu): collapsed menu title can't hide while use router link (#1934) * fix(module: select): fix data source of type IEnumerable<object> (#1932) * fix(module:select): fix data source of type IEnumerable<object> * Update Select.razor.cs * Update Select.OnDataSourceChangeTests.razor Co-authored-by: James Yeung <shunjiey@hotmail.com> * feat(module: InputNumber): Add OnFocus event (#1931) * Add on textbox focus * Adjust naming Co-authored-by: James Yeung <shunjiey@hotmail.com> * fix(module: list): resposive style doesn't work (#1937) * docs: Update index.zh-CN.md (#1936) Update document ConfigProvider global configuration example code Co-authored-by: James Yeung <shunjiey@hotmail.com> * change log 0.9.4 (#1938) * chore: test cs project to include test runner adapter (#1939) test: add general overlay disposal method mock Co-authored-by: Alan.Liu <lxyruanjian@126.com> Co-authored-by: James Yeung <shunjiey@hotmail.com> Co-authored-by: rabberbock <rabberbock@gmail.com> Co-authored-by: Andrzej Bakun <anddrzejb@poczta.fm> Co-authored-by: Chandan Rauniyar <chandankkrr@gmail.com> Co-authored-by: Luke Parker [SSW] <10430890+Hona@users.noreply.github.com> Co-authored-by: SmallY <45689960+iamSmallY@users.noreply.github.com> Co-authored-by: Maksim <maksalmak@gmail.com> Co-authored-by: Tony Yip <tonyyip1969@gmail.com> Co-authored-by: SmRiley <45205313+SmRiley@users.noreply.github.com>
This commit is contained in:
commit
0ef438cc30
@ -14,6 +14,32 @@ timeline: true
|
||||
- Major version release is not included in this schedule for breaking change and new features.
|
||||
|
||||
---
|
||||
### 0.9.4
|
||||
|
||||
2021-09-12
|
||||
|
||||
- Table
|
||||
- 🐞 Fixed an issue that initialization is refreshed twice when PageSize is not equal to 10. [#1933](https://github.com/ant-design-blazor/ant-design-blazor/pull/1933) [@ElderJames](https://github.com/ElderJames)
|
||||
- 🆕 Addd CellData for CellRender. [#1907](https://github.com/ant-design-blazor/ant-design-blazor/pull/1907) [@ElderJames](https://github.com/ElderJames)
|
||||
- ⚡️ Put fixed column style into js. [#1897](https://github.com/ant-design-blazor/ant-design-blazor/pull/1897) [@ElderJames](https://github.com/ElderJames)
|
||||
- 📖 add dynamic table demo. [#1908](https://github.com/ant-design-blazor/ant-design-blazor/pull/1908) [@ElderJames](https://github.com/ElderJames)
|
||||
|
||||
- InputNumber
|
||||
- 🆕 Add OnFocus event [#1931](https://github.com/ant-design-blazor/ant-design-blazor/pull/1931) [@Hona](https://github.com/Hona)
|
||||
- 🐞 Fixed inputmode to support mobile numeric keypad. [#1923](https://github.com/ant-design-blazor/ant-design-blazor/pull/1923) [@CAPCHIK](https://github.com/CAPCHIK)
|
||||
|
||||
- Select
|
||||
- 🐞 Fixed the data source which has members of different types. [#1932](https://github.com/ant-design-blazor/ant-design-blazor/pull/1932) [@anranruye](https://github.com/anranruye)
|
||||
- 🐞 Fixed the problem that the selected item will be reset when setting DataSource [#1906](https://github.com/ant-design-blazor/ant-design-blazor/pull/1906) [@anddrzejb](https://github.com/anddrzejb)
|
||||
|
||||
- 🐞 Fixed Menu that the Title of MenuItem with RouterLink is not hidden when it is collapsed. [#1934](https://github.com/ant-design-blazor/ant-design-blazor/pull/1934) [@ElderJames](https://github.com/ElderJames)
|
||||
- 🐞 Fixed Overlay with a series of issues related to dropdown & popup。 [#1848](https://github.com/ant-design-blazor/ant-design-blazor/pull/1848) [@anddrzejb](https://github.com/anddrzejb)
|
||||
- 💄 Fixed loading icon styles. [#1902](https://github.com/ant-design-blazor/ant-design-blazor/pull/1902) [@CAPCHIK](https://github.com/CAPCHIK)
|
||||
- 🐞 Added parameter `Rows`. [#1920](https://github.com/ant-design-blazor/ant-design-blazor/pull/1920) [@anddrzejb](https://github.com/anddrzejb)
|
||||
- 🐞 Add stop propogation. [#1917](https://github.com/ant-design-blazor/ant-design-blazor/pull/1917) [@Hona](https://github.com/Hona)
|
||||
- 🐞 Fixed Form modifies the bound model to throw an exception in Rule validation mode. [#1901](https://github.com/ant-design-blazor/ant-design-blazor/pull/1901) [@lxyruanjian](https://github.com/lxyruanjian)
|
||||
- 🐞 Fixed List resposive style doesn't work. [#1937](https://github.com/ant-design-blazor/ant-design-blazor/pull/1937) [@ElderJames](https://github.com/ElderJames)
|
||||
- ⚡️ Fixed EventListener avoid memory leak issue. [#1857](https://github.com/ant-design-blazor/ant-design-blazor/pull/1857) [@tonyyip1969](https://github.com/tonyyip1969)
|
||||
|
||||
### 0.9.3
|
||||
|
||||
@ -26,7 +52,7 @@ timeline: true
|
||||
- 📖 Add an example of nested table. [#1884](https://github.com/ant-design-blazor/ant-design-blazor/pull/1884) [@ElderJames](https://github.com/ElderJames)
|
||||
- 🐞 Fixed Time column built-in filter will ignore milliseconds when filtering.[#1864](https://github.com/ant-design-blazor/ant-design-blazor/pull/1864) [@iamSmallY](https://github.com/iamSmallY)
|
||||
- 🐞 Fixed the issue that operations such as page turning, sorting and filtering are not refreshed by using client mode. [#1858](https://github.com/ant-design-blazor/ant-design-blazor/pull/1858) [@ElderJames](https://github.com/ElderJames)
|
||||
[#1875](https://github.com/ant-design/ant-design/pull/1875) [@nikolaykrondev](https://github.com/nikolaykrondev)
|
||||
[#1875](https://github.com/ant-design-blazor/ant-design-blazor/pull/1875) [@nikolaykrondev](https://github.com/nikolaykrondev)
|
||||
- 🐞 Fixed the issue that OnChange is called multiple times after initialization. [#1855](https://github.com/ant-design-blazor/ant-design-blazor/pull/1855) [@ElderJames](https://github.com/ElderJames)
|
||||
|
||||
- 🆕 Breadcrumb add Href and Overlay dropdown. [#1859](https://github.com/ant-design-blazor/ant-design-blazor/pull/1859) [@CAPCHIK](https://github.com/CAPCHIK)
|
||||
|
@ -15,6 +15,33 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
### 0.9.4
|
||||
|
||||
2021-09-12
|
||||
|
||||
- Table
|
||||
- 🐞 修复 在 PageSize 不等于 10 时,初始化时会被刷新两次的问题。[#1933](https://github.com/ant-design-blazor/ant-design-blazor/pull/1933) [@ElderJames](https://github.com/ElderJames)
|
||||
- 🆕 传递 `CellData` 给 CellRender 模板,可访问当前单元格和行的一些信息。[#1907](https://github.com/ant-design-blazor/ant-design-blazor/pull/1907) [@ElderJames](https://github.com/ElderJames)
|
||||
- ⚡️ 将固定列的样式处理放到 JS,以提升性能。[#1897](https://github.com/ant-design-blazor/ant-design-blazor/pull/1897) [@ElderJames](https://github.com/ElderJames)
|
||||
- 📖 增加 动态表格 demo。[#1908](https://github.com/ant-design-blazor/ant-design-blazor/pull/1908) [@ElderJames](https://github.com/ElderJames)
|
||||
|
||||
- InputNumber
|
||||
- 🆕 增加 OnFocus 事件。[#1931](https://github.com/ant-design-blazor/ant-design-blazor/pull/1931) [@Hona](https://github.com/Hona)
|
||||
- 🐞 修复 inputmode,支持手机数字键盘。[#1923](https://github.com/ant-design-blazor/ant-design-blazor/pull/1923) [@CAPCHIK](https://github.com/CAPCHIK)
|
||||
|
||||
- Select
|
||||
- 🐞 修复 对异构选项类型的支持。[#1932](https://github.com/ant-design-blazor/ant-design-blazor/pull/1932) [@anranruye](https://github.com/anranruye)
|
||||
- 🐞 修复 设置 DataSource 时,选中项会被的重置问题。[#1906](https://github.com/ant-design-blazor/ant-design-blazor/pull/1906) [@anddrzejb](https://github.com/anddrzejb)
|
||||
|
||||
- 🐞 修复 Overlay 与 dropdown、选项框、popup 有关的一系列问题。[#1848](https://github.com/ant-design-blazor/ant-design-blazor/pull/1848) [@anddrzejb](https://github.com/anddrzejb)
|
||||
- 💄 修复 Button 的 loading 样式。[#1902](https://github.com/ant-design-blazor/ant-design-blazor/pull/1902) [@CAPCHIK](https://github.com/CAPCHIK)
|
||||
- 🐞 增加 TextArea 的 `Rows` 属性,支持固定的行数。[#1920](https://github.com/ant-design-blazor/ant-design-blazor/pull/1920) [@anddrzejb](https://github.com/anddrzejb)
|
||||
- 🐞 增加 Input 的 StopPropogation 属性,以减少事件触发,提升性能。[#1917](https://github.com/ant-design-blazor/ant-design-blazor/pull/1917) [@Hona](https://github.com/Hona)
|
||||
- 🐞 修复 Form 移除已释放的 FormItem 实例。[#1901](https://github.com/ant-design-blazor/ant-design-blazor/pull/1901) [@lxyruanjian](https://github.com/lxyruanjian)
|
||||
- ⚡️ 事件订阅器的内存泄漏问题。[#1857](https://github.com/ant-design-blazor/ant-design-blazor/pull/1857) [@tonyyip1969](https://github.com/tonyyip1969)
|
||||
- 🐞 修复 List 组件的响应式无效的问题。 [#1937](https://github.com/ant-design-blazor/ant-design-blazor/pull/1937) [@ElderJames](https://github.com/ElderJames)
|
||||
- 🐞 修复 Menu 有 RouterLink 的 MenuItem 在收起时 Title 不隐藏的问题。[#1934](https://github.com/ant-design-blazor/ant-design-blazor/pull/1934) [@ElderJames](https://github.com/ElderJames)
|
||||
|
||||
### 0.9.3
|
||||
|
||||
2021-08-29
|
||||
@ -26,7 +53,7 @@ timeline: true
|
||||
- 📖 增加 表格嵌套的示例。[#1884](https://github.com/ant-design-blazor/ant-design-blazor/pull/1884) [@ElderJames](https://github.com/ElderJames)
|
||||
- 🐞 修复 时间列内置筛选器在筛选时将忽略毫秒。[#1864](https://github.com/ant-design-blazor/ant-design-blazor/pull/1864) [@iamSmallY](https://github.com/iamSmallY)
|
||||
- 🐞 修复 使用客户端模式翻页、排序、筛选等操作不刷新的问题。[#1858](https://github.com/ant-design-blazor/ant-design-blazor/pull/1858) [@ElderJames](https://github.com/ElderJames)
|
||||
[#1875](https://github.com/ant-design/ant-design/pull/1875) [@nikolaykrondev](https://github.com/nikolaykrondev)
|
||||
[#1875](https://github.com/ant-design-blazor/ant-design-blazor/pull/1875) [@nikolaykrondev](https://github.com/nikolaykrondev)
|
||||
- 🐞 修复 初始化后 OnChange 调用多次的问题。[#1855](https://github.com/ant-design-blazor/ant-design-blazor/pull/1855) [@ElderJames](https://github.com/ElderJames)
|
||||
|
||||
- 🆕 新增 Breadcrumb 的 Href 和 Overlay 下拉菜单。 [#1859](https://github.com/ant-design-blazor/ant-design-blazor/pull/1859) [@CAPCHIK](https://github.com/CAPCHIK)
|
||||
|
@ -159,6 +159,7 @@ WebAssembly 静态托管页面示例
|
||||
$ git clone git@github.com:ant-design-blazor/ant-design-blazor.git
|
||||
$ cd ant-design-blazor
|
||||
$ npm install
|
||||
$ dotnet build ./site/AntDesign.Docs.Build/AntDesign.Docs.Build.csproj
|
||||
$ npm start
|
||||
```
|
||||
|
||||
|
@ -165,6 +165,7 @@ Options for the template:
|
||||
$ git clone git@github.com:ant-design-blazor/ant-design-blazor.git
|
||||
$ cd ant-design-blazor
|
||||
$ npm install
|
||||
$ dotnet build ./site/AntDesign.Docs.Build/AntDesign.Docs.Build.csproj
|
||||
$ npm start
|
||||
```
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace AntDesign
|
||||
private string _affixStyle;
|
||||
|
||||
[Inject]
|
||||
private DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
#region Parameters
|
||||
|
||||
@ -92,15 +92,14 @@ namespace AntDesign
|
||||
await RenderAffixAsync();
|
||||
if (!_rootListened && string.IsNullOrEmpty(TargetSelector))
|
||||
{
|
||||
DomEventService.AddEventListener(RootScollSelector, "scroll", OnWindowScroll, false);
|
||||
DomEventService.AddEventListener(RootScollSelector, "resize", OnWindowResize, false);
|
||||
|
||||
DomEventListener.AddShared<JsonElement>(RootScollSelector, "scroll", OnWindowScroll);
|
||||
DomEventListener.AddShared<JsonElement>(RootScollSelector, "resize", OnWindowResize);
|
||||
_rootListened = true;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(TargetSelector))
|
||||
{
|
||||
DomEventService.AddEventListener(TargetSelector, "scroll", OnTargetScroll);
|
||||
DomEventService.AddEventListener(TargetSelector, "resize", OnTargetResize);
|
||||
DomEventListener.AddExclusive<JsonElement>(TargetSelector, "scroll", OnTargetScroll);
|
||||
DomEventListener.AddExclusive<JsonElement>(TargetSelector, "resize", OnTargetResize);
|
||||
_targetListened = true;
|
||||
}
|
||||
}
|
||||
@ -143,8 +142,8 @@ namespace AntDesign
|
||||
containerRect = new DomRect()
|
||||
{
|
||||
Top = 0,
|
||||
Bottom = window.innerHeight,
|
||||
Height = window.innerHeight,
|
||||
Bottom = window.InnerHeight,
|
||||
Height = window.InnerHeight,
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -159,7 +158,7 @@ namespace AntDesign
|
||||
{
|
||||
if (domRect.Bottom > bottomDist)
|
||||
{
|
||||
_affixStyle = _hiddenStyle + $"bottom: { window.innerHeight - bottomDist}px; position: fixed;";
|
||||
_affixStyle = _hiddenStyle + $"bottom: { window.InnerHeight - bottomDist}px; position: fixed;";
|
||||
Affixed = true;
|
||||
}
|
||||
else
|
||||
@ -189,17 +188,7 @@ namespace AntDesign
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (_rootListened)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>(RootScollSelector, "scroll", OnWindowScroll);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(RootScollSelector, "resize", OnWindowResize);
|
||||
}
|
||||
|
||||
if (_targetListened)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>(TargetSelector, "scroll", OnTargetScroll);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(TargetSelector, "resize", OnTargetResize);
|
||||
}
|
||||
DomEventListener.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace AntDesign
|
||||
private bool _linksChanged = false;
|
||||
|
||||
[Inject]
|
||||
private DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
#region Parameters
|
||||
|
||||
@ -129,7 +129,7 @@ namespace AntDesign
|
||||
{
|
||||
if (GetCurrentAnchor is null)
|
||||
{
|
||||
DomEventService.AddEventListener("window", "scroll", OnScroll, false);
|
||||
DomEventListener.AddShared<JsonElement>("window", "scroll", OnScroll);
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,9 +297,8 @@ namespace AntDesign
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "scroll", OnScroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@
|
||||
Style="display: inline"
|
||||
IsButton="@true"
|
||||
Disabled="false"
|
||||
Trigger="new TriggerType[] { TriggerType.Click }"
|
||||
Trigger="new[] { Trigger.Click }"
|
||||
BoundaryAdjustMode="@BoundaryAdjustMode"
|
||||
PopupContainerSelector="@PopupContainerSelector"
|
||||
OverlayEnterCls="ant-slide-up-enter ant-slide-up-enter-active ant-slide-up"
|
||||
OverlayLeaveCls="ant-slide-up-leave ant-slide-up-leave-active ant-slide-up"
|
||||
|
@ -163,6 +163,13 @@ namespace AntDesign
|
||||
/// </summary>
|
||||
public object ActiveValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Overlay adjustment strategy (when for example browser resize is happening). Check
|
||||
/// enum for details.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public TriggerBoundaryAdjustMode BoundaryAdjustMode { get; set; } = TriggerBoundaryAdjustMode.InView;
|
||||
|
||||
[Parameter]
|
||||
public bool ShowPanel { get; set; } = false;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
</CascadingValue>
|
||||
@if (_overflow)
|
||||
{
|
||||
<Popover Trigger="new[] { TriggerType.Hover }"
|
||||
<Popover Trigger="new[] { Trigger.Hover }"
|
||||
Placement=MaxPopoverPlacement
|
||||
OverlayClassName="@_popoverClassMapper.Class">
|
||||
<ContentTemplate>
|
||||
|
@ -15,7 +15,7 @@ namespace AntDesign
|
||||
public string MaxStyle { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public PlacementType MaxPopoverPlacement { get; set; } = PlacementType.Top;
|
||||
public Placement MaxPopoverPlacement { get; set; } = Placement.Top;
|
||||
|
||||
private ClassMapper _popoverClassMapper = new ClassMapper();
|
||||
|
||||
|
@ -10,7 +10,7 @@ namespace AntDesign
|
||||
private bool _visible = false;
|
||||
|
||||
[Inject]
|
||||
public DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Icon { get; set; } = "vertical-align-top";
|
||||
@ -55,11 +55,11 @@ namespace AntDesign
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(TargetSelector))
|
||||
{
|
||||
DomEventService.AddEventListener("window", "scroll", OnScroll);
|
||||
DomEventListener.AddExclusive<JsonElement>("window", "scroll", OnScroll);
|
||||
}
|
||||
else
|
||||
{
|
||||
DomEventService.AddEventListener(TargetSelector, "scroll", OnScroll);
|
||||
DomEventListener.AddExclusive<JsonElement>(TargetSelector, "scroll", OnScroll);
|
||||
}
|
||||
await base.OnFirstAfterRenderAsync();
|
||||
}
|
||||
@ -88,15 +88,8 @@ namespace AntDesign
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventListener.DisposeExclusive();
|
||||
base.Dispose(disposing);
|
||||
if (string.IsNullOrWhiteSpace(TargetSelector))
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "scroll", OnScroll);
|
||||
}
|
||||
else
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>(TargetSelector, "scroll", OnScroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
@if (Overlay != null)
|
||||
{
|
||||
<Dropdown Placement="@PlacementType.BottomCenter">
|
||||
<Dropdown Placement="@Placement.BottomCenter">
|
||||
<Unbound>
|
||||
<span @ref="context.Current" class="ant-breadcrumb-overlay-link">
|
||||
<span class="ant-breadcrumb-link">
|
||||
|
@ -2,20 +2,31 @@
|
||||
@inherits AntDomComponentBase
|
||||
|
||||
<CascadingValue Value="this" IsFixed="@true">
|
||||
<button class="@ClassMapper.Class" style="@(this.Danger ? this._btnWave + Style:Style)" id="@Id" type="@HtmlType" @ref="@Ref"
|
||||
<button class="@ClassMapper.Class" style="@(this.Danger ? this._btnWave + Style : Style)" id="@Id" type="@HtmlType" @ref="@Ref"
|
||||
@onclick="HandleOnClick" disabled="@Disabled"
|
||||
@onclick:stopPropagation="@OnClickStopPropagation"
|
||||
@onmouseup="OnMouseUp"
|
||||
ant-click-animating-without-extra-node="@(this._animating ? "true":"false")">
|
||||
@if (Loading)
|
||||
{
|
||||
<Icon Type="loading"></Icon>
|
||||
<span class="ant-btn-loading-icon">
|
||||
<Icon Type="loading" />
|
||||
</span>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Icon))
|
||||
else if (!string.IsNullOrEmpty(Icon))
|
||||
{
|
||||
<Icon Type="@Icon" Style="@IconStyle"></Icon>
|
||||
<Icon Type="@Icon" />
|
||||
}
|
||||
@if (ChildContent != default)
|
||||
{
|
||||
@if (NoSpanWrap)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@ChildContent</span>
|
||||
}
|
||||
}
|
||||
|
||||
@ChildContent
|
||||
</button>
|
||||
</CascadingValue>
|
||||
|
@ -49,7 +49,7 @@ namespace AntDesign
|
||||
public bool Danger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the `Button` is disabled.
|
||||
/// Whether the `Button` is disabled.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; }
|
||||
@ -73,21 +73,10 @@ namespace AntDesign
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Show loading indicator. You have to write the loading logic on your own.
|
||||
/// Show loading indicator. You have to write the loading logic on your own.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool Loading
|
||||
{
|
||||
get => _loading;
|
||||
set
|
||||
{
|
||||
if (_loading != value)
|
||||
{
|
||||
_loading = value;
|
||||
UpdateIconDisplay(_loading);
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool Loading { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback when `Button` is clicked
|
||||
@ -114,19 +103,23 @@ namespace AntDesign
|
||||
public string Size { get; set; } = AntSizeLDSType.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Type of the button.
|
||||
/// Type of the button.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string Type { get; set; } = ButtonType.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Do not wrap with <span>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool NoSpanWrap { get; set; }
|
||||
|
||||
public IList<Icon> Icons { get; set; } = new List<Icon>();
|
||||
|
||||
protected string IconStyle { get; set; }
|
||||
|
||||
private bool _animating = false;
|
||||
|
||||
private string _btnWave = "--antd-wave-shadow-color: rgb(255, 120, 117);";
|
||||
private bool _loading = false;
|
||||
|
||||
protected void SetClassMap()
|
||||
{
|
||||
@ -152,12 +145,6 @@ namespace AntDesign
|
||||
base.OnInitialized();
|
||||
SetClassMap();
|
||||
SetButtonColorStyle();
|
||||
UpdateIconDisplay(_loading);
|
||||
}
|
||||
|
||||
private void UpdateIconDisplay(bool loading)
|
||||
{
|
||||
IconStyle = $"display:{(loading ? "none" : "inline-block")}";
|
||||
}
|
||||
|
||||
private async Task HandleOnClick(MouseEventArgs args)
|
||||
|
@ -74,7 +74,7 @@ namespace AntDesign
|
||||
|
||||
#endregion Parameters
|
||||
|
||||
[Inject] public DomEventService DomEventService { get; set; }
|
||||
[Inject] public IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
public void Next() => GoTo(_slicks.IndexOf(ActiveSlick) + 1);
|
||||
|
||||
@ -130,7 +130,7 @@ namespace AntDesign
|
||||
if (firstRender)
|
||||
{
|
||||
Resize();
|
||||
DomEventService.AddEventListener("window", "resize", Resize, false);
|
||||
DomEventListener.AddShared<JsonElement>("window", "resize", Resize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,10 +208,8 @@ namespace AntDesign
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "resize", Resize);
|
||||
|
||||
DomEventListener.Dispose();
|
||||
_slicks.Clear();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,8 @@
|
||||
<CascadingValue Value=@("ant-cascader-menus") Name="PrefixCls">
|
||||
<OverlayTrigger Visible="@_dropdownOpened"
|
||||
OnMaskClick="CascaderOnBlur"
|
||||
Trigger="new TriggerType[] { }"
|
||||
Trigger="new Trigger[] { }"
|
||||
BoundaryAdjustMode="@BoundaryAdjustMode"
|
||||
PopupContainerSelector="@PopupContainerSelector"
|
||||
OverlayEnterCls="ant-slide-up-enter ant-slide-up-enter-active ant-slide-up"
|
||||
OverlayLeaveCls="ant-slide-up-leave ant-slide-up-leave-active ant-slide-up">
|
||||
|
@ -13,6 +13,11 @@ namespace AntDesign
|
||||
{
|
||||
[Parameter] public bool AllowClear { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Overlay adjustment strategy (when for example browser resize is happening)
|
||||
/// </summary>
|
||||
[Parameter] public TriggerBoundaryAdjustMode BoundaryAdjustMode { get; set; } = TriggerBoundaryAdjustMode.None;
|
||||
|
||||
[Parameter] public bool ChangeOnSelect { get; set; }
|
||||
|
||||
[Parameter] public string DefaultValue { get; set; }
|
||||
|
@ -11,10 +11,11 @@
|
||||
@ref="Ref"
|
||||
@onmouseenter="OnOverlayMouseEnter"
|
||||
@onmouseleave="OnOverlayMouseLeave"
|
||||
@onmouseup="OnOverlayMouseUp"
|
||||
@onmouseup="OnOverlayMouseUp"
|
||||
@onclick:stopPropagation>
|
||||
<CascadingValue Value="this" Name="Overlay" IsFixed="@true">
|
||||
<CascadingValue Value="Trigger" Name="ParentTrigger">
|
||||
|
||||
<CascadingValue Value="this" Name="Overlay" IsFixed>
|
||||
<CascadingValue Value="Trigger" Name="ParentTrigger" IsFixed>
|
||||
@if (!string.IsNullOrEmpty(OverlayChildPrefixCls))
|
||||
{
|
||||
<CascadingValue Value="OverlayChildPrefixCls" Name="PrefixCls">
|
||||
|
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.Core.JsInterop.Modules.Components;
|
||||
using AntDesign.JsInterop;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@ -9,14 +9,21 @@ namespace AntDesign.Internal
|
||||
{
|
||||
public sealed partial class Overlay : AntDomComponentBase
|
||||
{
|
||||
[CascadingParameter(Name = "Trigger")]
|
||||
public OverlayTrigger Trigger { get; set; }
|
||||
[CascadingParameter(Name = "ArrowPointAtCenter")]
|
||||
public bool ArrowPointAtCenter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used in nested overlays (for example menu -> submenu) when
|
||||
/// trigger is another overlay.
|
||||
/// </summary>
|
||||
[CascadingParameter(Name = "ParentTrigger")]
|
||||
public OverlayTrigger ParentTrigger { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string OverlayChildPrefixCls { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Component that will trigger the overlay to show.
|
||||
/// </summary>
|
||||
[CascadingParameter(Name = "Trigger")]
|
||||
public OverlayTrigger Trigger { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
@ -31,13 +38,14 @@ namespace AntDesign.Internal
|
||||
public EventCallback OnOverlayMouseUp { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Action OnShow { get; set; }
|
||||
public EventCallback OnShow { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Action OnHide { get; set; }
|
||||
public EventCallback OnHide { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string OverlayChildPrefixCls { get; set; } = "";
|
||||
|
||||
[CascadingParameter(Name = "ArrowPointAtCenter")]
|
||||
public bool ArrowPointAtCenter { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int HideMillisecondsDelay { get; set; } = 100;
|
||||
@ -69,7 +77,7 @@ namespace AntDesign.Internal
|
||||
public bool HiddenMode { get; set; } = false;
|
||||
|
||||
[Inject]
|
||||
private DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
private bool _hasAddOverlayToBody = false;
|
||||
private bool _isPreventHide = false;
|
||||
@ -80,6 +88,7 @@ namespace AntDesign.Internal
|
||||
private bool _isWaitForOverlayFirstRender = false;
|
||||
|
||||
private bool _preVisible = false;
|
||||
private bool _isOverlayDuringShowing = false;
|
||||
private bool _isOverlayShow = false;
|
||||
private bool _isOverlayHiding = false;
|
||||
private bool _lastDisabledState = false;
|
||||
@ -87,14 +96,20 @@ namespace AntDesign.Internal
|
||||
private int? _overlayLeft = null;
|
||||
private int? _overlayTop = null;
|
||||
|
||||
private string _overlayStyle = "";
|
||||
//if this style needs to be changed, also change
|
||||
//the removal of that style in js interop overlay.ts class (in constructor)
|
||||
private string _overlayStyle = "display: none;"; //initial value prevents from screen flickering when adding overlay to dom; it will be overwritten immediately
|
||||
private string _overlayCls = "";
|
||||
|
||||
private const int ARROW_SIZE = 13;
|
||||
private const int HORIZONTAL_ARROW_SHIFT = 13;
|
||||
private const int VERTICAL_ARROW_SHIFT = 5;
|
||||
private bool _shouldRender = true;
|
||||
|
||||
private int _overlayClientWidth = 0;
|
||||
protected override bool ShouldRender()
|
||||
{
|
||||
if (_shouldRender)
|
||||
return base.ShouldRender();
|
||||
_shouldRender = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
@ -121,7 +136,7 @@ namespace AntDesign.Internal
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
DomEventService.AddEventListener("window", "beforeunload", Reloading, false);
|
||||
DomEventListener.AddShared<JsonElement>("window", "beforeunload", Reloading);
|
||||
}
|
||||
|
||||
if (_lastDisabledState != Trigger.Disabled)
|
||||
@ -143,7 +158,7 @@ namespace AntDesign.Internal
|
||||
if (_isWaitForOverlayFirstRender && _isOverlayFirstRender)
|
||||
{
|
||||
_isOverlayFirstRender = false;
|
||||
await Show(_overlayLeft, _overlayTop);
|
||||
//await Show(_overlayLeft, _overlayTop);
|
||||
|
||||
_isWaitForOverlayFirstRender = false;
|
||||
_overlayLeft = null;
|
||||
@ -159,10 +174,10 @@ namespace AntDesign.Internal
|
||||
_ = InvokeAsync(async () =>
|
||||
{
|
||||
await Task.Delay(100);
|
||||
await JsInvokeAsync(JSInteropConstants.DelElementFrom, Ref, Trigger.PopupContainerSelector);
|
||||
await JsInvokeAsync(JSInteropConstants.OverlayComponentHelper.DeleteOverlayFromContainer, Ref.Id);
|
||||
});
|
||||
}
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "beforeunload", Reloading);
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@ -172,24 +187,14 @@ namespace AntDesign.Internal
|
||||
{
|
||||
return;
|
||||
}
|
||||
await Task.Yield();
|
||||
HtmlElement trigger;
|
||||
if (Trigger.ChildContent is not null)
|
||||
_isOverlayDuringShowing = true;
|
||||
|
||||
if (_isOverlayFirstRender)
|
||||
{
|
||||
trigger = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetFirstChildDomInfo, Trigger.Ref);
|
||||
// fix bug in submenu: Overlay show when OvelayTrigger is not rendered complete.
|
||||
// I try to render Overlay when OvelayTrigger’s OnAfterRender is called, but is still get negative absoluteLeft
|
||||
// This may be a bad solution, but for now I can only do it this way.
|
||||
while (trigger.AbsoluteLeft <= 0 && trigger.ClientWidth <= 0)
|
||||
{
|
||||
await Task.Delay(50);
|
||||
trigger = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetFirstChildDomInfo, Trigger.Ref);
|
||||
}
|
||||
}
|
||||
else //(Trigger.Unbound != null)
|
||||
{
|
||||
trigger = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, Trigger.Ref);
|
||||
Trigger.SetShouldRender(false);
|
||||
await Task.Yield();
|
||||
}
|
||||
else
|
||||
|
||||
_overlayLeft = overlayLeft;
|
||||
_overlayTop = overlayTop;
|
||||
@ -198,66 +203,51 @@ namespace AntDesign.Internal
|
||||
{
|
||||
_isWaitForOverlayFirstRender = true;
|
||||
|
||||
StateHasChanged();
|
||||
return;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
_isOverlayShow = true;
|
||||
_isOverlayHiding = false;
|
||||
|
||||
await UpdateParentOverlayState(true);
|
||||
|
||||
await AddOverlayToBody();
|
||||
|
||||
HtmlElement overlayElement = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, Ref);
|
||||
HtmlElement containerElement = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, Trigger.PopupContainerSelector);
|
||||
|
||||
int left = await GetOverlayLeftWithBoundaryAdjust(trigger, overlayElement, containerElement);
|
||||
int top = await GetOverlayTopWithBoundaryAdjust(trigger, overlayElement, containerElement);
|
||||
|
||||
int zIndex = await JsInvokeAsync<int>(JSInteropConstants.GetMaxZIndex);
|
||||
|
||||
if (Trigger.Placement.IsIn(PlacementType.BottomRight, PlacementType.TopRight))
|
||||
{
|
||||
var right = ChangeOverlayLeftToRight(left, overlayElement, containerElement);
|
||||
_overlayStyle = $"z-index:{zIndex};left: unset;right: {right}px;top: {top}px;{GetTransformOrigin()}";
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayStyle = $"z-index:{zIndex};left: {left}px;top: {top}px;{GetTransformOrigin()}";
|
||||
}
|
||||
await AddOverlayToBody(overlayLeft, overlayTop);
|
||||
_isOverlayShow = true;
|
||||
_isOverlayDuringShowing = false;
|
||||
_isOverlayHiding = false;
|
||||
|
||||
_overlayCls = Trigger.GetOverlayEnterClass();
|
||||
|
||||
await Trigger.OnVisibleChange.InvokeAsync(true);
|
||||
|
||||
StateHasChanged();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
OnShow?.Invoke();
|
||||
if (OnShow.HasDelegate)
|
||||
OnShow.InvokeAsync(null);
|
||||
}
|
||||
|
||||
internal async Task Hide(bool force = false)
|
||||
{
|
||||
if (_isOverlayDuringShowing)
|
||||
{
|
||||
//If Show() method is processing, wait up to 1000 ms
|
||||
//for it to end processing
|
||||
await WaitFor(() => _isOverlayShow);
|
||||
}
|
||||
if (!_isOverlayShow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Delay(HideMillisecondsDelay);
|
||||
|
||||
if (!force && !IsContainTrigger(TriggerType.Click) && (_isPreventHide || _mouseInOverlay || _isChildOverlayShow))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isOverlayFirstRender = true;
|
||||
|
||||
_isWaitForOverlayFirstRender = false;
|
||||
_isOverlayHiding = true;
|
||||
|
||||
_overlayCls = Trigger.GetOverlayLeaveClass();
|
||||
|
||||
await Trigger.OnOverlayHiding.InvokeAsync(true);
|
||||
|
||||
await UpdateParentOverlayState(false);
|
||||
|
||||
StateHasChanged();
|
||||
@ -271,7 +261,8 @@ namespace AntDesign.Internal
|
||||
|
||||
StateHasChanged();
|
||||
|
||||
OnHide?.Invoke();
|
||||
if (OnHide.HasDelegate)
|
||||
OnHide.InvokeAsync(null);
|
||||
}
|
||||
|
||||
internal void PreventHide(bool prevent)
|
||||
@ -308,275 +299,84 @@ namespace AntDesign.Internal
|
||||
/// Indicates that a page is being refreshed
|
||||
/// </summary>
|
||||
private bool _isReloading;
|
||||
private OverlayPosition _position;
|
||||
|
||||
private void Reloading(JsonElement jsonElement) => _isReloading = true;
|
||||
|
||||
private async Task AddOverlayToBody()
|
||||
private int _recurenceGuard = 0;
|
||||
private async Task AddOverlayToBody(int? overlayLeft = null, int? overlayTop = null)
|
||||
{
|
||||
if (!_hasAddOverlayToBody)
|
||||
{
|
||||
await JsInvokeAsync(JSInteropConstants.AddElementTo, Ref, Trigger.PopupContainerSelector);
|
||||
bool triggerIsWrappedInDiv = Trigger.Unbound is null;
|
||||
_recurenceGuard++;
|
||||
|
||||
_hasAddOverlayToBody = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> GetOverlayTopWithBoundaryAdjust(HtmlElement trigger, HtmlElement overlay, HtmlElement containerElement)
|
||||
{
|
||||
int top = GetOverlayTop(trigger, overlay, containerElement);
|
||||
|
||||
/*
|
||||
* 边界检测和方向调整
|
||||
* boundary detection and orientation adjustment
|
||||
*/
|
||||
return await BoundaryAdjust(trigger, overlay, containerElement, top, "top");
|
||||
}
|
||||
|
||||
private async Task<int> GetOverlayLeftWithBoundaryAdjust(HtmlElement trigger, HtmlElement overlay, HtmlElement containerElement)
|
||||
{
|
||||
int left = GetOverlayLeft(trigger, overlay, containerElement);
|
||||
|
||||
/*
|
||||
* 边界检测和方向调整
|
||||
* boundary detection and orientation adjustment
|
||||
*/
|
||||
return await BoundaryAdjust(trigger, overlay, containerElement, left, "left");
|
||||
}
|
||||
|
||||
private int GetOverlayTop(HtmlElement trigger, HtmlElement overlay, HtmlElement containerElement)
|
||||
{
|
||||
int top = 0;
|
||||
|
||||
int triggerTop = (int)(containerElement.ScrollTop + trigger.AbsoluteTop - containerElement.AbsoluteTop);
|
||||
int triggerHeight = trigger.ClientHeight != 0 ? trigger.ClientHeight : trigger.OffsetHeight;
|
||||
|
||||
// contextMenu
|
||||
if (_overlayTop != null)
|
||||
{
|
||||
triggerTop += (int)_overlayTop;
|
||||
triggerHeight = 0;
|
||||
}
|
||||
|
||||
if (Trigger.Placement.IsIn(PlacementType.Left, PlacementType.Right))
|
||||
{
|
||||
top = triggerTop + triggerHeight / 2 - overlay.ClientHeight / 2;
|
||||
}
|
||||
else if (Trigger.Placement.IsIn(PlacementType.LeftTop, PlacementType.RightTop))
|
||||
{
|
||||
top = triggerTop;
|
||||
|
||||
if (ArrowPointAtCenter)
|
||||
//In ServerSide it may happen that trigger element reference has not yet been retrieved.
|
||||
if (!(await WaitFor(() => Trigger.Ref.Id is not null)))
|
||||
{
|
||||
top += -VERTICAL_ARROW_SHIFT - ARROW_SIZE / 2 + triggerHeight / 2;
|
||||
//Place where Error Boundary could be utilized
|
||||
throw new ArgumentNullException("Trigger.Ref.Id cannot be null when attaching overlay to it.");
|
||||
}
|
||||
}
|
||||
else if (Trigger.Placement.IsIn(PlacementType.LeftBottom, PlacementType.RightBottom))
|
||||
{
|
||||
top = triggerTop - overlay.ClientHeight + triggerHeight;
|
||||
|
||||
if (ArrowPointAtCenter)
|
||||
_position = await JsInvokeAsync<OverlayPosition>(JSInteropConstants.OverlayComponentHelper.AddOverlayToContainer,
|
||||
Ref.Id, Ref, Trigger.Ref, Trigger.Placement, Trigger.PopupContainerSelector,
|
||||
Trigger.BoundaryAdjustMode, triggerIsWrappedInDiv, Trigger.PrefixCls,
|
||||
VerticalOffset, HorizontalOffset, ArrowPointAtCenter, overlayTop, overlayLeft);
|
||||
if (_position is null && _recurenceGuard <= 10) //up to 10 attempts
|
||||
{
|
||||
top += VERTICAL_ARROW_SHIFT + ARROW_SIZE / 2 - triggerHeight / 2;
|
||||
Console.WriteLine($"Failed to add overlay to the container. Container: {Trigger.PopupContainerSelector}, trigger: {Trigger.Ref.Id}, overlay: {Ref.Id}. Awaiting and rerunning.");
|
||||
await Task.Delay(10);
|
||||
await AddOverlayToBody(overlayLeft, overlayTop);
|
||||
}
|
||||
}
|
||||
else if (Trigger.Placement.IsIn(PlacementType.BottomLeft, PlacementType.BottomCenter, PlacementType.Bottom, PlacementType.BottomRight))
|
||||
{
|
||||
top = triggerTop + triggerHeight + VerticalOffset;
|
||||
}
|
||||
else if (Trigger.Placement.IsIn(PlacementType.TopLeft, PlacementType.TopCenter, PlacementType.Top, PlacementType.TopRight))
|
||||
{
|
||||
top = triggerTop - overlay.ClientHeight - VerticalOffset;
|
||||
}
|
||||
top -= containerElement.ClientTop;
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
private int GetOverlayLeft(HtmlElement trigger, HtmlElement overlay, HtmlElement containerElement)
|
||||
{
|
||||
int left = 0;
|
||||
|
||||
int triggerLeft = trigger.AbsoluteLeft - containerElement.AbsoluteLeft;
|
||||
int triggerWidth = trigger.ClientWidth != 0 ? trigger.ClientWidth : trigger.OffsetWidth;
|
||||
|
||||
if (overlay.ClientWidth > 0)
|
||||
{
|
||||
_overlayClientWidth = overlay.ClientWidth;
|
||||
}
|
||||
|
||||
// contextMenu
|
||||
if (_overlayLeft != null)
|
||||
{
|
||||
triggerLeft += (int)_overlayLeft;
|
||||
triggerWidth = 0;
|
||||
}
|
||||
|
||||
if (Trigger.Placement.IsIn(PlacementType.Left, PlacementType.LeftTop, PlacementType.LeftBottom))
|
||||
{
|
||||
left = triggerLeft - _overlayClientWidth - HorizontalOffset;
|
||||
}
|
||||
else if (Trigger.Placement.IsIn(PlacementType.Right, PlacementType.RightTop, PlacementType.RightBottom))
|
||||
{
|
||||
left = triggerLeft + triggerWidth + HorizontalOffset;
|
||||
}
|
||||
else if (Trigger.Placement.IsIn(PlacementType.BottomLeft, PlacementType.TopLeft))
|
||||
{
|
||||
left = triggerLeft;
|
||||
|
||||
if (ArrowPointAtCenter)
|
||||
else
|
||||
{
|
||||
left += -HORIZONTAL_ARROW_SHIFT - ARROW_SIZE / 2 + triggerWidth / 2;
|
||||
_hasAddOverlayToBody = true;
|
||||
_overlayStyle = _position.PositionCss + GetTransformOrigin();
|
||||
if (_position.Placement != Trigger.Placement)
|
||||
{
|
||||
Trigger.ChangePlacementForShow(PlacementType.Create(_position.Placement));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Trigger.Placement.IsIn(PlacementType.BottomCenter, PlacementType.Bottom, PlacementType.TopCenter, PlacementType.Top))
|
||||
{
|
||||
left = triggerLeft + triggerWidth / 2 - _overlayClientWidth / 2;
|
||||
}
|
||||
else if (Trigger.Placement.IsIn(PlacementType.BottomRight, PlacementType.TopRight))
|
||||
{
|
||||
left = triggerLeft + triggerWidth - _overlayClientWidth;
|
||||
|
||||
if (ArrowPointAtCenter)
|
||||
{
|
||||
left += HORIZONTAL_ARROW_SHIFT + ARROW_SIZE / 2 - triggerWidth / 2;
|
||||
}
|
||||
}
|
||||
left -= containerElement.ClientLeft;
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
/*
|
||||
* 边界检测和方向调整
|
||||
* boundary detection and orientation adjustment
|
||||
*/
|
||||
|
||||
private async Task<int> BoundaryAdjust(
|
||||
HtmlElement trigger, HtmlElement overlay, HtmlElement containerElement,
|
||||
int curPos, string direction)
|
||||
{
|
||||
if (Trigger.BoundaryAdjustMode == TriggerBoundaryAdjustMode.None)
|
||||
{
|
||||
return curPos;
|
||||
}
|
||||
|
||||
int overlaySize = direction == "top" ? overlay.ClientHeight : overlay.ClientWidth;
|
||||
int boundarySize = await GetWindowBoundarySize(direction, containerElement);
|
||||
|
||||
// 距离边界的长度或宽度/distance from top or left boundry
|
||||
int distanceFromBoundry = await GetOverlayDistanceFromBoundary(direction, curPos);
|
||||
|
||||
if (Trigger.Trigger.Contains(TriggerType.ContextMenu))
|
||||
{
|
||||
if (overlaySize + curPos > boundarySize)
|
||||
{
|
||||
curPos -= overlaySize;
|
||||
}
|
||||
else if (curPos < 0)
|
||||
{
|
||||
curPos += overlaySize;
|
||||
}
|
||||
|
||||
return curPos;
|
||||
}
|
||||
|
||||
int overlayPosWithSize = GetOverlayPosWithSize(curPos, overlaySize);
|
||||
|
||||
if ((overlayPosWithSize > boundarySize || overlayPosWithSize < 0)
|
||||
&& distanceFromBoundry >= overlaySize) // check if still outof boundary after reverse placement
|
||||
{
|
||||
// 翻转位置/reverse placement
|
||||
Trigger.ChangePlacementForShow(Trigger.Placement.GetReverseType());
|
||||
|
||||
int reversePos = direction switch
|
||||
{
|
||||
"top" => GetOverlayTop(trigger, overlay, containerElement),
|
||||
_ => GetOverlayLeft(trigger, overlay, containerElement)
|
||||
};
|
||||
|
||||
curPos = reversePos;
|
||||
}
|
||||
|
||||
return curPos < 0 ? 0 : curPos;
|
||||
}
|
||||
|
||||
private int GetOverlayPosWithSize(int overlayPos, int overlaySize)
|
||||
{
|
||||
PlacementDirection placementDirection = Trigger.Placement.GetDirection();
|
||||
|
||||
if (placementDirection.IsIn(PlacementDirection.Top, PlacementDirection.Left))
|
||||
{
|
||||
return overlayPos - overlaySize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return overlayPos + overlaySize;
|
||||
await UpdatePosition(overlayLeft, overlayTop);
|
||||
}
|
||||
_recurenceGuard = 0;
|
||||
}
|
||||
|
||||
private int GetOverlayBoundaryPos(int overlaySize, int boundarySize)
|
||||
/// <summary>
|
||||
/// Will probe a check predicate every given milliseconds until predicate is true or until
|
||||
/// runs out of number of probings.
|
||||
/// </summary>
|
||||
/// <param name="check">A predicate that will be run every time after waitTimeInMilisecondsPerProbing will pass.</param>
|
||||
/// <param name="probings">Maximum number of probings. After this number is reached, the method finishes.</param>
|
||||
/// <param name="waitTimeInMilisecondsPerProbing">How long to wait between each probing.</param>
|
||||
/// <returns>Task</returns>
|
||||
private async Task<bool> WaitFor(Func<bool> check, int probings = 100, int waitTimeInMilisecondsPerProbing = 10)
|
||||
{
|
||||
PlacementDirection placementDirection = Trigger.Placement.GetDirection();
|
||||
|
||||
if (placementDirection.IsIn(PlacementDirection.Top, PlacementDirection.Left))
|
||||
if (!check())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return boundarySize - overlaySize;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> GetOverlayDistanceFromBoundary(string direction, int overlayPos)
|
||||
{
|
||||
if (Trigger.BoundaryAdjustMode == TriggerBoundaryAdjustMode.InScroll)
|
||||
{
|
||||
return overlayPos;
|
||||
}
|
||||
|
||||
JsonElement scrollInfo = await JsInvokeAsync<JsonElement>(JSInteropConstants.GetScroll);
|
||||
int windowScrollX = (int)scrollInfo.GetProperty("x").GetDouble();
|
||||
int windowScrollY = (int)scrollInfo.GetProperty("y").GetDouble();
|
||||
|
||||
return direction switch
|
||||
{
|
||||
"top" => overlayPos - windowScrollY,
|
||||
_ => overlayPos - windowScrollX
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<int> GetWindowBoundarySize(string direction, HtmlElement containerElement)
|
||||
{
|
||||
if (Trigger.BoundaryAdjustMode == TriggerBoundaryAdjustMode.InScroll)
|
||||
{
|
||||
return direction switch
|
||||
for (int i = 0; i < probings; i++)
|
||||
{
|
||||
"top" => containerElement.ScrollHeight,
|
||||
_ => containerElement.ScrollWidth
|
||||
};
|
||||
await Task.Delay(waitTimeInMilisecondsPerProbing);
|
||||
if (check())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Window windowElement = await JsInvokeAsync<Window>(JSInteropConstants.GetWindow, Ref);
|
||||
JsonElement scrollInfo = await JsInvokeAsync<JsonElement>(JSInteropConstants.GetScroll);
|
||||
int windowScrollX = (int)scrollInfo.GetProperty("x").GetDouble();
|
||||
int windowScrollY = (int)scrollInfo.GetProperty("y").GetDouble();
|
||||
|
||||
return direction switch
|
||||
{
|
||||
"top" => (int)windowElement.innerHeight + windowScrollY,
|
||||
_ => (int)windowElement.innerWidth + windowScrollX
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetTransformOrigin()
|
||||
{
|
||||
return $"transform-origin: {Trigger.Placement.TranformOrigin}";
|
||||
return $"transform-origin: {Trigger.GetPlacementType().TranformOrigin}";
|
||||
}
|
||||
|
||||
private bool IsContainTrigger(TriggerType triggerType)
|
||||
{
|
||||
foreach (TriggerType trigger in Trigger.Trigger)
|
||||
foreach (TriggerType trigger in Trigger.GetTriggerType())
|
||||
{
|
||||
if (trigger == triggerType)
|
||||
{
|
||||
@ -630,44 +430,25 @@ namespace AntDesign.Internal
|
||||
if (_hasAddOverlayToBody)
|
||||
return "visibility: hidden;";
|
||||
|
||||
|
||||
return "display: inline-flex; visibility: hidden;";
|
||||
}
|
||||
|
||||
internal async Task UpdatePosition(int? overlayLeft = null, int? overlayTop = null)
|
||||
{
|
||||
HtmlElement trigger;
|
||||
if (Trigger.ChildContent != null)
|
||||
bool triggerIsWrappedInDiv = Trigger.Unbound is null;
|
||||
|
||||
_position = await JsInvokeAsync<OverlayPosition>(JSInteropConstants.OverlayComponentHelper.UpdateOverlayPosition,
|
||||
Ref.Id, Ref, Trigger.Ref, Trigger.Placement, Trigger.PopupContainerSelector,
|
||||
Trigger.BoundaryAdjustMode, triggerIsWrappedInDiv, Trigger.PrefixCls,
|
||||
VerticalOffset, HorizontalOffset, ArrowPointAtCenter, overlayTop, overlayLeft);
|
||||
if (_position is not null)
|
||||
{
|
||||
trigger = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetFirstChildDomInfo, Trigger.Ref);
|
||||
if (_position.Placement != Trigger.Placement)
|
||||
{
|
||||
Trigger.ChangePlacementForShow(PlacementType.Create(_position.Placement));
|
||||
}
|
||||
_overlayStyle = _position.PositionCss + GetTransformOrigin();
|
||||
}
|
||||
else //(Trigger.Unbound != null)
|
||||
{
|
||||
trigger = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, Trigger.Ref);
|
||||
}
|
||||
|
||||
_overlayLeft = overlayLeft;
|
||||
_overlayTop = overlayTop;
|
||||
|
||||
HtmlElement overlayElement = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, Ref);
|
||||
HtmlElement containerElement = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, Trigger.PopupContainerSelector);
|
||||
|
||||
int left = await GetOverlayLeftWithBoundaryAdjust(trigger, overlayElement, containerElement);
|
||||
int top = await GetOverlayTopWithBoundaryAdjust(trigger, overlayElement, containerElement);
|
||||
|
||||
int zIndex = await JsInvokeAsync<int>(JSInteropConstants.GetMaxZIndex);
|
||||
|
||||
if (Trigger.Placement.IsIn(PlacementType.BottomRight, PlacementType.TopRight))
|
||||
{
|
||||
var right = ChangeOverlayLeftToRight(left, overlayElement, containerElement);
|
||||
_overlayStyle = $"z-index:{zIndex};left: unset;right: {right}px;top: {top}px;{GetTransformOrigin()}";
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayStyle = $"z-index:{zIndex};left: {left}px;top: {top}px;{GetTransformOrigin()}";
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private int ChangeOverlayLeftToRight(int left, HtmlElement overlay, HtmlElement container)
|
||||
|
@ -34,5 +34,5 @@
|
||||
OnHide="OnOverlayHide"
|
||||
OnOverlayMouseEnter="OnOverlayMouseEnter"
|
||||
OnOverlayMouseLeave="OnOverlayMouseLeave"
|
||||
OnOverlayMouseUp="OnOverlayMouseUp" />
|
||||
OnOverlayMouseUp="OnOverlayMouseUp" />
|
||||
</CascadingValue>
|
@ -31,7 +31,8 @@ namespace AntDesign.Internal
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overlay adjustment strategy (when for example browser resize is happening)
|
||||
/// Overlay adjustment strategy (when for example browser resize is happening). Check
|
||||
/// enum for details.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public TriggerBoundaryAdjustMode BoundaryAdjustMode { get; set; } = TriggerBoundaryAdjustMode.InView;
|
||||
@ -90,12 +91,14 @@ namespace AntDesign.Internal
|
||||
/// <summary>
|
||||
/// Callback when mouse enters trigger boundaries.
|
||||
/// </summary>
|
||||
[Parameter] public Action OnMouseEnter { get; set; }
|
||||
[Parameter]
|
||||
public EventCallback OnMouseEnter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback when mouse leaves trigger boundaries.
|
||||
/// </summary>
|
||||
[Parameter] public Action OnMouseLeave { get; set; }
|
||||
[Parameter]
|
||||
public EventCallback OnMouseLeave { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback when overlay is hiding.
|
||||
@ -158,19 +161,21 @@ namespace AntDesign.Internal
|
||||
private PlacementType _paramPlacement = PlacementType.BottomLeft;
|
||||
|
||||
[Parameter]
|
||||
public PlacementType Placement
|
||||
public Placement Placement
|
||||
{
|
||||
get
|
||||
{
|
||||
return RTL ? _placement.GetRTLPlacement() : _placement;
|
||||
return (RTL ? _placement.GetRTLPlacement() : _placement).Placement;
|
||||
}
|
||||
set
|
||||
{
|
||||
_placement = value;
|
||||
_paramPlacement = value;
|
||||
_placement = PlacementType.Create(value);
|
||||
_paramPlacement = PlacementType.Create(value);
|
||||
}
|
||||
}
|
||||
|
||||
internal PlacementType GetPlacementType() => _placement;
|
||||
|
||||
/// <summary>
|
||||
/// Override default placement class which is based on `Placement` parameter.
|
||||
/// </summary>
|
||||
@ -189,7 +194,14 @@ namespace AntDesign.Internal
|
||||
/// Trigger mode. Could be multiple by passing an array.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public TriggerType[] Trigger { get; set; } = new TriggerType[] { TriggerType.Hover };
|
||||
public Trigger[] Trigger //TODO: this should probably be a flag not an array
|
||||
{
|
||||
get { return _trigger.Select(t => t.Trigger).ToArray(); }
|
||||
set
|
||||
{
|
||||
_trigger = value.Select(t => TriggerType.Create(t)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public string TriggerCls { get; set; }
|
||||
@ -204,6 +216,8 @@ namespace AntDesign.Internal
|
||||
set => Ref = value;
|
||||
}
|
||||
|
||||
internal TriggerType[] GetTriggerType() => _trigger;
|
||||
|
||||
/// <summary>
|
||||
/// ChildElement with ElementReference set to avoid wrapping div.
|
||||
/// </summary>
|
||||
@ -217,21 +231,31 @@ namespace AntDesign.Internal
|
||||
public bool Visible { get; set; } = false;
|
||||
|
||||
[Inject]
|
||||
protected DomEventService DomEventService { get; set; }
|
||||
protected IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
private bool _mouseInTrigger = false;
|
||||
private bool _mouseInOverlay = false;
|
||||
private bool _mouseUpInOverlay = false;
|
||||
|
||||
protected Overlay _overlay = null;
|
||||
private TriggerType[] _trigger = new TriggerType[] { TriggerType.Hover };
|
||||
private bool _shouldRender = true;
|
||||
|
||||
internal void SetShouldRender(bool shouldRender) => _shouldRender = shouldRender;
|
||||
|
||||
protected override bool ShouldRender()
|
||||
{
|
||||
if (_shouldRender)
|
||||
return base.ShouldRender();
|
||||
_shouldRender = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
DomEventService.AddEventListener("document", "mouseup", OnMouseUp, false);
|
||||
DomEventService.AddEventListener("window", "resize", OnWindowResize, false);
|
||||
DomEventService.AddEventListener("document", "scroll", OnWindowScroll, false);
|
||||
DomEventListener.AddShared<JsonElement>("document", "mouseup", OnMouseUp);
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
@ -245,15 +269,14 @@ namespace AntDesign.Internal
|
||||
{
|
||||
Ref = RefBack.Current;
|
||||
|
||||
DomEventService.AddEventListener(Ref, "click", OnUnboundClick, true);
|
||||
DomEventService.AddEventListener(Ref, "mouseover", OnUnboundMouseEnter, true);
|
||||
DomEventService.AddEventListener(Ref, "mouseout", OnUnboundMouseLeave, true);
|
||||
DomEventService.AddEventListener(Ref, "focusin", OnUnboundFocusIn, true);
|
||||
DomEventService.AddEventListener(Ref, "focusout", OnUnboundFocusOut, true);
|
||||
DomEventService.AddEventListener(Ref, "contextmenu", OnContextMenu, true, true);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "click", OnUnboundClick);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "mouseover", OnUnboundMouseEnter);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "mouseout", OnUnboundMouseLeave);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "focusin", OnUnboundFocusIn);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "focusout", OnUnboundFocusOut);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "contextmenu", OnContextMenu, true);
|
||||
}
|
||||
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(TriggerCls))
|
||||
{
|
||||
if (Unbound != null)
|
||||
@ -295,19 +318,8 @@ namespace AntDesign.Internal
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>("document", "mouseup", OnMouseUp);
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "resize", OnWindowResize);
|
||||
DomEventService.RemoveEventListerner<JsonElement>("document", "scroll", OnWindowScroll);
|
||||
DomEventListener.Dispose();
|
||||
|
||||
if (Unbound != null)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "click", OnUnboundClick);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "mouseover", OnUnboundMouseEnter);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "mouseout", OnUnboundMouseLeave);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "focusin", OnUnboundFocusIn);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "focusout", OnUnboundFocusOut);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "contextmenu", OnContextMenu);
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@ -321,8 +333,13 @@ namespace AntDesign.Internal
|
||||
|
||||
await Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
_shouldRender = false;
|
||||
}
|
||||
|
||||
OnMouseEnter?.Invoke();
|
||||
if (OnMouseEnter.HasDelegate)
|
||||
OnMouseEnter.InvokeAsync(null);
|
||||
}
|
||||
|
||||
protected virtual async Task OnTriggerMouseLeave()
|
||||
@ -335,8 +352,13 @@ namespace AntDesign.Internal
|
||||
|
||||
await Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
_shouldRender = false;
|
||||
}
|
||||
|
||||
OnMouseLeave?.Invoke();
|
||||
if (OnMouseLeave.HasDelegate)
|
||||
OnMouseLeave.InvokeAsync(null);
|
||||
}
|
||||
|
||||
protected virtual async Task OnTriggerFocusIn()
|
||||
@ -432,8 +454,8 @@ namespace AntDesign.Internal
|
||||
{
|
||||
int offsetX = 10;
|
||||
int offsetY = 10;
|
||||
#if NET5_0
|
||||
// offsetX/offsetY were only supported in Net5
|
||||
#if NET5_0_OR_GREATER
|
||||
// offsetX/offsetY were only supported in Net5 or grater
|
||||
offsetX = (int)args.OffsetX;
|
||||
offsetY = (int)args.OffsetY;
|
||||
#endif
|
||||
@ -462,24 +484,10 @@ namespace AntDesign.Internal
|
||||
}
|
||||
}
|
||||
|
||||
protected async void OnWindowResize(JsonElement element)
|
||||
{
|
||||
RestorePlacement();
|
||||
|
||||
if (IsOverlayShow())
|
||||
{
|
||||
await GetOverlayComponent().UpdatePosition();
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnWindowScroll(JsonElement element)
|
||||
{
|
||||
RestorePlacement();
|
||||
}
|
||||
|
||||
protected virtual bool IsContainTrigger(TriggerType triggerType)
|
||||
{
|
||||
return Trigger.Contains(triggerType);
|
||||
return _trigger.Contains(triggerType);
|
||||
}
|
||||
|
||||
protected virtual async Task OverlayVisibleChange(bool visible)
|
||||
@ -505,18 +513,13 @@ namespace AntDesign.Internal
|
||||
_placement = placement;
|
||||
}
|
||||
|
||||
internal void RestorePlacement()
|
||||
{
|
||||
_placement = _paramPlacement;
|
||||
}
|
||||
|
||||
internal virtual string GetPlacementClass()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PlacementCls))
|
||||
{
|
||||
return PlacementCls;
|
||||
}
|
||||
return $"{PrefixCls}-placement-{Placement.Name}";
|
||||
return $"{PrefixCls}-placement-{_placement.Name}";
|
||||
}
|
||||
|
||||
internal virtual string GetOverlayEnterClass()
|
||||
@ -525,7 +528,7 @@ namespace AntDesign.Internal
|
||||
{
|
||||
return OverlayEnterCls;
|
||||
}
|
||||
return $"ant-slide-{Placement.SlideName}-enter ant-slide-{Placement.SlideName}-enter-active ant-slide-{Placement.SlideName}";
|
||||
return $"ant-slide-{_placement.SlideName}-enter ant-slide-{_placement.SlideName}-enter-active ant-slide-{_placement.SlideName}";
|
||||
}
|
||||
|
||||
internal virtual string GetOverlayLeaveClass()
|
||||
@ -534,7 +537,7 @@ namespace AntDesign.Internal
|
||||
{
|
||||
return OverlayLeaveCls;
|
||||
}
|
||||
return $"ant-slide-{Placement.SlideName}-leave ant-slide-{Placement.SlideName}-leave-active ant-slide-{Placement.SlideName}";
|
||||
return $"ant-slide-{_placement.SlideName}-leave ant-slide-{_placement.SlideName}-leave-active ant-slide-{_placement.SlideName}";
|
||||
}
|
||||
|
||||
internal virtual string GetOverlayHiddenClass()
|
||||
|
@ -1,32 +1,72 @@
|
||||
namespace AntDesign
|
||||
{
|
||||
public enum Placement
|
||||
{
|
||||
TopLeft,
|
||||
TopCenter,
|
||||
Top,
|
||||
TopRight,
|
||||
Left,
|
||||
LeftTop,
|
||||
LeftBottom,
|
||||
Right,
|
||||
RightTop,
|
||||
RightBottom,
|
||||
BottomLeft,
|
||||
BottomCenter,
|
||||
Bottom,
|
||||
BottomRight
|
||||
}
|
||||
|
||||
public sealed class PlacementType : EnumValue<PlacementType>
|
||||
{
|
||||
public static readonly PlacementType TopLeft = new PlacementType("topLeft", "down", "33% 100%", 0);
|
||||
public static readonly PlacementType TopCenter = new PlacementType("topCenter", "down", "50% 100%", 1);
|
||||
public static readonly PlacementType Top = new PlacementType("top", "down", "50% 100%", 1);
|
||||
public static readonly PlacementType TopRight = new PlacementType("topRight", "down", "66% 100%", 2);
|
||||
public static readonly PlacementType TopLeft = new PlacementType("topLeft", "down", "33% 100%", 0, Placement.TopLeft);
|
||||
public static readonly PlacementType TopCenter = new PlacementType("topCenter", "down", "50% 100%", 1, Placement.TopCenter);
|
||||
public static readonly PlacementType Top = new PlacementType("top", "down", "50% 100%", 1, Placement.Top);
|
||||
public static readonly PlacementType TopRight = new PlacementType("topRight", "down", "66% 100%", 2, Placement.TopRight);
|
||||
|
||||
public static readonly PlacementType Left = new PlacementType("left", "up", "100% 50%%", 3);
|
||||
public static readonly PlacementType LeftTop = new PlacementType("leftTop", "down", "100% 33%", 4);
|
||||
public static readonly PlacementType LeftBottom = new PlacementType("leftBottom", "up", "100% 66%", 5);
|
||||
public static readonly PlacementType Left = new PlacementType("left", "up", "100% 50%%", 3, Placement.Left);
|
||||
public static readonly PlacementType LeftTop = new PlacementType("leftTop", "down", "100% 33%", 4, Placement.LeftTop);
|
||||
public static readonly PlacementType LeftBottom = new PlacementType("leftBottom", "up", "100% 66%", 5, Placement.LeftBottom);
|
||||
|
||||
public static readonly PlacementType Right = new PlacementType("right", "up", "0 50%", 6);
|
||||
public static readonly PlacementType RightTop = new PlacementType("rightTop", "down", "0 33%", 7);
|
||||
public static readonly PlacementType RightBottom = new PlacementType("rightBottom", "up", "0 66%", 8);
|
||||
public static readonly PlacementType Right = new PlacementType("right", "up", "0 50%", 6, Placement.Right);
|
||||
public static readonly PlacementType RightTop = new PlacementType("rightTop", "down", "0 33%", 7, Placement.RightTop);
|
||||
public static readonly PlacementType RightBottom = new PlacementType("rightBottom", "up", "0 66%", 8, Placement.RightBottom);
|
||||
|
||||
public static readonly PlacementType BottomLeft = new PlacementType("bottomLeft", "up", "33% 0", 9);
|
||||
public static readonly PlacementType BottomCenter = new PlacementType("bottomCenter", "up", "50% 0", 10);
|
||||
public static readonly PlacementType Bottom = new PlacementType("bottom", "up", "50% 0", 10);
|
||||
public static readonly PlacementType BottomRight = new PlacementType("bottomRight", "up", "66% 0", 11);
|
||||
public static readonly PlacementType BottomLeft = new PlacementType("bottomLeft", "up", "33% 0", 9, Placement.BottomLeft);
|
||||
public static readonly PlacementType BottomCenter = new PlacementType("bottomCenter", "up", "50% 0", 10, Placement.BottomCenter);
|
||||
public static readonly PlacementType Bottom = new PlacementType("bottom", "up", "50% 0", 10, Placement.Bottom);
|
||||
public static readonly PlacementType BottomRight = new PlacementType("bottomRight", "up", "66% 0", 11, Placement.BottomRight);
|
||||
|
||||
public static PlacementType Create(Placement placement) => placement switch
|
||||
{
|
||||
Placement.TopLeft => PlacementType.TopLeft,
|
||||
Placement.TopCenter => PlacementType.TopCenter,
|
||||
Placement.Top => PlacementType.Top,
|
||||
Placement.TopRight => PlacementType.TopRight,
|
||||
Placement.Left => PlacementType.Left,
|
||||
Placement.LeftTop => PlacementType.LeftTop,
|
||||
Placement.LeftBottom => PlacementType.LeftBottom,
|
||||
Placement.Right => PlacementType.Right,
|
||||
Placement.RightTop => PlacementType.RightTop,
|
||||
Placement.RightBottom => PlacementType.RightBottom,
|
||||
Placement.BottomLeft => PlacementType.BottomLeft,
|
||||
Placement.BottomCenter => PlacementType.BottomCenter,
|
||||
Placement.Bottom => PlacementType.Bottom,
|
||||
Placement.BottomRight => PlacementType.BottomRight,
|
||||
_ => PlacementType.BottomLeft
|
||||
};
|
||||
|
||||
public string SlideName { get; private set; }
|
||||
public string TranformOrigin { get; private set; }
|
||||
|
||||
public PlacementType(string name, string slideName, string transformOrigin, int value) : base(name, value)
|
||||
public Placement Placement { get; private set; } = Placement.BottomLeft;
|
||||
|
||||
private PlacementType(string name, string slideName, string transformOrigin, int value, Placement placement) : base(name, value)
|
||||
{
|
||||
SlideName = slideName;
|
||||
TranformOrigin = transformOrigin;
|
||||
Placement = placement;
|
||||
}
|
||||
|
||||
internal PlacementType GetReverseType()
|
||||
|
@ -9,12 +9,18 @@
|
||||
None,
|
||||
/// <summary>
|
||||
/// 在可视范围内(默认模式)
|
||||
/// in view(default mode)
|
||||
/// The default, the viewport boundaries are the boundaries that are used for calculation if overlay
|
||||
/// is fully visible.
|
||||
/// Attempt to fit the overlay so it is always fully visible in the viewport.
|
||||
/// So if the overlay is outside of the viewport ("overflows"), then reposition calculation is going
|
||||
/// to be attempted.
|
||||
/// </summary>
|
||||
InView,
|
||||
/// <summary>
|
||||
/// 在滚动范围内
|
||||
/// in scroll
|
||||
/// The document boundaries are the boundaries used for calculation if overlay needs to be reposition.
|
||||
/// So even if the overlay is outside of the viewport, the overlay may still be shown as long as it
|
||||
/// does not "overflow" the document boundaries.
|
||||
/// </summary>
|
||||
InScroll,
|
||||
}
|
||||
|
@ -1,14 +1,41 @@
|
||||
namespace AntDesign
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace AntDesign
|
||||
{
|
||||
[Flags]
|
||||
public enum Trigger
|
||||
{
|
||||
Click = 1 << 0,
|
||||
Hover = 1 << 1,
|
||||
Focus = 1 << 2,
|
||||
ContextMenu = 1 << 3
|
||||
}
|
||||
|
||||
public sealed class TriggerType : EnumValue<TriggerType>
|
||||
{
|
||||
public static readonly TriggerType Click = new TriggerType(nameof(Click), 0);
|
||||
public static readonly TriggerType Hover = new TriggerType(nameof(Hover), 1);
|
||||
public static readonly TriggerType Focus = new TriggerType(nameof(Focus), 2);
|
||||
public static readonly TriggerType ContextMenu = new TriggerType(nameof(ContextMenu), 3);
|
||||
public static readonly TriggerType Click = new TriggerType(nameof(Click), 0, Trigger.Click);
|
||||
public static readonly TriggerType Hover = new TriggerType(nameof(Hover), 1, Trigger.Hover);
|
||||
public static readonly TriggerType Focus = new TriggerType(nameof(Focus), 2, Trigger.Focus);
|
||||
public static readonly TriggerType ContextMenu = new TriggerType(nameof(ContextMenu), 3, Trigger.ContextMenu);
|
||||
|
||||
private TriggerType(string name, int value) : base(name, value)
|
||||
public static TriggerType Create(Trigger trigger)
|
||||
{
|
||||
return trigger switch
|
||||
{
|
||||
Trigger.Click => Click,
|
||||
Trigger.Hover => Hover,
|
||||
Trigger.Focus => Focus,
|
||||
Trigger.ContextMenu => ContextMenu,
|
||||
_ => throw new InvalidEnumArgumentException($"Unrecognized value of Trigger enum ({trigger}).")
|
||||
};
|
||||
}
|
||||
|
||||
private TriggerType(string name, int value, Trigger trigger) : base(name, value)
|
||||
{
|
||||
Trigger = trigger;
|
||||
}
|
||||
|
||||
public Trigger Trigger { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using AntDesign;
|
||||
using AntDesign.JsInterop;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
@ -12,6 +13,12 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
public static IServiceCollection AddAntDesign(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddScoped<DomEventService>();
|
||||
services.TryAddTransient<IDomEventListener>((sp) =>
|
||||
{
|
||||
var domEventService = sp.GetRequiredService<DomEventService>();
|
||||
return domEventService.CreateDomEventListerner();
|
||||
});
|
||||
|
||||
services.TryAddScoped(sp => new HtmlRenderService(new HtmlRenderer(sp, sp.GetRequiredService<ILoggerFactory>(),
|
||||
s => HtmlEncoder.Default.Encode(s)))
|
||||
);
|
||||
|
255
components/core/JsInterop/DomEventListener.cs
Normal file
255
components/core/JsInterop/DomEventListener.cs
Normal file
@ -0,0 +1,255 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.Core.Extensions;
|
||||
using AntDesign.Core.JsInterop.ObservableApi;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace AntDesign.JsInterop
|
||||
{
|
||||
public class DomEventListener : IDomEventListener
|
||||
{
|
||||
private Dictionary<string, IDisposable> _dotNetObjectStore = new();
|
||||
private bool? _isResizeObserverSupported = null;
|
||||
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
private readonly DomEventSubscriptionStore _domEventSubscriptionsStore;
|
||||
private readonly string _id;
|
||||
|
||||
public DomEventListener(IJSRuntime jsRuntime, DomEventSubscriptionStore domEventSubscriptionStore)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
_domEventSubscriptionsStore = domEventSubscriptionStore;
|
||||
_id = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
private string FormatKey(object dom, string eventName)
|
||||
{
|
||||
var selector = dom is ElementReference eleRef ? eleRef.Id : dom.ToString();
|
||||
if (selector.IsIn("window", "document"))
|
||||
{
|
||||
return $"DEL-{selector}-{eventName}";
|
||||
}
|
||||
return $"DEL-{_id}-{selector}-{eventName}";
|
||||
}
|
||||
|
||||
public void AddExclusive<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false)
|
||||
{
|
||||
var key = FormatKey(dom, eventName);
|
||||
if (_dotNetObjectStore.ContainsKey(key))
|
||||
return;
|
||||
|
||||
var dotNetObject = DotNetObjectReference.Create(new Invoker<T>((p) =>
|
||||
{
|
||||
callback(p);
|
||||
}));
|
||||
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, dotNetObject);
|
||||
_dotNetObjectStore.Add(key, dotNetObject);
|
||||
}
|
||||
|
||||
public void RemoveExclusive(object dom, string eventName)
|
||||
{
|
||||
var key = FormatKey(dom, eventName);
|
||||
if (_dotNetObjectStore.TryGetValue(key, out IDisposable value))
|
||||
{
|
||||
value.Dispose();
|
||||
}
|
||||
_dotNetObjectStore.Remove(key);
|
||||
}
|
||||
|
||||
public void DisposeExclusive()
|
||||
{
|
||||
foreach (var (k, v) in _dotNetObjectStore)
|
||||
{
|
||||
v.Dispose();
|
||||
}
|
||||
_dotNetObjectStore.Clear();
|
||||
}
|
||||
|
||||
#region SharedEventListerner
|
||||
|
||||
public virtual void AddShared<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false)
|
||||
{
|
||||
string key = FormatKey(dom, eventName);
|
||||
if (!_domEventSubscriptionsStore.ContainsKey(key))
|
||||
{
|
||||
_domEventSubscriptionsStore[key] = new List<DomEventSubscription>();
|
||||
|
||||
var dotNetObject = DotNetObjectReference.Create(new Invoker<string>((p) =>
|
||||
{
|
||||
for (var i = 0; i < _domEventSubscriptionsStore[key].Count; i++)
|
||||
{
|
||||
var subscription = _domEventSubscriptionsStore[key][i];
|
||||
object tP = JsonSerializer.Deserialize(p, subscription.Type);
|
||||
subscription.Delegate.DynamicInvoke(tP);
|
||||
}
|
||||
}));
|
||||
|
||||
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, dotNetObject);
|
||||
}
|
||||
_domEventSubscriptionsStore[key].Add(new DomEventSubscription(callback, typeof(T), _id));
|
||||
}
|
||||
|
||||
public void RemoveShared<T>(object dom, string eventName, Action<T> callback)
|
||||
{
|
||||
string key = FormatKey(dom, eventName);
|
||||
if (_domEventSubscriptionsStore.ContainsKey(key))
|
||||
{
|
||||
var subscription = _domEventSubscriptionsStore[key].SingleOrDefault(s => s.Delegate == (Delegate)callback);
|
||||
if (subscription != null && subscription.Id == _id)
|
||||
{
|
||||
_domEventSubscriptionsStore[key].Remove(subscription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DisposeShared()
|
||||
{
|
||||
bool find = true;
|
||||
|
||||
while (find)
|
||||
{
|
||||
var (key, subscription) = _domEventSubscriptionsStore.FindDomEventSubscription(_id);
|
||||
if (!string.IsNullOrEmpty(key) && subscription != null)
|
||||
{
|
||||
_domEventSubscriptionsStore[key].Remove(subscription);
|
||||
}
|
||||
else
|
||||
find = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ResizeObserver
|
||||
public async ValueTask AddResizeObserver(ElementReference dom, Action<List<ResizeObserverEntry>> callback)
|
||||
{
|
||||
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
|
||||
if (!(await IsResizeObserverSupported()))
|
||||
{
|
||||
Action<JsonElement> action = (je) => callback.Invoke(new List<ResizeObserverEntry> { new ResizeObserverEntry() });
|
||||
AddShared<JsonElement>("window", "resize", action, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_domEventSubscriptionsStore.ContainsKey(key))
|
||||
{
|
||||
_domEventSubscriptionsStore[key] = new List<DomEventSubscription>();
|
||||
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Create, key, DotNetObjectReference.Create(new Invoker<string>((p) =>
|
||||
{
|
||||
for (var i = 0; i < _domEventSubscriptionsStore[key].Count; i++)
|
||||
{
|
||||
var subscription = _domEventSubscriptionsStore[key][i];
|
||||
object tP = JsonSerializer.Deserialize(p, subscription.Type);
|
||||
subscription.Delegate.DynamicInvoke(tP);
|
||||
}
|
||||
})));
|
||||
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Observe, key, dom);
|
||||
}
|
||||
_domEventSubscriptionsStore[key].Add(new DomEventSubscription(callback, typeof(List<ResizeObserverEntry>), _id));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask RemoveResizeObserver(ElementReference dom, Action<List<ResizeObserverEntry>> callback)
|
||||
{
|
||||
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
|
||||
if (_domEventSubscriptionsStore.ContainsKey(key))
|
||||
{
|
||||
var subscription = _domEventSubscriptionsStore[key].SingleOrDefault(s => s.Delegate == (Delegate)callback);
|
||||
if (subscription != null)
|
||||
{
|
||||
_domEventSubscriptionsStore[key].Remove(subscription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeResizeObserver(ElementReference dom)
|
||||
{
|
||||
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
|
||||
if (await IsResizeObserverSupported())
|
||||
{
|
||||
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Dispose, key);
|
||||
}
|
||||
_domEventSubscriptionsStore.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
public async ValueTask DisconnectResizeObserver(ElementReference dom)
|
||||
{
|
||||
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
|
||||
if (await IsResizeObserverSupported())
|
||||
{
|
||||
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Disconnect, key);
|
||||
}
|
||||
if (_domEventSubscriptionsStore.ContainsKey(key))
|
||||
{
|
||||
_domEventSubscriptionsStore[key].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<bool> IsResizeObserverSupported() => _isResizeObserverSupported ??= await _jsRuntime.IsResizeObserverSupported();
|
||||
|
||||
#endregion
|
||||
|
||||
#region EventListenerToFirstChild
|
||||
|
||||
public void AddEventListenerToFirstChild(object dom, string eventName, Action<JsonElement> callback, bool preventDefault = false)
|
||||
{
|
||||
AddEventListenerToFirstChildInternal<string>(dom, eventName, preventDefault, (e) =>
|
||||
{
|
||||
JsonElement jsonElement = JsonDocument.Parse(e).RootElement;
|
||||
callback(jsonElement);
|
||||
});
|
||||
}
|
||||
|
||||
public void AddEventListenerToFirstChild<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false)
|
||||
{
|
||||
AddEventListenerToFirstChildInternal<string>(dom, eventName, preventDefault, (e) =>
|
||||
{
|
||||
T obj = JsonSerializer.Deserialize<T>(e);
|
||||
callback(obj);
|
||||
});
|
||||
}
|
||||
|
||||
private void AddEventListenerToFirstChildInternal<T>(object dom, string eventName, bool preventDefault, Action<T> callback)
|
||||
{
|
||||
var key = FormatKey(dom, eventName);
|
||||
if (!_dotNetObjectStore.ContainsKey(key))
|
||||
{
|
||||
var dotNetObject = DotNetObjectReference.Create(new Invoker<T>((p) =>
|
||||
{
|
||||
callback?.Invoke(p);
|
||||
}));
|
||||
|
||||
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListenerToFirstChild, dom, eventName, preventDefault, dotNetObject);
|
||||
_dotNetObjectStore.Add(key, dotNetObject);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeExclusive();
|
||||
DisposeShared();
|
||||
}
|
||||
}
|
||||
|
||||
public class Invoker<T>
|
||||
{
|
||||
private Action<T> _action;
|
||||
|
||||
public Invoker(Action<T> invoker)
|
||||
{
|
||||
_action = invoker;
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public void Invoke(T param)
|
||||
{
|
||||
_action.Invoke(param);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,187 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.Core.Extensions;
|
||||
using AntDesign.Core.JsInterop.ObservableApi;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace AntDesign.JsInterop
|
||||
{
|
||||
public class DomEventService
|
||||
{
|
||||
private ConcurrentDictionary<string, List<DomEventSubscription>> _domEventListeners = new ConcurrentDictionary<string, List<DomEventSubscription>>();
|
||||
private DomEventSubscriptionStore _domEventSubscriptionStore = new();
|
||||
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
private bool? _isResizeObserverSupported = null;
|
||||
|
||||
public DomEventService(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
private void AddEventListenerToFirstChildInternal<T>(object dom, string eventName, bool preventDefault, Action<T> callback)
|
||||
public virtual IDomEventListener CreateDomEventListerner()
|
||||
{
|
||||
if (!_domEventListeners.ContainsKey(FormatKey(dom, eventName)))
|
||||
{
|
||||
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListenerToFirstChild, dom, eventName, preventDefault, DotNetObjectReference.Create(new Invoker<T>((p) =>
|
||||
{
|
||||
callback?.Invoke(p);
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListener(object dom, string eventName, Action<JsonElement> callback, bool exclusive = true, bool preventDefault = false)
|
||||
{
|
||||
AddEventListener<JsonElement>(dom, eventName, callback, exclusive, preventDefault);
|
||||
}
|
||||
|
||||
public virtual void AddEventListener<T>(object dom, string eventName, Action<T> callback, bool exclusive = true, bool preventDefault = false)
|
||||
{
|
||||
if (exclusive)
|
||||
{
|
||||
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, DotNetObjectReference.Create(new Invoker<T>((p) =>
|
||||
{
|
||||
callback(p);
|
||||
})));
|
||||
}
|
||||
else
|
||||
{
|
||||
string key = FormatKey(dom, eventName);
|
||||
if (!_domEventListeners.ContainsKey(key))
|
||||
{
|
||||
_domEventListeners[key] = new List<DomEventSubscription>();
|
||||
|
||||
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, DotNetObjectReference.Create(new Invoker<string>((p) =>
|
||||
{
|
||||
for (var i = 0; i < _domEventListeners[key].Count; i++)
|
||||
{
|
||||
var subscription = _domEventListeners[key][i];
|
||||
object tP = JsonSerializer.Deserialize(p, subscription.Type);
|
||||
subscription.Delegate.DynamicInvoke(tP);
|
||||
}
|
||||
})));
|
||||
}
|
||||
_domEventListeners[key].Add(new DomEventSubscription(callback, typeof(T)));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEventListenerToFirstChild(object dom, string eventName, Action<JsonElement> callback, bool preventDefault = false)
|
||||
{
|
||||
AddEventListenerToFirstChildInternal<string>(dom, eventName, preventDefault, (e) =>
|
||||
{
|
||||
JsonElement jsonElement = JsonDocument.Parse(e).RootElement;
|
||||
callback(jsonElement);
|
||||
});
|
||||
}
|
||||
|
||||
public void AddEventListenerToFirstChild<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false)
|
||||
{
|
||||
AddEventListenerToFirstChildInternal<string>(dom, eventName, preventDefault, (e) =>
|
||||
{
|
||||
T obj = JsonSerializer.Deserialize<T>(e);
|
||||
callback(obj);
|
||||
});
|
||||
}
|
||||
|
||||
public async ValueTask AddResizeObserver(ElementReference dom, Action<List<ResizeObserverEntry>> callback)
|
||||
{
|
||||
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
|
||||
if (!(await IsResizeObserverSupported()))
|
||||
{
|
||||
Action<JsonElement> action = (je) => callback.Invoke(new List<ResizeObserverEntry> { new ResizeObserverEntry() });
|
||||
AddEventListener("window", "resize", action, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_domEventListeners.ContainsKey(key))
|
||||
{
|
||||
_domEventListeners[key] = new List<DomEventSubscription>();
|
||||
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Create, key, DotNetObjectReference.Create(new Invoker<string>((p) =>
|
||||
{
|
||||
for (var i = 0; i < _domEventListeners[key].Count; i++)
|
||||
{
|
||||
var subscription = _domEventListeners[key][i];
|
||||
object tP = JsonSerializer.Deserialize(p, subscription.Type);
|
||||
subscription.Delegate.DynamicInvoke(tP);
|
||||
}
|
||||
})));
|
||||
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Observe, key, dom);
|
||||
}
|
||||
_domEventListeners[key].Add(new DomEventSubscription(callback, typeof(List<ResizeObserverEntry>)));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask RemoveResizeObserver(ElementReference dom, Action<List<ResizeObserverEntry>> callback)
|
||||
{
|
||||
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
|
||||
if (_domEventListeners.ContainsKey(key))
|
||||
{
|
||||
var subscription = _domEventListeners[key].SingleOrDefault(s => s.Delegate == (Delegate)callback);
|
||||
if (subscription != null)
|
||||
{
|
||||
_domEventListeners[key].Remove(subscription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeResizeObserver(ElementReference dom)
|
||||
{
|
||||
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
|
||||
if (await IsResizeObserverSupported())
|
||||
{
|
||||
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Dispose, key);
|
||||
}
|
||||
_domEventListeners.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
public async ValueTask DisconnectResizeObserver(ElementReference dom)
|
||||
{
|
||||
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
|
||||
if (await IsResizeObserverSupported())
|
||||
{
|
||||
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Disconnect, key);
|
||||
}
|
||||
if (_domEventListeners.ContainsKey(key))
|
||||
{
|
||||
_domEventListeners[key].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatKey(object dom, string eventName) => $"{dom}-{eventName}";
|
||||
|
||||
public void RemoveEventListerner<T>(object dom, string eventName, Action<T> callback)
|
||||
{
|
||||
string key = FormatKey(dom, eventName);
|
||||
if (_domEventListeners.ContainsKey(key))
|
||||
{
|
||||
var subscription = _domEventListeners[key].SingleOrDefault(s => s.Delegate == (Delegate)callback);
|
||||
if (subscription != null)
|
||||
{
|
||||
_domEventListeners[key].Remove(subscription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<bool> IsResizeObserverSupported() => _isResizeObserverSupported ??= await _jsRuntime.IsResizeObserverSupported();
|
||||
}
|
||||
|
||||
public class Invoker<T>
|
||||
{
|
||||
private Action<T> _action;
|
||||
|
||||
public Invoker(Action<T> invoker)
|
||||
{
|
||||
this._action = invoker;
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public void Invoke(T param)
|
||||
{
|
||||
_action.Invoke(param);
|
||||
return new DomEventListener(_jsRuntime, _domEventSubscriptionStore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AntDesign.JsInterop
|
||||
{
|
||||
internal class DomEventSubscription
|
||||
public class DomEventSubscriptionStore : ConcurrentDictionary<string, List<DomEventSubscription>>
|
||||
{
|
||||
public (string key, DomEventSubscription subscription) FindDomEventSubscription(string id)
|
||||
{
|
||||
string key = string.Empty;
|
||||
DomEventSubscription subscription = null;
|
||||
|
||||
foreach (var (k, subscriptionList) in this)
|
||||
{
|
||||
key = k;
|
||||
|
||||
foreach (var item in subscriptionList)
|
||||
{
|
||||
if (item.Id == id)
|
||||
{
|
||||
subscription = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (subscription != null)
|
||||
break;
|
||||
}
|
||||
return (key, subscription);
|
||||
}
|
||||
}
|
||||
|
||||
public class DomEventSubscription
|
||||
{
|
||||
internal Delegate Delegate { get; set; }
|
||||
internal Type Type { get; set; }
|
||||
internal string Id { get; set; }
|
||||
|
||||
public DomEventSubscription(Delegate @delegate, Type type)
|
||||
public DomEventSubscription(Delegate @delegate, Type type, string id)
|
||||
{
|
||||
Delegate = @delegate;
|
||||
Type = type;
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
components/core/JsInterop/IDomEventListener.cs
Normal file
26
components/core/JsInterop/IDomEventListener.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.Core.JsInterop.ObservableApi;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.JsInterop
|
||||
{
|
||||
public interface IDomEventListener
|
||||
{
|
||||
void AddEventListenerToFirstChild(object dom, string eventName, Action<JsonElement> callback, bool preventDefault = false);
|
||||
void AddEventListenerToFirstChild<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false);
|
||||
void AddExclusive<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false);
|
||||
ValueTask AddResizeObserver(ElementReference dom, Action<List<ResizeObserverEntry>> callback);
|
||||
void AddShared<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false);
|
||||
ValueTask DisconnectResizeObserver(ElementReference dom);
|
||||
void DisposeExclusive();
|
||||
ValueTask DisposeResizeObserver(ElementReference dom);
|
||||
void RemoveExclusive(object dom, string eventName);
|
||||
ValueTask RemoveResizeObserver(ElementReference dom, Action<List<ResizeObserverEntry>> callback);
|
||||
void RemoveShared<T>(object dom, string eventName, Action<T> callback);
|
||||
void DisposeShared();
|
||||
void Dispose();
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@ namespace AntDesign
|
||||
public static string GetScroll => DomInfoHelper.GetScroll;
|
||||
public static string HasFocus => DomInfoHelper.HasFocus;
|
||||
public static string GetInnerText => DomInfoHelper.GetInnerText;
|
||||
public static string GetMaxZIndex => DomInfoHelper.GetMaxZIndex;
|
||||
|
||||
#endregion
|
||||
|
||||
#region styleManipulation
|
||||
@ -87,12 +89,12 @@ namespace AntDesign
|
||||
#region overlay
|
||||
public static string AddPreventEnterOnOverlayVisible => OverlayComponentHelper.AddPreventEnterOnOverlayVisible;
|
||||
public static string RemovePreventEnterOnOverlayVisible => OverlayComponentHelper.RemovePreventEnterOnOverlayVisible;
|
||||
public static string GetMaxZIndex => OverlayComponentHelper.GetMaxZIndex;
|
||||
//public static string AddOverlayToContainer => OverlayComponentHelper.AddOverlayToContainer;
|
||||
#endregion
|
||||
|
||||
#region table
|
||||
public static string BindTableHeaderAndBodyScroll => TableComponentHelper.BindTableHeaderAndBodyScroll;
|
||||
public static string UnbindTableHeaderAndBodyScroll => TableComponentHelper.UnbindTableHeaderAndBodyScroll;
|
||||
public static string BindTableScroll => TableComponentHelper.BindTableScroll;
|
||||
public static string UnbindTableScroll => TableComponentHelper.UnbindTableScroll;
|
||||
#endregion
|
||||
|
||||
public static string DisposeObj => $"{FUNC_PREFIX}state.disposeObj";
|
||||
@ -109,6 +111,7 @@ namespace AntDesign
|
||||
public static string GetScroll => $"{FUNC_PREFIX}getScroll";
|
||||
public static string HasFocus => $"{FUNC_PREFIX}hasFocus";
|
||||
public static string GetInnerText => $"{FUNC_PREFIX}getInnerText";
|
||||
public static string GetMaxZIndex => $"{FUNC_PREFIX}getMaxZIndex";
|
||||
}
|
||||
|
||||
public static class EventHelper
|
||||
@ -184,6 +187,7 @@ namespace AntDesign
|
||||
{
|
||||
private const string FUNC_PREFIX = JSInteropConstants.FUNC_PREFIX + "inputHelper.";
|
||||
public static string RegisterResizeTextArea => $"{FUNC_PREFIX}registerResizeTextArea";
|
||||
public static string GetTextAreaInfo => $"{FUNC_PREFIX}getTextAreaInfo";
|
||||
public static string DisposeResizeTextArea => $"{FUNC_PREFIX}disposeResizeTextArea";
|
||||
public static string SetSelectionStart => $"{FUNC_PREFIX}setSelectionStart";
|
||||
}
|
||||
@ -210,14 +214,16 @@ namespace AntDesign
|
||||
private const string FUNC_PREFIX = JSInteropConstants.FUNC_PREFIX + "overlayHelper.";
|
||||
public static string AddPreventEnterOnOverlayVisible => $"{FUNC_PREFIX}addPreventEnterOnOverlayVisible";
|
||||
public static string RemovePreventEnterOnOverlayVisible => $"{FUNC_PREFIX}removePreventEnterOnOverlayVisible";
|
||||
public static string GetMaxZIndex => $"{FUNC_PREFIX}getMaxZIndex";
|
||||
public static string AddOverlayToContainer => $"{FUNC_PREFIX}addOverlayToContainer";
|
||||
public static string UpdateOverlayPosition => $"{FUNC_PREFIX}updateOverlayPosition";
|
||||
public static string DeleteOverlayFromContainer => $"{FUNC_PREFIX}deleteOverlayFromContainer";
|
||||
}
|
||||
|
||||
public static class TableComponentHelper
|
||||
{
|
||||
private const string FUNC_PREFIX = JSInteropConstants.FUNC_PREFIX + "tableHelper.";
|
||||
public static string BindTableHeaderAndBodyScroll => $"{FUNC_PREFIX}bindTableHeaderAndBodyScroll";
|
||||
public static string UnbindTableHeaderAndBodyScroll => $"{FUNC_PREFIX}unbindTableHeaderAndBodyScroll";
|
||||
public static string BindTableScroll => $"{FUNC_PREFIX}bindTableScroll";
|
||||
public static string UnbindTableScroll => $"{FUNC_PREFIX}unbindTableScroll";
|
||||
}
|
||||
|
||||
public static class UploadComponentHelper
|
||||
|
@ -0,0 +1,45 @@
|
||||
import { infoHelper as domInfoHelper } from '../modules/dom/infoHelper';
|
||||
|
||||
export class intersectionObserver {
|
||||
// @ts-ignore: TS2304: Cannot find name 'IntersectionObserver'
|
||||
private static intersectionObservers: Map<string, IntersectionObserver> = new Map<string, IntersectionObserver>();
|
||||
|
||||
|
||||
static create(key: string, invoker, isDotNetInvoker: boolean = true) {
|
||||
// @ts-ignore: TS2304: Cannot find name 'IntersectionObserver'
|
||||
let observer;
|
||||
|
||||
if (isDotNetInvoker) {
|
||||
observer = new IntersectionObserver(mutations => intersectionObserver.observerCallback(mutations, invoker))
|
||||
} else {
|
||||
observer = new IntersectionObserver(mutations => invoker(mutations))
|
||||
}
|
||||
intersectionObserver.intersectionObservers.set(key, observer)
|
||||
}
|
||||
|
||||
static observe(key: string, element, options?: IntersectionObserverInit) {
|
||||
const observer = intersectionObserver.intersectionObservers.get(key);
|
||||
if (observer) {
|
||||
let domElement = domInfoHelper.get(element);
|
||||
observer.observe(domElement);
|
||||
}
|
||||
}
|
||||
|
||||
static disconnect(key: string): void {
|
||||
const observer = this.intersectionObservers.get(key)
|
||||
if (observer) {
|
||||
observer.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
static dispose(key: string): void {
|
||||
this.disconnect(key)
|
||||
this.intersectionObservers.delete(key)
|
||||
}
|
||||
|
||||
private static observerCallback(mutations, invoker) {
|
||||
//TODO: serialize a proper object (check resizeObserver.ts for sample)
|
||||
const entriesJson = JSON.stringify(mutations)
|
||||
invoker.invokeMethodAsync('Invoke', entriesJson)
|
||||
}
|
||||
}
|
45
components/core/JsInterop/ObservableApi/mutationObserver.ts
Normal file
45
components/core/JsInterop/ObservableApi/mutationObserver.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { infoHelper as domInfoHelper } from '../modules/dom/infoHelper';
|
||||
|
||||
export class mutationObserver {
|
||||
// @ts-ignore: TS2304: Cannot find name 'MutationObserver'
|
||||
private static mutationObservers: Map<string, MutationObserver> = new Map<string, MutationObserver>();
|
||||
|
||||
|
||||
static create(key: string, invoker, isDotNetInvoker: boolean = true) {
|
||||
// @ts-ignore: TS2304: Cannot find name 'MutationObserver'
|
||||
let observer;
|
||||
|
||||
if (isDotNetInvoker) {
|
||||
observer = new MutationObserver(mutations => mutationObserver.observerCallback(mutations, invoker))
|
||||
} else {
|
||||
observer = new MutationObserver(mutations => invoker(mutations))
|
||||
}
|
||||
mutationObserver.mutationObservers.set(key, observer)
|
||||
}
|
||||
|
||||
static observe(key: string, element, options?: MutationObserverInit) {
|
||||
const observer = mutationObserver.mutationObservers.get(key);
|
||||
if (observer) {
|
||||
let domElement = domInfoHelper.get(element);
|
||||
observer.observe(domElement, options);
|
||||
}
|
||||
}
|
||||
|
||||
static disconnect(key: string): void {
|
||||
const observer = this.mutationObservers.get(key)
|
||||
if (observer) {
|
||||
observer.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
static dispose(key: string): void {
|
||||
this.disconnect(key)
|
||||
this.mutationObservers.delete(key)
|
||||
}
|
||||
|
||||
private static observerCallback(mutations, invoker) {
|
||||
//TODO: serialize a proper object (check resizeObserver.ts for sample)
|
||||
const entriesJson = JSON.stringify(mutations)
|
||||
invoker.invokeMethodAsync('Invoke', entriesJson)
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
export { resizeObserver as resize } from './resizeObserver';
|
||||
export { resizeObserver as resize } from './resizeObserver';
|
||||
export { mutationObserver } from './mutationObserver';
|
@ -17,9 +17,15 @@ export class resizeObserver {
|
||||
// @ts-ignore: TS2304: Cannot find name 'ResizeObserver'
|
||||
private static resizeObservers: Map<string, ResizeObserver> = new Map<string, ResizeObserver>();
|
||||
|
||||
static create(key, invoker) {
|
||||
static create(key, invoker, isDotNetInvoker: boolean = true ) {
|
||||
// @ts-ignore: TS2304: Cannot find name 'ResizeObserver'
|
||||
const observer = new ResizeObserver((entries, observer) => resizeObserver.observerCallBack(entries, observer, invoker));
|
||||
let observer;
|
||||
|
||||
if (isDotNetInvoker) {
|
||||
observer = new ResizeObserver((entries, observer) => resizeObserver.observerCallBack(entries, observer, invoker));
|
||||
} else {
|
||||
observer = new ResizeObserver((entries, observer) => invoker(entries, observer));
|
||||
}
|
||||
resizeObserver.resizeObservers.set(key, observer)
|
||||
}
|
||||
|
||||
@ -48,17 +54,14 @@ export class resizeObserver {
|
||||
}
|
||||
|
||||
static dispose(key: string): void {
|
||||
console.log("dispose", key);
|
||||
this.disconnect(key)
|
||||
this.resizeObservers.delete(key)
|
||||
}
|
||||
|
||||
private static observerCallBack(entries, observer, invoker) {
|
||||
console.log("observerCallBack start", entries)
|
||||
if (invoker) {
|
||||
const mappedEntries = new Array<ResizeObserverEntry>()
|
||||
entries.forEach(entry => {
|
||||
console.log("observerCallBack entry", entry)
|
||||
entries.forEach(entry => {
|
||||
if (entry) {
|
||||
const mEntry = new ResizeObserverEntry()
|
||||
if (entry.borderBoxSize) {
|
||||
|
@ -1,13 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AntDesign.JsInterop
|
||||
{
|
||||
public class Window
|
||||
{
|
||||
public decimal innerHeight { get; set; }
|
||||
[JsonPropertyName("innerWidth")]
|
||||
public decimal InnerWidth { get; set; }
|
||||
|
||||
public decimal innerWidth { get; set; }
|
||||
[JsonPropertyName("innerHeight")]
|
||||
public decimal InnerHeight { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
namespace AntDesign.Core.JsInterop.Modules.Components
|
||||
{
|
||||
public class OverlayPosition
|
||||
{
|
||||
public decimal? Top { get; set; }
|
||||
public string TopCss => $"top: " + GetAsString(Top);
|
||||
public decimal? Bottom { get; set; }
|
||||
public string BottomCss => $"bottom: " + GetAsString(Bottom);
|
||||
public decimal? Left { get; set; }
|
||||
public string LeftCss => $"left: " + GetAsString(Left);
|
||||
public decimal? Right { get; set; }
|
||||
public string RightCss => $"right: " + GetAsString(Right);
|
||||
public string PositionCss => ZIndexCss + LeftCss + RightCss + TopCss + BottomCss;
|
||||
|
||||
public int ZIndex { get; set; }
|
||||
public string ZIndexCss => $"z-index:{ZIndex};";
|
||||
|
||||
public Placement Placement { get; set; }
|
||||
|
||||
private string GetAsString(decimal? value)
|
||||
{
|
||||
if (value is null)
|
||||
return "unset;";
|
||||
else
|
||||
return value.ToString() + "px;";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
import { domInfoHelper, eventHelper } from '../dom/exports'
|
||||
import { domInfoHelper } from '../dom/exports'
|
||||
import { state } from '../stateProvider';
|
||||
|
||||
|
||||
export class inputHelper {
|
||||
|
||||
private static getTextAreaInfo(element) {
|
||||
static getTextAreaInfo(element) {
|
||||
var result = {};
|
||||
var dom = domInfoHelper.get(element);
|
||||
if (!dom) return null;
|
||||
@ -32,7 +32,7 @@ export class inputHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
static registerResizeTextArea(element, minRows, maxRows, objReference) {
|
||||
static registerResizeTextArea(element: HTMLTextAreaElement, minRows: number, maxRows: number, objReference) {
|
||||
if (!objReference) {
|
||||
this.disposeResizeTextArea(element);
|
||||
}
|
||||
@ -44,22 +44,25 @@ export class inputHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static disposeResizeTextArea(element) {
|
||||
static disposeResizeTextArea(element: HTMLTextAreaElement) {
|
||||
element.removeEventListener("input", state.eventCallbackRegistry[element.id + "input"]);
|
||||
state.objReferenceDict[element.id] = null;
|
||||
state.eventCallbackRegistry[element.id + "input"] = null;
|
||||
}
|
||||
|
||||
static resizeTextArea(element, minRows, maxRows) {
|
||||
var dims = this.getTextAreaInfo(element);
|
||||
var rowHeight = dims["lineHeight"];
|
||||
var offsetHeight = dims["paddingTop"] + dims["paddingBottom"] + dims["borderTop"] + dims["borderBottom"];
|
||||
var oldHeight = parseFloat(element.style.height);
|
||||
element.style.height = 'auto';
|
||||
|
||||
static resizeTextArea(element: HTMLTextAreaElement, minRows: number, maxRows: number) {
|
||||
let dims = this.getTextAreaInfo(element);
|
||||
let rowHeight = dims["lineHeight"];
|
||||
let offsetHeight = dims["paddingTop"] + dims["paddingBottom"] + dims["borderTop"] + dims["borderBottom"];
|
||||
let oldHeight = parseFloat(element.style.height);
|
||||
//use rows attribute to evaluate real scroll height
|
||||
let oldRows = element.rows;
|
||||
element.rows = minRows;
|
||||
element.style.height = 'auto';
|
||||
|
||||
var rows = Math.trunc(element.scrollHeight / rowHeight);
|
||||
rows = Math.max(minRows, rows);
|
||||
|
||||
element.rows = oldRows;
|
||||
rows = Math.max(minRows, rows);
|
||||
var newHeight = 0;
|
||||
if (rows > maxRows) {
|
||||
rows = maxRows;
|
||||
@ -75,7 +78,7 @@ export class inputHelper {
|
||||
}
|
||||
if (oldHeight !== newHeight) {
|
||||
let textAreaObj = state.objReferenceDict[element.id];
|
||||
textAreaObj.invokeMethodAsync("ChangeSizeAsyncJs", parseFloat(element.scrollWidth), newHeight);
|
||||
textAreaObj.invokeMethodAsync("ChangeSizeAsyncJs", element.scrollWidth, newHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
1119
components/core/JsInterop/modules/components/overlay.ts
Normal file
1119
components/core/JsInterop/modules/components/overlay.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,77 @@
|
||||
import { domInfoHelper, eventHelper } from '../dom/exports'
|
||||
import { domInfoHelper, eventHelper, domManipulationHelper, domTypes } from '../dom/exports'
|
||||
import { Placement, TriggerBoundyAdjustMode, overlayConstraints, overlayPosition, Overlay } from './overlay'
|
||||
import { state } from '../stateProvider';
|
||||
|
||||
export class overlayHelper {
|
||||
static overlayRegistry: { [key: string]: Overlay} = {};
|
||||
|
||||
static addOverlayToContainer(blazorId: string,
|
||||
overlaySelector, triggerSelector, placement: Placement, containerSelector: string,
|
||||
triggerBoundyAdjustMode: TriggerBoundyAdjustMode, triggerIsWrappedInDiv: boolean, triggerPrefixCls: string,
|
||||
verticalOffset: number, horizontalOffset: number, arrowPointAtCenter: boolean,
|
||||
overlayTop?: number, overlayLeft?: number
|
||||
): overlayPosition {
|
||||
const overlayElement = domInfoHelper.get(overlaySelector) as HTMLDivElement;
|
||||
const containerElement = domInfoHelper.get(containerSelector) as HTMLElement;
|
||||
const triggerElement = domInfoHelper.get(triggerSelector) as HTMLElement;
|
||||
|
||||
if (!domManipulationHelper.addElementTo(overlaySelector, containerElement)) {
|
||||
console.log("Failed to add overlay. Details:", {
|
||||
triggerPrefixCls: triggerPrefixCls,
|
||||
overlaySelector: overlaySelector,
|
||||
containerElement: containerElement
|
||||
} );
|
||||
return null;
|
||||
}
|
||||
|
||||
let overlayPresets: domTypes.position;
|
||||
if (overlayTop || overlayLeft) {
|
||||
overlayPresets = { x: overlayLeft, y: overlayTop };
|
||||
}
|
||||
|
||||
let overlayConstraints: overlayConstraints = {
|
||||
verticalOffset: verticalOffset,
|
||||
horizontalOffset: horizontalOffset,
|
||||
arrowPointAtCenter: arrowPointAtCenter
|
||||
};
|
||||
|
||||
let overlay = new Overlay(blazorId, overlayElement, containerElement, triggerElement, placement, triggerBoundyAdjustMode, triggerIsWrappedInDiv, triggerPrefixCls, overlayConstraints);
|
||||
//register object in store, so it can be retrieved during update/dispose
|
||||
this.overlayRegistry[blazorId] = overlay;
|
||||
|
||||
return overlay.calculatePosition(false, true, overlayPresets);
|
||||
}
|
||||
|
||||
|
||||
static updateOverlayPosition(blazorId: string, overlaySelector, triggerSelector, placement: Placement, containerSelector: string,
|
||||
triggerBoundyAdjustMode: TriggerBoundyAdjustMode, triggerIsWrappedInDiv: boolean, triggerPrefixCls: string,
|
||||
verticalOffset: number, horizontalOffset: number, arrowPointAtCenter: boolean,
|
||||
overlayTop?: number, overlayLeft?: number): overlayPosition {
|
||||
const overlay = this.overlayRegistry[blazorId];
|
||||
if (overlay){
|
||||
let overlayPresets: domTypes.position;
|
||||
if (overlayTop || overlayLeft) {
|
||||
overlayPresets = { x: overlayLeft, y: overlayTop };
|
||||
}
|
||||
return overlay.calculatePosition(false, false, overlayPresets);
|
||||
} else {
|
||||
//When page is slow, it may happen that rendering of an overlay may not happen, even if
|
||||
//blazor thinks it did happen. In such a case, when overlay object is not found, just try
|
||||
//to render it again.
|
||||
return overlayHelper.addOverlayToContainer(blazorId, overlaySelector, triggerSelector, placement, containerSelector,triggerBoundyAdjustMode, triggerIsWrappedInDiv, triggerPrefixCls,
|
||||
verticalOffset, horizontalOffset, arrowPointAtCenter,
|
||||
overlayTop, overlayLeft);
|
||||
}
|
||||
}
|
||||
|
||||
static deleteOverlayFromContainer(blazorId: string) {
|
||||
const overlay = this.overlayRegistry[blazorId];
|
||||
if (overlay) {
|
||||
overlay.dispose();
|
||||
delete this.overlayRegistry[blazorId];
|
||||
}
|
||||
}
|
||||
|
||||
static addPreventEnterOnOverlayVisible(element, overlayElement) {
|
||||
if (element && overlayElement) {
|
||||
let dom: HTMLElement = domInfoHelper.get(element);
|
||||
@ -21,8 +91,5 @@ export class overlayHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getMaxZIndex() {
|
||||
return [...document.all].reduce((r, e) => Math.max(r, +window.getComputedStyle(e).zIndex || 0), 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,52 @@
|
||||
export class tableHelper {
|
||||
static bindTableHeaderAndBodyScroll(bodyRef, headerRef) {
|
||||
bodyRef.bindScrollLeftToHeader = () => {
|
||||
headerRef.scrollLeft = bodyRef.scrollLeft;
|
||||
static bindTableScroll(bodyRef, tableRef, headerRef, scrollX, scrollY) {
|
||||
bodyRef.bindScroll = () => {
|
||||
if (scrollX) {
|
||||
tableHelper.SetScrollPositionClassName(bodyRef, tableRef);
|
||||
}
|
||||
if (scrollY) {
|
||||
headerRef.scrollLeft = bodyRef.scrollLeft;
|
||||
}
|
||||
}
|
||||
bodyRef.addEventListener('scroll', bodyRef.bindScrollLeftToHeader);
|
||||
bodyRef.bindScroll();
|
||||
bodyRef.addEventListener('scroll', bodyRef.bindScroll);
|
||||
window.addEventListener('resize', bodyRef.bindScroll);
|
||||
}
|
||||
|
||||
static unbindTableHeaderAndBodyScroll(bodyRef) {
|
||||
static unbindTableScroll(bodyRef) {
|
||||
if (bodyRef) {
|
||||
bodyRef.removeEventListener('scroll', bodyRef.bindScrollLeftToHeader);
|
||||
bodyRef.removeEventListener('scroll', bodyRef.bindScroll);
|
||||
window.removeEventListener('resize', bodyRef.bindScroll);
|
||||
}
|
||||
}
|
||||
|
||||
static SetScrollPositionClassName(bodyRef, tableRef) {
|
||||
|
||||
let scrollLeft = bodyRef.scrollLeft;
|
||||
let scrollWidth = bodyRef.scrollWidth;
|
||||
let clientWidth = bodyRef.clientWidth;
|
||||
|
||||
let pingLeft = false;
|
||||
let pingRight = false;
|
||||
|
||||
if ((scrollWidth == clientWidth && scrollWidth != 0)) {
|
||||
pingLeft = false;
|
||||
pingRight = false;
|
||||
}
|
||||
else if (scrollLeft == 0) {
|
||||
pingLeft = false;
|
||||
pingRight = true;
|
||||
}
|
||||
else if (Math.abs(scrollWidth - (scrollLeft + clientWidth)) <= 1) {
|
||||
pingRight = false;
|
||||
pingLeft = true;
|
||||
}
|
||||
else {
|
||||
pingLeft = true;
|
||||
pingRight = true;
|
||||
}
|
||||
|
||||
pingLeft ? tableRef.classList.add("ant-table-ping-left") : tableRef.classList.remove("ant-table-ping-left");
|
||||
pingRight ? tableRef.classList.add("ant-table-ping-right") : tableRef.classList.remove("ant-table-ping-right");
|
||||
}
|
||||
}
|
@ -5,20 +5,20 @@ export class eventHelper {
|
||||
static triggerEvent(element: HTMLInputElement, eventType: string, eventName: string) {
|
||||
//TODO: replace with event constructors https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent
|
||||
//Not used
|
||||
var evt = document.createEvent(eventType);
|
||||
const evt = document.createEvent(eventType);
|
||||
evt.initEvent(eventName);
|
||||
return element.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
static addDomEventListener(element, eventName: string, preventDefault: boolean, invoker: any) {
|
||||
let callback = args => {
|
||||
const callback = args => {
|
||||
const obj = {};
|
||||
for (let k in args) {
|
||||
if (k !== 'originalTarget') { //firefox occasionally raises Permission Denied when this property is being stringified
|
||||
obj[k] = args[k];
|
||||
}
|
||||
}
|
||||
let json = JSON.stringify(obj, (k, v) => {
|
||||
const json = JSON.stringify(obj, (k, v) => {
|
||||
if (v instanceof Node) return 'Node';
|
||||
if (v instanceof Window) return 'Window';
|
||||
return v;
|
||||
@ -29,14 +29,14 @@ export class eventHelper {
|
||||
}
|
||||
};
|
||||
|
||||
if (element == 'window') {
|
||||
if (eventName == 'resize') {
|
||||
if (element === 'window') {
|
||||
if (eventName === 'resize') {
|
||||
window.addEventListener(eventName, this.debounce(() => callback({ innerWidth: window.innerWidth, innerHeight: window.innerHeight }), 200, false));
|
||||
} else {
|
||||
window.addEventListener(eventName, callback);
|
||||
}
|
||||
} else {
|
||||
let dom = domInfoHelper.get(element);
|
||||
const dom = domInfoHelper.get(element);
|
||||
if (dom) {
|
||||
(dom as HTMLElement).addEventListener(eventName, callback);
|
||||
}
|
||||
@ -44,7 +44,7 @@ export class eventHelper {
|
||||
}
|
||||
|
||||
static addDomEventListenerToFirstChild(element, eventName, preventDefault, invoker) {
|
||||
var dom = domInfoHelper.get(element);
|
||||
const dom = domInfoHelper.get(element);
|
||||
|
||||
if (dom && dom.firstElementChild) {
|
||||
this.addDomEventListener(dom.firstElementChild, eventName, preventDefault, invoker);
|
||||
@ -53,7 +53,7 @@ export class eventHelper {
|
||||
|
||||
static addPreventKeys(inputElement, keys: string[]) {
|
||||
if (inputElement) {
|
||||
let dom = domInfoHelper.get(inputElement);
|
||||
const dom = domInfoHelper.get(inputElement);
|
||||
keys = keys.map(function (x) { return x.toUpperCase(); })
|
||||
state.eventCallbackRegistry[inputElement.id + "keydown"] = (e) => this.preventKeys(e, keys);
|
||||
(dom as HTMLElement).addEventListener("keydown", state.eventCallbackRegistry[inputElement.id + "keydown"], false);
|
||||
@ -69,7 +69,7 @@ export class eventHelper {
|
||||
|
||||
static removePreventKeys(inputElement) {
|
||||
if (inputElement) {
|
||||
let dom = domInfoHelper.get(inputElement);
|
||||
const dom = domInfoHelper.get(inputElement);
|
||||
if (dom) {
|
||||
(dom as HTMLElement).removeEventListener("keydown", state.eventCallbackRegistry[inputElement.id + "keydown"]);
|
||||
state.eventCallbackRegistry[inputElement.id + "keydown"] = null;
|
||||
|
@ -47,7 +47,7 @@ export class infoHelper {
|
||||
}
|
||||
|
||||
static getElementAbsolutePos(element: any): domTypes.position {
|
||||
let res: domTypes.position = {
|
||||
const res: domTypes.position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
@ -68,7 +68,7 @@ export class infoHelper {
|
||||
static getBoundingClientRect(element: any): domTypes.domRect {
|
||||
const domElement = this.get(element);
|
||||
if (domElement && domElement.getBoundingClientRect) {
|
||||
let rect = domElement.getBoundingClientRect();
|
||||
const rect = domElement.getBoundingClientRect();
|
||||
// Fixes #1468. This wrapping is necessary for old browsers. Remove this when one day we no longer support them.
|
||||
return {
|
||||
width: rect.width,
|
||||
@ -107,13 +107,43 @@ export class infoHelper {
|
||||
}
|
||||
|
||||
static hasFocus(selector) {
|
||||
let dom = this.get(selector);
|
||||
const dom = this.get(selector);
|
||||
return (document.activeElement === dom);
|
||||
}
|
||||
|
||||
static getInnerText(element) {
|
||||
let dom = this.get(element);
|
||||
const dom = this.get(element);
|
||||
if (dom) return dom.innerText;
|
||||
return null;
|
||||
}
|
||||
|
||||
static getMaxZIndex(): number {
|
||||
return [...document.querySelectorAll("*")].reduce((r, e) => Math.max(r, +window.getComputedStyle(e).zIndex || 0), 0)
|
||||
}
|
||||
|
||||
static isFixedPosition(element) {
|
||||
let node = this.get(element);
|
||||
while (node && node.nodeName.toLowerCase() !== 'body') {
|
||||
if (window.getComputedStyle(node).getPropertyValue('position').toLowerCase() === 'fixed')
|
||||
{ return true; }
|
||||
node = node.parentNode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static findAncestorWithZIndex(element: HTMLElement): number {
|
||||
let node = this.get(element);
|
||||
let zIndexAsString: string;
|
||||
let zIndex: number;
|
||||
while (node && node.nodeName.toLowerCase() !== 'body') {
|
||||
zIndexAsString = window.getComputedStyle(node).zIndex;
|
||||
zIndex = Number.parseInt(zIndexAsString);
|
||||
if (!Number.isNaN(zIndex)) {
|
||||
return zIndex;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -12,11 +12,17 @@ export class manipulationHelper {
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
static addElementTo(addElement, elementSelector) {
|
||||
static addElementTo(addElement, elementSelector): boolean {
|
||||
let parent = domInfoHelper.get(elementSelector);
|
||||
if (parent && addElement) {
|
||||
parent.appendChild(addElement);
|
||||
if (parent instanceof Node && addElement instanceof Node) {
|
||||
parent.appendChild(addElement);
|
||||
return true;
|
||||
} else {
|
||||
console.log("does not implement node", parent, addElement);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static delElementFrom(delElement, elementSelector) {
|
||||
@ -95,6 +101,7 @@ export class manipulationHelper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static blur(selector) {
|
||||
let dom = domInfoHelper.get(selector);
|
||||
if (dom) {
|
||||
@ -102,20 +109,21 @@ export class manipulationHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static scrollTo(selector: Element | string) {
|
||||
let element = domInfoHelper.get(selector);
|
||||
|
||||
if (element && element instanceof HTMLElement) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
||||
}
|
||||
}
|
||||
static scrollTo(selector: Element | string, parentElement?: HTMLElement) {
|
||||
const element = domInfoHelper.get(selector);
|
||||
if (parentElement && element && element instanceof HTMLElement) {
|
||||
parentElement.scrollTop = element.offsetTop;
|
||||
} else if (element && element instanceof HTMLElement) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
||||
}
|
||||
}
|
||||
|
||||
static slideTo(targetPageY) {
|
||||
var timer = setInterval(function () {
|
||||
var currentY = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
var distance = targetPageY > currentY ? targetPageY - currentY : currentY - targetPageY;
|
||||
var speed = Math.ceil(distance / 10);
|
||||
if (currentY == targetPageY) {
|
||||
const timer = setInterval(function () {
|
||||
const currentY = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
const distance = targetPageY > currentY ? targetPageY - currentY : currentY - targetPageY;
|
||||
const speed = Math.ceil(distance / 10);
|
||||
if (currentY === targetPageY) {
|
||||
clearInterval(timer);
|
||||
} else {
|
||||
window.scrollTo(0, targetPageY > currentY ? currentY + speed : currentY - speed);
|
||||
|
@ -60,5 +60,4 @@ export class styleHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -7,12 +7,13 @@
|
||||
<OverlayTrigger @ref="@_dropDown"
|
||||
Visible="Open"
|
||||
IsButton="@true"
|
||||
BoundaryAdjustMode="@BoundaryAdjustMode"
|
||||
Disabled="IsDisabled(0)"
|
||||
PopupContainerSelector="@PopupContainerSelector"
|
||||
OnVisibleChange="OverlayVisibleChange"
|
||||
OverlayEnterCls="ant-slide-up-enter ant-slide-up-enter-active ant-slide-up"
|
||||
OverlayLeaveCls="ant-slide-up-leave ant-slide-up-leave-active ant-slide-up"
|
||||
Trigger="new TriggerType[] { TriggerType.Click }">
|
||||
Trigger="new[] { Trigger.Click }">
|
||||
<Overlay>
|
||||
<div class="@(PrefixCls)-panel-container" @onclick="@PickerClicked">
|
||||
<div class="@_panelClassMapper.Class">
|
||||
|
@ -9,11 +9,12 @@
|
||||
IsButton="@true"
|
||||
Disabled="IsDisabled()"
|
||||
PopupContainerSelector="@PopupContainerSelector"
|
||||
BoundaryAdjustMode="@BoundaryAdjustMode"
|
||||
OnVisibleChange="OverlayVisibleChange"
|
||||
OverlayClassName="ant-picker-dropdown-range"
|
||||
OverlayEnterCls="ant-slide-up-enter ant-slide-up-enter-active ant-slide-up"
|
||||
OverlayLeaveCls="ant-slide-up-leave ant-slide-up-leave-active ant-slide-up"
|
||||
Trigger="new TriggerType[] { TriggerType.Click }">
|
||||
Trigger="new[] { Trigger.Click }">
|
||||
<Overlay>
|
||||
<div class="@(PrefixCls)-range-arrow" style="@_rangeArrowStyle" />
|
||||
<div class="@(PrefixCls)-panel-container" @onclick="@PickerClicked">
|
||||
|
@ -70,6 +70,12 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public OneOf<bool, bool[]> Disabled { get; set; } = new bool[] { false, false };
|
||||
|
||||
/// <summary>
|
||||
/// Overlay adjustment strategy (when for example browser resize is happening)
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public TriggerBoundaryAdjustMode BoundaryAdjustMode { get; set; } = TriggerBoundaryAdjustMode.InView;
|
||||
|
||||
[Parameter]
|
||||
public bool Bordered { get; set; } = true;
|
||||
|
||||
|
@ -47,7 +47,7 @@ namespace AntDesign
|
||||
private List<List<(IDescriptionsItem item, int realSpan)>> _itemMatrix = new List<List<(IDescriptionsItem item, int realSpan)>>();
|
||||
|
||||
[Inject]
|
||||
public DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
private int _realColumn;
|
||||
|
||||
@ -84,7 +84,6 @@ namespace AntDesign
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
SetClassMap();
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
@ -92,7 +91,7 @@ namespace AntDesign
|
||||
{
|
||||
if (firstRender && Column.IsT1)
|
||||
{
|
||||
DomEventService.AddEventListener<object>("window", "resize", OnResize, false);
|
||||
DomEventListener.AddShared<object>("window", "resize", OnResize);
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
@ -100,9 +99,8 @@ namespace AntDesign
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
|
||||
DomEventService.RemoveEventListerner<object>("window", "resize", OnResize);
|
||||
}
|
||||
|
||||
private async void OnResize(object o)
|
||||
|
@ -1,55 +1,56 @@
|
||||
@namespace AntDesign
|
||||
@inherits AntDomComponentBase
|
||||
<CascadingValue Value="@($"#ant-drawer-wrap_{Id}")" Name="PopupContainerSelector">
|
||||
<div class="@ClassMapper.Class" @ref="@Ref" style="@_drawerStyle @InnerZIndexStyle @Style" id="@Id">
|
||||
@if (Mask)
|
||||
{
|
||||
<div class="ant-drawer-mask" @onclick="_=>MaskClick()" style="@MaskStyle"></div>
|
||||
}
|
||||
<div class="ant-drawer-content-wrapper @WrapClassName " style="@WrapperStyle" id="@($"ant-drawer-wrap_{Id}")">
|
||||
<div class="ant-drawer-content">
|
||||
<div class="ant-drawer-wrapper-body" style="@(IsLeftOrRight?"height:100%":"")">
|
||||
@if (_title.Value != null || Closable)
|
||||
{
|
||||
|
||||
<div class="@ClassMapper.Class" @ref="@Ref" style="@_drawerStyle @InnerZIndexStyle @Style" id="@Id">
|
||||
@if (Mask)
|
||||
{
|
||||
<div class="ant-drawer-mask" @onclick="_=>MaskClick()" style="@MaskStyle"></div>
|
||||
}
|
||||
<div class="ant-drawer-content-wrapper @WrapClassName " style="@WrapperStyle">
|
||||
<div class="ant-drawer-content">
|
||||
<div class="ant-drawer-wrapper-body" style="@(IsLeftOrRight?"height:100%":"")">
|
||||
@if (_title.Value != null || Closable)
|
||||
{
|
||||
|
||||
<div class="@TitleClassMapper.Class">
|
||||
@if (_title.Value != null)
|
||||
<div class="@TitleClassMapper.Class">
|
||||
@if (_title.Value != null)
|
||||
{
|
||||
<div class="ant-drawer-title">
|
||||
@if (TitleTemplate != null)
|
||||
{
|
||||
@TitleTemplate
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(TitleString))
|
||||
{
|
||||
@((MarkupString)TitleString)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (Closable)
|
||||
{
|
||||
<button @onclick="_=>CloseClick()" aria-label="Close" class="ant-drawer-close">
|
||||
<Icon Type="close" />
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="ant-drawer-body" style="@BodyStyle">
|
||||
@if (ContentTemplate != null)
|
||||
{
|
||||
<div class="ant-drawer-title">
|
||||
@if (TitleTemplate != null)
|
||||
{
|
||||
@TitleTemplate
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(TitleString))
|
||||
{
|
||||
@((MarkupString)TitleString)
|
||||
}
|
||||
</div>
|
||||
@ContentTemplate
|
||||
}
|
||||
@if (Closable)
|
||||
@if (string.IsNullOrEmpty(ContentString))
|
||||
{
|
||||
<button @onclick="_=>CloseClick()" aria-label="Close" class="ant-drawer-close">
|
||||
<Icon Type="close" />
|
||||
</button>
|
||||
@((MarkupString)ContentString)
|
||||
}
|
||||
@ChildContent
|
||||
</div>
|
||||
}
|
||||
<div class="ant-drawer-body" style="@BodyStyle">
|
||||
@if (ContentTemplate != null)
|
||||
{
|
||||
@ContentTemplate
|
||||
}
|
||||
@if (string.IsNullOrEmpty(ContentString))
|
||||
{
|
||||
@((MarkupString)ContentString)
|
||||
}
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
@if (Handler != null)
|
||||
{
|
||||
@Handler
|
||||
}
|
||||
</div>
|
||||
@if (Handler != null)
|
||||
{
|
||||
@Handler
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</CascadingValue>
|
@ -389,5 +389,11 @@ namespace AntDesign
|
||||
|
||||
_drawerStyle = style;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_timer?.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ namespace AntDesign
|
||||
{
|
||||
string prefixCls = "ant-btn";
|
||||
ClassMapper.Clear();
|
||||
Placement = PlacementType.BottomRight;
|
||||
Placement = Placement.BottomRight;
|
||||
base.OnInitialized();
|
||||
ClassMapper.If($"{prefixCls}-block", () => Block);
|
||||
|
||||
@ -208,25 +208,20 @@ namespace AntDesign
|
||||
if (firstRender)
|
||||
{
|
||||
Ref = RefBack.Current;
|
||||
DomEventService.AddEventListener(Ref, "click", OnUnboundClick, true);
|
||||
DomEventService.AddEventListener(Ref, "mouseover", OnUnboundMouseEnter, true);
|
||||
DomEventService.AddEventListener(Ref, "mouseout", OnUnboundMouseLeave, true);
|
||||
DomEventService.AddEventListener(Ref, "focusin", OnUnboundFocusIn, true);
|
||||
DomEventService.AddEventListener(Ref, "focusout", OnUnboundFocusOut, true);
|
||||
DomEventService.AddEventListener(Ref, "contextmenu", OnContextMenu, true, true);
|
||||
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "click", OnUnboundClick);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "mouseover", OnUnboundMouseEnter);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "mouseout", OnUnboundMouseLeave);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "focusin", OnUnboundFocusIn);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "focusout", OnUnboundFocusOut);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "contextmenu", OnContextMenu, true);
|
||||
}
|
||||
return base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "click", OnUnboundClick);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "mouseover", OnUnboundMouseEnter);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "mouseout", OnUnboundMouseLeave);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "focusin", OnUnboundFocusIn);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "focusout", OnUnboundFocusOut);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "contextmenu", OnContextMenu);
|
||||
|
||||
DomEventListener.DisposeExclusive();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
@ -284,6 +284,11 @@ namespace AntDesign
|
||||
_formItems.Add(formItem);
|
||||
}
|
||||
|
||||
void IForm.RemoveFormItem(IFormItem formItem)
|
||||
{
|
||||
_formItems.Remove(formItem);
|
||||
}
|
||||
|
||||
void IForm.AddControl(IControlValueAccessor valueAccessor)
|
||||
{
|
||||
this._controls.Add(valueAccessor);
|
||||
|
@ -270,6 +270,8 @@ namespace AntDesign
|
||||
CurrentEditContext.OnValidationStateChanged -= _validationStateChangedHandler;
|
||||
}
|
||||
|
||||
Form?.RemoveFormItem(this);
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,8 @@ namespace AntDesign.Internal
|
||||
|
||||
internal void AddFormItem(IFormItem formItem);
|
||||
|
||||
internal void RemoveFormItem(IFormItem formItem);
|
||||
|
||||
internal void AddControl(IControlValueAccessor valueAccessor);
|
||||
|
||||
internal void RemoveControl(IControlValueAccessor valueAccessor);
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.JsInterop;
|
||||
@ -52,13 +53,13 @@ namespace AntDesign
|
||||
/// Used to set gutter during pre-rendering
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public BreakpointType DefaultBreakpoint { get; set; } = BreakpointType.Xxl;
|
||||
public BreakpointType? DefaultBreakpoint { get; set; } = BreakpointType.Xxl;
|
||||
|
||||
[Inject]
|
||||
public DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
private string _gutterStyle;
|
||||
private BreakpointType _currentBreakPoint;
|
||||
private BreakpointType? _currentBreakPoint;
|
||||
|
||||
private IList<Col> _cols = new List<Col>();
|
||||
|
||||
@ -89,7 +90,7 @@ namespace AntDesign
|
||||
|
||||
if (DefaultBreakpoint != null)
|
||||
{
|
||||
SetGutterStyle(DefaultBreakpoint.Name);
|
||||
SetGutterStyle(DefaultBreakpoint);
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
@ -100,8 +101,8 @@ namespace AntDesign
|
||||
if (firstRender)
|
||||
{
|
||||
var dimensions = await JsInvokeAsync<Window>(JSInteropConstants.GetWindow);
|
||||
DomEventService.AddEventListener<Window>("window", "resize", OnResize, false);
|
||||
OptimizeSize(dimensions.innerWidth);
|
||||
DomEventListener.AddShared<Window>("window", "resize", OnResize);
|
||||
OptimizeSize(dimensions.InnerWidth);
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
@ -110,7 +111,7 @@ namespace AntDesign
|
||||
internal void AddCol(Col col)
|
||||
{
|
||||
this._cols.Add(col);
|
||||
var gutter = this.GetGutter((_currentBreakPoint ?? DefaultBreakpoint).Name);
|
||||
var gutter = this.GetGutter(_currentBreakPoint ?? DefaultBreakpoint);
|
||||
col.RowGutterChanged(gutter);
|
||||
}
|
||||
|
||||
@ -119,9 +120,9 @@ namespace AntDesign
|
||||
this._cols.Remove(col);
|
||||
}
|
||||
|
||||
private async void OnResize(Window window)
|
||||
private void OnResize(Window window)
|
||||
{
|
||||
OptimizeSize(window.innerWidth);
|
||||
OptimizeSize(window.InnerWidth);
|
||||
}
|
||||
|
||||
private void OptimizeSize(decimal windowWidth)
|
||||
@ -129,7 +130,7 @@ namespace AntDesign
|
||||
BreakpointType actualBreakpoint = _breakpoints[_breakpoints.Length - 1];
|
||||
for (int i = 0; i < _breakpoints.Length; i++)
|
||||
{
|
||||
if (windowWidth <= _breakpoints[i].Width && (windowWidth >= (i > 0 ? _breakpoints[i - 1].Width : 0)))
|
||||
if (windowWidth <= (int)_breakpoints[i] && (windowWidth >= (i > 0 ? (int)_breakpoints[i - 1] : 0)))
|
||||
{
|
||||
actualBreakpoint = _breakpoints[i];
|
||||
}
|
||||
@ -137,7 +138,7 @@ namespace AntDesign
|
||||
|
||||
this._currentBreakPoint = actualBreakpoint;
|
||||
|
||||
SetGutterStyle(actualBreakpoint.Name);
|
||||
SetGutterStyle(actualBreakpoint);
|
||||
|
||||
if (OnBreakpoint.HasDelegate)
|
||||
{
|
||||
@ -147,7 +148,7 @@ namespace AntDesign
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void SetGutterStyle(string breakPoint)
|
||||
private void SetGutterStyle(BreakpointType? breakPoint)
|
||||
{
|
||||
var gutter = this.GetGutter(breakPoint);
|
||||
_cols.ForEach(x => x.RowGutterChanged(gutter));
|
||||
@ -162,27 +163,28 @@ namespace AntDesign
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private (int horizontalGutter, int verticalGutter) GetGutter(string breakPoint)
|
||||
private (int horizontalGutter, int verticalGutter) GetGutter(BreakpointType? breakPoint)
|
||||
{
|
||||
GutterType gutter = 0;
|
||||
if (this.Gutter.Value != null)
|
||||
gutter = this.Gutter;
|
||||
|
||||
var breakPointName = Enum.GetName(typeof(BreakpointType), breakPoint);
|
||||
|
||||
return gutter.Match(
|
||||
num => (num, 0),
|
||||
dic => breakPoint != null && dic.ContainsKey(breakPoint) ? (dic[breakPoint], 0) : (0, 0),
|
||||
dic => breakPoint != null && dic.ContainsKey(breakPointName) ? (dic[breakPointName], 0) : (0, 0),
|
||||
tuple => tuple,
|
||||
tupleDicInt => (tupleDicInt.Item1.ContainsKey(breakPoint) ? tupleDicInt.Item1[breakPoint] : 0, tupleDicInt.Item2),
|
||||
tupleIntDic => (tupleIntDic.Item1, tupleIntDic.Item2.ContainsKey(breakPoint) ? tupleIntDic.Item2[breakPoint] : 0),
|
||||
tupleDicDic => (tupleDicDic.Item1.ContainsKey(breakPoint) ? tupleDicDic.Item1[breakPoint] : 0, tupleDicDic.Item2.ContainsKey(breakPoint) ? tupleDicDic.Item2[breakPoint] : 0)
|
||||
tupleDicInt => (tupleDicInt.Item1.ContainsKey(breakPointName) ? tupleDicInt.Item1[breakPointName] : 0, tupleDicInt.Item2),
|
||||
tupleIntDic => (tupleIntDic.Item1, tupleIntDic.Item2.ContainsKey(breakPointName) ? tupleIntDic.Item2[breakPointName] : 0),
|
||||
tupleDicDic => (tupleDicDic.Item1.ContainsKey(breakPointName) ? tupleDicDic.Item1[breakPointName] : 0, tupleDicDic.Item2.ContainsKey(breakPointName) ? tupleDicDic.Item2[breakPointName] : 0)
|
||||
);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
|
||||
DomEventService.RemoveEventListerner<Window>("window", "resize", OnResize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="ant-input-number-input-wrap">
|
||||
<input @ref="Ref" role="spinbutton" aria-valuemin="@Min" aria-valuemax="@Max" autocomplete="off" max="@Max" min="@Min" step="@Step"
|
||||
aria-valuenow="@CurrentValue" class="ant-input-number-input" @bind="@CurrentValueAsString" @oninput="OnInput" @onkeydown="OnKeyDown" @onfocus="@OnFocus" @onblur="@OnBlurAsync" disabled="@Disabled" />
|
||||
<input @ref="Ref" role="spinbutton" aria-valuemin="@Min" aria-valuemax="@Max" autocomplete="off" max="@Max" min="@Min" step="@Step" inputmode="@_inputNumberMode"
|
||||
aria-valuenow="@CurrentValue" class="ant-input-number-input" @bind="@CurrentValueAsString" @oninput="OnInput" @onkeydown="OnKeyDown" @onfocus="@OnFocusAsync" @onblur="@OnBlurAsync" disabled="@Disabled" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -74,6 +74,9 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public EventCallback<TValue> OnChange { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<FocusEventArgs> OnFocus { get; set; }
|
||||
|
||||
private readonly bool _isNullable;
|
||||
private bool _hasDefaultValue;
|
||||
|
||||
@ -115,40 +118,40 @@ namespace AntDesign
|
||||
|
||||
private static readonly Dictionary<Type, object> _defaultMaximum = new Dictionary<Type, object>()
|
||||
{
|
||||
{ typeof(sbyte),sbyte.MaxValue },
|
||||
{ typeof(sbyte), sbyte.MaxValue },
|
||||
{ typeof(byte), byte.MaxValue },
|
||||
|
||||
{ typeof(short),short.MaxValue },
|
||||
{ typeof(ushort),ushort.MaxValue },
|
||||
{ typeof(short), short.MaxValue },
|
||||
{ typeof(ushort), ushort.MaxValue },
|
||||
|
||||
{ typeof(int),int.MaxValue },
|
||||
{ typeof(uint),uint.MaxValue },
|
||||
{ typeof(int), int.MaxValue },
|
||||
{ typeof(uint), uint.MaxValue },
|
||||
|
||||
{ typeof(long),long.MaxValue },
|
||||
{ typeof(ulong),ulong.MaxValue },
|
||||
{ typeof(long), long.MaxValue },
|
||||
{ typeof(ulong), ulong.MaxValue },
|
||||
|
||||
{ typeof(float),float.PositiveInfinity },
|
||||
{ typeof(double),double.PositiveInfinity },
|
||||
{ typeof(decimal),decimal.MaxValue },
|
||||
{ typeof(float), float.PositiveInfinity },
|
||||
{ typeof(double), double.PositiveInfinity },
|
||||
{ typeof(decimal), decimal.MaxValue },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<Type, object> _defaultMinimum = new Dictionary<Type, object>()
|
||||
{
|
||||
{ typeof(sbyte),sbyte.MinValue },
|
||||
{ typeof(sbyte), sbyte.MinValue },
|
||||
{ typeof(byte), byte.MinValue },
|
||||
|
||||
{ typeof(short),short.MinValue },
|
||||
{ typeof(ushort),ushort.MinValue },
|
||||
{ typeof(short), short.MinValue },
|
||||
{ typeof(ushort), ushort.MinValue },
|
||||
|
||||
{ typeof(int),int.MinValue },
|
||||
{ typeof(uint),uint.MinValue },
|
||||
{ typeof(int), int.MinValue },
|
||||
{ typeof(uint), uint.MinValue },
|
||||
|
||||
{ typeof(long),long.MinValue },
|
||||
{ typeof(ulong),ulong.MinValue },
|
||||
{ typeof(long), long.MinValue },
|
||||
{ typeof(ulong), ulong.MinValue },
|
||||
|
||||
{ typeof(float),float.NegativeInfinity},
|
||||
{ typeof(double),double.NegativeInfinity },
|
||||
{ typeof(decimal),decimal.MinValue },
|
||||
{ typeof(float), float.NegativeInfinity},
|
||||
{ typeof(double), double.NegativeInfinity },
|
||||
{ typeof(decimal), decimal.MinValue },
|
||||
};
|
||||
|
||||
private static Type[] _floatTypes = new Type[] { typeof(float), typeof(double), typeof(decimal) };
|
||||
@ -160,6 +163,8 @@ namespace AntDesign
|
||||
private CancellationTokenSource _decreaseTokenSource;
|
||||
private TValue _defaultValue;
|
||||
|
||||
private string _inputNumberMode = "numeric";
|
||||
|
||||
public InputNumber()
|
||||
{
|
||||
_isNullable = _surfaceType.IsGenericType && _surfaceType.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
@ -203,6 +208,7 @@ namespace AntDesign
|
||||
MethodCallExpression expRound = Expression.Call(null, typeof(InputNumberMath).GetMethod(nameof(InputNumberMath.Round), new Type[] { _surfaceType, typeof(int) }), num, decimalPlaces);
|
||||
var lambdaRound = Expression.Lambda<Func<TValue, int, TValue>>(expRound, num, decimalPlaces);
|
||||
_roundFunc = lambdaRound.Compile();
|
||||
_inputNumberMode = "decimal";
|
||||
}
|
||||
|
||||
if (_defaultMaximum.ContainsKey(underlyingType)) Max = (TValue)_defaultMaximum[underlyingType];
|
||||
@ -404,10 +410,15 @@ namespace AntDesign
|
||||
_inputString = args.Value?.ToString();
|
||||
}
|
||||
|
||||
private void OnFocus()
|
||||
private async Task OnFocusAsync(FocusEventArgs args)
|
||||
{
|
||||
_focused = true;
|
||||
CurrentValue = Value;
|
||||
|
||||
if (OnFocus.HasDelegate)
|
||||
{
|
||||
await OnFocus.InvokeAsync(args);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnBlurAsync()
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -32,7 +31,7 @@ namespace AntDesign
|
||||
protected virtual bool EnableOnPressEnter => OnPressEnter.HasDelegate;
|
||||
|
||||
[Inject]
|
||||
public DomEventService DomEventService { get; set; }
|
||||
protected IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The label text displayed before (on the left side of) the input field.
|
||||
@ -52,6 +51,13 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public bool AllowClear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls the autocomplete attribute of the input HTML element.
|
||||
/// Default = true
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool AutoComplete { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool AutoFocus
|
||||
{
|
||||
@ -168,8 +174,11 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls onclick & blur event propagation.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool AutoComplete { get; set; } = true;
|
||||
public bool StopPropagation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The suffix icon for the Input.
|
||||
@ -451,9 +460,10 @@ namespace AntDesign
|
||||
return;
|
||||
}
|
||||
|
||||
_debounceTimer?.Dispose();
|
||||
if (_debounceTimer != null)
|
||||
{
|
||||
await _debounceTimer.DisposeAsync();
|
||||
|
||||
_debounceTimer = null;
|
||||
}
|
||||
}
|
||||
@ -477,23 +487,21 @@ namespace AntDesign
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
DomEventService.AddEventListener(Ref, "compositionstart", OnCompositionStart);
|
||||
DomEventService.AddEventListener(Ref, "compositionend", OnCompositionEnd);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "compositionstart", OnCompositionStart);
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "compositionend", OnCompositionEnd);
|
||||
if (this.AutoFocus)
|
||||
{
|
||||
IsFocused = true;
|
||||
await this.FocusAsync(Ref);
|
||||
}
|
||||
DomEventService.AddEventListener(Ref, "focus", OnFocusInternal, true);
|
||||
|
||||
DomEventListener.AddExclusive<JsonElement>(Ref, "focus", OnFocusInternal);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "compositionstart", OnCompositionStart);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "compositionend", OnCompositionEnd);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(Ref, "focus", OnFocusInternal);
|
||||
|
||||
DomEventListener.DisposeExclusive();
|
||||
_debounceTimer?.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
@ -641,6 +649,13 @@ namespace AntDesign
|
||||
//TODO: Use built in @onfocus once https://github.com/dotnet/aspnetcore/issues/30070 is solved
|
||||
//builder.AddAttribute(76, "onfocus", CallbackFactory.Create(this, OnFocusAsync));
|
||||
builder.AddAttribute(77, "onmouseup", CallbackFactory.Create(this, OnMouseUpAsync));
|
||||
|
||||
if (StopPropagation)
|
||||
{
|
||||
builder.AddEventStopPropagationAttribute(78, "onchange", true);
|
||||
builder.AddEventStopPropagationAttribute(79, "onblur", true);
|
||||
}
|
||||
|
||||
builder.AddElementReferenceCapture(90, r => Ref = r);
|
||||
builder.CloseElement();
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.Core.Extensions;
|
||||
using AntDesign.JsInterop;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
@namespace AntDesign
|
||||
@inherits Input<string>
|
||||
<!--TODO: minheight, maxheight, onResize-->
|
||||
|
||||
@{
|
||||
Dictionary<string, object> attributes =
|
||||
@ -19,7 +18,7 @@
|
||||
{ "style", Style },
|
||||
{ "class", ClassMapper.Class },
|
||||
{ "disabled", Disabled },
|
||||
{ "readonly", ReadOnly },
|
||||
{ "readonly", ReadOnly },
|
||||
};
|
||||
|
||||
if (Attributes != null)
|
||||
@ -34,13 +33,13 @@
|
||||
@if (Suffix != null)
|
||||
{
|
||||
<span class="@_warpperClassMapper.Class">
|
||||
<textarea @ref="Ref" @attributes="attributes"/>
|
||||
<textarea @ref="Ref" @attributes="attributes" @onchange:stopPropagation="@StopPropagation" @onblur:stopPropagation="@StopPropagation"/>
|
||||
@Suffix
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea @ref="Ref" @attributes="attributes"/>
|
||||
<textarea @ref="Ref" @attributes="attributes" @onchange:stopPropagation="@StopPropagation" @onblur:stopPropagation="@StopPropagation"/>
|
||||
}
|
||||
<AntDesign.Text Style="float: right; pointer-events: none; white-space: nowrap; color: rgba(0, 0, 0, 0.45)"> @($"/ {MaxLength}")</AntDesign.Text>
|
||||
</div>
|
||||
@ -50,12 +49,12 @@ else
|
||||
@if (Suffix != null)
|
||||
{
|
||||
<span class="@_warpperClassMapper.Class">
|
||||
<textarea @ref="Ref" @attributes="attributes"/>
|
||||
<textarea @ref="Ref" @attributes="attributes" @onchange:stopPropagation="@StopPropagation" @onblur:stopPropagation="@StopPropagation"/>
|
||||
@Suffix
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea @ref="Ref" @attributes="attributes"/>
|
||||
<textarea @ref="Ref" @attributes="attributes" @onchange:stopPropagation="@StopPropagation" @onblur:stopPropagation="@StopPropagation"/>
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.JsInterop;
|
||||
@ -28,10 +26,39 @@ namespace AntDesign
|
||||
private uint _minRows = DEFAULT_MIN_ROWS;
|
||||
private uint _maxRows = uint.MaxValue;
|
||||
private bool _hasMinOrMaxSet;
|
||||
private bool _hasMinSet;
|
||||
private DotNetObjectReference<TextArea> _reference;
|
||||
|
||||
/// <summary>
|
||||
/// Will adjust (grow or shrink) the `TextArea` according to content.
|
||||
/// Can work in connection with `MaxRows` & `MinRows`.
|
||||
/// Sets resize attribute of the textarea HTML element to: none.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool AutoSize { get; set; }
|
||||
public bool AutoSize
|
||||
{
|
||||
get => _autoSize;
|
||||
set
|
||||
{
|
||||
if (_hasMinOrMaxSet && !value)
|
||||
{
|
||||
Debug.WriteLine("AntBlazor.TextArea: AutoSize cannot be set to false when either MinRows or MaxRows has been set.AutoSize has been switched to true.");
|
||||
_autoSize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_autoSize = value;
|
||||
}
|
||||
if (_autoSize)
|
||||
{
|
||||
_resizeStyle = "resize: none";
|
||||
}
|
||||
else
|
||||
{
|
||||
_resizeStyle = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When `false`, value will be set to `null` when content is empty
|
||||
@ -58,12 +85,13 @@ namespace AntDesign
|
||||
if (value >= MinRows)
|
||||
{
|
||||
_maxRows = value;
|
||||
Debug.WriteLineIf(!AutoSize, "AntBlazor.TextArea: AutoSize cannot be set to false when either MinRows or MaxRows has been set.AutoSize has been switched to true.");
|
||||
AutoSize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_maxRows = uint.MaxValue;
|
||||
Debug.WriteLine($"Value of {nameof(MaxRows)}({MaxRows}) has to be between {nameof(MinRows)}({MinRows}) and {uint.MaxValue}");
|
||||
Debug.WriteLine($"AntBlazor.TextArea: Value of {nameof(MaxRows)}({MaxRows}) has to be between {nameof(MinRows)}({MinRows}) and {uint.MaxValue}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,19 +111,28 @@ namespace AntDesign
|
||||
set
|
||||
{
|
||||
_hasMinOrMaxSet = true;
|
||||
_hasMinSet = true;
|
||||
if (value >= DEFAULT_MIN_ROWS && value <= MaxRows)
|
||||
{
|
||||
_minRows = value;
|
||||
Debug.WriteLineIf(!AutoSize, "AntBlazor.TextArea: AutoSize cannot be set to false when either MinRows or MaxRows has been set.AutoSize has been switched to true.");
|
||||
AutoSize = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_minRows = DEFAULT_MIN_ROWS;
|
||||
Debug.WriteLine($"Value of {nameof(MinRows)}({MinRows}) has to be between {DEFAULT_MIN_ROWS} and {nameof(MaxRows)}({MaxRows})");
|
||||
Debug.WriteLine($"AntBlazor.TextArea: Value of {nameof(MinRows)}({MinRows}) has to be between {DEFAULT_MIN_ROWS} and {nameof(MaxRows)}({MaxRows})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the height of the TextArea expressed in number of rows.
|
||||
/// Default value is 3.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public uint Rows { get; set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Callback when the size changes
|
||||
/// </summary>
|
||||
@ -132,41 +169,25 @@ namespace AntDesign
|
||||
|
||||
if (AutoSize)
|
||||
{
|
||||
DomEventService.AddEventListener("window", "beforeunload", Reloading, false);
|
||||
|
||||
await CalculateRowHeightAsync();
|
||||
DomEventListener.AddShared<JsonElement>("window", "beforeunload", Reloading);
|
||||
}
|
||||
await CalculateRowHeightAsync();
|
||||
}
|
||||
|
||||
protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage)
|
||||
{
|
||||
validationErrorMessage = null;
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
if (DefaultToEmptyString)
|
||||
result = string.Empty;
|
||||
else
|
||||
result = default;
|
||||
validationErrorMessage = null;
|
||||
return true;
|
||||
}
|
||||
result = value;
|
||||
return true;
|
||||
|
||||
var success = BindConverter.TryConvertTo<string>(
|
||||
value, CultureInfo.CurrentCulture, out var parsedValue);
|
||||
|
||||
if (success)
|
||||
{
|
||||
result = parsedValue;
|
||||
validationErrorMessage = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default;
|
||||
validationErrorMessage = $"{FieldIdentifier.FieldName} field isn't valid.";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@ -174,7 +195,7 @@ namespace AntDesign
|
||||
if (AutoSize && !_isReloading)
|
||||
{
|
||||
_reference?.Dispose();
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "beforeunload", Reloading);
|
||||
DomEventListener.Dispose();
|
||||
|
||||
_ = InvokeAsync(async () =>
|
||||
{
|
||||
@ -189,6 +210,8 @@ namespace AntDesign
|
||||
/// Indicates that a page is being refreshed
|
||||
/// </summary>
|
||||
private bool _isReloading;
|
||||
private bool _autoSize;
|
||||
private string _resizeStyle = "";
|
||||
|
||||
private void Reloading(JsonElement jsonElement) => _isReloading = true;
|
||||
|
||||
@ -205,33 +228,39 @@ namespace AntDesign
|
||||
{
|
||||
_reference = DotNetObjectReference.Create<TextArea>(this);
|
||||
}
|
||||
var textAreaInfo = await JsInvokeAsync<TextAreaInfo>(JSInteropConstants.RegisterResizeTextArea, Ref, MinRows, MaxRows, _reference);
|
||||
|
||||
// var textAreaInfo = await JsInvokeAsync<TextAreaInfo>(JSInteropConstants.GetTextAreaInfo, Ref);
|
||||
uint rows = Rows;
|
||||
if (_hasMinSet)
|
||||
rows = MinRows;
|
||||
|
||||
TextAreaInfo textAreaInfo;
|
||||
if (AutoSize)
|
||||
{
|
||||
textAreaInfo = await JsInvokeAsync<TextAreaInfo>(
|
||||
JSInteropConstants.InputComponentHelper.RegisterResizeTextArea, Ref, rows, MaxRows, _reference);
|
||||
}
|
||||
else
|
||||
{
|
||||
textAreaInfo = await JsInvokeAsync<TextAreaInfo>(
|
||||
JSInteropConstants.InputComponentHelper.GetTextAreaInfo, Ref);
|
||||
}
|
||||
|
||||
_rowHeight = textAreaInfo.LineHeight;
|
||||
_offsetHeight = textAreaInfo.PaddingTop + textAreaInfo.PaddingBottom
|
||||
+ textAreaInfo.BorderTop + textAreaInfo.BorderBottom;
|
||||
|
||||
uint rows = (uint)(textAreaInfo.ScrollHeight / _rowHeight);
|
||||
if (_hasMinOrMaxSet)
|
||||
rows = Math.Max((uint)MinRows, rows);
|
||||
|
||||
double height = 0;
|
||||
if (rows > MaxRows)
|
||||
{
|
||||
rows = MaxRows;
|
||||
|
||||
height = rows * _rowHeight + _offsetHeight;
|
||||
Style = $"height: {height}px;";
|
||||
Style = $"height: {MaxRows * _rowHeight + _offsetHeight}px;{_resizeStyle};overflow-x: hidden";
|
||||
}
|
||||
else
|
||||
{
|
||||
height = rows * _rowHeight + _offsetHeight;
|
||||
Style = $"height: {height}px;overflow-y: hidden;";
|
||||
string overflow = _autoSize ? "hidden" : "visible";
|
||||
Style = $"height: {rows * _rowHeight + _offsetHeight}px;overflow-y: {overflow};{_resizeStyle};overflow-x: hidden";
|
||||
}
|
||||
}
|
||||
|
||||
private class TextAreaInfo
|
||||
internal class TextAreaInfo
|
||||
{
|
||||
public double ScrollHeight { get; set; }
|
||||
public double LineHeight { get; set; }
|
||||
|
@ -1,19 +1,12 @@
|
||||
namespace AntDesign
|
||||
{
|
||||
public sealed class BreakpointType : EnumValue<BreakpointType>
|
||||
public enum BreakpointType
|
||||
{
|
||||
public static readonly BreakpointType Xs = new BreakpointType(nameof(Xs).ToLowerInvariant(), 1, 480);
|
||||
public static readonly BreakpointType Sm = new BreakpointType(nameof(Sm).ToLowerInvariant(), 2, 576);
|
||||
public static readonly BreakpointType Md = new BreakpointType(nameof(Md).ToLowerInvariant(), 3, 768);
|
||||
public static readonly BreakpointType Lg = new BreakpointType(nameof(Lg).ToLowerInvariant(), 4, 992);
|
||||
public static readonly BreakpointType Xl = new BreakpointType(nameof(Xl).ToLowerInvariant(), 5, 1200);
|
||||
public static readonly BreakpointType Xxl = new BreakpointType(nameof(Xxl).ToLowerInvariant(), 6, 1600);
|
||||
|
||||
public int Width { get; private set; }
|
||||
|
||||
private BreakpointType(string name, int value, int width) : base(name, value)
|
||||
{
|
||||
Width = width;
|
||||
}
|
||||
Xs = 480,
|
||||
Sm = 576,
|
||||
Md = 768,
|
||||
Lg = 992,
|
||||
Xl = 1200,
|
||||
Xxl = 1600,
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace AntDesign
|
||||
|
||||
[CascadingParameter] public Layout Parent { get; set; }
|
||||
|
||||
[Parameter] public BreakpointType Breakpoint { get; set; }
|
||||
[Parameter] public BreakpointType? Breakpoint { get; set; }
|
||||
|
||||
[Parameter] public SiderTheme Theme { get; set; } = SiderTheme.Dark;
|
||||
|
||||
@ -50,7 +50,8 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public EventCallback<bool> OnBreakpoint { get; set; }
|
||||
|
||||
[Inject] public DomEventService DomEventService { get; set; }
|
||||
[Inject]
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
private int ComputedWidth => _isCollapsed ? CollapsedWidth : Width;
|
||||
|
||||
@ -112,14 +113,14 @@ namespace AntDesign
|
||||
if (firstRender && Breakpoint != null)
|
||||
{
|
||||
var dimensions = await JsInvokeAsync<Window>(JSInteropConstants.GetWindow);
|
||||
DomEventService.AddEventListener<Window>("window", "resize", OnResize, false);
|
||||
OptimizeSize(dimensions.innerWidth);
|
||||
DomEventListener.AddShared<Window>("window", "resize", OnResize);
|
||||
OptimizeSize(dimensions.InnerWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnResize(Window window)
|
||||
{
|
||||
OptimizeSize(window.innerWidth);
|
||||
OptimizeSize(window.InnerWidth);
|
||||
}
|
||||
|
||||
public void ToggleCollapsed()
|
||||
@ -138,7 +139,7 @@ namespace AntDesign
|
||||
var originalCollapsed = _isCollapsed;
|
||||
var originlBrokenPoint = _brokenPoint;
|
||||
|
||||
if (windowWidth < Breakpoint?.Width)
|
||||
if (windowWidth < (int)Breakpoint)
|
||||
{
|
||||
_brokenPoint = true;
|
||||
_isCollapsed = true;
|
||||
@ -171,9 +172,8 @@ namespace AntDesign
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
|
||||
DomEventService.RemoveEventListerner<Window>("window", "resize", OnResize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
@typeparam TItem
|
||||
|
||||
<div class="@ClassMapper.Class" style="@Style" Id="@Id" @ref="Ref">
|
||||
|
||||
@if (Header != null)
|
||||
{
|
||||
<div class="@PrefixName-header">
|
||||
@ -11,31 +10,27 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (DataSource != null && DataSource.Any())
|
||||
@if (DataSource?.Any() == true)
|
||||
{
|
||||
<Spin Spinning="Loading">
|
||||
@if (Grid != null)
|
||||
{
|
||||
<Row Gutter="Grid.Gutter">
|
||||
@foreach (var item in DataSource)
|
||||
{
|
||||
<CascadingValue Value="ItemLayout">
|
||||
<CascadingValue Value="()=>HandleItemClick(item)" TValue="Action" Name="ItemClick">
|
||||
@ChildContent(item)
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
}
|
||||
</Row>
|
||||
<Row Gutter="Grid.Gutter" OnBreakpoint="OnBreakpoint">
|
||||
@foreach (var item in DataSource)
|
||||
{
|
||||
<CascadingValue Value="this" IsFixed>
|
||||
@ChildContent(item)
|
||||
</CascadingValue>
|
||||
}
|
||||
</Row>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="ant-list-items">
|
||||
@foreach (var item in DataSource)
|
||||
{
|
||||
<CascadingValue Value="ItemLayout">
|
||||
<CascadingValue Value="()=>HandleItemClick(item)" TValue="Action" Name="ItemClick">
|
||||
@ChildContent(item)
|
||||
</CascadingValue>
|
||||
<CascadingValue Value="this" IsFixed>
|
||||
@ChildContent(item)
|
||||
</CascadingValue>
|
||||
}
|
||||
</ul>
|
||||
|
@ -16,7 +16,7 @@ namespace AntDesign
|
||||
public int Xxl { get; set; }
|
||||
}
|
||||
|
||||
public partial class AntList<TItem> : AntDomComponentBase
|
||||
public partial class AntList<TItem> : AntDomComponentBase, IAntList
|
||||
{
|
||||
public string PrefixName { get; set; } = "ant-list";
|
||||
|
||||
@ -40,8 +40,6 @@ namespace AntDesign
|
||||
|
||||
[Parameter] public bool Split { get; set; } = true;
|
||||
|
||||
[Parameter] public EventCallback<TItem> OnItemClick { get; set; }
|
||||
|
||||
[Parameter] public ListGridType Grid { get; set; }
|
||||
|
||||
[Parameter] public PaginationOptions Pagination { get; set; }
|
||||
@ -56,49 +54,63 @@ namespace AntDesign
|
||||
}
|
||||
}
|
||||
|
||||
private string SizeCls => Size switch
|
||||
{
|
||||
"large" => "lg",
|
||||
"small" => "sm",
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
ListGridType IAntList.Grid => Grid;
|
||||
ListItemLayout IAntList.ItemLayout => ItemLayout;
|
||||
double IAntList.ColumnWidth => _columnWidth;
|
||||
|
||||
double _columnWidth;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
SetClassMap();
|
||||
|
||||
if (Grid?.Column > 0)
|
||||
{
|
||||
_columnWidth = 100d / Grid.Column;
|
||||
}
|
||||
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
protected void SetClassMap()
|
||||
{
|
||||
// large => lg
|
||||
// small => sm
|
||||
string sizeCls = string.Empty;
|
||||
switch (Size)
|
||||
{
|
||||
case "large":
|
||||
sizeCls = "lg";
|
||||
break;
|
||||
|
||||
case "small":
|
||||
sizeCls = "sm";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ClassMapper.Clear()
|
||||
.Add(PrefixName)
|
||||
.If($"{PrefixName}-split", () => Split)
|
||||
.If($"{PrefixName}-rtl", () => RTL)
|
||||
.If($"{PrefixName}-bordered", () => Bordered)
|
||||
.GetIf(() => $"{PrefixName}-{sizeCls}", () => !string.IsNullOrEmpty(sizeCls))
|
||||
.GetIf(() => $"{PrefixName}-{SizeCls}", () => !string.IsNullOrEmpty(SizeCls))
|
||||
.If($"{PrefixName}-vertical", () => ItemLayout == ListItemLayout.Vertical)
|
||||
.If($"{PrefixName}-loading", () => (Loading))
|
||||
.If($"{PrefixName}-grid", () => Grid != null)
|
||||
.If($"{PrefixName}-something-after-last-item", () => IsSomethingAfterLastItem);
|
||||
}
|
||||
|
||||
private void HandleItemClick(TItem item)
|
||||
private void OnBreakpoint(BreakpointType breakPoint)
|
||||
{
|
||||
if (OnItemClick.HasDelegate)
|
||||
var column = breakPoint switch
|
||||
{
|
||||
OnItemClick.InvokeAsync(item);
|
||||
BreakpointType.Xs => Grid.Xs,
|
||||
BreakpointType.Sm => Grid.Sm,
|
||||
BreakpointType.Md => Grid.Md,
|
||||
BreakpointType.Lg => Grid.Lg,
|
||||
BreakpointType.Xl => Grid.Xl,
|
||||
BreakpointType.Xxl => Grid.Xxl,
|
||||
_ => 4,
|
||||
};
|
||||
|
||||
if (column > 0)
|
||||
{
|
||||
_columnWidth = 100d / column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
components/list/IAntList.cs
Normal file
20
components/list/IAntList.cs
Normal file
@ -0,0 +1,20 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign
|
||||
{
|
||||
internal interface IAntList
|
||||
{
|
||||
internal ListGridType Grid { get; }
|
||||
|
||||
internal ListItemLayout ItemLayout { get; }
|
||||
|
||||
internal double ColumnWidth { get; }
|
||||
}
|
||||
}
|
@ -9,18 +9,20 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<AntDesign.Col Flex="1" Style="@ColStyle">
|
||||
<div class="@ClassMapper.Class" style="@Style" Id="@Id" @onclick="HandleClick" @onclick:stopPropagation @ref="Ref">
|
||||
@itemChildren(this)
|
||||
</div>
|
||||
</AntDesign.Col>
|
||||
<div style="width: @(AntList.ColumnWidth)%; max-width: @(AntList.ColumnWidth)%;">
|
||||
<AntDesign.Col Flex="1" >
|
||||
<div class="@ClassMapper.Class" style="@Style" Id="@Id" @onclick="HandleClick" @onclick:stopPropagation @ref="Ref">
|
||||
@itemChildren(this)
|
||||
</div>
|
||||
</AntDesign.Col>
|
||||
</div>
|
||||
}
|
||||
@code{
|
||||
|
||||
|
||||
RenderFragment<ListItem> itemChildren = content =>
|
||||
@<Template>
|
||||
@if (content.ItemLayout == ListItemLayout.Vertical && content.Extra != null)
|
||||
@if (content.IsVerticalAndExtra)
|
||||
{
|
||||
<div class="@content.PrefixName-main">
|
||||
@content.ChildContent
|
||||
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.JsInterop;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@ -19,8 +14,6 @@ namespace AntDesign
|
||||
|
||||
[Parameter] public RenderFragment[] Actions { get; set; }
|
||||
|
||||
[Parameter] public ListGridType Grid { get; set; }
|
||||
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
|
||||
[Parameter] public string ColStyle { get; set; }
|
||||
@ -31,86 +24,21 @@ namespace AntDesign
|
||||
|
||||
[Parameter] public bool NoFlex { get; set; }
|
||||
|
||||
[CascadingParameter] public ListItemLayout ItemLayout { get; set; }
|
||||
|
||||
[CascadingParameter(Name = "ItemClick")] public Action ItemClick { get; set; }
|
||||
[CascadingParameter]
|
||||
private IAntList AntList { get; set; }
|
||||
|
||||
[Inject]
|
||||
public DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
public bool IsVerticalAndExtra()
|
||||
{
|
||||
return this.ItemLayout == ListItemLayout.Vertical && this.Extra != null;
|
||||
}
|
||||
public bool IsVerticalAndExtra => AntList?.ItemLayout == ListItemLayout.Vertical && this.Extra != null;
|
||||
private ListGridType Grid => AntList.Grid;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
SetClassMap();
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender && Grid != null)
|
||||
{
|
||||
await this.SetGutterStyle();
|
||||
DomEventService.AddEventListener<object>("window", "resize", OnResize, false);
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
private async void OnResize(object o)
|
||||
{
|
||||
await SetGutterStyle();
|
||||
}
|
||||
|
||||
private static Hashtable _gridResponsiveMap = new Hashtable()
|
||||
{
|
||||
[nameof(BreakpointEnum.xs)] = "(max-width: 575px)",
|
||||
[nameof(BreakpointEnum.sm)] = "(max-width: 576px)",
|
||||
[nameof(BreakpointEnum.md)] = "(max-width: 768px)",
|
||||
[nameof(BreakpointEnum.lg)] = "(max-width: 992px)",
|
||||
[nameof(BreakpointEnum.xl)] = "(max-width: 1200px)",
|
||||
[nameof(BreakpointEnum.xxl)] = "(max-width: 1600px)",
|
||||
};
|
||||
|
||||
private async Task SetGutterStyle()
|
||||
{
|
||||
string breakPoint = null;
|
||||
|
||||
await typeof(BreakpointEnum).GetEnumNames().ForEachAsync(async bp =>
|
||||
{
|
||||
if (await JsInvokeAsync<bool>(JSInteropConstants.MatchMedia, _gridResponsiveMap[bp]))
|
||||
{
|
||||
breakPoint = bp;
|
||||
}
|
||||
});
|
||||
|
||||
var column = GetColumn(breakPoint);
|
||||
|
||||
int columnCount = column > 0 ? column : (Grid?.Column ?? 0);
|
||||
if (Grid != null && columnCount > 0)
|
||||
{
|
||||
ColStyle = $"width:{100 / columnCount}%;max-width:{100 / columnCount}%";
|
||||
}
|
||||
|
||||
InvokeStateHasChanged();
|
||||
}
|
||||
|
||||
private int GetColumn(string breakPoint)
|
||||
{
|
||||
var column = 0;
|
||||
if (Grid != null && !string.IsNullOrEmpty(breakPoint))
|
||||
{
|
||||
var value = GetModelValue(breakPoint, Grid);
|
||||
int.TryParse(value, out column);
|
||||
return column;
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
protected void SetClassMap()
|
||||
{
|
||||
ClassMapper.Clear()
|
||||
@ -118,48 +46,18 @@ namespace AntDesign
|
||||
.If($"{PrefixName}-no-flex", () => NoFlex);
|
||||
}
|
||||
|
||||
private bool IsFlexMode()
|
||||
{
|
||||
if (ItemLayout == ListItemLayout.Vertical)
|
||||
{
|
||||
return Extra != null;
|
||||
}
|
||||
|
||||
return (Actions != null || Grid != null) && ItemCount > 1;
|
||||
}
|
||||
|
||||
private string GetModelValue(string fieldName, object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (obj == null || string.IsNullOrEmpty(fieldName)) return null;
|
||||
var o = obj.GetType().GetProperty(fieldName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance).GetValue(obj, null);
|
||||
var value = o?.ToString() ?? null;
|
||||
if (string.IsNullOrEmpty(value)) return null;
|
||||
return value;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleClick()
|
||||
{
|
||||
if (OnClick.HasDelegate)
|
||||
{
|
||||
OnClick.InvokeAsync(this);
|
||||
}
|
||||
|
||||
ItemClick?.Invoke();
|
||||
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
|
||||
DomEventService.RemoveEventListerner<object>("window", "resize", OnResize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ namespace AntDesign
|
||||
|
||||
public Dictionary<string, object> Attributes { get; set; }
|
||||
|
||||
[Inject] private DomEventService DomEventService { get; set; }
|
||||
internal List<MentionsOption> LstOriginalOptions { get; set; } = new List<MentionsOption>();
|
||||
|
||||
private string DropdownStyle { get; set; }
|
||||
@ -63,9 +62,6 @@ namespace AntDesign
|
||||
{
|
||||
Value = DefaultValue;
|
||||
}
|
||||
|
||||
//DomEventService.AddEventListener(Ref, "keyup", OnTextAreaKeyup);
|
||||
// DomEventService.AddEventListener(Ref, "onmouseup", OnTextAreaMouseUp);
|
||||
}
|
||||
|
||||
internal bool FirstTime { get; set; } = true;
|
||||
|
@ -20,6 +20,7 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public MenuTheme Theme { get; set; } = MenuTheme.Light;
|
||||
|
||||
internal MenuMode? InitialMode { get; private set; }
|
||||
[Parameter]
|
||||
public MenuMode Mode
|
||||
{
|
||||
@ -110,7 +111,7 @@ namespace AntDesign
|
||||
public EventCallback<string[]> SelectedKeysChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TriggerType TriggerSubMenuAction { get; set; } = TriggerType.Hover;
|
||||
public Trigger TriggerSubMenuAction { get; set; } = Trigger.Hover;
|
||||
|
||||
internal MenuMode InternalMode { get; private set; }
|
||||
|
||||
@ -223,7 +224,7 @@ namespace AntDesign
|
||||
throw new ArgumentException($"{nameof(Menu)} in the {Mode} mode cannot be {nameof(InlineCollapsed)}");
|
||||
|
||||
InternalMode = Mode;
|
||||
|
||||
InitialMode = Mode;
|
||||
Parent?.AddMenu(this);
|
||||
|
||||
OpenKeys = DefaultOpenKeys?.ToArray() ?? OpenKeys;
|
||||
|
@ -2,43 +2,67 @@
|
||||
@inherits AntDomComponentBase
|
||||
|
||||
<CascadingValue Value="this" IsFixed>
|
||||
<Tooltip Title="@content(this)" Placement="@PlacementType.Right" Disabled="TooltipDisabled">
|
||||
<Unbound>
|
||||
<li class="@ClassMapper.Class" role="menuitem" style=" @(PaddingLeft>0? $"padding-left:{PaddingLeft}px;":"") @Style" @onclick="HandleOnClick" @key="Key" @ref="context.Current">
|
||||
@* There is no need to render the tooltip if there is no inline mode. Tooltip will be only showing menu content if menu is collapsed to icon version && only for root menu *@
|
||||
@if (RootMenu.Mode == MenuMode.Inline && ParentMenu is null)
|
||||
{
|
||||
<Tooltip Title="@content(this)" Placement="@Placement.Right" Disabled="TooltipDisabled">
|
||||
<Unbound Context="tooltip">
|
||||
<li class="@ClassMapper.Class" role="menuitem" style=" @(PaddingLeft>0? $"padding-left:{PaddingLeft}px;":"") @Style" @onclick="HandleOnClick" @key="Key" @ref="tooltip.Current">
|
||||
@if (IconTemplate != null)
|
||||
{
|
||||
@IconTemplate
|
||||
}
|
||||
else if (Icon != null)
|
||||
{
|
||||
<Icon Type="@Icon" />
|
||||
}
|
||||
<span class="ant-menu-title-content">
|
||||
@if (RouterLink == null)
|
||||
{
|
||||
@content(this)
|
||||
}
|
||||
else
|
||||
{
|
||||
<MenuLink Href="@RouterLink" Match="@RouterMatch">@content(this)</MenuLink>
|
||||
}
|
||||
</span>
|
||||
</li>
|
||||
</Unbound>
|
||||
</Tooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="@ClassMapper.Class" role="menuitem" style=" @(PaddingLeft>0? $"padding-left:{PaddingLeft}px;":"") @Style" @onclick="HandleOnClick" @key="Key">
|
||||
@if (Icon != null)
|
||||
{
|
||||
<Icon Type="@Icon" />
|
||||
}
|
||||
|
||||
@if (IconTemplate != null)
|
||||
{
|
||||
@IconTemplate
|
||||
}
|
||||
else if (Icon != null)
|
||||
{
|
||||
<Icon Type="@Icon" />
|
||||
}
|
||||
|
||||
@if (RouterLink == null)
|
||||
{
|
||||
@content(this)
|
||||
}
|
||||
else
|
||||
{
|
||||
<MenuLink Href="@RouterLink" Match="@RouterMatch">@content(this)</MenuLink>
|
||||
}
|
||||
</li>
|
||||
</Unbound>
|
||||
</Tooltip>
|
||||
<span class="ant-menu-title-content">
|
||||
@if (RouterLink == null)
|
||||
{
|
||||
@content(this)
|
||||
}
|
||||
else
|
||||
{
|
||||
<MenuLink Href="@RouterLink" Match="@RouterMatch">@content(this)</MenuLink>
|
||||
}
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
RenderFragment<MenuItem> content = item =>@<Template>
|
||||
<span>
|
||||
@if (item.Title != null)
|
||||
{
|
||||
@item.Title
|
||||
}
|
||||
else
|
||||
{
|
||||
@item.ChildContent
|
||||
}
|
||||
</span>
|
||||
</Template>;
|
||||
RenderFragment<MenuItem> content = item =>
|
||||
@<Template>
|
||||
@if (item.Title != null)
|
||||
{
|
||||
@item.Title
|
||||
}
|
||||
else
|
||||
{
|
||||
@item.ChildContent
|
||||
}
|
||||
</Template>
|
||||
;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ else
|
||||
{
|
||||
if (Placement == null)
|
||||
{
|
||||
Placement = (RootMenu.Mode == MenuMode.Horizontal && Parent == null) ? PlacementType.BottomLeft : PlacementType.RightTop;
|
||||
Placement = (RootMenu.Mode == MenuMode.Horizontal && Parent == null) ? AntDesign.Placement.BottomLeft : AntDesign.Placement.RightTop;
|
||||
}
|
||||
|
||||
<CascadingValue Value="this" Name="SubMenu" IsFixed="@true">
|
||||
@ -41,11 +41,11 @@ else
|
||||
ComplexAutoCloseAndVisible="true"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"
|
||||
Disabled="Disabled"
|
||||
Placement="Placement"
|
||||
Placement="Placement.Value"
|
||||
OnVisibleChange="OnOverlayVisibleChange"
|
||||
OnOverlayHiding="OnOverlayHiding"
|
||||
Trigger="new[] { RootMenu?.TriggerSubMenuAction }"
|
||||
PlacementCls="@($"{prefixCls}-placement-{Placement.Name} {prefixCls}-popup")"
|
||||
Trigger="new Trigger[] { RootMenu?.TriggerSubMenuAction ?? Trigger.Hover }"
|
||||
PlacementCls="@($"{prefixCls}-placement-{_placement.Name} {prefixCls}-popup")"
|
||||
OverlayEnterCls="@($"{prefixCls}-{RootMenu.Theme} ")"
|
||||
OverlayLeaveCls="@($"{prefixCls}-{RootMenu.Theme} ")"
|
||||
OverlayHiddenCls="@($"{RootMenu.PrefixCls}-hidden")">
|
||||
|
@ -16,7 +16,21 @@ namespace AntDesign
|
||||
public SubMenu Parent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public PlacementType Placement { get; set; }
|
||||
public Placement? Placement
|
||||
{
|
||||
get { return _placement?.Placement; }
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_placement = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_placement = PlacementType.Create(value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public string Title { get; set; }
|
||||
@ -57,6 +71,7 @@ namespace AntDesign
|
||||
private OverlayTrigger _overlayTrigger;
|
||||
|
||||
internal bool _overlayVisible;
|
||||
private PlacementType? _placement;
|
||||
|
||||
private void SetClass()
|
||||
{
|
||||
@ -136,7 +151,16 @@ namespace AntDesign
|
||||
base.OnParametersSet();
|
||||
|
||||
if (!RootMenu.InlineCollapsed && RootMenu.OpenKeys.Contains(Key))
|
||||
IsOpen = true;
|
||||
{
|
||||
if (RootMenu.InitialMode != RootMenu.Mode)
|
||||
{
|
||||
IsOpen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
|
@ -7,16 +7,18 @@
|
||||
{
|
||||
<div class=@($"{Config.PrefixCls}-mask {GetMaskClsName()}") style="@Config.MaskStyle"></div>
|
||||
}
|
||||
<div tabindex="-1" id=@($"{Config.PrefixCls}-wrap_{DialogWrapperId}") class=@($"{Config.PrefixCls}-wrap {Config.GetWrapClassNameExtended()}") role="dialog"
|
||||
<div tabindex="-1" class=@($"{Config.PrefixCls}-wrap {Config.GetWrapClassNameExtended()}") role="dialog"
|
||||
@onclick="@EventUtil.AsNonRenderingEventHandler(OnMaskClick)"
|
||||
@onmouseup="@EventUtil.AsNonRenderingEventHandler(OnMaskMouseUp)"
|
||||
@onkeydown="@OnKeyDown"
|
||||
style="@_wrapStyle">
|
||||
<div @ref="@_modal" role="document" class=@($"{Config.PrefixCls} {GetModalClsName()}")
|
||||
@onclick:stopPropagation
|
||||
@onmousedown="@EventUtil.AsNonRenderingEventHandler(OnDialogMouseDown)"
|
||||
style="@GetStyle()">
|
||||
style="@GetStyle()"
|
||||
>
|
||||
<div id="@_sentinelStart" tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div>
|
||||
<div class=@($"{Config.PrefixCls}-content")>
|
||||
<div class=@($"{Config.PrefixCls}-content") id=@($"{Config.PrefixCls}-wrap_{DialogWrapperId}") >
|
||||
@if (Config.Closable)
|
||||
{
|
||||
<button type="button" aria-label="Close" class=@($"{Config.PrefixCls}-close") @onclick="@OnCloserClick">
|
||||
|
@ -25,6 +25,7 @@
|
||||
Size="@(IsSmall ? "small" : "default")"
|
||||
Class="@($"{prefixCls}-size-changer")"
|
||||
DefaultValue="@(PageSize > 0 ? PageSize : pageSizeOptions[0])"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.InView"
|
||||
OnSelectedItemChanged="@(i => {if (ChangeSize.HasDelegate)ChangeSize.InvokeAsync(i);})">
|
||||
<SelectOptions>
|
||||
@foreach(var opt in pageSizeOptions)
|
||||
|
@ -54,13 +54,13 @@ namespace AntDesign
|
||||
public Popconfirm()
|
||||
{
|
||||
PrefixCls = "ant-popover";
|
||||
Placement = PlacementType.Top;
|
||||
Trigger = new[] { TriggerType.Click };
|
||||
Placement = Placement.Top;
|
||||
Trigger = new[] { AntDesign.Trigger.Click };
|
||||
}
|
||||
|
||||
internal override async Task Show(int? overlayLeft = null, int? overlayTop = null)
|
||||
{
|
||||
if (Trigger.Contains(TriggerType.Hover))
|
||||
if (Trigger.Contains(AntDesign.Trigger.Hover))
|
||||
{
|
||||
await Task.Delay((int)(MouseEnterDelay * 1000));
|
||||
}
|
||||
@ -70,7 +70,7 @@ namespace AntDesign
|
||||
|
||||
internal override async Task Hide(bool force = false)
|
||||
{
|
||||
if (Trigger.Contains(TriggerType.Hover))
|
||||
if (Trigger.Contains(AntDesign.Trigger.Hover))
|
||||
{
|
||||
await Task.Delay((int)(MouseLeaveDelay * 1000));
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ namespace AntDesign
|
||||
public Popover()
|
||||
{
|
||||
PrefixCls = "ant-popover";
|
||||
Placement = PlacementType.Top;
|
||||
Placement = Placement.Top;
|
||||
}
|
||||
|
||||
internal override string GetOverlayEnterClass()
|
||||
@ -47,7 +47,7 @@ namespace AntDesign
|
||||
|
||||
internal override async Task Show(int? overlayLeft = null, int? overlayTop = null)
|
||||
{
|
||||
if (Trigger.Contains(TriggerType.Hover))
|
||||
if (Trigger.Contains(AntDesign.Trigger.Hover))
|
||||
{
|
||||
await Task.Delay((int)(MouseEnterDelay * 1000));
|
||||
}
|
||||
@ -57,7 +57,7 @@ namespace AntDesign
|
||||
|
||||
internal override async Task Hide(bool force = false)
|
||||
{
|
||||
if (Trigger.Contains(TriggerType.Hover))
|
||||
if (Trigger.Contains(AntDesign.Trigger.Hover))
|
||||
{
|
||||
await Task.Delay((int)(MouseLeaveDelay * 1000));
|
||||
}
|
||||
|
@ -11,8 +11,9 @@
|
||||
<OverlayTrigger @ref="@_dropDown"
|
||||
Visible="Open"
|
||||
Disabled="Disabled"
|
||||
Trigger="new[] { TriggerType.Click }"
|
||||
HiddenMode
|
||||
Trigger="new[] { Trigger.Click }"
|
||||
HiddenMode
|
||||
BoundaryAdjustMode="@BoundaryAdjustMode"
|
||||
OnMouseEnter="@(() => { OnMouseEnter?.Invoke(); })"
|
||||
OnMouseLeave="@(() => { OnMouseLeave?.Invoke(); })"
|
||||
OnVisibleChange="@OnOverlayVisibleChangeAsync"
|
||||
@ -23,7 +24,7 @@
|
||||
<div style="@_dropdownStyle">
|
||||
@if (SelectOptions != null)
|
||||
{
|
||||
<div class="" style="max-height: @PopupContainerMaxHeight; overflow-y: auto;">
|
||||
<div class="" style="max-height: @PopupContainerMaxHeight; overflow-y: auto;" @ref="_scrollableSelectDiv">
|
||||
<div>
|
||||
<div class="" role="listbox" style="display: flex; flex-direction: column;">
|
||||
@if (CustomTagSelectOptionItem != null)
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.Core.Helpers.MemberPath;
|
||||
@ -21,84 +22,70 @@ namespace AntDesign
|
||||
{
|
||||
#region Parameters
|
||||
|
||||
/// <summary>
|
||||
/// Overlay adjustment strategy (when for example browser resize is happening)
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public TriggerBoundaryAdjustMode BoundaryAdjustMode { get; set; } = TriggerBoundaryAdjustMode.None;
|
||||
|
||||
/// <summary>
|
||||
/// Toggle the border style.
|
||||
/// </summary>
|
||||
[Parameter] public bool Bordered { get; set; } = true;
|
||||
|
||||
|
||||
bool _dataSourceHasChanged = false;
|
||||
IEnumerable<TItem> _dataSourceCopy;
|
||||
IEnumerable<TItem> _dataSourceShallowCopy;
|
||||
//private bool? _isTItemPrimitive;
|
||||
//private bool IsTItemPrimitive
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// if (_isTItemPrimitive is null)
|
||||
// {
|
||||
// _isTItemPrimitive = IsSimpleType(typeof(TItem));
|
||||
// }
|
||||
// return _isTItemPrimitive!.Value;
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// The datasource for this component.
|
||||
/// MethodInfo will contain attached MemberwiseClone protected
|
||||
/// method. Due to its protection level, it has to be accessed
|
||||
/// using reflection. It will be used during generation of
|
||||
/// the DataSource shallow copy (which is a new list of DataSource
|
||||
/// items with shallow copy of each item).
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IEnumerable<TItem> DataSource
|
||||
private MethodInfo _dataSourceItemShallowCopyMehtod;
|
||||
private MethodInfo GetDataSourceItemCloneMethod()
|
||||
{
|
||||
get => _datasource;
|
||||
set
|
||||
if (_dataSourceItemShallowCopyMehtod is null)
|
||||
{
|
||||
if (value == null && _datasource == null)
|
||||
_dataSourceItemShallowCopyMehtod = this.GetType().GetGenericArguments()[1]
|
||||
.GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (DataSourceEqualityComparer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == null && _datasource != null)
|
||||
{
|
||||
if (!_isInitialized)
|
||||
{
|
||||
_selectedValue = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectOptionItems.Clear();
|
||||
SelectedOptionItems.Clear();
|
||||
Value = default;
|
||||
|
||||
_datasource = null;
|
||||
|
||||
OnDataSourceChanged?.Invoke();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != null && !value.Any() && SelectOptionItems.Any())
|
||||
{
|
||||
SelectOptionItems.Clear();
|
||||
SelectedOptionItems.Clear();
|
||||
|
||||
Value = default;
|
||||
var sameObject = object.ReferenceEquals(_datasource, value);
|
||||
|
||||
_datasource = value;
|
||||
|
||||
if (!sameObject)
|
||||
OnDataSourceChanged?.Invoke();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
bool hasChanged;
|
||||
|
||||
if (_datasource == null)
|
||||
{
|
||||
hasChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasChanged = !value.SequenceEqual(_datasource);
|
||||
}
|
||||
|
||||
if (hasChanged)
|
||||
{
|
||||
OnDataSourceChanged?.Invoke();
|
||||
|
||||
_datasource = value;
|
||||
}
|
||||
DataSourceEqualityComparer = new DataSourceEqualityComparer<TItemValue, TItem>(this);
|
||||
}
|
||||
}
|
||||
return _dataSourceItemShallowCopyMehtod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The datasource for this component.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IEnumerable<TItem> DataSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// EqualityComparer that will be used during DataSource change
|
||||
/// detection. If no comparer set, default .Net is going to be
|
||||
/// used.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IEqualityComparer<TItem> DataSourceEqualityComparer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Activates the first item that is not deactivated.
|
||||
/// </summary>
|
||||
@ -315,6 +302,7 @@ namespace AntDesign
|
||||
if (_valueHasChanged)
|
||||
{
|
||||
_selectedValue = value;
|
||||
_valueHasChanged = _isInitialized;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -339,13 +327,9 @@ namespace AntDesign
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion Parameters
|
||||
|
||||
[Inject] private DomEventService DomEventService { get; set; }
|
||||
[Inject] private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
#region Properties
|
||||
|
||||
@ -359,12 +343,14 @@ namespace AntDesign
|
||||
}
|
||||
|
||||
internal ElementReference DropDownRef => _dropDown.GetOverlayComponent().Ref;
|
||||
private ElementReference _scrollableSelectDiv;
|
||||
|
||||
private string _dropdownStyle = string.Empty;
|
||||
private TItemValue _selectedValue;
|
||||
private TItemValue _defaultValue;
|
||||
private bool _defaultValueIsNotNull;
|
||||
private IEnumerable<TItem> _datasource;
|
||||
private bool _afterFirstRender;
|
||||
private bool _optionsHasInitialized;
|
||||
private bool _defaultValueApplied;
|
||||
private bool _defaultActiveFirstOptionApplied;
|
||||
@ -376,7 +362,7 @@ namespace AntDesign
|
||||
|
||||
private string _labelName;
|
||||
|
||||
private Func<TItem, string> _getLabel;
|
||||
internal Func<TItem, string> _getLabel;
|
||||
|
||||
|
||||
private string _groupName = string.Empty;
|
||||
@ -389,7 +375,7 @@ namespace AntDesign
|
||||
|
||||
private string _valueName;
|
||||
|
||||
private Func<TItem, TItemValue> _getValue;
|
||||
internal Func<TItem, TItemValue> _getValue;
|
||||
|
||||
private bool _disableSubmitFormOnEnter;
|
||||
private bool _showArrowIcon = true;
|
||||
@ -441,10 +427,15 @@ namespace AntDesign
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
EvaluateDataSourceChange();
|
||||
if (SelectOptions == null)
|
||||
{
|
||||
CreateDeleteSelectOptions();
|
||||
_optionsHasInitialized = true;
|
||||
if (!_optionsHasInitialized || _dataSourceHasChanged)
|
||||
{
|
||||
CreateDeleteSelectOptions();
|
||||
_optionsHasInitialized = true;
|
||||
_dataSourceHasChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_valueHasChanged && _optionsHasInitialized)
|
||||
@ -456,10 +447,116 @@ namespace AntDesign
|
||||
EditContext?.NotifyFieldChanged(FieldIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnParametersSet();
|
||||
}
|
||||
|
||||
private void EvaluateDataSourceChange()
|
||||
{
|
||||
if (DataSource == null && _datasource == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (DataSource == null && _datasource != null)
|
||||
{
|
||||
SelectOptionItems.Clear();
|
||||
SelectedOptionItems.Clear();
|
||||
Value = default;
|
||||
|
||||
_datasource = null;
|
||||
_dataSourceCopy = null;
|
||||
_dataSourceShallowCopy = null;
|
||||
|
||||
OnDataSourceChanged?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
if (DataSource != null && !DataSource.Any() && SelectOptionItems.Any())
|
||||
{
|
||||
SelectOptionItems.Clear();
|
||||
SelectedOptionItems.Clear();
|
||||
|
||||
Value = default;
|
||||
|
||||
_datasource = DataSource;
|
||||
_dataSourceShallowCopy = new List<TItem>();
|
||||
_dataSourceCopy = new List<TItem>();
|
||||
|
||||
OnDataSourceChanged?.Invoke();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (DataSource != null)
|
||||
{
|
||||
if (_datasource == null)
|
||||
{
|
||||
_dataSourceHasChanged = true;
|
||||
}
|
||||
else if (_isPrimitive)
|
||||
{
|
||||
_dataSourceHasChanged = !DataSource.SequenceEqual(_dataSourceCopy);
|
||||
}
|
||||
else if (_getValue is null)
|
||||
{
|
||||
_dataSourceHasChanged = !DataSource.SequenceEqual(_dataSourceCopy) ||
|
||||
!DataSource.SequenceEqual(_dataSourceShallowCopy, DataSourceEqualityComparer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dataSourceHasChanged = !DataSource.SequenceEqual(_dataSourceShallowCopy, DataSourceEqualityComparer);
|
||||
}
|
||||
|
||||
if (_dataSourceHasChanged)
|
||||
{
|
||||
OnDataSourceChanged?.Invoke();
|
||||
_datasource = DataSource;
|
||||
if (_isPrimitive)
|
||||
{
|
||||
_dataSourceCopy = _datasource.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_getValue is null)
|
||||
{
|
||||
_dataSourceCopy = _datasource.ToList();
|
||||
}
|
||||
var cloneMethod = GetDataSourceItemCloneMethod();
|
||||
_dataSourceShallowCopy = _datasource.Select(x => (TItem)cloneMethod.Invoke(x, null)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used only when ChildElement SelectOptions is used.
|
||||
/// Will run this process if after initalization an item
|
||||
/// is added that is also marked as selected.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal async Task ProcessSelectedSelectOptions()
|
||||
{
|
||||
if (_isInitialized && _afterFirstRender)
|
||||
{
|
||||
if (Mode == "default")
|
||||
{
|
||||
if (LastValueBeforeReset is not null)
|
||||
{
|
||||
OnValueChange(LastValueBeforeReset);
|
||||
LastValueBeforeReset = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnValueChange(Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await OnValuesChangeAsync(Values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (SelectOptions != null)
|
||||
@ -471,7 +568,7 @@ namespace AntDesign
|
||||
{
|
||||
await SetInitialValuesAsync();
|
||||
|
||||
DomEventService.AddEventListener("window", "resize", OnWindowResize, false);
|
||||
DomEventListener.AddShared<JsonElement>("window", "resize", OnWindowResize);
|
||||
await SetDropdownStyleAsync();
|
||||
|
||||
_defaultValueApplied = !(_defaultValueIsNotNull || _defaultValuesHasItems);
|
||||
@ -511,11 +608,12 @@ namespace AntDesign
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
_afterFirstRender = true;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "resize", OnWindowResize);
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@ -567,7 +665,7 @@ namespace AntDesign
|
||||
|
||||
foreach (var item in _datasource)
|
||||
{
|
||||
TItemValue value = _getValue == null ? THelper.ChangeType<TItemValue>(item) : _getValue(item);
|
||||
TItemValue value = _getValue == null ? (TItemValue)(object)item : _getValue(item);
|
||||
|
||||
var exists = false;
|
||||
SelectOptionItem<TItemValue, TItem> selectOption;
|
||||
@ -725,7 +823,7 @@ namespace AntDesign
|
||||
/// <returns></returns>
|
||||
private async Task ElementScrollIntoViewAsync(ElementReference element)
|
||||
{
|
||||
await JsInvokeAsync(JSInteropConstants.ScrollTo, element);
|
||||
await JsInvokeAsync(JSInteropConstants.ScrollTo, element, _scrollableSelectDiv);
|
||||
}
|
||||
|
||||
|
||||
@ -960,14 +1058,20 @@ namespace AntDesign
|
||||
return newItem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected virtual string GetLabel(TItem item)
|
||||
{
|
||||
return item.ToString();
|
||||
}
|
||||
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// When newly set Value is not found in SelectOptionItems, it is reset to
|
||||
/// default. This property holds the value before reset. It may be needed
|
||||
/// to be reaplied (for example when new Value is set at the same time
|
||||
/// as new SelectOption is added, but Value in the component is set
|
||||
/// before new SelectOptionItem has been created).
|
||||
/// </summary>
|
||||
internal TItemValue LastValueBeforeReset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Method is called every time if the value of the @bind-Value was changed by the two-way binding.
|
||||
@ -987,6 +1091,11 @@ namespace AntDesign
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
if (SelectOptions is not null)
|
||||
{
|
||||
LastValueBeforeReset = value;
|
||||
}
|
||||
|
||||
if (!AllowClear)
|
||||
_ = TrySetDefaultValueAsync();
|
||||
else
|
||||
@ -1399,9 +1508,6 @@ namespace AntDesign
|
||||
currentSelected.IsActive = true;
|
||||
ActiveOption = currentSelected;
|
||||
|
||||
// ToDo: Sometime the element does not scroll, you have to call the function twice
|
||||
await ElementScrollIntoViewAsync(currentSelected.Ref);
|
||||
await Task.Delay(1);
|
||||
await ElementScrollIntoViewAsync(currentSelected.Ref);
|
||||
}
|
||||
|
||||
@ -1504,9 +1610,6 @@ namespace AntDesign
|
||||
currentSelected.IsActive = true;
|
||||
ActiveOption = currentSelected;
|
||||
|
||||
// ToDo: Sometime the element does not scroll, you have to call the function twice
|
||||
await ElementScrollIntoViewAsync(currentSelected.Ref);
|
||||
await Task.Delay(1);
|
||||
await ElementScrollIntoViewAsync(currentSelected.Ref);
|
||||
}
|
||||
|
||||
@ -1703,9 +1806,6 @@ namespace AntDesign
|
||||
|
||||
currentSelected.IsActive = true;
|
||||
ActiveOption = currentSelected;
|
||||
// ToDo: Sometime the element does not scroll, you have to call the function twice
|
||||
await ElementScrollIntoViewAsync(currentSelected.Ref);
|
||||
await Task.Delay(1);
|
||||
await ElementScrollIntoViewAsync(currentSelected.Ref);
|
||||
}
|
||||
else if (ActiveOption == null)//position on first element in the list
|
||||
|
@ -161,6 +161,7 @@ namespace AntDesign
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
bool isAlreadySelected = false;
|
||||
if (SelectParent.SelectOptions == null)
|
||||
{
|
||||
// The SelectOptionItem was already created, now only the SelectOption has to be
|
||||
@ -176,8 +177,9 @@ namespace AntDesign
|
||||
GroupName = Model.GroupName;
|
||||
Value = Model.Value;
|
||||
Model.ChildComponent = this;
|
||||
isAlreadySelected = IsAlreadySelected(Model);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// The SelectOption was not created by using a DataSource, a SelectOptionItem must be created.
|
||||
InternalId = Guid.NewGuid();
|
||||
@ -194,11 +196,27 @@ namespace AntDesign
|
||||
};
|
||||
|
||||
SelectParent.SelectOptionItems.Add(newSelectOptionItem);
|
||||
isAlreadySelected = IsAlreadySelected(newSelectOptionItem);
|
||||
}
|
||||
|
||||
SetClassMap();
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
if (isAlreadySelected)
|
||||
{
|
||||
await SelectParent.ProcessSelectedSelectOptions();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAlreadySelected(SelectOptionItem<TItemValue, TItem> selectOption)
|
||||
{
|
||||
if (SelectParent.Mode == "default")
|
||||
{
|
||||
return selectOption.Value.Equals(SelectParent.Value) || selectOption.Value.Equals(SelectParent.LastValueBeforeReset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SelectParent.Values is null || SelectParent.Values.Contains(selectOption.Value);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetClassMap()
|
||||
|
38
components/select/internal/DataSourceEqualityComparer.cs
Normal file
38
components/select/internal/DataSourceEqualityComparer.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AntDesign
|
||||
{
|
||||
internal class DataSourceEqualityComparer<TItemValue, TItem> : IEqualityComparer<TItem>
|
||||
{
|
||||
private Select<TItemValue, TItem> SelectParent { get; }
|
||||
|
||||
public DataSourceEqualityComparer(Select<TItemValue, TItem> selectParent)
|
||||
{
|
||||
SelectParent = selectParent;
|
||||
}
|
||||
|
||||
public bool Equals(TItem x, TItem y)
|
||||
{
|
||||
if (SelectParent._getLabel is null)
|
||||
{
|
||||
if (SelectParent._getValue is null)
|
||||
{
|
||||
return x.ToString().Equals(y.ToString());
|
||||
}
|
||||
return x.ToString().Equals(y.ToString())
|
||||
&& SelectParent._getValue(x).Equals(SelectParent._getValue(y));
|
||||
}
|
||||
if (SelectParent._getValue is null)
|
||||
{
|
||||
return SelectParent._getLabel(x).Equals(SelectParent._getLabel(y));
|
||||
}
|
||||
return SelectParent._getLabel(x).Equals(SelectParent._getLabel(y))
|
||||
&& SelectParent._getValue(x).Equals(SelectParent._getValue(y));
|
||||
}
|
||||
|
||||
public int GetHashCode(TItem obj)
|
||||
{
|
||||
return obj.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ using Microsoft.JSInterop;
|
||||
|
||||
namespace AntDesign.Select.Internal
|
||||
{
|
||||
public partial class SelectContent<TItemValue, TItem>: IDisposable
|
||||
public partial class SelectContent<TItemValue, TItem> : IDisposable
|
||||
{
|
||||
[CascadingParameter(Name = "ParentSelect")] internal SelectBase<TItemValue, TItem> ParentSelect { get; set; }
|
||||
[CascadingParameter(Name = "ParentLabelTemplate")] internal RenderFragment<TItem> ParentLabelTemplate { get; set; }
|
||||
@ -57,7 +57,7 @@ namespace AntDesign.Select.Internal
|
||||
[Parameter] public string SearchValue { get; set; }
|
||||
[Parameter] public ForwardRef RefBack { get; set; } = new ForwardRef();
|
||||
[Inject] protected IJSRuntime Js { get; set; }
|
||||
[Inject] private DomEventService DomEventService { get; set; }
|
||||
[Inject] private IDomEventListener DomEventListener { get; set; }
|
||||
protected ElementReference Ref
|
||||
{
|
||||
get { return _ref; }
|
||||
@ -108,7 +108,7 @@ namespace AntDesign.Select.Internal
|
||||
{
|
||||
if (ParentSelect.EnableSearch)
|
||||
{
|
||||
DomEventService.AddEventListener("window", "beforeunload", Reloading, false);
|
||||
DomEventListener.AddShared<JsonElement>("window", "beforeunload", Reloading);
|
||||
await Js.InvokeVoidAsync(JSInteropConstants.AddPreventKeys, ParentSelect._inputRef, new[] { "ArrowUp", "ArrowDown" });
|
||||
await Js.InvokeVoidAsync(JSInteropConstants.AddPreventEnterOnOverlayVisible, ParentSelect._inputRef, ParentSelect._dropDownRef);
|
||||
}
|
||||
@ -135,11 +135,11 @@ namespace AntDesign.Select.Internal
|
||||
_suffixElement = await Js.InvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _suffixRef);
|
||||
_suffixElement.Width += 7;
|
||||
}
|
||||
await DomEventService.AddResizeObserver(_overflow, OnOveralyResize);
|
||||
await DomEventListener.AddResizeObserver(_overflow, OnOveralyResize);
|
||||
await CalculateResponsiveTags();
|
||||
}
|
||||
DomEventService.AddEventListener(ParentSelect._inputRef, "focusout", OnBlurInternal, true);
|
||||
DomEventService.AddEventListener(ParentSelect._inputRef, "focus", OnFocusInternal, true);
|
||||
DomEventListener.AddExclusive<JsonElement>(ParentSelect._inputRef, "focusout", OnBlurInternal);
|
||||
DomEventListener.AddExclusive<JsonElement>(ParentSelect._inputRef, "focus", OnFocusInternal);
|
||||
}
|
||||
else if (_currentItemCount != ParentSelect.SelectedOptionItems.Count)
|
||||
{
|
||||
@ -418,14 +418,12 @@ namespace AntDesign.Select.Internal
|
||||
{
|
||||
await Task.Delay(100);
|
||||
if (ParentSelect.IsResponsive)
|
||||
await DomEventService.DisposeResizeObserver(_overflow);
|
||||
await DomEventListener.DisposeResizeObserver(_overflow);
|
||||
await Js.InvokeVoidAsync(JSInteropConstants.RemovePreventKeys, ParentSelect._inputRef);
|
||||
await Js.InvokeVoidAsync(JSInteropConstants.RemovePreventEnterOnOverlayVisible, ParentSelect._inputRef);
|
||||
});
|
||||
}
|
||||
DomEventService.RemoveEventListerner<JsonElement>(ParentSelect._inputRef, "focus", OnFocusInternal);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(ParentSelect._inputRef, "focusout", OnBlurInternal);
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "beforeunload", Reloading);
|
||||
DomEventListener.Dispose();
|
||||
|
||||
if (IsDisposed) return;
|
||||
|
||||
|
@ -120,7 +120,7 @@ namespace AntDesign
|
||||
}
|
||||
|
||||
[Inject]
|
||||
private DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
#region Parameters
|
||||
|
||||
@ -261,8 +261,6 @@ namespace AntDesign
|
||||
(double, double) typedValue = DataConvertionExtensions.Convert<TValue, (double, double)>(CurrentValue);
|
||||
if (value != typedValue.Item1)
|
||||
CurrentValue = DataConvertionExtensions.Convert<(double, double), TValue>((_leftValue, RightValue));
|
||||
if (_toolTipLeft != null)
|
||||
_toolTipLeft.ChildElementMoved();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -303,8 +301,6 @@ namespace AntDesign
|
||||
//CurrentValue = DoubleToGeneric(_rightValue);
|
||||
CurrentValue = DataConvertionExtensions.Convert<double, TValue>(_rightValue);
|
||||
}
|
||||
if (_toolTipRight != null)
|
||||
_toolTipRight.ChildElementMoved();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -366,7 +362,7 @@ namespace AntDesign
|
||||
/// Set Tooltip display position. Ref Tooltip
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public PlacementType TooltipPlacement { get; set; }
|
||||
public Placement TooltipPlacement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, Tooltip will show always, or it will not show anyway, even if dragging or hovering.
|
||||
@ -404,11 +400,6 @@ namespace AntDesign
|
||||
|
||||
#endregion Parameters
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
public async override Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
await base.SetParametersAsync(parameters);
|
||||
@ -460,9 +451,9 @@ namespace AntDesign
|
||||
if (!dict.ContainsKey(nameof(TooltipPlacement)))
|
||||
{
|
||||
if (Vertical)
|
||||
TooltipPlacement = PlacementType.Right;
|
||||
TooltipPlacement = Placement.Right;
|
||||
else
|
||||
TooltipPlacement = PlacementType.Top;
|
||||
TooltipPlacement = Placement.Top;
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,8 +478,8 @@ namespace AntDesign
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
DomEventService.AddEventListener("window", "mousemove", OnMouseMove, false);
|
||||
DomEventService.AddEventListener("window", "mouseup", OnMouseUp, false);
|
||||
DomEventListener.AddShared<JsonElement>("window", "mousemove", OnMouseMove);
|
||||
DomEventListener.AddShared<JsonElement>("window", "mouseup", OnMouseUp);
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
@ -496,9 +487,7 @@ namespace AntDesign
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "mousemove", OnMouseMove);
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "mouseup", OnMouseUp);
|
||||
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@ -509,11 +498,9 @@ namespace AntDesign
|
||||
if (_toolTipRight != null && HasTooltip)
|
||||
{
|
||||
_rightHandle = _toolTipRight.Ref;
|
||||
await _toolTipRight.ChildElementMoved();
|
||||
if (_toolTipLeft != null)
|
||||
{
|
||||
_leftHandle = _toolTipLeft.Ref;
|
||||
await _toolTipLeft.ChildElementMoved();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -668,7 +655,7 @@ namespace AntDesign
|
||||
_right = false;
|
||||
if (_mouseDown)
|
||||
RightValue = _initialLeftValue;
|
||||
LeftValue = rightV;
|
||||
LeftValue = rightV;
|
||||
await FocusAsync(_leftHandle);
|
||||
}
|
||||
else
|
||||
@ -715,7 +702,7 @@ namespace AntDesign
|
||||
_right = true;
|
||||
if (_mouseDown)
|
||||
LeftValue = _initialRightValue;
|
||||
RightValue = leftV;
|
||||
RightValue = leftV;
|
||||
await FocusAsync(_rightHandle);
|
||||
}
|
||||
else
|
||||
|
@ -106,5 +106,11 @@ namespace AntDesign
|
||||
_isLoading = Spinning;
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_delayTimer?.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,72 +1,89 @@
|
||||
@namespace AntDesign
|
||||
@using AntDesign.TableModels
|
||||
@inherits ColumnBase
|
||||
|
||||
@if (IsInitialize)
|
||||
{
|
||||
return;
|
||||
return;
|
||||
}
|
||||
else if (IsPlaceholder)
|
||||
{
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"></td>
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"></td>
|
||||
}
|
||||
else if (IsMeasure)
|
||||
{
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"><div style="height: 0px; overflow: hidden;"> </div></td>
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"><div style="height: 0px; overflow: hidden;"> </div></td>
|
||||
}
|
||||
else if (IsColGroup)
|
||||
{
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<col class="ant-table-expand-icon-col">
|
||||
}
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<col class="ant-table-expand-icon-col">
|
||||
}
|
||||
|
||||
if (Width != null)
|
||||
{
|
||||
<col style="width: @((CssSizeLength)Width); min-width: @((CssSizeLength)Width);">
|
||||
}
|
||||
else
|
||||
{
|
||||
<col />
|
||||
}
|
||||
if (Width != null)
|
||||
{
|
||||
<col style="width: @((CssSizeLength)Width); min-width: @((CssSizeLength)Width);">
|
||||
}
|
||||
else
|
||||
{
|
||||
<col />
|
||||
}
|
||||
}
|
||||
else if (IsHeader && HeaderColSpan != 0)
|
||||
{
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<th class="ant-table-cell ant-table-row-expand-icon-cell"></th>
|
||||
}
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<th class="ant-table-cell ant-table-row-expand-icon-cell"></th>
|
||||
}
|
||||
|
||||
<th class="@ClassMapper.Class" style="@FixedStyle @HeaderStyle" colspan="@HeaderColSpan">
|
||||
@if (TitleTemplate != null)@TitleTemplate else @Title
|
||||
</th>
|
||||
<th class="@ClassMapper.Class" style="@FixedStyle @HeaderStyle" colspan="@HeaderColSpan">
|
||||
@if (TitleTemplate != null)
|
||||
{
|
||||
@TitleTemplate
|
||||
}
|
||||
else
|
||||
{
|
||||
@Title
|
||||
}
|
||||
</th>
|
||||
}
|
||||
else if (!IsHeader && RowSpan != 0 && ColSpan != 0)
|
||||
{
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<td class="ant-table-cell ant-table-row-expand-icon-cell">
|
||||
@if (Table.RowExpandable(RowData) && (!Table.TreeMode || !RowData.HasChildren))
|
||||
{
|
||||
<button type="button" @onclick="ToggleTreeNode"
|
||||
class="ant-table-row-expand-icon @(RowData.Expanded?"ant-table-row-expand-icon-expanded":"ant-table-row-expand-icon-collapsed")"
|
||||
aria-label="@(RowData.Expanded?Table.Locale.Collapse:Table.Locale.Expand)"></button>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<td class="ant-table-cell ant-table-row-expand-icon-cell">
|
||||
@if (Table.RowExpandable(RowData) && (!Table.TreeMode || !RowData.HasChildren))
|
||||
{
|
||||
<button type="button" @onclick="ToggleTreeNode"
|
||||
class="ant-table-row-expand-icon @(RowData.Expanded?"ant-table-row-expand-icon-expanded":"ant-table-row-expand-icon-collapsed")"
|
||||
aria-label="@(RowData.Expanded?Table.Locale.Collapse:Table.Locale.Expand)"></button>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
|
||||
<td class="@ClassMapper.Class" style="@FixedStyle @Style" rowspan="@RowSpan" colspan="@ColSpan" data-label="@Context.HeaderColumns[ColIndex].Title">
|
||||
@if (ColIndex == Table.TreeExpandIconColumnIndex && Table.TreeMode)
|
||||
{
|
||||
<span class="ant-table-row-indent indent-level-@RowData.Level" style="padding-left: @((CssSizeLength)(RowData.Level * Table.IndentSize));"></span>
|
||||
@if (RowData.HasChildren)
|
||||
{
|
||||
<button type="button" @onclick="ToggleTreeNode" class="ant-table-row-expand-icon @(RowData?.Expanded==true?"ant-table-row-expand-icon-expanded":"ant-table-row-expand-icon-collapsed")" aria-label="@(RowData?.Expanded==true?Table.Locale.Collapse:Table.Locale.Expand)"></button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced" aria-label="@Table.Locale.Expand"></button>
|
||||
}
|
||||
}
|
||||
@ChildContent
|
||||
</td>
|
||||
<td class="@ClassMapper.Class" style="@FixedStyle @Style" rowspan="@RowSpan" colspan="@ColSpan" data-label="@Context.HeaderColumns[ColIndex].Title">
|
||||
@if (ColIndex == Table.TreeExpandIconColumnIndex && Table.TreeMode)
|
||||
{
|
||||
<span class="ant-table-row-indent indent-level-@RowData.Level" style="padding-left: @((CssSizeLength)(RowData.Level * Table.IndentSize));"></span>
|
||||
@if (RowData.HasChildren)
|
||||
{
|
||||
<button type="button" @onclick="ToggleTreeNode" class="ant-table-row-expand-icon @(RowData?.Expanded==true?"ant-table-row-expand-icon-expanded":"ant-table-row-expand-icon-collapsed")" aria-label="@(RowData?.Expanded==true?Table.Locale.Collapse:Table.Locale.Expand)"></button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced" aria-label="@Table.Locale.Expand"></button>
|
||||
}
|
||||
}
|
||||
|
||||
@if (CellRender != null)
|
||||
{
|
||||
var cellData = new CellData(RowData);
|
||||
@CellRender(cellData)
|
||||
}
|
||||
else
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</td>
|
||||
}
|
@ -8,386 +8,404 @@
|
||||
|
||||
@if (IsInitialize)
|
||||
{
|
||||
return;
|
||||
return;
|
||||
}
|
||||
else if (Hidden)
|
||||
{
|
||||
return;
|
||||
return;
|
||||
}
|
||||
else if (IsPlaceholder)
|
||||
{
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"></td>
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"></td>
|
||||
}
|
||||
else if (IsMeasure)
|
||||
{
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"><div style="height: 0px; overflow: hidden;"> </div></td>
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"><div style="height: 0px; overflow: hidden;"> </div></td>
|
||||
}
|
||||
else if (IsColGroup)
|
||||
{
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<col class="ant-table-expand-icon-col">
|
||||
}
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<col class="ant-table-expand-icon-col">
|
||||
}
|
||||
|
||||
if (Width != null)
|
||||
{
|
||||
<col style="width: @((CssSizeLength)Width); min-width: @((CssSizeLength)Width);">
|
||||
}
|
||||
else
|
||||
{
|
||||
<col />
|
||||
}
|
||||
if (Width != null)
|
||||
{
|
||||
<col style="width: @((CssSizeLength)Width); min-width: @((CssSizeLength)Width);">
|
||||
}
|
||||
else
|
||||
{
|
||||
<col />
|
||||
}
|
||||
}
|
||||
else if (IsHeader && HeaderColSpan != 0)
|
||||
{
|
||||
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<th class="ant-table-cell ant-table-row-expand-icon-cell"></th>
|
||||
}
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<th class="ant-table-cell ant-table-row-expand-icon-cell"></th>
|
||||
}
|
||||
|
||||
var headerCellAttributes = OnHeaderCell?.Invoke();
|
||||
<CascadingValue Name="IsHeader" Value="false" IsFixed>
|
||||
<th class="@ClassMapper.Class" style="@FixedStyle @HeaderStyle" colspan="@(HeaderColSpan > 1 ? HeaderColSpan : false)" title="@(Ellipsis ? Title : false)" @attributes="headerCellAttributes">
|
||||
@if (Sortable || (_filterable && _filters?.Any() == true))
|
||||
{
|
||||
@FilterToolTipSorter
|
||||
}
|
||||
else if (TitleTemplate != null)
|
||||
{
|
||||
@TitleTemplate
|
||||
}
|
||||
else
|
||||
{
|
||||
@Title
|
||||
}
|
||||
</th>
|
||||
</CascadingValue>
|
||||
var headerCellAttributes = OnHeaderCell?.Invoke();
|
||||
<CascadingValue Name="IsHeader" Value="false" IsFixed>
|
||||
<th class="@ClassMapper.Class" style="@FixedStyle @HeaderStyle" colspan="@(HeaderColSpan > 1 ? HeaderColSpan : false)" title="@(Ellipsis ? HeaderTitle : false)" @attributes="headerCellAttributes">
|
||||
@if (Sortable || (_filterable && _filters?.Any() == true))
|
||||
{
|
||||
@FilterToolTipSorter
|
||||
}
|
||||
else if (TitleTemplate != null)
|
||||
{
|
||||
@TitleTemplate
|
||||
}
|
||||
else
|
||||
{
|
||||
@HeaderTitle
|
||||
}
|
||||
</th>
|
||||
</CascadingValue>
|
||||
}
|
||||
else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
{
|
||||
|
||||
var fieldText = !string.IsNullOrWhiteSpace(Format) ? Formatter<TData>.Format(Field, Format) : Field?.ToString();
|
||||
var fieldText = !string.IsNullOrWhiteSpace(Format) ? Formatter<TData>.Format(Field, Format) : Field?.ToString();
|
||||
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<td class="ant-table-cell ant-table-row-expand-icon-cell">
|
||||
@if (Table.RowExpandable(RowData) && (!Table.TreeMode || !RowData.HasChildren))
|
||||
{
|
||||
<button type="button" @onclick="ToggleTreeNode" @onclick:stopPropagation
|
||||
class="ant-table-row-expand-icon @(RowData.Expanded ? "ant-table-row-expand-icon-expanded" : "ant-table-row-expand-icon-collapsed")"
|
||||
aria-label="@(RowData.Expanded ? Table.Locale.Collapse : Table.Locale.Expand)"></button>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
@if (AppendExpandColumn)
|
||||
{
|
||||
<td class="ant-table-cell ant-table-row-expand-icon-cell">
|
||||
@if (Table.RowExpandable(RowData) && (!Table.TreeMode || !RowData.HasChildren))
|
||||
{
|
||||
<button type="button" @onclick="ToggleTreeNode" @onclick:stopPropagation
|
||||
class="ant-table-row-expand-icon @(RowData.Expanded ? "ant-table-row-expand-icon-expanded" : "ant-table-row-expand-icon-collapsed")"
|
||||
aria-label="@(RowData.Expanded ? Table.Locale.Collapse : Table.Locale.Expand)"></button>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
var cellData = new CellData(RowData);
|
||||
var cellAttributes = OnCell?.Invoke(cellData);
|
||||
<CascadingValue Name="IsBody" Value="false" IsFixed>
|
||||
<td class="@ClassMapper.Class" style="@FixedStyle @Style" rowspan="@RowSpan" colspan="@ColSpan" title="@(Ellipsis ? fieldText : false)" @attributes="cellAttributes">
|
||||
@if (ColIndex == Table.TreeExpandIconColumnIndex && Table.TreeMode)
|
||||
{
|
||||
<span class="ant-table-row-indent indent-level-@RowData.Level" style="padding-left: @((CssSizeLength)(RowData.Level * Table.IndentSize));"></span>
|
||||
@if (RowData.HasChildren)
|
||||
{
|
||||
<button type="button" @onclick="ToggleTreeNode" @onclick:stopPropagation class="ant-table-row-expand-icon @(RowData?.Expanded == true ? "ant-table-row-expand-icon-expanded" : "ant-table-row-expand-icon-collapsed")" aria-label="@(RowData?.Expanded == true ? Table.Locale.Collapse : Table.Locale.Expand)"></button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced" aria-label="@Table.Locale.Expand"></button>
|
||||
}
|
||||
}
|
||||
|
||||
var cellAttributes = OnCell?.Invoke(RowData);
|
||||
<CascadingValue Name="IsBody" Value="false" IsFixed>
|
||||
<td class="@ClassMapper.Class" style="@FixedStyle @Style" rowspan="@RowSpan" colspan="@ColSpan" title="@(Ellipsis ? fieldText : false)" @attributes="cellAttributes">
|
||||
@if (ColIndex == Table.TreeExpandIconColumnIndex && Table.TreeMode)
|
||||
{
|
||||
<span class="ant-table-row-indent indent-level-@RowData.Level" style="padding-left: @((CssSizeLength)(RowData.Level * Table.IndentSize));"></span>
|
||||
@if (RowData.HasChildren)
|
||||
{
|
||||
<button type="button" @onclick="ToggleTreeNode" @onclick:stopPropagation class="ant-table-row-expand-icon @(RowData?.Expanded == true ? "ant-table-row-expand-icon-expanded" : "ant-table-row-expand-icon-collapsed")" aria-label="@(RowData?.Expanded == true ? Table.Locale.Collapse : Table.Locale.Expand)"></button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="ant-table-row-expand-icon ant-table-row-expand-icon-spaced" aria-label="@Table.Locale.Expand"></button>
|
||||
}
|
||||
}
|
||||
|
||||
@if (CellRender != null)
|
||||
{
|
||||
@CellRender(Field)
|
||||
}
|
||||
else if (ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(Format))
|
||||
{
|
||||
@(Formatter<TData>.Format(Field, Format))
|
||||
}
|
||||
else
|
||||
{
|
||||
@Field
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</CascadingValue>
|
||||
@if (CellRender != null)
|
||||
{
|
||||
@CellRender(cellData)
|
||||
}
|
||||
else if (ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(Format))
|
||||
{
|
||||
@(Formatter<TData>.Format(Field, Format))
|
||||
}
|
||||
else
|
||||
{
|
||||
@Field
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</CascadingValue>
|
||||
}
|
||||
|
||||
@code
|
||||
{
|
||||
string HeaderTitle => Title ?? DisplayName ?? FieldName;
|
||||
|
||||
RenderFragment SortHeader =>
|
||||
@<Template>
|
||||
<span>
|
||||
@if (TitleTemplate != null) @TitleTemplate else @Title
|
||||
</span>
|
||||
@{
|
||||
bool hasDescendingSorter = SortDirection.Descending.IsIn(SortDirections);
|
||||
bool hasAscendingSorter = SortDirection.Ascending.IsIn(SortDirections);
|
||||
}
|
||||
<span class="ant-table-column-sorter@(hasDescendingSorter && hasAscendingSorter ? " ant-table-column-sorter-full" : "")">
|
||||
<span class="ant-table-column-sorter-inner">
|
||||
@if (hasAscendingSorter)
|
||||
{
|
||||
<Icon Type="caret-up" Class=@($"ant-table-column-sorter-up{(_sortDirection == SortDirection.Ascending ? " active" : "")}") />
|
||||
}
|
||||
@if (hasDescendingSorter)
|
||||
{
|
||||
<Icon Type="caret-down" Class=@($"ant-table-column-sorter-down{(_sortDirection == SortDirection.Descending ? " active" : "")}") />
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
</Template>;
|
||||
RenderFragment SortHeader =>
|
||||
@<Template>
|
||||
<span>
|
||||
@if (TitleTemplate != null)
|
||||
{
|
||||
@TitleTemplate
|
||||
}
|
||||
else
|
||||
{
|
||||
@HeaderTitle
|
||||
}
|
||||
</span>
|
||||
@{
|
||||
bool hasDescendingSorter = SortDirection.Descending.IsIn(SortDirections);
|
||||
bool hasAscendingSorter = SortDirection.Ascending.IsIn(SortDirections);
|
||||
}
|
||||
<span class="ant-table-column-sorter@(hasDescendingSorter && hasAscendingSorter ? " ant-table-column-sorter-full" : "")">
|
||||
<span class="ant-table-column-sorter-inner">
|
||||
@if (hasAscendingSorter)
|
||||
{
|
||||
<Icon Type="caret-up" Class=@($"ant-table-column-sorter-up{(_sortDirection == SortDirection.Ascending ? " active" : "")}") />
|
||||
}
|
||||
@if (hasDescendingSorter)
|
||||
{
|
||||
<Icon Type="caret-down" Class=@($"ant-table-column-sorter-down{(_sortDirection == SortDirection.Descending ? " active" : "")}") />
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
</Template>
|
||||
;
|
||||
|
||||
RenderFragment ToolTipSorter =>
|
||||
@<Template>
|
||||
@if (ShowSorterTooltip)
|
||||
{
|
||||
<Tooltip Title="@SorterTooltip">
|
||||
<Unbound>
|
||||
<div class="ant-table-column-sorters" @ref="context.Current" @onclick="HandleSort">
|
||||
@SortHeader
|
||||
</div>
|
||||
</Unbound>
|
||||
</Tooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="ant-table-column-sorters" @onclick="HandleSort">
|
||||
@SortHeader
|
||||
</div>
|
||||
}
|
||||
</Template>;
|
||||
RenderFragment ToolTipSorter =>
|
||||
@<Template>
|
||||
@if (ShowSorterTooltip)
|
||||
{
|
||||
<Tooltip Title="@SorterTooltip">
|
||||
<Unbound>
|
||||
<div class="ant-table-column-sorters" @ref="context.Current" @onclick="HandleSort">
|
||||
@SortHeader
|
||||
</div>
|
||||
</Unbound>
|
||||
</Tooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="ant-table-column-sorters" @onclick="HandleSort">
|
||||
@SortHeader
|
||||
</div>
|
||||
}
|
||||
</Template>
|
||||
;
|
||||
|
||||
RenderFragment FilterToolTipSorter =>
|
||||
@<Template>
|
||||
@if (_filterable && _filters?.Any() == true)
|
||||
{
|
||||
<div class="ant-table-filter-column">
|
||||
@if (Sortable)
|
||||
{
|
||||
@ToolTipSorter
|
||||
}
|
||||
else if (TitleTemplate != null)
|
||||
{
|
||||
@TitleTemplate
|
||||
}
|
||||
else
|
||||
{
|
||||
@Title
|
||||
}
|
||||
RenderFragment FilterToolTipSorter =>
|
||||
@<Template>
|
||||
@if (_filterable && _filters?.Any() == true)
|
||||
{
|
||||
<div class="ant-table-filter-column">
|
||||
@if (Sortable)
|
||||
{
|
||||
@ToolTipSorter
|
||||
}
|
||||
else if (TitleTemplate != null)
|
||||
{
|
||||
@TitleTemplate
|
||||
}
|
||||
else
|
||||
{
|
||||
@HeaderTitle
|
||||
}
|
||||
|
||||
<Dropdown Trigger="new[] { TriggerType.Click }" Visible="_filterOpened" Placement="PlacementType.BottomRight" OnMaskClick="() => { if (_filterOpened) FilterConfirm(); }">
|
||||
<Unbound>
|
||||
<span @ref="context.Current" role="button" tabindex="-1" class="ant-dropdown-trigger ant-table-filter-trigger@((_filterOpened ? " ant-dropdown-open" : "") + (_hasFilterSelected ? " active" : ""))"
|
||||
@onclick="() => { if (_filterOpened) FilterConfirm(); else _filterOpened = true; }">
|
||||
<Icon Type="filter" Theme="fill" />
|
||||
</span>
|
||||
</Unbound>
|
||||
<Overlay>
|
||||
<div class="ant-table-filter-dropdown">
|
||||
@if (_filters?.Any() == true && _columnFilterType == TableFilterType.List)
|
||||
{
|
||||
<Menu AutoCloseDropdown="false" SelectedKeys="_selectedFilterValues">
|
||||
@foreach (var filter in _filters)
|
||||
{
|
||||
<MenuItem Key="@filter.Value?.ToString()" OnClick="() => FilterSelected(filter)">
|
||||
@if (FilterMultiple)
|
||||
{
|
||||
<Checkbox Value="filter.Selected" ValueChanged="value => FilterSelected(filter)">@filter.Text</Checkbox>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Radio TValue="bool" Checked="filter.Selected" CheckedChanged="value => FilterSelected(filter)">@filter.Text</Radio>
|
||||
}
|
||||
</MenuItem>
|
||||
}
|
||||
</Menu>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div id="@("popup-container-for-" + Id)" style="position:relative!important"></div>
|
||||
int filterCount = _filters.Count();
|
||||
@for (int index = 0; index < filterCount; index++)
|
||||
{
|
||||
var filter = _filters.ElementAt(index);
|
||||
var noInput = filter.FilterCompareOperator == TableFilterCompareOperator.IsNull || filter.FilterCompareOperator == TableFilterCompareOperator.IsNotNull;
|
||||
<div @key="filter" style="padding:10px;min-width:150px;@(index > 0 ? "border-top:1px solid #f0f0f0" : "")">
|
||||
@if (index > 0)
|
||||
{
|
||||
<div>
|
||||
<Space Style="margin-bottom:10px">
|
||||
<SpaceItem>
|
||||
<Select Value="filter.FilterCondition" TItemValue="TableFilterCondition" TItem="TableFilterCondition" ValueChanged="value => SetFilterCondition(filter,value)" PopupContainerSelector="@("#popup-container-for-" + Id)">
|
||||
<SelectOptions>
|
||||
<SelectOption TItem="TableFilterCondition" TItemValue="TableFilterCondition" Value="@TableFilterCondition.And" Label="@Table?.Locale.FilterOptions.And"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCondition" TItemValue="TableFilterCondition" Value="@TableFilterCondition.Or" Label="@Table?.Locale.FilterOptions.Or"></SelectOption>
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
<Space Style="width:100%;">
|
||||
<SpaceItem Style="flex:auto">
|
||||
<Select Value="filter.FilterCompareOperator" TItemValue="TableFilterCompareOperator" TItem="TableFilterCompareOperator" Style="width: 100%; overflow: visible" ValueChanged="value => SetFilterCompareOperator(filter,value)" PopupContainerSelector="@("#popup-container-for-" + Id)" DropdownMatchSelectWidth="false">
|
||||
<SelectOptions>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Equals" Label="@Table?.Locale.FilterOptions.Equals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.NotEquals" Label="@Table?.Locale.FilterOptions.NotEquals"></SelectOption>
|
||||
@if (_columnDataType == typeof(string))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Contains" Label="@Table?.Locale.FilterOptions.Contains"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.StartsWith" Label="@Table?.Locale.FilterOptions.StartsWith"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.EndsWith" Label="@Table?.Locale.FilterOptions.EndsWith"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType.IsEnum)
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Contains" Label="@Table?.Locale.FilterOptions.Contains"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.NotContains" Label="@Table?.Locale.FilterOptions.NotContains"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType == typeof(DateTime))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThan" Label="@Table?.Locale.FilterOptions.GreaterThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThan" Label="@Table?.Locale.FilterOptions.LessThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThanOrEquals" Label="@Table?.Locale.FilterOptions.GreaterThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThanOrEquals" Label="@Table?.Locale.FilterOptions.LessThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.TheSameDateWith" Label="@Table?.Locale.FilterOptions.TheSameDateWith"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType == typeof(Guid)) { }
|
||||
else
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThan" Label="@Table?.Locale.FilterOptions.GreaterThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThan" Label="@Table?.Locale.FilterOptions.LessThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThanOrEquals" Label="@Table?.Locale.FilterOptions.GreaterThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThanOrEquals" Label="@Table?.Locale.FilterOptions.LessThanOrEquals"></SelectOption>
|
||||
}
|
||||
@if (THelper.IsTypeNullable<TData>() || _columnDataType == typeof(string))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.IsNull" Label="@Table?.Locale.FilterOptions.IsNull"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.IsNotNull" Label="@Table?.Locale.FilterOptions.IsNotNull"></SelectOption>
|
||||
}
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</SpaceItem>
|
||||
@if (!noInput)
|
||||
{
|
||||
<SpaceItem>
|
||||
@FilterInput(filter)
|
||||
</SpaceItem>
|
||||
}
|
||||
@if (filterCount > 1)
|
||||
{
|
||||
<SpaceItem>
|
||||
<Button Size="small" Icon="close" Type="primary" OnClick="()=> RemoveFilter(filter)">
|
||||
</Button>
|
||||
</SpaceItem>
|
||||
}
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="ant-table-filter-dropdown-btns">
|
||||
<Button Size="small" Type="link" OnClick="ResetFilters">
|
||||
@Table?.Locale.FilterReset
|
||||
</Button>
|
||||
@if (_columnFilterType == TableFilterType.FieldType)
|
||||
{
|
||||
<Button Size="small" Icon="plus" Type="primary" OnClick="AddFilter">
|
||||
</Button>
|
||||
}
|
||||
<Button Size="small" Type="primary" OnClick="() => FilterConfirm()">
|
||||
@Table?.Locale.FilterConfirm
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Overlay>
|
||||
</Dropdown>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ToolTipSorter
|
||||
}
|
||||
</Template>;
|
||||
<Dropdown Trigger="new[] { Trigger.Click }" Visible="_filterOpened" Placement="Placement.BottomRight" OnMaskClick="() => { if (_filterOpened) FilterConfirm(); }">
|
||||
<Unbound>
|
||||
<span @ref="context.Current" role="button" tabindex="-1" class="ant-dropdown-trigger ant-table-filter-trigger@((_filterOpened ? " ant-dropdown-open" : "") + (_hasFilterSelected ? " active" : ""))"
|
||||
@onclick="() => { if (_filterOpened) FilterConfirm(); else _filterOpened = true; }">
|
||||
<Icon Type="filter" Theme="fill" />
|
||||
</span>
|
||||
</Unbound>
|
||||
<Overlay>
|
||||
<div class="ant-table-filter-dropdown">
|
||||
@if (_filters?.Any() == true && _columnFilterType == TableFilterType.List)
|
||||
{
|
||||
<Menu AutoCloseDropdown="false" SelectedKeys="_selectedFilterValues">
|
||||
@foreach (var filter in _filters)
|
||||
{
|
||||
<MenuItem Key="@filter.Value?.ToString()" OnClick="() => FilterSelected(filter)">
|
||||
@if (FilterMultiple)
|
||||
{
|
||||
<Checkbox Value="filter.Selected" ValueChanged="value => FilterSelected(filter)">@filter.Text</Checkbox>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Radio TValue="bool" Checked="filter.Selected" CheckedChanged="value => FilterSelected(filter)">@filter.Text</Radio>
|
||||
}
|
||||
</MenuItem>
|
||||
}
|
||||
</Menu>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div id="@("popup-container-for-" + Id)" style="position:relative!important"></div>
|
||||
int filterCount = _filters.Count();
|
||||
@for (int index = 0; index < filterCount; index++)
|
||||
{
|
||||
var filter = _filters.ElementAt(index);
|
||||
var noInput = filter.FilterCompareOperator == TableFilterCompareOperator.IsNull || filter.FilterCompareOperator == TableFilterCompareOperator.IsNotNull;
|
||||
<div @key="filter" style="padding:10px;min-width:150px;@(index > 0 ? "border-top:1px solid #f0f0f0" : "")">
|
||||
@if (index > 0)
|
||||
{
|
||||
<div>
|
||||
<Space Style="margin-bottom:10px">
|
||||
<SpaceItem>
|
||||
<Select Value="filter.FilterCondition" TItemValue="TableFilterCondition" TItem="TableFilterCondition" ValueChanged="value => SetFilterCondition(filter,value)" PopupContainerSelector="@("#popup-container-for-" + Id)" BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None">
|
||||
<SelectOptions>
|
||||
<SelectOption TItem="TableFilterCondition" TItemValue="TableFilterCondition" Value="@TableFilterCondition.And" Label="@Table?.Locale.FilterOptions.And"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCondition" TItemValue="TableFilterCondition" Value="@TableFilterCondition.Or" Label="@Table?.Locale.FilterOptions.Or"></SelectOption>
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
<Space Style="width:100%;">
|
||||
<SpaceItem Style="flex:auto">
|
||||
<Select Value="filter.FilterCompareOperator" TItemValue="TableFilterCompareOperator" TItem="TableFilterCompareOperator" Style="width: 100%; overflow: visible" ValueChanged="value => SetFilterCompareOperator(filter,value)" PopupContainerSelector="@("#popup-container-for-" + Id)" BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None" DropdownMatchSelectWidth="false">
|
||||
<SelectOptions>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Equals" Label="@Table?.Locale.FilterOptions.Equals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.NotEquals" Label="@Table?.Locale.FilterOptions.NotEquals"></SelectOption>
|
||||
@if (_columnDataType == typeof(string))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Contains" Label="@Table?.Locale.FilterOptions.Contains"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.StartsWith" Label="@Table?.Locale.FilterOptions.StartsWith"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.EndsWith" Label="@Table?.Locale.FilterOptions.EndsWith"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType.IsEnum)
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Contains" Label="@Table?.Locale.FilterOptions.Contains"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.NotContains" Label="@Table?.Locale.FilterOptions.NotContains"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType == typeof(DateTime))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThan" Label="@Table?.Locale.FilterOptions.GreaterThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThan" Label="@Table?.Locale.FilterOptions.LessThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThanOrEquals" Label="@Table?.Locale.FilterOptions.GreaterThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThanOrEquals" Label="@Table?.Locale.FilterOptions.LessThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.TheSameDateWith" Label="@Table?.Locale.FilterOptions.TheSameDateWith"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType == typeof(Guid)) { }
|
||||
else
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThan" Label="@Table?.Locale.FilterOptions.GreaterThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThan" Label="@Table?.Locale.FilterOptions.LessThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThanOrEquals" Label="@Table?.Locale.FilterOptions.GreaterThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThanOrEquals" Label="@Table?.Locale.FilterOptions.LessThanOrEquals"></SelectOption>
|
||||
}
|
||||
@if (THelper.IsTypeNullable<TData>() || _columnDataType == typeof(string))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.IsNull" Label="@Table?.Locale.FilterOptions.IsNull"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.IsNotNull" Label="@Table?.Locale.FilterOptions.IsNotNull"></SelectOption>
|
||||
}
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</SpaceItem>
|
||||
@if (!noInput)
|
||||
{
|
||||
<SpaceItem>
|
||||
@FilterInput(filter)
|
||||
</SpaceItem>
|
||||
}
|
||||
@if (filterCount > 1)
|
||||
{
|
||||
<SpaceItem>
|
||||
<Button Size="small" Icon="close" Type="primary" OnClick="()=> RemoveFilter(filter)">
|
||||
</Button>
|
||||
</SpaceItem>
|
||||
}
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="ant-table-filter-dropdown-btns">
|
||||
<Button Size="small" Type="link" OnClick="ResetFilters">
|
||||
@Table?.Locale.FilterReset
|
||||
</Button>
|
||||
@if (_columnFilterType == TableFilterType.FieldType)
|
||||
{
|
||||
<Button Size="small" Icon="plus" Type="primary" OnClick="AddFilter">
|
||||
</Button>
|
||||
}
|
||||
<Button Size="small" Type="primary" OnClick="() => FilterConfirm()">
|
||||
@Table?.Locale.FilterConfirm
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Overlay>
|
||||
</Dropdown>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ToolTipSorter
|
||||
}
|
||||
</Template>
|
||||
;
|
||||
|
||||
RenderFragment<TableFilter> FilterInput => filter =>
|
||||
@<Template>
|
||||
@if (_columnDataType == typeof(DateTime))
|
||||
{
|
||||
if (filter.FilterCompareOperator != TableFilterCompareOperator.TheSameDateWith)
|
||||
{
|
||||
<DatePicker Value="(DateTime?)filter.Value" ShowTime="@true"
|
||||
TValue="DateTime?" ValueChanged="value => SetFilterValue(filter, value?.AddMilliseconds(-value.Value.Millisecond))" PopupContainerSelector="@("#popup-container-for-" + Id)"></DatePicker>
|
||||
}
|
||||
else
|
||||
{
|
||||
<DatePicker Value="(DateTime?)filter.Value" TValue="DateTime?" ValueChanged="value => SetFilterValue(filter, value)" PopupContainerSelector="@("#popup-container-for-" + Id)"></DatePicker>
|
||||
}
|
||||
}
|
||||
else if (_columnDataType.IsEnum)
|
||||
{
|
||||
<EnumSelect TEnum="TData" Mode="multiple" Values="EnumHelper<TData>.Split(filter.Value)" PopupContainerSelector="@("#popup-container-for-" + Id)"
|
||||
Style="width:180px;" ValuesChanged="value => SetFilterValue(filter, EnumHelper<TData>.Combine(value))">
|
||||
</EnumSelect>
|
||||
}
|
||||
else if (_columnDataType == typeof(byte))
|
||||
{
|
||||
<InputNumber Value="(byte?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="byte?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(decimal))
|
||||
{
|
||||
<InputNumber Value="(decimal?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="decimal?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(double))
|
||||
{
|
||||
<InputNumber Value="(double?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="double?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(short))
|
||||
{
|
||||
<InputNumber Value="(short?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="short?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(int))
|
||||
{
|
||||
<InputNumber Value="(int?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="int?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(long))
|
||||
{
|
||||
<InputNumber Value="(long?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="long?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(sbyte))
|
||||
{
|
||||
<InputNumber Value="(sbyte?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="sbyte?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(float))
|
||||
{
|
||||
<InputNumber Value="(float?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="float?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(ushort))
|
||||
{
|
||||
<InputNumber Value="(ushort?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="ushort?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(uint))
|
||||
{
|
||||
<InputNumber Value="(uint?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="uint?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(ulong))
|
||||
{
|
||||
<InputNumber Value="(ulong?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="ulong?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(Guid))
|
||||
{
|
||||
<Input Value="(Guid?)filter.Value" TValue="Guid?" ValueChanged="value => SetFilterValue(filter, value)" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<Input TValue="TData" Value="(TData)filter.Value" ValueChanged="value => SetFilterValue(filter, value)" />
|
||||
}
|
||||
</Template>;
|
||||
RenderFragment<TableFilter> FilterInput => filter =>
|
||||
@<Template>
|
||||
@if (_columnDataType == typeof(DateTime))
|
||||
{
|
||||
if (filter.FilterCompareOperator != TableFilterCompareOperator.TheSameDateWith)
|
||||
{
|
||||
<DatePicker Value="(DateTime?)filter.Value" ShowTime="@true"
|
||||
TValue="DateTime?" ValueChanged="value => SetFilterValue(filter, value?.AddMilliseconds(-value.Value.Millisecond))"
|
||||
PopupContainerSelector="@("#popup-container-for-" + Id)"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<DatePicker Value="(DateTime?)filter.Value" TValue="DateTime?" ValueChanged="value => SetFilterValue(filter, value)"
|
||||
PopupContainerSelector="@("#popup-container-for-" + Id)"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"/>
|
||||
}
|
||||
}
|
||||
else if (_columnDataType.IsEnum)
|
||||
{
|
||||
<EnumSelect TEnum="TData" Mode="multiple" Values="EnumHelper<TData>.Split(filter.Value)"
|
||||
PopupContainerSelector="@("#popup-container-for-" + Id)"
|
||||
Style="width:180px;"
|
||||
ValuesChanged="value => SetFilterValue(filter, EnumHelper<TData>.Combine(value))"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"/>
|
||||
}
|
||||
else if (_columnDataType == typeof(byte))
|
||||
{
|
||||
<InputNumber Value="(byte?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="byte?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(decimal))
|
||||
{
|
||||
<InputNumber Value="(decimal?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="decimal?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(double))
|
||||
{
|
||||
<InputNumber Value="(double?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="double?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(short))
|
||||
{
|
||||
<InputNumber Value="(short?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="short?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(int))
|
||||
{
|
||||
<InputNumber Value="(int?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="int?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(long))
|
||||
{
|
||||
<InputNumber Value="(long?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="long?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(sbyte))
|
||||
{
|
||||
<InputNumber Value="(sbyte?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="sbyte?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(float))
|
||||
{
|
||||
<InputNumber Value="(float?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="float?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(ushort))
|
||||
{
|
||||
<InputNumber Value="(ushort?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="ushort?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(uint))
|
||||
{
|
||||
<InputNumber Value="(uint?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="uint?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(ulong))
|
||||
{
|
||||
<InputNumber Value="(ulong?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="ulong?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(Guid))
|
||||
{
|
||||
<Input Value="(Guid?)filter.Value" TValue="Guid?" ValueChanged="value => SetFilterValue(filter, value)" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<Input TValue="TData" Value="(TData)filter.Value" ValueChanged="value => SetFilterValue(filter, value)" />
|
||||
}
|
||||
</Template>
|
||||
;
|
||||
}
|
@ -22,8 +22,6 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public Expression<Func<TData>> FieldExpression { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<TData> CellRender { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TData Field
|
||||
@ -70,7 +68,7 @@ namespace AntDesign
|
||||
public SortDirection DefaultSortOrder { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<RowData, Dictionary<string, object>> OnCell { get; set; }
|
||||
public Func<CellData, Dictionary<string, object>> OnCell { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<Dictionary<string, object>> OnHeaderCell { get; set; }
|
||||
|
@ -70,6 +70,9 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public virtual RenderFragment<CellData> CellRender { get; set; }
|
||||
|
||||
public int ColIndex { get; set; }
|
||||
|
||||
protected bool AppendExpandColumn => Table.HasExpandTemplate && ColIndex == (Table.TreeMode ? Table.TreeExpandIconColumnIndex : Table.ExpandIconColumnIndex);
|
||||
|
@ -1,48 +1,62 @@
|
||||
@namespace AntDesign
|
||||
@using AntDesign.TableModels
|
||||
@inherits ColumnBase
|
||||
|
||||
@if (IsInitialize)
|
||||
{
|
||||
return;
|
||||
return;
|
||||
}
|
||||
else if (IsPlaceholder)
|
||||
{
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"></td>
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"></td>
|
||||
}
|
||||
else if (IsMeasure)
|
||||
{
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"><div style="height: 0px; overflow: hidden;"> </div></td>
|
||||
<td style="padding: 0px; border: 0px; height: 0px;"><div style="height: 0px; overflow: hidden;"> </div></td>
|
||||
}
|
||||
else if (IsColGroup)
|
||||
{
|
||||
if (Width != null)
|
||||
{
|
||||
<col class="ant-table-selection-col" style="width: @((CssSizeLength)Width); min-width: @((CssSizeLength)Width);">
|
||||
}
|
||||
else
|
||||
{
|
||||
<col class="ant-table-selection-col">
|
||||
}
|
||||
if (Width != null)
|
||||
{
|
||||
<col class="ant-table-selection-col" style="width: @((CssSizeLength)Width); min-width: @((CssSizeLength)Width);">
|
||||
}
|
||||
else
|
||||
{
|
||||
<col class="ant-table-selection-col">
|
||||
}
|
||||
}
|
||||
else if (IsHeader && HeaderColSpan != 0)
|
||||
{
|
||||
<th class="@ClassMapper.Class ant-table-selection-column" style="@FixedStyle @HeaderStyle" @key="@Key" colspan="@HeaderColSpan">
|
||||
@if (Type == "checkbox")
|
||||
{
|
||||
<Checkbox Checked="_checked" CheckedChange="HandleCheckedChange" Indeterminate="Indeterminate" />
|
||||
}
|
||||
</th>
|
||||
}
|
||||
<th class="@ClassMapper.Class ant-table-selection-column" style="@FixedStyle @HeaderStyle" @key="@Key" colspan="@HeaderColSpan">
|
||||
@if (Type == "checkbox")
|
||||
{
|
||||
<Checkbox Checked="_checked" CheckedChange="HandleCheckedChange" Indeterminate="Indeterminate" />
|
||||
}
|
||||
</th>
|
||||
}
|
||||
else if (!IsHeader && RowSpan != 0 && ColSpan != 0)
|
||||
{
|
||||
<td class="@ClassMapper.Class ant-table-selection-column" style="@FixedStyle @Style" @key="@Key" rowspan="@RowSpan" colspan="@ColSpan">
|
||||
@if (Type == "checkbox")
|
||||
{
|
||||
<Checkbox Checked="_checked" Disabled="Disabled" CheckedChange="HandleCheckedChange" />
|
||||
}
|
||||
else if (Type == "radio")
|
||||
{
|
||||
<Radio Checked="_checked" Disabled="Disabled" CheckedChange="HandleCheckedChange" TValue="bool" />
|
||||
}
|
||||
</td>
|
||||
<td class="@ClassMapper.Class ant-table-selection-column" style="@FixedStyle @Style" @key="@Key" rowspan="@RowSpan" colspan="@ColSpan">
|
||||
|
||||
@if (CellRender != null)
|
||||
{
|
||||
var cellData = new CellData(RowData);
|
||||
@CellRender(cellData)
|
||||
}
|
||||
else if (ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (Type == "checkbox")
|
||||
{
|
||||
<Checkbox Checked="_checked" Disabled="Disabled" CheckedChange="HandleCheckedChange" />
|
||||
}
|
||||
else if (Type == "radio")
|
||||
{
|
||||
<Radio Checked="_checked" Disabled="Disabled" CheckedChange="HandleCheckedChange" TValue="bool" />
|
||||
}
|
||||
}
|
||||
</td>
|
||||
}
|
@ -1,5 +1,15 @@
|
||||
@namespace AntDesign
|
||||
@using AntDesign.TableModels
|
||||
@inherits ColumnBase
|
||||
|
||||
<td class="@ClassMapper.Class" style="@FixedStyle @Style" colspan="@ColSpan">@ChildContent</td>
|
||||
|
||||
<td class="@ClassMapper.Class" style="@FixedStyle @Style" colspan="@ColSpan">
|
||||
@if (CellRender != null)
|
||||
{
|
||||
var cellData = new CellData(RowData);
|
||||
@CellRender(cellData)
|
||||
}
|
||||
else
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</td>
|
@ -27,7 +27,7 @@
|
||||
<CascadingValue Name="IsInitialize" Value="true" IsFixed>
|
||||
@ChildContent(_fieldModel)
|
||||
</CascadingValue>
|
||||
<div class="@ClassMapper.Class">
|
||||
<div class="@ClassMapper.Class" @ref="_tableRef">
|
||||
@if (TitleTemplate != null || Title != null)
|
||||
{
|
||||
<div class="ant-table-title">
|
||||
|
@ -136,7 +136,7 @@ namespace AntDesign
|
||||
public bool Responsive { get; set; } = true;
|
||||
|
||||
[Inject]
|
||||
public DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
public ColumnContext ColumnContext { get; set; }
|
||||
|
||||
@ -146,20 +146,20 @@ namespace AntDesign
|
||||
|
||||
private IList<SummaryRow> _summaryRows;
|
||||
|
||||
private bool _hasFirstLoad;
|
||||
private bool _waitingReload;
|
||||
private bool _waitingReloadAndInvokeChange;
|
||||
private bool _treeMode;
|
||||
|
||||
private bool _hasFixLeft;
|
||||
private bool _hasFixRight;
|
||||
private bool _pingRight;
|
||||
private bool _pingLeft;
|
||||
private int _treeExpandIconColumnIndex;
|
||||
private readonly ClassMapper _wrapperClassMapper = new ClassMapper();
|
||||
private string TableLayoutStyle => TableLayout == null ? "" : $"table-layout: {TableLayout};";
|
||||
|
||||
private ElementReference _tableHeaderRef;
|
||||
private ElementReference _tableBodyRef;
|
||||
private ElementReference _tableRef;
|
||||
|
||||
private bool ServerSide => _hasRemoteDataSourceAttribute ? RemoteDataSource : Total > _dataSourceCount;
|
||||
|
||||
@ -325,8 +325,8 @@ namespace AntDesign
|
||||
.If($"{prefixCls}-fixed-column {prefixCls}-scroll-horizontal", () => ScrollX != null)
|
||||
.If($"{prefixCls}-has-fix-left", () => _hasFixLeft)
|
||||
.If($"{prefixCls}-has-fix-right", () => _hasFixRight)
|
||||
.If($"{prefixCls}-ping-left", () => _pingLeft)
|
||||
.If($"{prefixCls}-ping-right", () => _pingRight)
|
||||
//.If($"{prefixCls}-ping-left", () => _pingLeft)
|
||||
//.If($"{prefixCls}-ping-right", () => _pingRight)
|
||||
.If($"{prefixCls}-rtl", () => RTL)
|
||||
;
|
||||
|
||||
@ -359,9 +359,21 @@ namespace AntDesign
|
||||
FlushCache();
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
base.OnAfterRender(firstRender);
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
DomEventListener.AddShared<JsonElement>("window", "beforeunload", Reloading);
|
||||
|
||||
if (ScrollY != null || ScrollX != null)
|
||||
{
|
||||
await JsInvokeAsync(JSInteropConstants.BindTableScroll, _tableBodyRef, _tableRef, _tableHeaderRef, ScrollX != null, ScrollY != null);
|
||||
}
|
||||
|
||||
_hasFirstLoad = true;
|
||||
}
|
||||
|
||||
if (!firstRender)
|
||||
{
|
||||
@ -371,28 +383,6 @@ namespace AntDesign
|
||||
_shouldRender = false;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
DomEventService.AddEventListener("window", "beforeunload", Reloading, false);
|
||||
if (ScrollX != null)
|
||||
{
|
||||
await SetScrollPositionClassName();
|
||||
|
||||
DomEventService.AddEventListener("window", "resize", OnResize, false);
|
||||
DomEventService.AddEventListener(_tableBodyRef, "scroll", OnScroll);
|
||||
}
|
||||
|
||||
if (ScrollY != null && ScrollX != null)
|
||||
{
|
||||
await JsInvokeAsync(JSInteropConstants.BindTableHeaderAndBodyScroll, _tableBodyRef, _tableHeaderRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
@ -402,12 +392,19 @@ namespace AntDesign
|
||||
_waitingReloadAndInvokeChange = false;
|
||||
_waitingReload = false;
|
||||
|
||||
ReloadAndInvokeChange();
|
||||
if (_hasFirstLoad)
|
||||
{
|
||||
ReloadAndInvokeChange();
|
||||
}
|
||||
}
|
||||
else if (_waitingReload)
|
||||
{
|
||||
_waitingReload = false;
|
||||
InternalReload();
|
||||
|
||||
if (_hasFirstLoad)
|
||||
{
|
||||
InternalReload();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.RenderMode == RenderMode.ParametersHashCodeChanged)
|
||||
@ -437,61 +434,9 @@ namespace AntDesign
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async void OnResize(JsonElement _) => await SetScrollPositionClassName();
|
||||
|
||||
private async void OnScroll(JsonElement _) => await SetScrollPositionClassName();
|
||||
|
||||
private async Task SetScrollPositionClassName(bool clear = false)
|
||||
{
|
||||
if (_isReloading)
|
||||
return;
|
||||
|
||||
var element = await JsInvokeAsync<HtmlElement>(JSInteropConstants.GetDomInfo, _tableBodyRef);
|
||||
var scrollWidth = element.ScrollWidth;
|
||||
var scrollLeft = element.ScrollLeft;
|
||||
var clientWidth = element.ClientWidth;
|
||||
|
||||
var beforePingLeft = _pingLeft;
|
||||
var beforePingRight = _pingRight;
|
||||
|
||||
if ((scrollWidth == clientWidth && scrollWidth != 0) || clear)
|
||||
{
|
||||
_pingLeft = false;
|
||||
_pingRight = false;
|
||||
}
|
||||
else if (scrollLeft == 0)
|
||||
{
|
||||
_pingLeft = false;
|
||||
_pingRight = true;
|
||||
}
|
||||
// allow the gap between 1 px, it's magic ✨
|
||||
else if (Math.Abs(scrollWidth - (scrollLeft + clientWidth)) < 1)
|
||||
{
|
||||
_pingRight = false;
|
||||
_pingLeft = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pingLeft = true;
|
||||
_pingRight = true;
|
||||
}
|
||||
|
||||
if (beforePingLeft != _pingLeft || beforePingRight != _pingRight)
|
||||
{
|
||||
_shouldRender = true;
|
||||
}
|
||||
|
||||
if (!clear)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "resize", OnResize);
|
||||
DomEventService.RemoveEventListerner<JsonElement>(_tableBodyRef, "scroll", OnScroll);
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "beforeunload", Reloading);
|
||||
DomEventListener.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@ -499,12 +444,12 @@ namespace AntDesign
|
||||
{
|
||||
if (!_isReloading)
|
||||
{
|
||||
if (ScrollY != null && ScrollX != null)
|
||||
if (ScrollY != null || ScrollX != null)
|
||||
{
|
||||
await JsInvokeAsync(JSInteropConstants.UnbindTableHeaderAndBodyScroll, _tableBodyRef);
|
||||
await JsInvokeAsync(JSInteropConstants.UnbindTableScroll, _tableBodyRef);
|
||||
}
|
||||
}
|
||||
DomEventService.RemoveEventListerner<JsonElement>("window", "beforeunload", Reloading);
|
||||
DomEventListener.Dispose();
|
||||
}
|
||||
|
||||
bool ITable.RowExpandable(RowData rowData)
|
||||
|
20
components/table/TableModels/CellData.cs
Normal file
20
components/table/TableModels/CellData.cs
Normal file
@ -0,0 +1,20 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace AntDesign.TableModels
|
||||
{
|
||||
public class CellData
|
||||
{
|
||||
public RowData RowData { get; }
|
||||
|
||||
public CellData(RowData rowData)
|
||||
{
|
||||
RowData = rowData;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,4 @@
|
||||
// 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;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
@ -54,7 +50,7 @@ namespace AntDesign
|
||||
internal List<TabPane> _panes = new List<TabPane>();
|
||||
|
||||
[Inject]
|
||||
public DomEventService DomEventService { get; set; }
|
||||
private IDomEventListener DomEventListener { get; set; }
|
||||
|
||||
#region Parameters
|
||||
|
||||
@ -394,13 +390,13 @@ namespace AntDesign
|
||||
|
||||
if (IsHorizontal && !_wheelDisabled)
|
||||
{
|
||||
DomEventService.AddEventListener<string>(_scrollTabBar, "wheel", OnWheel, true, true);
|
||||
DomEventListener.AddExclusive<string>(_scrollTabBar, "wheel", OnWheel, true);
|
||||
_wheelDisabled = true;
|
||||
}
|
||||
|
||||
if (!IsHorizontal && _wheelDisabled)
|
||||
{
|
||||
DomEventService.RemoveEventListerner<string>(_scrollTabBar, "wheel", OnWheel);
|
||||
DomEventListener.RemoveExclusive(_scrollTabBar, "wheel");
|
||||
_wheelDisabled = false;
|
||||
}
|
||||
|
||||
@ -568,5 +564,11 @@ namespace AntDesign
|
||||
}
|
||||
|
||||
#endregion DRAG & DROP
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DomEventListener.DisposeExclusive();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ namespace AntDesign
|
||||
public Tooltip()
|
||||
{
|
||||
PrefixCls = "ant-tooltip";
|
||||
Placement = PlacementType.Top;
|
||||
Placement = Placement.Top;
|
||||
}
|
||||
|
||||
internal override string GetOverlayEnterClass()
|
||||
@ -38,7 +38,7 @@ namespace AntDesign
|
||||
|
||||
internal override async Task Show(int? overlayLeft = null, int? overlayTop = null)
|
||||
{
|
||||
if (Trigger.Contains(TriggerType.Hover))
|
||||
if (Trigger.Contains(AntDesign.Trigger.Hover))
|
||||
{
|
||||
await Task.Delay((int)(MouseEnterDelay * 1000));
|
||||
}
|
||||
@ -48,7 +48,7 @@ namespace AntDesign
|
||||
|
||||
internal override async Task Hide(bool force = false)
|
||||
{
|
||||
if (Trigger.Contains(TriggerType.Hover))
|
||||
if (Trigger.Contains(AntDesign.Trigger.Hover))
|
||||
{
|
||||
await Task.Delay((int)(MouseLeaveDelay * 1000));
|
||||
}
|
||||
@ -56,7 +56,7 @@ namespace AntDesign
|
||||
await base.Hide(force);
|
||||
}
|
||||
|
||||
internal async Task ChildElementMoved() =>await GetOverlayComponent().UpdatePosition();
|
||||
//internal async Task ChildElementMoved() =>await GetOverlayComponent().UpdatePosition();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
<OverlayTrigger @ref="@_dropDown"
|
||||
Visible="Open"
|
||||
Disabled="Disabled"
|
||||
Trigger="new[] { TriggerType.Click }"
|
||||
Trigger="new[] { Trigger.Click }"
|
||||
HiddenMode
|
||||
OnMouseEnter="@(() => { OnMouseEnter?.Invoke(); })"
|
||||
OnMouseLeave="@(() => { OnMouseLeave?.Invoke(); })"
|
||||
|
@ -2,7 +2,8 @@
|
||||
<div style="padding: 100px; height: 1000px; background: #eee; position: relative " id="area">
|
||||
<AutoComplete PopupContainerSelector="#area"
|
||||
@bind-Value="@value"
|
||||
Options="@options" />
|
||||
Options="@options"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.InScroll"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -18,6 +18,7 @@ When there is a need for autocomplete functionality.
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| Backfill | backfill selected item the input when using keyboard | bool | false |
|
||||
| BoundaryAdjustMode | `Dropdown` adjustment strategy (when for example browser resize is happening) | TriggerBoundaryAdjustMode | TriggerBoundaryAdjustMode.InView |
|
||||
| Options | Data source for autocomplete | AutocompleteDataSource | - |
|
||||
| Disabled | Set disabled | bool | - |
|
||||
| Placeholder | Placeholder text | string | - |
|
||||
|
@ -19,6 +19,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/qtJm4yt45/AutoComplete.svg
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| Backfill | 使用键盘选择选项的时候把选中项回填到输入框中 | boolean | false |
|
||||
| BoundaryAdjustMode | `Dropdown` adjustment strategy (when for example browser resize is happening) | TriggerBoundaryAdjustMode | TriggerBoundaryAdjustMode.InView |
|
||||
| Options | 自动完成的数据源 | AutocompleteDataSource | - |
|
||||
| Disabled | 是否禁用 | bool | - |
|
||||
| Placeholder | 占位符文本 | string | - |
|
||||
|
@ -1,7 +1,7 @@
|
||||
<AvatarGroup>
|
||||
<Avatar Src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
|
||||
<Avatar Style="background-color: #f56a00">K</Avatar>
|
||||
<Tooltip Title=@("Ant User") Placement="PlacementType.Top">
|
||||
<Tooltip Title=@("Ant User") Placement="Placement.Top">
|
||||
<Unbound>
|
||||
<Avatar Style="background-color: #87d068;" Icon="user" RefBack="@context"/>
|
||||
</Unbound>
|
||||
@ -12,7 +12,7 @@
|
||||
<AvatarGroup MaxCount="2" MaxStyle="color: #f56a00; background-color:#fde3cf;">
|
||||
<Avatar Src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
|
||||
<Avatar Style="background-color: #f56a00">K</Avatar>
|
||||
<Tooltip Title=@("Ant User") Placement="PlacementType.Top" >
|
||||
<Tooltip Title=@("Ant User") Placement="Placement.Top" >
|
||||
<Unbound>
|
||||
<Avatar Style="background-color: #87d068;" Icon="user" RefBack="@context"/>
|
||||
</Unbound>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user