merge master to feature

This commit is contained in:
James Yeung 2022-08-21 22:53:33 +08:00
commit 3ac7701da4
86 changed files with 2038 additions and 1228 deletions

View File

@ -14,6 +14,34 @@ timeline: true
- Major version release is not included in this schedule for breaking change and new features.
---
### 0.11.0
`2022-06-16`
🌈Every cloud has a silver lining.
- Table
- 🔥 support for Table virtualization [#2143](https://github.com/ant-design-blazor/ant-design-blazor/pull/2143) [@anranruye](https://github.com/anranruye)
- 🔥 Support to control/restore table filter/sorter state using existing QueryModel [#2129](https://github.com/ant-design-blazor/ant-design-blazor/pull/2129) [@AnaNikolasevic](https://github.com/AnaNikolasevic)
- 🆕 support setting table scrollbar width using `ScrollBarWidth` parameter. [#2451](https://github.com/ant-design-blazor/ant-design-blazor/pull/2451) [@ElderJames](https://github.com/ElderJames)
- 🆕 support for using built-in logic when defining the PaginationTemplate. [#2220](https://github.com/ant-design-blazor/ant-design-blazor/pull/2220) [@anranruye](https://github.com/anranruye)
- 🛠 make Responsive default to false (with a breaking change). [#2419](https://github.com/ant-design-blazor/ant-design-blazor/pull/2419) [@ElderJames](https://github.com/ElderJames)
- 🛠 Use Small size Pagination to fit compact Table [#2246](https://github.com/ant-design-blazor/ant-design-blazor/pull/2246) [@anranruye](https://github.com/anranruye)
- TreeSelect
- 🐞 Fixed TreeSelect expressions and selection [#2507](https://github.com/ant-design-blazor/ant-design-blazor/pull/2507) [@ElderJames](https://github.com/ElderJames)
- 🐞 Fixed TreeSelect binding default where it did not show selected items [#2134](https://github.com/ant-design-blazor/ant-design-blazor/pull/2134) [@gmij](https://github.com/gmij)
- 🆕 Add Upload support for incorporating build-in InputFile [#2443](https://github.com/ant-design-blazor/ant-design-blazor/pull/2443) [@ElderJames](https://github.com/ElderJames)
- 🆕 Add Select search debounce. [#2275](https://github.com/ant-design-blazor/ant-design-blazor/pull/2275) [@tompru](https://github.com/tompru)
- 🆕 Component library added .net 6 target framework [#2484](https://github.com/ant-design-blazor/ant-design-blazor/pull/2484) [@ElderJames](https://github.com/ElderJames)
- ⌨️ Add Form Feedback Icon when Invalid [#2418](https://github.com/ant-design-blazor/ant-design-blazor/pull/2418) [@bweissronin](https://github.com/bweissronin)
- ⌨️ Add Checkbox supports trigger check when clicking label [#2296](https://github.com/ant-design-blazor/ant-design-blazor/pull/2296) [@bweissronin](https://github.com/bweissronin)
- ⌨️ Add Icon `Alt` Parameter to set the alt attribute that pairs with role="img" [#2302](https://github.com/ant-design-blazor/ant-design-blazor/pull/2302) [@bweissronin](https://github.com/bweissronin)
- ⌨️ Add Button `AriaLabel` Parameter [#2278](https://github.com/ant-design-blazor/ant-design-blazor/pull/2278) [@bweissronin](https://github.com/bweissronin)
- 🐞 Fixed Tree incorrect checking during initialization. [#2506](https://github.com/ant-design-blazor/ant-design-blazor/pull/2506) [@ElderJames](https://github.com/ElderJames)
- 🐞 Fixed DatePicker that week selection issue when unable to click date selection. [#2463](https://github.com/ant-design-blazor/ant-design-blazor/pull/2463) [@WhyILoveSpringRoll](https://github.com/WhyILoveSpringRoll)
- 📖 docs(faq): add CSS isolation explanation. [#2158](https://github.com/ant-design-blazor/ant-design-blazor/pull/2158) [@dennisrahmen](https://github.com/dennisrahmen)
### 0.10.7

View File

@ -14,6 +14,39 @@ timeline: true
- 主版本号:含有破坏性更新和新特性,不在发布周期内。
---
### 0.11.0
`2022-06-16`
🌈守得云开见月明~
- Table
- 🔥 支持虚拟化[#2143](https://github.com/ant-design-blazor/ant-design-blazor/pull/2143) [@anranruye](https://github.com/anranruye)
- 🔥 支持使用已有的 QueryModel 控制/恢复表格筛选排序状态[#2129](https://github.com/ant-design-blazor/ant-design-blazor/pull/2129) [@AnaNikolasevic](https://github.com/AnaNikolasevic)
- 🆕 支持用 `ScrollBarWidth` 属性来设置滚动条的宽度。[#2451](https://github.com/ant-design-blazor/ant-design-blazor/pull/2451) [@ElderJames](https://github.com/ElderJames)
- 🆕 允许在定义 `PaginationTemplate` 时使用组件内置逻辑。[#2220](https://github.com/ant-design-blazor/ant-design-blazor/pull/2220) [@anranruye](https://github.com/anranruye)
- 🛠 修改 Responsive 属性默认值为false需要响应式样式时需要设为true。[#2419](https://github.com/ant-design-blazor/ant-design-blazor/pull/2419) [@ElderJames](https://github.com/ElderJames)
- 🛠 使用 Small 大小的Pagination来适配紧凑型Table[#2246](https://github.com/ant-design-blazor/ant-design-blazor/pull/2246) [@anranruye](https://github.com/anranruye)
- 🆕 增加 Upload 支持结合原生 InputFile 组件。[#2443](https://github.com/ant-design-blazor/ant-design-blazor/pull/2443) [@ElderJames](https://github.com/ElderJames)
- 🆕 增加 Select 搜索框防抖延时绑定。[#2275](https://github.com/ant-design-blazor/ant-design-blazor/pull/2275) [@tompru](https://github.com/tompru)
- 🆕 组件库增加 .NET 6 目标框架。[#2484](https://github.com/ant-design-blazor/ant-design-blazor/pull/2484) [@ElderJames](https://github.com/ElderJames)
- TreeSelect
- 🐞 修复表达式和选择功能。[#2507](https://github.com/ant-design-blazor/ant-design-blazor/pull/2507) [@ElderJames](https://github.com/ElderJames)
- 🐞 修复绑定默认值时,没有显示选中项。[#2134](https://github.com/ant-design-blazor/ant-design-blazor/pull/2134) [@gmij](https://github.com/gmij)
- 🐞 修复 DatePicker 点击日期时,不能选中周的问题[#2463](https://github.com/ant-design-blazor/ant-design-blazor/pull/2463) [@WhyILoveSpringRoll](https://github.com/WhyILoveSpringRoll)
- 🐞 修复 Tree 初始化时 checkbox 不能正常选中的问题。[#2506](https://github.com/ant-design-blazor/ant-design-blazor/pull/2506) [@ElderJames](https://github.com/ElderJames)
- ⌨️ 增加 Form 支持当验证失败时显示Feedback图标。[#2418](https://github.com/ant-design-blazor/ant-design-blazor/pull/2418) [@bweissronin](https://github.com/bweissronin)
- ⌨️ 增加 Checkbox 支持点击标签时触发勾选[#2296](https://github.com/ant-design-blazor/ant-design-blazor/pull/2296) [@bweissronin](https://github.com/bweissronin)
- ⌨️ 增加 Icon Alt 属性,与原来的`role="img"` 搭配[#2302](https://github.com/ant-design-blazor/ant-design-blazor/pull/2302) [@bweissronin](https://github.com/bweissronin)
- ⌨️ 增加 Button AriaLabel 属性[#2278](https://github.com/ant-design-blazor/ant-design-blazor/pull/2278) [@bweissronin](https://github.com/bweissronin)
- 📖 常用问答增加 CSS 隔离的组件样式修改方式[#2158](https://github.com/ant-design-blazor/ant-design-blazor/pull/2158) [@dennisrahmen](https://github.com/dennisrahmen)
### 0.10.7
`2022-05-22`

View File

@ -1,6 +1,6 @@
<p align="center">
<a href="https://yangshunjie.com/ant-design-blazor/">
<img src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/logo.svg?sanitize=true">
<img src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/master/logo.svg?sanitize=true">
</a>
</p>
@ -52,7 +52,7 @@ WebAssembly 静态托管页面示例
- 可直接运行在 [.NET MAUI](https://dotnet.microsoft.com/zh-cn/apps/maui?WT.mc_id=DT-MVP-5003987)、[WPF](https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/wpf?view=aspnetcore-6.0&WT.mc_id=DT-MVP-5003987)、[Windows Forms](https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/windows-forms?view=aspnetcore-6.0) 等 Blazor 混合客户端环境中。
- 可直接运行在 [Electron](http://electron.atom.io/) 等基于 Web 标准的环境上。
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> Edge / IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> Edge / IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| Edge 16 / IE 11† | 522 | 57 | 11 | 44 | Chromium 57 |

View File

@ -1,6 +1,6 @@
<p align="center">
<a href="https://yangshunjie.com/ant-design-blazor/">
<img src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/logo.svg?sanitize=true">
<img src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/master/logo.svg?sanitize=true">
</a>
</p>
@ -51,7 +51,7 @@ WebAssembly static hosting examples:
- Run directly on [.NET MAUI](https://dotnet.microsoft.com/zh-cn/apps/maui?WT.mc_id=DT-MVP-5003987) / [WPF](https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/wpf?view=aspnetcore-6.0&WT.mc_id=DT-MVP-5003987) / [Windows Forms](https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/windows-forms?view=aspnetcore-6.0) and other Blazor Hybrid workloads.
- Run directly on [Electron](http://electron.atom.io/) and other Web standards-based environments.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> Edge / IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> Edge / IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| Edge 16 / IE 11† | 522 | 57 | 11 | 44 | Chromium 57 |
@ -166,7 +166,7 @@ Options for the template
- Clone to local development
```bash
$ git clone git@github.com:ant-design-blazor/ant-design-blazor.git
$ git clone https://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
@ -221,7 +221,7 @@ If you encounter any problems in the process, feel free to ask for help via foll
<details>
<summary>Scan QR Code with DingTalk</summary>
<img src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/docs/assets/dingtalk.jpg" width="300">
<img src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/master/docs/assets/dingtalk.jpg" width="300">
</details>
## Code of Conduct

View File

@ -121,6 +121,7 @@ namespace AntDesign
{
base.OnValueChange(value);
RefreshNodeValue(value);
RefreshDisplayText();
}
/// <summary>
@ -178,13 +179,16 @@ namespace AntDesign
/// <summary>
/// 清除已选择项
/// </summary>
private void ClearSelected()
private async Task ClearSelected()
{
_selectedNodes.Clear();
_hoverSelectedNodes.Clear();
_displayText = string.Empty;
SetValue(string.Empty);
_dropdownOpened = false;
_searchValue = string.Empty;
await this.FocusAsync(_inputRef);
}
/// <summary>

View File

@ -25,15 +25,58 @@ namespace AntDesign
}
}
/// <summary>
/// <para>
/// 是否显示右上角的关闭按钮
/// </para>
/// <para>
/// Whether a close (x) button is visible on top right of the Drawer dialog or not.
/// </para>
/// </summary>
[Parameter]
public bool Closable { get; set; } = true;
/// <summary>
/// <para>
/// 点击蒙层是否允许关闭
/// </para>
/// <para>
/// Clicking on the mask (area outside the Drawer) to close the Drawer or not.
/// </para>
/// </summary>
[Parameter]
public bool MaskClosable { get; set; } = true;
/// <summary>
/// <para>
/// 是否显示蒙层
/// </para>
/// <para>
/// Whether to show mask or not.
/// </para>
/// </summary>
[Parameter]
public bool Mask { get; set; } = true;
/// <summary>
/// <para>
/// 蒙层样式
/// </para>
/// <para>
/// Style for Drawer's mask element.
/// </para>
/// </summary>
[Parameter]
public string MaskStyle { get; set; }
/// <summary>
/// <para>
/// 是否支持键盘 esc 关闭
/// </para>
/// <para>
/// Whether to support keyboard esc off
/// </para>
/// </summary>
[Parameter]
public bool Keyboard { get; set; } = true;
@ -43,6 +86,14 @@ namespace AntDesign
private OneOf<RenderFragment, string> _title;
/// <summary>
/// <para>
/// 标题
/// </para>
/// <para>
/// The title for Drawer.
/// </para>
/// </summary>
[Parameter]
public OneOf<RenderFragment, string> Title
{
@ -63,20 +114,68 @@ namespace AntDesign
}
/// <summary>
/// "left" | "right" | "top" | "bottom"
/// <para>
/// Drawer 的位置,字符串, "left" | "right" | "top" | "bottom"
/// </para>
/// <para>
/// The placement of the Drawer, option could be left, top, right, bottom
/// </para>
/// </summary>
[Parameter] public string Placement { get; set; } = "right";
[Parameter]
public string Placement { get; set; } = "right";
[Parameter] public string MaskStyle { get; set; }
/// <summary>
/// <para>
/// Drawer body 样式
/// </para>
/// <para>
/// Body style for modal body element. Such as height, padding etc.
/// </para>
/// </summary>
[Parameter]
public string BodyStyle { get; set; }
[Parameter] public string BodyStyle { get; set; }
/// <summary>
/// <para>
/// Drawer对话框外层容器的类名
/// </para>
/// <para>
/// The class name of the container of the Drawer dialog.
/// </para>
/// </summary>
[Parameter]
public string WrapClassName { get; set; }
[Parameter] public string WrapClassName { get; set; }
/// <summary>
/// <para>
/// 宽度,仅当 <see cref="Placement"/> 为 "left" 或 "right" 时生效
/// </para>
/// <para>
/// Width of the Drawer dialog, only when placement is 'left' or 'right'.
/// </para>
/// </summary>
[Parameter]
public int Width { get; set; } = 256;
[Parameter] public int Width { get; set; } = 256;
[Parameter] public int Height { get; set; } = 256;
/// <summary>
/// <para>
/// 高度,仅当 <see cref="Placement"/> 为 "top" 或 "bottom" 时生效
/// </para>
/// <para>
/// Height of the Drawer dialog, only when placement is 'top' or 'bottom'.
/// </para>
/// </summary>
[Parameter]
public int Height { get; set; } = 256;
/// <summary>
/// <para>
/// 设置 Drawer 的 z-index
/// </para>
/// <para>
/// The z-index of the Drawer.
/// </para>
/// </summary>
[Parameter]
public int ZIndex
{
@ -93,10 +192,36 @@ namespace AntDesign
private string InnerZIndexStyle => (_status.IsOpen() || _status == ComponentStatus.Closing) ? _zIndexStyle : "z-index:-9999;";
[Parameter] public int OffsetX { get; set; } = 0;
/// <summary>
/// <para>
/// X 轴方向的偏移量,只在方向为 'left'或'right' 时生效.
/// </para>
/// <para>
/// The the X coordinate offset(px), only when placement is 'left' or 'right'.
/// </para>
/// </summary>
[Parameter]
public int OffsetX { get; set; } = 0;
[Parameter] public int OffsetY { get; set; } = 0;
/// <summary>
/// <para>
/// Y 轴方向的偏移量,只在方向为 'top'或'bottom' 时生效
/// </para>
/// <para>
/// The the Y coordinate offset(px), only when placement is 'top' or 'bottom'.
/// </para>
/// </summary>
[Parameter]
public int OffsetY { get; set; } = 0;
/// <summary>
/// <para>
/// Drawer 是否可见
/// </para>
/// <para>
/// Whether the Drawer dialog is visible or not.
/// </para>
/// </summary>
[Parameter]
public bool Visible
{
@ -115,10 +240,30 @@ namespace AntDesign
}
}
[Parameter] public EventCallback OnClose { get; set; }
[Parameter] public RenderFragment Handler { get; set; }
/// <summary>
/// <para>
/// 在 Drawer 打开前的回调事件
/// </para>
/// <para>
/// Specify a callback that will be called before drawer displayed
/// </para>
/// </summary>
[Parameter]
public Func<Task> OnOpen { get; set; }
#endregion
/// <summary>
/// <para>
/// 在 关闭 前的回调事件,应当在 OnClose 将 <see cref="Visible"/> 设置为false
/// </para>
/// <para>
/// Specify a callback that will be called when a user clicks mask, close button or Cancel button.
/// </para>
/// </summary>
[Parameter]
public EventCallback OnClose { get; set; }
[Parameter]
public RenderFragment Handler { get; set; }
private OneOf<RenderFragment, string> _content;
@ -126,6 +271,8 @@ namespace AntDesign
private RenderFragment ContentTemplate { get; set; }
#endregion
private ComponentStatus _status;
private bool _hasInvokeClosed;
private bool _isOpen = default;
@ -257,6 +404,12 @@ namespace AntDesign
case ComponentStatus.Opening:
{
_status = ComponentStatus.Opened;
if (OnOpen != null)
{
await OnOpen.Invoke();
}
_hasInvokeClosed = false;
if (string.IsNullOrWhiteSpace(Style))
{

View File

@ -22,6 +22,7 @@
OffsetX="@drawerConfig.OffsetX"
OffsetY="@drawerConfig.OffsetY"
Visible="@drawerConfig.Visible"
OnOpen="@drawerRef.OnOpen"
OnClose="@drawerRef.CloseAsync">
@drawerConfig.ChildContent
</Drawer>

View File

@ -50,8 +50,6 @@ namespace AntDesign
public override async Task OpenAsync()
{
await _service.OpenAsync(this);
if (OnOpen != null)
await OnOpen.Invoke();
}
/// <summary>

View File

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Components.Web;
namespace AntDesign
{
public class DrawerClosingEventArgs
{
private bool _rejected;
public DrawerClosingEventArgs() { }
public DrawerClosingEventArgs(bool reject)
{
_rejected = reject;
}
internal bool Rejected { get => _rejected; }
/// <summary>
/// Reject to close
/// </summary>
public void Reject()
{
_rejected = true;
}
}
}

View File

@ -0,0 +1,17 @@
// 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.
#nullable enable
namespace AntDesign;
public class DrawerOpenEventArgs
{
/// <summary>
/// 获取或设置一个值,该值指示是否应取消事件。
/// 返回结果: true 如果应取消事件;否则为 false。
/// Gets or sets a value indicating whether the event should be cancelled.
/// Return result: true if the event should be cancelled; otherwise false.
/// </summary>
public bool Cancel { get; set; }
}

View File

@ -30,7 +30,7 @@ namespace AntDesign
{
_step = value;
NumberFormatInfo nfi = CultureInfo.NumberFormat;
var stepStr = _step.ToString();
var stepStr = Convert.ToDecimal(_step).ToString(nfi);
if (string.IsNullOrEmpty(_format))
{
_format = string.Join('.', stepStr.Split(nfi.NumberDecimalSeparator).Select(n => new string('0', n.Length)));

View File

@ -333,6 +333,7 @@ namespace AntDesign
{
if (CurrentValueAsString != args?.Value?.ToString())
{
CurrentValueAsString = args?.Value?.ToString();
if (OnChange.HasDelegate)
{
await OnChange.InvokeAsync(Value);
@ -636,11 +637,12 @@ namespace AntDesign
}
// onchange 和 onblur 事件会导致点击 OnSearch 按钮时不触发 Click 事件,暂时取消这两个事件
if (!IgnoreOnChangeAndBlur)
{
//2022-8-3 去掉if后search也能正常工作
//if (!IgnoreOnChangeAndBlur)
//{
builder.AddAttribute(70, "onchange", CallbackFactory.Create(this, OnChangeAsync));
builder.AddAttribute(71, "onblur", CallbackFactory.Create(this, OnBlurAsync));
}
//}
builder.AddAttribute(72, "onkeypress", CallbackFactory.Create(this, OnKeyPressAsync));
builder.AddAttribute(73, "onkeydown", CallbackFactory.Create(this, OnkeyDownAsync));

View File

@ -44,6 +44,9 @@ namespace AntDesign
}
}
[Parameter]
public EventCallback<bool> CollapsedChanged { get; set; }
[Parameter]
public EventCallback<bool> OnCollapse { get; set; }
@ -132,6 +135,11 @@ namespace AntDesign
{
OnCollapse.InvokeAsync(_isCollapsed);
}
if (CollapsedChanged.HasDelegate)
{
CollapsedChanged.InvokeAsync(_isCollapsed);
}
}
private void OptimizeSize(decimal windowWidth)
@ -165,6 +173,11 @@ namespace AntDesign
{
OnCollapse.InvokeAsync(_isCollapsed);
}
if (CollapsedChanged.HasDelegate)
{
CollapsedChanged.InvokeAsync(_isCollapsed);
}
}
StateHasChanged();

View File

@ -312,6 +312,21 @@ namespace AntDesign
return style;
}
/// <summary>
/// clear ant-model enter class, which will disable user-select.
/// more details see style/mixins/modal-mask.less
/// </summary>
/// <returns></returns>
internal async Task CleanShowAnimationAsync()
{
var animationTimeMs = 300;
await Task.Delay(animationTimeMs);
_maskAnimationClsName = "";
_modalAnimationClsName = "";
await InvokeStateHasChangedAsync();
}
/// <summary>
/// Hide Dialog through animation
/// </summary>

View File

@ -102,6 +102,7 @@ namespace AntDesign
_hideDone = false;
_hasDestroy = false;
await _dialog.CleanShowAnimationAsync();
if (OnAfterShow.HasDelegate)
{
await OnAfterShow.InvokeAsync(null);

View File

@ -6,7 +6,7 @@ namespace AntDesign
{
public sealed class ModalAnimation
{
public const string ModalEnter = " ant-zoom-enter ant-zoom-enter-active ant-zoom-enter";
public const string ModalEnter = " ant-zoom-enter ant-zoom-enter-active ant-zoom";
public const string ModalLeave = " ant-zoom-leave ant-zoom-leave-active ant-zoom";
public const string MaskEnter = " ant-fade-enter ant-fade-enter-active ant-fade";

View File

@ -209,6 +209,7 @@
Page="@key"
Active="@(_current == key)"
ShowTitle="ShowTitle"
Class=""
ItemRender="@ItemRender"/>
);
}
@ -239,7 +240,7 @@
Page="@right"
Active="@(_current == right)"
ShowTitle="ShowTitle"
Class="@($"{PrefixCls}-item-after-jump-prev")"
Class="@($"{PrefixCls}-item-before-jump-next")"
ItemRender="@ItemRender"/>;
pagerList.AddLast(jumpNext);
}

View File

@ -46,7 +46,6 @@
var prefixCls = $"{RootPrefixCls}-item";
ClassMapper.Add(prefixCls).Add($"{prefixCls}-{Page}")
.If($"{prefixCls}-active", () => Active)
.If(Class, () => !string.IsNullOrWhiteSpace(Class))
.If($"{prefixCls}-disabled", () => Page == 0);
base.OnInitialized();

View File

@ -737,8 +737,8 @@ namespace AntDesign
{
if (!updateSelectOption.IsSelected)
{
updateSelectOption.IsSelected = isSelected;
SelectedOptionItems.Add(updateSelectOption);
//updateSelectOption.IsSelected = isSelected;
//SelectedOptionItems.Add(updateSelectOption);
}
processedSelectedCount--;
}

View File

@ -17,7 +17,7 @@ using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using OneOf;
#endregion
#endregion using block
namespace AntDesign
{
@ -34,7 +34,6 @@ namespace AntDesign
internal ElementReference _inputRef;
protected bool _isInitialized;
protected bool _isPrimitive;
/// <summary>
@ -50,7 +49,6 @@ namespace AntDesign
protected string _prevSearchValue = string.Empty;
protected string _searchValue = string.Empty;
protected SelectContent<TItemValue, TItem> _selectContent;
protected IEnumerable<TItemValue> _selectedValues;
@ -58,77 +56,93 @@ namespace AntDesign
protected Action<TItem, string> _setLabel;
protected Action<TItem, TItemValue> _setValue;
/// <summary>
/// Show clear button. Has no effect if <see cref="AntInputComponentBase{TValue}.Value"/> type default
/// is also in the list of <see cref="SelectOption{TItemValue, TItem}"/>,
/// Show clear button. Has no effect if <see cref="AntInputComponentBase{TValue}.Value"/> type default
/// is also in the list of <see cref="SelectOption{TItemValue, TItem}"/>,
/// unless used with <see cref="ValueOnClear"/>.
/// </summary>
[Parameter] public bool AllowClear { get; set; }
/// <summary>
/// Whether the current search will be cleared on selecting an item.
/// </summary>
[Parameter] public bool AutoClearSearchValue { get; set; } = true;
/// <summary>
/// Whether the Select component is disabled.
/// </summary>
[Parameter] public bool Disabled { get; set; }
/// <summary>
/// Set mode of Select - default | multiple | tags
/// </summary>
[Parameter] public string Mode { get; set; } = "default";
/// <summary>
/// Indicates whether the search function is active or not. Always true for mode tags.
/// </summary>
[Parameter] public bool EnableSearch { get; set; }
/// <summary>
/// Delays the processing of the search input event until the user has stopped
/// Delays the processing of the search input event until the user has stopped
/// typing for a predetermined amount of time
/// </summary>
[Parameter]
public int SearchDebounceMilliseconds { get; set; }
/// <summary>
/// Show loading indicator. You have to write the loading logic on your own.
/// </summary>
[Parameter] public bool Loading { get; set; }
/// <summary>
/// Controlled open state of dropdown.
/// </summary>
[Parameter] public bool Open { get; set; }
/// <summary>
/// Placeholder of select.
/// </summary>
[Parameter] public string Placeholder { get; set; }
/// <summary>
/// Called when focus.
/// </summary>
[Parameter] public EventCallback OnFocus { get; set; }
/// <summary>
/// The name of the property to be used as a group indicator.
/// If the value is set, the entries are displayed in groups.
/// The name of the property to be used as a group indicator.
/// If the value is set, the entries are displayed in groups.
/// Use additional SortByGroup and SortByLabel.
/// </summary>
[Parameter] public SortDirection SortByGroup { get; set; } = SortDirection.None;
/// <summary>
/// Sort items by label value. None | Ascending | Descending
/// </summary>
[Parameter] public SortDirection SortByLabel { get; set; } = SortDirection.None;
/// <summary>
/// Hides the selected items when they are selected.
/// </summary>
[Parameter] public bool HideSelected { get; set; }
/// <summary>
/// Used for the two-way binding.
/// </summary>
[Parameter] public override EventCallback<TItemValue> ValueChanged { get; set; }
/// <summary>
/// Used for the two-way binding.
/// </summary>
[Parameter] public EventCallback<IEnumerable<TItemValue>> ValuesChanged { get; set; }
/// <summary>
/// The custom suffix icon.
/// </summary>
[Parameter] public RenderFragment SuffixIcon { get; set; }
/// <summary>
/// The custom prefix icon.
/// </summary>
@ -136,6 +150,7 @@ namespace AntDesign
protected IEnumerable<TItemValue> _defaultValues;
protected bool _defaultValuesHasItems;
/// <summary>
/// Used when Mode = multiple | tags - The values are used during initialization and when pressing the Reset button within Forms.
/// </summary>
@ -177,6 +192,7 @@ namespace AntDesign
internal HashSet<SelectOptionItem<TItemValue, TItem>> SelectOptionItems { get; } = new();
internal List<SelectOptionItem<TItemValue, TItem>> SelectedOptionItems { get; } = new();
/// <summary>
/// Called when the selected item changes.
/// </summary>
@ -187,8 +203,9 @@ namespace AntDesign
/// </summary>
[Parameter] public EventCallback<IEnumerable<TItem>> OnSelectedItemsChanged { get; set; }
internal virtual SelectMode SelectMode => Mode.ToSelectMode();
[Parameter] public string Status { get; set; }
internal virtual SelectMode SelectMode => Mode.ToSelectMode();
/// <summary>
/// Currently active (highlighted) option.
@ -263,7 +280,6 @@ namespace AntDesign
public Func<string, TItemValue> CustomTagLabelToValue { get; set; } =
label => (TItemValue)TypeDescriptor.GetConverter(typeof(TItemValue)).ConvertFromInvariantString(label);
/// <summary>
/// Determines if SelectOptions has any selected items
/// </summary>
@ -351,7 +367,7 @@ namespace AntDesign
[Parameter] public int MaxTagTextLength { get; set; }
/// <summary>
/// Whether to embed label in value, turn the format of value from TItemValue to string (JSON)
/// Whether to embed label in value, turn the format of value from TItemValue to string (JSON)
/// e.g. { "value": TItemValue, "label": "Label value" }
/// </summary>
[Parameter]
@ -383,6 +399,7 @@ namespace AntDesign
private bool _hasValueOnClear;
private TItemValue _valueOnClear;
/// <summary>
/// When Clear button is pressed, Value will be set to
/// whatever is set in ValueOnClear
@ -404,14 +421,12 @@ namespace AntDesign
/// <returns>true if SelectOptions has no values and the searchValue is empty; otherwise false </returns>
protected bool ShowPlaceholder => !HasValue && string.IsNullOrEmpty(_searchValue);
/// <summary>
/// The Method is called every time if the value of the @bind-Values was changed by the two-way binding.
/// </summary>
protected async Task OnValuesChangeAsync(IEnumerable<TItemValue> values)
{
if (
!_isInitialized) // This is important because otherwise the initial value is overwritten by the EventCallback of ValueChanged and would be NULL.
if (!_isInitialized) // This is important because otherwise the initial value is overwritten by the EventCallback of ValueChanged and would be NULL.
{
return;
}
@ -670,7 +685,6 @@ namespace AntDesign
}
}
/// <summary>
/// The method is called every time if the user select/de-select a item by mouse or keyboard.
/// Don't change the IsSelected property outside of this function.
@ -827,7 +841,6 @@ namespace AntDesign
_prevSearchValue = string.Empty;
}
/// <summary>
/// Method is called via EventCallBack after the user clicked on the Clear icon inside the Input element.
/// Set the IsSelected and IsHidden properties for all items to False. It updates the overlay position if
@ -943,8 +956,6 @@ namespace AntDesign
{
Focused = true;
SetClassMap();
await FocusAsync(_inputRef);
await OnFocus.InvokeAsync(Focused);
@ -962,7 +973,6 @@ namespace AntDesign
}
}
internal async Task OnArrowClick(MouseEventArgs args)
{
await _dropDown.OnClickDiv(args);
@ -985,7 +995,6 @@ namespace AntDesign
_ = ClearSelectedAsync();
}
/// <summary>
/// Clears the selectValue(s) property and send the null(default) value back through the two-way binding.
/// </summary>
@ -1005,4 +1014,4 @@ namespace AntDesign
protected abstract void SetClassMap();
}
}
}

View File

@ -2,297 +2,306 @@
@namespace AntDesign.Select.Internal
@typeparam TItemValue
@typeparam TItem
@inherits AntDomComponentBase
@if (ParentSelect.SelectMode == SelectMode.Default)
{
<div class="@Prefix-selector" @ref="@Ref" style="@(ParentSelect.PrefixIcon is null?"":"padding-left: 4px;")">
@if (ParentSelect.PrefixIcon != null)
{
<span class="@Prefix-prefix" unselectable="on" aria-hidden="true" style="user-select: none;display:flex;align-items: center;padding-right: 4px;">
@ParentSelect.PrefixIcon
</span>
}
<span class="@Prefix-selection-search" style="@_inputWidth">
<input @ref="ParentSelect._inputRef"
@oninput="@OnInputChange"
@onkeyup="OnKeyUp"
@onkeydown="OnKeyDown"
@attributes=@AdditonalAttributes()
@bind-value="@SearchValue"
id="@(ParentSelect.Id)_list"
type="search"
readonly="@(!ParentSelect.IsSearchEnabled)"
unselectable="@(ParentSelect.IsSearchEnabled ? false : "on")"
role="combobox"
class="@Prefix-selection-search-input"
autocomplete="off"
aria-owns="@(ParentSelect.Id)_list"
aria-expanded="@IsOverlayShow"
aria-autocomplete="list"
aria-controls="@(ParentSelect.Id)_list"
aria-haspopup="listbox"
style="@_inputStyle"/>
</span>
@if (ShowPlaceholder)
{
<span class="@Prefix-selection-placeholder">@Placeholder</span>
}
else
{
var selectedItem = ParentSelect.SelectedOptionItems.FirstOrDefault();
if (string.IsNullOrEmpty(SearchValue) && selectedItem != null)
{
@if (ParentLabelTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
<CascadingValue Value="@selectedItem" Name="SelectOption">
@ParentLabelTemplate(selectedItem.Item)
</CascadingValue>
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
@selectedItem.Label
</span>
}
}
}
</div>
@if (ParentSelect.SuffixIcon != null)
<div class="@Prefix-selector" @ref="@Ref" style="@(ParentSelect.PrefixIcon is null?"":"padding-left: 4px;")">
@if (ParentSelect.PrefixIcon != null)
{
<span class="ant-select-arrow" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="@ParentSelect.OnArrowClick" @onclick:stopPropagation="true">
@ParentSelect.SuffixIcon
</span>
<span class="@Prefix-prefix" unselectable="on" aria-hidden="true" style="user-select: none;display:flex;align-items: center;padding-right: 4px;">
@ParentSelect.PrefixIcon
</span>
}
else if (ParentSelect.Loading)
<span class="@Prefix-selection-search" style="@_inputWidth">
<input @ref="ParentSelect._inputRef"
@oninput="@OnInputChange"
@onkeyup="OnKeyUp"
@onkeydown="OnKeyDown"
@attributes=@AdditonalAttributes()
@bind-value="@SearchValue"
id="@(ParentSelect.Id)_list"
type="search"
readonly="@(!ParentSelect.IsSearchEnabled)"
unselectable="@(ParentSelect.IsSearchEnabled ? false : "on")"
role="combobox"
class="@Prefix-selection-search-input"
autocomplete="off"
aria-owns="@(ParentSelect.Id)_list"
aria-expanded="@IsOverlayShow"
aria-autocomplete="list"
aria-controls="@(ParentSelect.Id)_list"
aria-haspopup="listbox"
style="@_inputStyle" />
</span>
@if (ShowPlaceholder)
{
<span class="ant-select-arrow ant-select-arrow-loading" unselectable="on" aria-hidden="true" style="user-select: none;">
<Icon Type="loading"></Icon>
</span>
<span class="@Prefix-selection-placeholder">@Placeholder</span>
}
else
{
if (ShowArrowIcon)
var selectedItem = ParentSelect.SelectedOptionItems.FirstOrDefault();
if (string.IsNullOrEmpty(SearchValue) && selectedItem != null)
{
@if (ParentLabelTemplate != null)
{
<span class="ant-select-arrow" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="@ParentSelect.OnArrowClick" @onclick:stopPropagation="true">
@if (ParentSelect.IsSearchEnabled && IsOverlayShow)
{
if (ShowSearchIcon)
{
<Icon Type="search"></Icon>
}
}
else
{
<Icon Type="down"></Icon>
}
</span>
<CascadingValue Value="this" Name="SelectContent">
<CascadingValue Value="@selectedItem" Name="SelectOption">
@ParentLabelTemplate(selectedItem.Item)
</CascadingValue>
</CascadingValue>
}
else
{
<span class="ant-select-arrow" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="@ParentSelect.OnArrowClick" @onclick:stopPropagation="true">
@if (ParentSelect.IsSearchEnabled && IsOverlayShow)
{
if (ShowSearchIcon)
{
<Icon Type="search"></Icon>
}
}
</span>
}
@if (!ParentSelect.Disabled && ParentSelect.AllowClear && ParentSelect.HasValue)
{
<span class="ant-select-clear" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="OnClearClick" @onclick:stopPropagation="true">
<Icon Type="close-circle" Theme="fill"></Icon>
</span>
<span class="@Prefix-selection-item">
@getLabel(selectedItem)
</span>
}
}
}
</div>
@if (ParentSelect.SuffixIcon != null)
{
<span class="ant-select-arrow" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="@ParentSelect.OnArrowClick" @onclick:stopPropagation="true">
@ParentSelect.SuffixIcon
</span>
}
else if (ParentSelect.Loading)
{
<span class="ant-select-arrow ant-select-arrow-loading" unselectable="on" aria-hidden="true" style="user-select: none;">
<Icon Type="loading"></Icon>
</span>
}
else
{
if (ShowArrowIcon)
{
<span class="ant-select-arrow" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="@ParentSelect.OnArrowClick" @onclick:stopPropagation="true">
@if (ParentSelect.IsSearchEnabled && IsOverlayShow)
{
if (ShowSearchIcon)
{
<Icon Type="search"></Icon>
}
}
else
{
<Icon Type="down"></Icon>
}
</span>
}
else
{
<span class="ant-select-arrow" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="@ParentSelect.OnArrowClick" @onclick:stopPropagation="true">
@if (ParentSelect.IsSearchEnabled && IsOverlayShow)
{
if (ShowSearchIcon)
{
<Icon Type="search"></Icon>
}
}
</span>
}
@if (!ParentSelect.Disabled && ParentSelect.AllowClear && ParentSelect.HasValue)
{
<span class="ant-select-clear" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="OnClearClick" @onclick:stopPropagation="true">
<Icon Type="close-circle" Theme="fill"></Icon>
</span>
}
}
}
else //ParentSelect.SelectMode != SelectMode.Default
{
<div class="@Prefix-selector" @ref="@Ref" style="@(ParentSelect.PrefixIcon is null?"":"padding-left: 4px;")" >
<div class="@Prefix-selection-overflow" @ref="@_overflow">
@if (ParentSelect.PrefixIcon != null)
{
<div class="@Prefix-selection-overflow-item" style="opacity: 1;order:-100;">
<span @ref="@_prefixRef" class="@Prefix-prefix" unselectable="on" aria-hidden="true" style="user-select: none;display:flex;align-items: center;padding-right: 4px;min-height:28px;">
@ParentSelect.PrefixIcon
</span>
</div>
}
@if (!ShowPlaceholder)
{
var selectedItems = ParentSelect.SelectedOptionItems;
@if (ParentSelect.HasTagCount)
{
@for (int i = 0; i < Math.Min(ParentSelect.MaxTagCount.AsT0, selectedItems.Count); i++)
{
var selectedOption = selectedItems[i];
<div class="@Prefix-selection-overflow-item" style="@OverflowStyle(i)">
@if (ParentLabelTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
<CascadingValue Value="@selectedOption" Name="SelectOption">
@ParentLabelTemplate(selectedOption.Item)
</CascadingValue>
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">@FormatLabel(selectedOption.Label)</span>
<span unselectable="on" aria-hidden="true" style="user-select: none;" class="@Prefix-selection-item-remove"
@onmousedown="(e)=> RemoveClicked(e, selectedOption)" onclick="event.stopPropagation()">
<Icon Type="close"></Icon>
</span>
</span>
}
</div>
}
@if (selectedItems.Count > ParentSelect.MaxTagCount.AsT0)
{
<div class="@Prefix-selection-overflow-item @Prefix-selection-overflow-item-rest" style="opacity: 1; order: @(selectedItems.Count-1);">
@if (ParentMaxTagPlaceholerTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
@ParentMaxTagPlaceholerTemplate(selectedItems.Skip(ParentSelect.MaxTagCount.AsT0).Select(i => i.Item))
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">+ @(selectedItems.Count - ParentSelect.MaxTagCount.AsT0)@Ellipse</span>
</span>
}
</div>
}
}
else if (ParentSelect.IsResponsive)
{
@for (int i = 0; i < selectedItems.Count; i++)
{
var selectedOption = selectedItems[i];
<div class="@Prefix-selection-overflow-item" @key="@selectedOption.InternalId" @ref="@selectedOption.SelectedTagRef" style="@OverflowStyle(i)">
@if (ParentLabelTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
<CascadingValue Value="@selectedOption" Name="SelectOption">
@ParentLabelTemplate(selectedOption.Item)
</CascadingValue>
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">@FormatLabel(selectedOption.Label)</span>
<span unselectable="on" aria-hidden="true" style="user-select: none;" class="@Prefix-selection-item-remove"
@onmousedown="(e)=> RemoveClicked(e, selectedOption)" onclick="event.stopPropagation()">
<Icon Type="close"></Icon>
</span>
</span>
}
</div>
}
@if (selectedItems.Count > _calculatedMaxCount)
{
<div class="@Prefix-selection-overflow-item @Prefix-selection-overflow-item-rest" @key="@_internalId" @ref="@_aggregateTag" style="opacity 1; order: @(_calculatedMaxCount-1);">
@if (ParentMaxTagPlaceholerTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
@ParentMaxTagPlaceholerTemplate(selectedItems.Skip(_calculatedMaxCount).Select(i => i.Item))
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">+ @(selectedItems.Count-_calculatedMaxCount)@Ellipse</span>
</span>
}
</div>
}
}
else
{
string firstAfterPrefix = $"max-width: {GetFirstItemMaxWidth()}%;";
@foreach (var selectedOption in selectedItems)
{
<div class="@Prefix-selection-overflow-item" style="opacity: 1;@firstAfterPrefix">
@if (ParentLabelTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
<CascadingValue Value="@selectedOption" Name="SelectOption">
@ParentLabelTemplate(selectedOption.Item)
</CascadingValue>
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">@FormatLabel(selectedOption.Label)</span>
<span unselectable="on" aria-hidden="true" style="user-select: none;" class="@Prefix-selection-item-remove"
@onmousedown="(e)=> RemoveClicked(e, selectedOption)" onclick="event.stopPropagation()">
<Icon Type="close"></Icon>
</span>
</span>
}
</div>
firstAfterPrefix = "max-width: 98%;";
}
}
}
<div class="@Prefix-selection-overflow-item @Prefix-selection-overflow-item-suffix" style="opacity: 1; order: @(ParentSelect.IsResponsive?_calculatedMaxCount-1:ParentSelect.SelectedOptionItems.Count)">
<div class="@Prefix-selection-search" style="@_inputWidth">
<input @ref="ParentSelect._inputRef"
@oninput="OnInputChange"
@onkeyup="OnKeyUp"
@onkeydown="OnKeyDown"
@attributes=@AdditonalAttributes()
@bind-value="@SearchValue"
id="@ParentSelect.Id"
type="search"
readonly="@(!ParentSelect.IsSearchEnabled)"
unselectable="@(ParentSelect.IsSearchEnabled ? false : "on")"
role="combobox"
class="@Prefix-selection-search-input"
autocomplete="off"
aria-owns="@(ParentSelect.Id)_list"
aria-expanded="@IsOverlayShow"
aria-autocomplete="list"
aria-controls="@(ParentSelect.Id)_list"
aria-haspopup="listbox"
style="@_inputStyle"/>
<span class="@Prefix-selection-search-mirror" aria-hidden="true">&nbsp;</span>
</div>
</div>
@if (ShowPlaceholder)
{
<span class="@Prefix-selection-placeholder" style="@(ParentSelect.PrefixIcon != null?"margin-left: 7px;":"")">@Placeholder</span>
}
{
<div class="@Prefix-selector" @ref="@Ref" style="@(ParentSelect.PrefixIcon is null?"":"padding-left: 4px;")">
<div class="@Prefix-selection-overflow" @ref="@_overflow">
@if (ParentSelect.PrefixIcon != null)
{
<div class="@Prefix-selection-overflow-item" style="opacity: 1;order:-100;">
<span @ref="@_prefixRef" class="@Prefix-prefix" unselectable="on" aria-hidden="true" style="user-select: none;display:flex;align-items: center;padding-right: 4px;min-height:28px;">
@ParentSelect.PrefixIcon
</span>
</div>
</div>
}
@if (!ShowPlaceholder)
{
var selectedItems = ParentSelect.SelectedOptionItems;
@if (ParentSelect.HasTagCount)
{
@for (int i = 0; i < Math.Min(ParentSelect.MaxTagCount.AsT0, selectedItems.Count); i++)
{
var selectedOption = selectedItems[i];
<div class="@Prefix-selection-overflow-item" style="@OverflowStyle(i)">
@if (ParentLabelTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
<CascadingValue Value="@selectedOption" Name="SelectOption">
@ParentLabelTemplate(selectedOption.Item)
</CascadingValue>
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">@getTagLabel(this,selectedOption)</span>
<span unselectable="on" aria-hidden="true" style="user-select: none;" class="@Prefix-selection-item-remove"
@onmousedown="(e)=> RemoveClicked(e, selectedOption)" onclick="event.stopPropagation()">
<Icon Type="close"></Icon>
</span>
</span>
}
</div>
}
@if (selectedItems.Count > ParentSelect.MaxTagCount.AsT0)
{
<div class="@Prefix-selection-overflow-item @Prefix-selection-overflow-item-rest" style="opacity: 1; order: @(selectedItems.Count-1);">
@if (ParentMaxTagPlaceholerTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
@ParentMaxTagPlaceholerTemplate(selectedItems.Skip(ParentSelect.MaxTagCount.AsT0).Select(i => i.Item))
</CascadingValue>
}
else
{
@if (ParentSelect.SuffixIcon != null)
{
<span @ref="@_suffixRef" class="ant-select-arrow" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="@ParentSelect.OnArrowClick" @onclick:stopPropagation="true">
@ParentSelect.SuffixIcon
</span>
}
else if (ParentSelect.Loading)
{
<span @ref="@_suffixRef" class="ant-select-arrow ant-select-arrow-loading" unselectable="on" aria-hidden="true" style="user-select: none;">
<Icon Type="loading"></Icon>
</span>
}
@if (!ParentSelect.Disabled && ParentSelect.AllowClear && ParentSelect.HasValue)
{
<span @ref="@_suffixRef" class="ant-select-clear" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="OnClearClickAsync" @onclick:stopPropagation="true">
<Icon Type="close-circle" Theme="fill"></Icon>
</span>
}
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">+ @(selectedItems.Count - ParentSelect.MaxTagCount.AsT0)@Ellipse</span>
</span>
}
</div>
}
}
else if (ParentSelect.IsResponsive)
{
@for (int i = 0; i < selectedItems.Count; i++)
{
var selectedOption = selectedItems[i];
<div class="@Prefix-selection-overflow-item" @key="@selectedOption.InternalId" @ref="@selectedOption.SelectedTagRef" style="@OverflowStyle(i)">
@if (ParentLabelTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
<CascadingValue Value="@selectedOption" Name="SelectOption">
@ParentLabelTemplate(selectedOption.Item)
</CascadingValue>
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">@getTagLabel(this,selectedOption)</span>
<span unselectable="on" aria-hidden="true" style="user-select: none;" class="@Prefix-selection-item-remove"
@onmousedown="(e)=> RemoveClicked(e, selectedOption)" onclick="event.stopPropagation()">
<Icon Type="close"></Icon>
</span>
</span>
}
</div>
}
@if (selectedItems.Count > _calculatedMaxCount)
{
<div class="@Prefix-selection-overflow-item @Prefix-selection-overflow-item-rest" @key="@_internalId" @ref="@_aggregateTag" style="opacity 1; order: @(_calculatedMaxCount-1);">
@if (ParentMaxTagPlaceholerTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
@ParentMaxTagPlaceholerTemplate(selectedItems.Skip(_calculatedMaxCount).Select(i => i.Item))
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">+ @(selectedItems.Count-_calculatedMaxCount)@Ellipse</span>
</span>
}
</div>
}
}
else
{
string firstAfterPrefix = $"max-width: {GetFirstItemMaxWidth()}%;";
@foreach (var selectedOption in selectedItems)
{
<div class="@Prefix-selection-overflow-item" style="opacity: 1;@firstAfterPrefix">
@if (ParentLabelTemplate != null)
{
<CascadingValue Value="this" Name="SelectContent">
<CascadingValue Value="@selectedOption" Name="SelectOption">
@ParentLabelTemplate(selectedOption.Item)
</CascadingValue>
</CascadingValue>
}
else
{
<span class="@Prefix-selection-item">
<span class="@Prefix-selection-item-content">@getTagLabel(this,selectedOption)</span>
<span unselectable="on" aria-hidden="true" style="user-select: none;" class="@Prefix-selection-item-remove"
@onmousedown="(e)=> RemoveClicked(e, selectedOption)" onclick="event.stopPropagation()">
<Icon Type="close"></Icon>
</span>
</span>
}
</div>
firstAfterPrefix = "max-width: 98%;";
}
}
}
<div class="@Prefix-selection-overflow-item @Prefix-selection-overflow-item-suffix" style="opacity: 1; order: @(ParentSelect.IsResponsive?_calculatedMaxCount-1:ParentSelect.SelectedOptionItems.Count)">
<div class="@Prefix-selection-search" style="@_inputWidth">
<input @ref="ParentSelect._inputRef"
@oninput="OnInputChange"
@onkeyup="OnKeyUp"
@onkeydown="OnKeyDown"
@attributes=@AdditonalAttributes()
@bind-value="@SearchValue"
id="@ParentSelect.Id"
type="search"
readonly="@(!ParentSelect.IsSearchEnabled)"
unselectable="@(ParentSelect.IsSearchEnabled ? false : "on")"
role="combobox"
class="@Prefix-selection-search-input"
autocomplete="off"
aria-owns="@(ParentSelect.Id)_list"
aria-expanded="@IsOverlayShow"
aria-autocomplete="list"
aria-controls="@(ParentSelect.Id)_list"
aria-haspopup="listbox"
style="@_inputStyle" />
<span class="@Prefix-selection-search-mirror" aria-hidden="true">&nbsp;</span>
</div>
</div>
@if (ShowPlaceholder)
{
<span class="@Prefix-selection-placeholder" style="@(ParentSelect.PrefixIcon != null?"margin-left: 7px;":"")">@Placeholder</span>
}
</div>
</div>
@if (ParentSelect.SuffixIcon != null)
{
<span @ref="@_suffixRef" class="ant-select-arrow" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="@ParentSelect.OnArrowClick" @onclick:stopPropagation="true">
@ParentSelect.SuffixIcon
</span>
}
else if (ParentSelect.Loading)
{
<span @ref="@_suffixRef" class="ant-select-arrow ant-select-arrow-loading" unselectable="on" aria-hidden="true" style="user-select: none;">
<Icon Type="loading"></Icon>
</span>
}
@if (!ParentSelect.Disabled && ParentSelect.AllowClear && ParentSelect.HasValue)
{
<span @ref="@_suffixRef" class="ant-select-clear" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="OnClearClickAsync" @onclick:stopPropagation="true">
<Icon Type="close-circle" Theme="fill"></Icon>
</span>
}
}
@code {
RenderFragment getLabel(SelectOptionItem<TItemValue, TItem> option) =>
@<Template>@if (option.LabelTemplate != null)@option.LabelTemplate else @option.Label</Template>;
RenderFragment getTagLabel(SelectContent<TItemValue, TItem> content, SelectOptionItem<TItemValue, TItem> option)=>
@<Template>@if (option.LabelTemplate != null) @option.LabelTemplate else @content.FormatLabel(option.Label)</Template>;
}

View File

@ -18,7 +18,7 @@ using Microsoft.JSInterop;
namespace AntDesign.Select.Internal
{
public partial class SelectContent<TItemValue, TItem> : IDisposable
public partial class SelectContent<TItemValue, TItem> : AntDomComponentBase
{
[CascadingParameter(Name = "ParentSelect")] internal SelectBase<TItemValue, TItem> ParentSelect { get; set; }
[CascadingParameter(Name = "ParentLabelTemplate")] internal RenderFragment<TItem> ParentLabelTemplate { get; set; }
@ -64,21 +64,9 @@ namespace AntDesign.Select.Internal
[Parameter] public EventCallback<MouseEventArgs> OnClearClick { get; set; }
[Parameter] public EventCallback<SelectOptionItem<TItemValue, TItem>> OnRemoveSelected { get; set; }
[Parameter] public string SearchValue { get; set; }
[Parameter] public ForwardRef RefBack { get; set; } = new ForwardRef();
[Parameter] public int SearchDebounceMilliseconds { get; set; }
[Inject] protected IJSRuntime Js { get; set; }
[Inject] private IDomEventListener DomEventListener { get; set; }
protected ElementReference Ref
{
get { return _ref; }
set
{
_ref = value;
RefBack?.Set(value);
}
}
private const char Ellipse = (char)0x2026;
private const int ItemMargin = 4; //taken from each tag item
private string _inputStyle = string.Empty;
@ -450,9 +438,7 @@ namespace AntDesign.Select.Internal
//TODO: Use built in @onblur once https://github.com/dotnet/aspnetcore/issues/30070 is solved
private async void OnBlurInternal(JsonElement e) => await OnBlur.InvokeAsync(new());
public bool IsDisposed { get; private set; }
protected virtual void Dispose(bool disposing)
protected override void Dispose(bool disposing)
{
if (!_isReloading)
{
@ -466,22 +452,6 @@ namespace AntDesign.Select.Internal
});
}
DomEventListener?.Dispose();
if (IsDisposed) return;
IsDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~SelectContent()
{
// Finalizer calls Dispose(false)
Dispose(false);
}
}
}

View File

@ -28,6 +28,7 @@ namespace AntDesign.Select.Internal
public Guid InternalId { get; set; } = Guid.NewGuid();
private TItemValue _value;
public TItemValue Value
{
get => _value;
@ -38,6 +39,7 @@ namespace AntDesign.Select.Internal
}
private TItem _item;
public TItem Item
{
get => _item;
@ -74,6 +76,8 @@ namespace AntDesign.Select.Internal
}
}
public RenderFragment LabelTemplate { get; set; }
private string _groupName = string.Empty;
public string GroupName
@ -102,6 +106,7 @@ namespace AntDesign.Select.Internal
}
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
@ -118,6 +123,7 @@ namespace AntDesign.Select.Internal
}
private bool _isActive;
public bool IsActive
{
get => _isActive;
@ -134,6 +140,7 @@ namespace AntDesign.Select.Internal
}
private bool _isDisabled;
public bool IsDisabled
{
get => _isDisabled;
@ -150,6 +157,7 @@ namespace AntDesign.Select.Internal
}
private bool _isHidden;
public bool IsHidden
{
get => _isHidden;
@ -165,7 +173,6 @@ namespace AntDesign.Select.Internal
}
}
public bool IsAddedTag { get; set; }
private SelectOption<TItemValue, TItem> _childComponent;

View File

@ -10,26 +10,50 @@ namespace AntDesign
[Parameter]
public string Format { get; set; } = "hh:mm:ss";
public override DateTime Value
{
get
{
return base.Value;
}
set
{
if (base.Value != value)
{
base.Value = value;
Reset();
}
}
}
[Parameter]
public EventCallback OnFinish { get; set; }
[Parameter]
public int RefreshInterval { get; set; } = REFRESH_INTERVAL;
private Timer _timer;
private const int REFRESH_INTERVAL = 1000 / 30;
private const int REFRESH_INTERVAL = 1000 / 10;
private TimeSpan _countDown = TimeSpan.Zero;
protected override void OnInitialized()
{
base.OnInitialized();
SetTimer();
}
private void SetTimer()
{
_countDown = Value - DateTime.Now;
_timer = new Timer(StartCountDownForTimeSpan);
_timer.Change(0, REFRESH_INTERVAL);
_timer.Change(0, RefreshInterval);
}
private void StartCountDownForTimeSpan(object o)
{
_countDown = Value - DateTime.Now;
_countDown = _countDown.Add(TimeSpan.FromMilliseconds(-RefreshInterval));
if (_countDown.Ticks <= 0)
{
_countDown = TimeSpan.Zero;
@ -39,8 +63,23 @@ namespace AntDesign
InvokeAsync(() => OnFinish.InvokeAsync(o));
}
}
InvokeStateHasChanged();
}
public void Reset()
{
//避免初始化时调用Reset
if (_timer != null)
{
_timer.Dispose();
SetTimer();
}
}
protected override void Dispose(bool disposing)
{
_timer?.Dispose();
base.Dispose(disposing);
}
}
}
}

View File

@ -32,7 +32,7 @@ namespace AntDesign
/// <summary>
/// 数值内容
/// </summary>
[Parameter] public T Value { get; set; }
[Parameter] public virtual T Value { get; set; }
/// <summary>
/// 设置数值的样式

View File

@ -48,7 +48,12 @@ namespace AntDesign
if (_pending.Value != value.Value)
{
_pending = value;
if (value.Value == null)
{
_pendingItem = null;
SetItems();
return;
}
_pendingItem = _pending.Value == null ? null : new TimelineItem()
{
Class = "ant-timeline-item-pending"

View File

@ -13,7 +13,7 @@
@onfocusin="OnTriggerFocusIn"
@onfocusout="OnTriggerFocusOut"
@oncontextmenu:preventDefault
tabindex="0">
tabindex="@TabIndex">
@ChildContent
</div>
}

View File

@ -20,6 +20,9 @@ namespace AntDesign
[Parameter]
public double MouseLeaveDelay { get; set; } = 0.1;
[Parameter]
public int TabIndex { get; set; } = 0;
public Tooltip()
{
PrefixCls = "ant-tooltip";

View File

@ -1,45 +0,0 @@
// 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
{
public class SimpleTreeSelect<TItem> : TreeSelect<TItem> where TItem : class
{
protected IEnumerable<TItem> RootData => ChildrenMethodExpression?.Invoke(DataSource, RootValue);
/// <summary>
/// Specifies a method to return a child node
/// </summary>
[Parameter]
public Func<IEnumerable<TItem>, string, IList<TItem>> ChildrenMethodExpression { get; set; }
protected override Func<TreeNode<TItem>, IList<TItem>> TreeNodeChildrenExpression => node => ChildrenMethodExpression(DataSource, TreeNodeKeyExpression(node));
protected override Dictionary<string, object> TreeAttributes
{
get
{
return new()
{
{ "DataSource", RootData },
{ "TitleExpression", TreeNodeTitleExpression },
{ "DefaultExpandAll", TreeDefaultExpandAll },
{ "KeyExpression", TreeNodeKeyExpression },
{ "ChildrenExpression", TreeNodeChildrenExpression },
{ "DisabledExpression", TreeNodeDisabledExpression },
{ "IsLeafExpression", TreeNodeIsLeafExpression }
};
}
}
}
}

View File

@ -6,73 +6,88 @@
@using AntDesign.Select.Internal
<CascadingValue Value="this" IsFixed>
<CascadingValue Value=@("ant-select-dropdown") Name="PrefixCls" IsFixed>
<div class="@ClassMapper.Class" style="@Style" id="@Id" tabindex="-1" @ref="Ref">
<OverlayTrigger @ref="@_dropDown"
Visible="Open"
Disabled="Disabled"
Trigger="new[] { Trigger.Click }"
HiddenMode
OnMouseEnter="@(() => { OnMouseEnter?.Invoke(); })"
OnMouseLeave="@(() => { OnMouseLeave?.Invoke(); })"
OnVisibleChange="@OnOverlayVisibleChangeAsync"
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">
<Overlay >
<div style="@_dropdownStyle">
<div class="" style="max-height: @PopupContainerMaxHeight; overflow-y: auto;">
<div>
<div class="" role="listbox" style="display: flex; flex-direction: column;">
<Tree TItem="TItem"
BlockNode @ref="_tree" Multiple="Multiple"
@attributes="TreeAttributes"
SelectedKeys="SelectedKeys"
OnClick="OnTreeNodeClick"
OnUnSelect="OnTreeNodeUnSelect">
<Nodes>
@if (IsInnerModel)
{
<CascadingValue Name="Tree" Value="_tree" IsFixed="true">
@ChildContent
</CascadingValue>
}
</Nodes>
</Tree>
</div>
</div>
</div>
</div>
</Overlay>
<Unbound>
<CascadingValue Value="this" Name=@("ParentSelect") IsFixed>
<CascadingValue Value="@LabelTemplate" Name="ParentLabelTemplate">
<CascadingValue Value="@ShowSearchIcon" Name="ShowSearchIcon">
<CascadingValue Value="@ShowArrowIcon" Name="ShowArrowIcon">
<SelectContent Prefix="ant-select"
RefBack="@context"
TItemValue="string"
TItem="TItem"
SearchValue="@_searchValue"
SearchDebounceMilliseconds="@SearchDebounceMilliseconds"
IsOverlayShow="@_dropDown.IsOverlayShow()"
OnInput="@OnInputAsync"
OnKeyUp="@OnKeyUpAsync"
OnKeyDown="@OnKeyDownAsync"
OnFocus="@OnInputFocusAsync"
OnBlur="@OnInputBlurAsync"
OnClearClick="@OnInputClearClickAsync"
OnRemoveSelected="@OnRemoveSelectedAsync"
Placeholder="@Placeholder"
ShowPlaceholder="@ShowPlaceholder" />
</CascadingValue>
</CascadingValue>
<CascadingValue Value=@("ant-select-dropdown") Name="PrefixCls" IsFixed>
<div class="@ClassMapper.Class" style="@Style" id="@Id" tabindex="-1" @ref="Ref">
<OverlayTrigger @ref="@_dropDown"
Visible="Open"
Disabled="Disabled"
Trigger="new[] { Trigger.Click }"
HiddenMode
OnMouseEnter="@(() => { OnMouseEnter?.Invoke(); })"
OnMouseLeave="@(() => { OnMouseLeave?.Invoke(); })"
OnVisibleChange="@OnOverlayVisibleChangeAsync"
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">
<Overlay>
<div style="@_dropdownStyle">
<div class="" style="max-height: @PopupContainerMaxHeight; overflow-y: auto;">
<div>
<div class="" role="listbox" style="display: flex; flex-direction: column;">
<Tree @ref="_tree"
TItem="TItem"
BlockNode
DataSource="DataSource"
Multiple="Multiple"
Selectable="!TreeCheckable"
SelectedKeys="SelectedKeys"
Checkable="TreeCheckable"
OnClick="OnTreeNodeClick"
OnUnselect="OnTreeNodeUnSelect"
OnCheck="OnTreeCheck"
ShowLeafIcon="ShowLeafIcon"
ShowLine="ShowTreeLine"
TitleExpression="TitleExpression"
KeyExpression="KeyExpression"
IconExpression="IconExpression"
IsLeafExpression="IsLeafExpression"
ChildrenExpression="ChildrenExpression"
DisabledExpression="DisabledExpression"
DefaultExpandAll="TreeDefaultExpandAll"
>
<Nodes>
@if (IsTemplatedNodes)
{
<CascadingValue Name="Tree" Value="_tree" IsFixed="true">
@ChildContent
</CascadingValue>
</CascadingValue>
</Unbound>
</OverlayTrigger>
</div>
</CascadingValue>
}
</Nodes>
</Tree>
</div>
</div>
</div>
</div>
</Overlay>
<Unbound>
<CascadingValue Value="this" Name=@("ParentSelect") IsFixed>
<CascadingValue Value="@LabelTemplate" Name="ParentLabelTemplate">
<CascadingValue Value="@ShowSearchIcon" Name="ShowSearchIcon">
<CascadingValue Value="@ShowArrowIcon" Name="ShowArrowIcon">
<SelectContent Prefix="ant-select"
RefBack="@context"
TItemValue="string"
TItem="TItem"
SearchValue="@_searchValue"
SearchDebounceMilliseconds="@SearchDebounceMilliseconds"
IsOverlayShow="@_dropDown.IsOverlayShow()"
OnInput="@OnInputAsync"
OnKeyUp="@OnKeyUpAsync"
OnKeyDown="@OnKeyDownAsync"
OnFocus="@OnInputFocusAsync"
OnBlur="@OnInputBlurAsync"
OnClearClick="@OnInputClearClickAsync"
OnRemoveSelected="@OnRemoveSelectedAsync"
Placeholder="@Placeholder"
ShowPlaceholder="@ShowPlaceholder" />
</CascadingValue>
</CascadingValue>
</CascadingValue>
</CascadingValue>
</Unbound>
</OverlayTrigger>
</div>
</CascadingValue>
</CascadingValue>

View File

@ -1,12 +1,13 @@
using System;
// 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 System.Threading.Tasks;
using AntDesign.Internal;
using AntDesign.Select.Internal;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using AntDesign.Core.Helpers.MemberPath;
using AntDesign.JsInterop;
using OneOf;
using System.Linq;
@ -17,8 +18,6 @@ namespace AntDesign
{
[Parameter] public bool ShowExpand { get; set; } = true;
protected Tree<TItem> _tree;
[Parameter]
public bool Multiple
{
@ -56,162 +55,87 @@ namespace AntDesign
[Parameter] public bool TreeDefaultExpandAll { get; set; }
[Parameter]
public Func<IEnumerable<TItem>, string, TItem> DataItemExpression { get; set; }
[Parameter]
public Func<IList<TItem>, IEnumerable<string>, IEnumerable<TItem>> DataItemsExpression { get; set; }
[Parameter]
public string RootValue { get; set; } = "0";
protected Func<TreeNode<TItem>, string> TreeNodeTitleExpression
{
get
{
return node => TitleExpression(node.DataItem);
}
}
[Parameter] public OneOf<bool, string> DropdownMatchSelectWidth { get; set; } = true;
[Parameter] public string DropdownMaxWidth { get; set; } = "auto";
[Parameter] public string PopupContainerMaxHeight { get; set; } = "256px";
//[Parameter] public IEnumerable<ITreeData<TItem>> TreeData { get; set; }
[Parameter] public string DropdownStyle { get; set; }
[Parameter] public bool ShowTreeLine { get; set; }
[Parameter] public bool ShowLeafIcon { get; set; }
/// <summary>
/// Specifies a method that returns the text of the node.
/// </summary>
[Parameter]
public Func<TItem, string> TitleExpression { get; set; }
protected virtual Dictionary<string, object> TreeAttributes
{
get
{
return new()
{
{ "DataSource", DataSource },
{ "TitleExpression", DataSource == null ? null : TreeNodeTitleExpression },
{ "DefaultExpandAll", TreeDefaultExpandAll },
{ "KeyExpression", DataSource == null ? null : TreeNodeKeyExpression },
{ "ChildrenExpression", DataSource == null ? null : TreeNodeChildrenExpression },
{ "DisabledExpression", DataSource == null ? null : TreeNodeDisabledExpression },
{ "IsLeafExpression", DataSource == null ? null : TreeNodeIsLeafExpression }
};
}
}
protected Func<TreeNode<TItem>, string> TreeNodeKeyExpression
{
get
{
return node => KeyExpression(node.DataItem);
}
}
public Func<TreeNode<TItem>, string> TitleExpression { get; set; }
/// <summary>
/// Specifies a method that returns the key of the node.
/// </summary>
[Parameter]
public Func<TItem, string> KeyExpression { get; set; }
protected Func<TreeNode<TItem>, string> TreeNodeIconExpression
{
get
{
return node => IconExpression(node.DataItem);
}
}
public Func<TreeNode<TItem>, string> KeyExpression { get; set; }
/// <summary>
/// Specifies a method to return the node icon.
/// </summary>
[Parameter]
public Func<TItem, string> IconExpression { get; set; }
protected Func<TreeNode<TItem>, bool> TreeNodeIsLeafExpression
{
get
{
return node => IsLeafExpression(DataSource, node.DataItem);
}
}
private bool IsInnerModel => ChildContent != null;
public Func<TreeNode<TItem>, string> IconExpression { get; set; }
/// <summary>
/// Specifies a method that returns whether the expression is a leaf node.
/// </summary>
[Parameter]
public Func<IEnumerable<TItem>, TItem, bool> IsLeafExpression { get; set; }
protected virtual Func<TreeNode<TItem>, IList<TItem>> TreeNodeChildrenExpression
{
get
{
return node => ChildrenExpression == null ? null : ChildrenExpression(node.DataItem);
}
}
public Func<TreeNode<TItem>, bool> IsLeafExpression { get; set; }
/// <summary>
/// Specifies a method to return a child node
/// </summary>
[Parameter]
public virtual Func<TItem, IList<TItem>> ChildrenExpression { get; set; }
protected Func<TreeNode<TItem>, bool> TreeNodeDisabledExpression
{
get
{
return node => DisabledExpression != null && DisabledExpression(node.DataItem);
}
}
public Func<TreeNode<TItem>, IEnumerable<TItem>> ChildrenExpression { get; set; }
/// <summary>
/// Specifies a method to return a disabled node
/// </summary>
[Parameter]
public Func<TItem, bool> DisabledExpression { get; set; }
[Parameter] public OneOf<bool, string> DropdownMatchSelectWidth { get; set; } = true;
[Parameter] public string DropdownMaxWidth { get; set; } = "auto";
[Parameter] public string PopupContainerMaxHeight { get; set; } = "256px";
public Func<TreeNode<TItem>, bool> DisabledExpression { get; set; }
private bool IsMultiple => Multiple || TreeCheckable;
private bool IsTemplatedNodes => ChildContent != null;
internal override SelectMode SelectMode => IsMultiple ? SelectMode.Multiple : base.SelectMode;
private string[] SelectedKeys => Values?.ToArray();
//private readonly IList<TreeNode<TItem>> _selectedNodes = new List<TreeNode<TItem>>();
private string _dropdownStyle = string.Empty;
private bool _multiple;
private readonly string _dir = "ltr";
private Tree<TItem> _tree;
public override string Value
{
get => base.Value;
set
{
if (string.IsNullOrEmpty(value))
return;
if (base.Value == value)
return;
base.Value = value;
if (SelectOptionItems.Any(o => o.Value == value))
if (value == null)
{
_ = SetValueAsync(SelectOptionItems.First(o => o.Value == value));
}
else
{
var data = DataItemExpression?.Invoke(DataSource, value);
if (data != null)
{
var o = CreateOption(data, true);
_ = SetValueAsync(o);
}
ClearOptions();
}
UpdateValueAndSelection();
}
}
@ -220,12 +144,6 @@ namespace AntDesign
get => base.Values;
set
{
if (!_isInitialized)
return;
if (!Multiple)
throw new NotImplementedException("not Multiple select, no die");
if (value != null && _selectedValues != null)
{
var hasChanged = !value.SequenceEqual(_selectedValues);
@ -233,31 +151,24 @@ namespace AntDesign
if (!hasChanged)
return;
ClearOptions();
_selectedValues = value;
CreateOptions(value);
_ = OnValuesChangeAsync(value);
}
else if (value != null && _selectedValues == null)
{
_selectedValues = value;
CreateOptions(value);
_ = OnValuesChangeAsync(value);
}
else if (value == null && _selectedValues != null)
{
_selectedValues = default;
ClearOptions();
_ = OnValuesChangeAsync(default);
}
UpdateValuesSelection();
if (_isNotifyFieldChanged && (Form?.ValidateOnChange == true))
{
EditContext?.NotifyFieldChanged(FieldIdentifier);
}
}
}
@ -265,41 +176,26 @@ namespace AntDesign
{
SelectOptionItems.Clear();
SelectedOptionItems.Clear();
_tree?._allNodes.ForEach(x => x.SetSelected(false));
}
private void CreateOptions(IEnumerable<string> data)
{
if (IsInnerModel)
if (IsTemplatedNodes)
{
var d1 = data.Where(d => !SelectOptionItems.Any(o => o.Value == d));
CreateOptionsByTreeNode(d1);
return;
}
// 通过DataItemExpression来生成选中项
if (DataItemExpression != null)
data.ForEach(menuId =>
{
data.ForEach(menuId =>
var d = _tree._allNodes.FirstOrDefault(m => m.Key == menuId);
if (d != null)
{
var d = DataItemExpression?.Invoke(DataSource, menuId);
if (d != null)
{
var o = CreateOption(d, true);
}
});
}
else
{
// 通过目前的树节点生成选中项对于延时的节点建议使用DataItemExpression来加载
data.ForEach(menuId =>
{
var d = _tree._allNodes.FirstOrDefault(m => m.Key == menuId);
if (d != null)
{
var o = CreateOption(d, true);
}
});
}
var o = CreateOption(d, true);
}
});
}
private void CreateOptionsByTreeNode(IEnumerable<string> data)
@ -319,36 +215,30 @@ namespace AntDesign
var o = new SelectOptionItem<string, TItem>()
{
Label = data.Title,
LabelTemplate = data.TitleTemplate,
Value = data.Key,
Item = data.DataItem,
IsAddedTag = SelectMode != SelectMode.Default
IsAddedTag = SelectMode != SelectMode.Default,
};
if (append && !SelectOptionItems.Any(m => m.Value == o.Value))
SelectOptionItems.Add(o);
return o;
}
private SelectOptionItem<string, TItem> CreateOption(TItem data, bool append = false)
protected override Task OnFirstAfterRenderAsync()
{
var o = new SelectOptionItem<string, TItem>()
if (Value != null)
{
Label = TitleExpression(data),
Value = KeyExpression(data),
Item = data,
IsAddedTag = SelectMode != SelectMode.Default
};
if (append && !SelectOptionItems.Any(m => m.Value == o.Value))
SelectOptionItems.Add(o);
return o;
}
UpdateValueAndSelection();
}
protected override void OnInitialized()
{
SelectOptions = "".ToRenderFragment();
//_inputValue = Value;
base.OnInitialized();
}
if (Values != null)
{
UpdateValuesSelection();
}
return base.OnFirstAfterRenderAsync();
}
private void OnKeyDownAsync(KeyboardEventArgs args)
{
@ -377,6 +267,7 @@ namespace AntDesign
await SetDropdownStyleAsync();
}
}
protected async Task OnRemoveSelectedAsync(SelectOptionItem<string, TItem> selectOption)
{
if (selectOption == null) throw new ArgumentNullException(nameof(selectOption));
@ -389,43 +280,56 @@ namespace AntDesign
}
}
private async Task OnTreeNodeClick(TreeEventArgs<TItem> args)
{
if (!args.Node.Selected)
var node = args.Node;
if (!TreeCheckable && !node.Selected)
return;
var key = args.Node.Key;
var key = node.Key;
if (Value != null && Value.Equals(key))
return;
if (Values != null && Values.Contains(key))
return;
var data = args.Node;
SelectOptionItem<string, TItem> item;
if (IsInnerModel)
item = CreateOption(data, true);
else
item = CreateOption(data.DataItem, true);
var option = CreateOption(node, true);
//_selectedNodes.Add(data);
await SetValueAsync(option);
await SetValueAsync(item);
if (!Multiple)
{
var unselectedNodes = _tree._allNodes.Where(x => x.Key != node.Key);
unselectedNodes.ForEach(x => x.SetSelected(false));
}
if (SelectMode == SelectMode.Default)
{
await CloseAsync();
}
}
protected void OnTreeNodeUnSelect(TreeEventArgs<TItem> args)
protected async Task OnTreeNodeUnSelect(TreeEventArgs<TItem> args)
{
if (args == null) throw new ArgumentNullException(nameof(args));
var key = args.Node.Key;
var nodes = SelectOptionItems.Where(o => o.Value == key).ToArray();
foreach (var item in nodes)
// Prevent deselect in sigle selection mode
if (!Multiple && args.Node.Key == Value)
{
_ = SetValueAsync(item);
args.Node.SetSelected(true);
return;
}
// Deselect in Multiple mode
var node = SelectOptionItems.Where(o => o.Value == args.Node.Key).FirstOrDefault();
if (node != null)
{
await SetValueAsync(node);
}
}
private async Task OnTreeCheck(TreeEventArgs<TItem> args)
{
var option = CreateOption(args.Node, true);
await SetValueAsync(option);
}
protected async Task SetDropdownStyleAsync()
@ -444,7 +348,7 @@ namespace AntDesign
}
if (!DropdownMaxWidth.Equals("auto", StringComparison.CurrentCultureIgnoreCase))
maxWidth = $"max-width: {DropdownMaxWidth};";
_dropdownStyle = minWidth + definedWidth + maxWidth;
_dropdownStyle = minWidth + definedWidth + maxWidth + DropdownStyle ?? "";
if (Multiple)
{
@ -458,7 +362,6 @@ namespace AntDesign
}
}
protected override void SetClassMap()
{
var classPrefix = "ant-select";
@ -466,17 +369,51 @@ namespace AntDesign
.Add(classPrefix)
.Add("ant-tree-select")
.If("ant-select-lg", () => Size == "large")
.If("ant-select-rtl", () => _dir == "rtl")
.If("ant-select-sm", () => Size == "rtl")
.If("ant-select-sm", () => Size == "small")
.If("ant-select-rtl", () => RTL)
.If("ant-select-disabled", () => Disabled)
.If("ant-select-single", () => SelectMode == SelectMode.Default)
.If($"ant-select-multiple", () => SelectMode != SelectMode.Default)
.If("ant-select-multiple", () => SelectMode != SelectMode.Default)
.If("ant-select-show-arrow", () => !IsMultiple)
.If("ant-select-show-search", () => !IsMultiple)
.If("ant-select-allow-clear", () => AllowClear)
.If("ant-select-open", () => Open)
.If("ant-select-focused", () => Open || Focused)
.If("ant-select-status-error", () => Status == "error")
.If("ant-select-status-warning", () => Status == "warning")
;
}
private void UpdateValueAndSelection()
{
if (SelectOptionItems.Any(o => o.Value == Value))
{
_ = SetValueAsync(SelectOptionItems.First(o => o.Value == Value));
}
else
{
var data = _tree?._allNodes.FirstOrDefault(x => x.Key == Value);
if (data != null)
{
var o = CreateOption(data, true);
_ = SetValueAsync(o);
}
}
}
private void UpdateValuesSelection()
{
if (_tree == null)
return;
if (_selectedValues?.Any() != true)
{
ClearOptions();
return;
}
CreateOptions(_selectedValues);
_ = OnValuesChangeAsync(_selectedValues);
}
}
}

View File

@ -0,0 +1,21 @@
// 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
{
public interface ITreeData<TItem>
{
public string Key { get; set; }
public string Title { get; set; }
public TItem Value { get; }
public IEnumerable<TItem> Children { get; set; }
}
}

View File

@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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.
@ -94,7 +94,7 @@ namespace AntDesign
private bool _hasSetShowLeafIcon;
/// <summary>
/// Specific the Icon type of switcher
/// Specific the Icon type of switcher
/// </summary>
[Parameter]
public string SwitcherIcon { get; set; }
@ -190,9 +190,9 @@ namespace AntDesign
if (SelectedNodesDictionary.ContainsKey(treeNode.NodeId) == true)
SelectedNodesDictionary.Remove(treeNode.NodeId);
if (OnUnSelect.HasDelegate)
if (OnUnselect.HasDelegate)
{
OnUnSelect.InvokeAsync(new TreeEventArgs<TItem>(this, treeNode));
OnUnselect.InvokeAsync(new TreeEventArgs<TItem>(this, treeNode));
}
}
@ -355,6 +355,14 @@ namespace AntDesign
}
}
public void SelectAll()
{
foreach (var item in ChildNodes)
{
item.SetSelected(true);
}
}
/// <summary>
/// Specifies the keys of the default checked treeNodes
/// </summary>
@ -377,6 +385,7 @@ namespace AntDesign
_checkedNodes.TryAdd(treeNode.NodeId, treeNode);
else
_checkedNodes.TryRemove(treeNode.NodeId, out TreeNode<TItem> _);
_checkedKeys = _checkedNodes.Select(x => x.Value.Key).ToArray();
if (!old.SequenceEqual(_checkedKeys) && CheckedKeysChanged.HasDelegate)
@ -419,12 +428,6 @@ namespace AntDesign
private void SearchNodes()
{
if (string.IsNullOrWhiteSpace(_searchValue))
{
_allNodes.ForEach(m => { m.Expand(true); m.Matched = false; });
return;
}
var allList = _allNodes.ToList();
List<TreeNode<TItem>> searchDatas = null, exceptList = null;
@ -489,7 +492,7 @@ namespace AntDesign
/// Specifies a method to return a child node
/// </summary>
[Parameter]
public Func<TreeNode<TItem>, IList<TItem>> ChildrenExpression { get; set; }
public Func<TreeNode<TItem>, IEnumerable<TItem>> ChildrenExpression { get; set; }
/// <summary>
/// Specifies a method to return a disabled node
@ -536,7 +539,7 @@ namespace AntDesign
public EventCallback<TreeEventArgs<TItem>> OnSelect { get; set; }
[Parameter]
public EventCallback<TreeEventArgs<TItem>> OnUnSelect { get; set; }
public EventCallback<TreeEventArgs<TItem>> OnUnselect { get; set; }
/// <summary>
/// Click the expansion tree node icon to call back
@ -620,6 +623,33 @@ namespace AntDesign
base.OnInitialized();
}
protected override Task OnFirstAfterRenderAsync()
{
this.DefaultCheckedKeys?.ForEach(k =>
{
var node = this._allNodes.FirstOrDefault(x => x.Key == k);
if (node != null)
node.SetCheckedDefault(true);
});
this.DefaultSelectedKeys?.ForEach(k =>
{
var node = this._allNodes.FirstOrDefault(x => x.Key == k);
if (node != null)
node.SetSelected(true);
});
if (!this.DefaultExpandAll)
{
this.DefaultExpandedKeys?.ForEach(k =>
{
var node = this._allNodes.FirstOrDefault(x => x.Key == k);
if (node != null)
node.OpenPropagation();
});
}
return base.OnFirstAfterRenderAsync();
}
/// <summary>
/// Find Node
/// </summary>

View File

@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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.
@ -50,7 +50,7 @@ namespace AntDesign
public int TreeLevel => (ParentNode?.TreeLevel ?? -1) + 1;
/// <summary>
/// record the index in children nodes list of parent node.
/// record the index in children nodes list of parent node.
/// </summary>
internal int NodeIndex { get; set; }
@ -157,7 +157,7 @@ namespace AntDesign
_key = value;
}
}
private bool _disabled;
/// <summary>
@ -193,11 +193,7 @@ namespace AntDesign
public void SetSelected(bool value)
{
if (Disabled) return;
if (!TreeComponent.Selectable && TreeComponent.Checkable)
{
SetChecked(!Checked);
return;
}
if (_selected == value) return;
_selected = value;
if (value == true)
@ -397,12 +393,11 @@ namespace AntDesign
#region Checkbox
private bool _checked;
/// <summary>
/// According to check the
/// </summary>
[Parameter]
public bool Checked { get; set; }
public bool Checked { get; set; }
[Parameter]
public bool Indeterminate { get; set; }
@ -460,6 +455,24 @@ namespace AntDesign
TreeComponent.AddOrRemoveCheckNode(this);
StateHasChanged();
}
/// <summary>
/// Set the checkbox state when ini
/// </summary>
/// <param name="check"></param>
public void SetCheckedDefault(bool check)
{
if (TreeComponent.CheckStrictly)
{
this.Checked = check;
}
else
{
SetChildCheckedDefault(this, check);
if (ParentNode != null)
ParentNode.UpdateCheckStateDefault();
}
StateHasChanged();
}
/// <summary>
/// Sets the checkbox status of child nodes
@ -476,6 +489,20 @@ namespace AntDesign
foreach (var child in subnode.ChildNodes)
child?.SetChildChecked(child, check);
}
/// <summary>
/// Sets the checkbox status of child nodes whern bind default
/// </summary>
/// <param name="subnode"></param>
/// <param name="check"></param>
private void SetChildCheckedDefault(TreeNode<TItem> subnode, bool check)
{
this.Checked = check;
this.Indeterminate = false;
TreeComponent.AddOrRemoveCheckNode(this);
if (subnode.HasChildNodes)
foreach (var child in subnode.ChildNodes)
child?.SetChildCheckedDefault(child, check);
}
/// <summary>
/// Update check status
@ -540,6 +567,66 @@ namespace AntDesign
if (ParentNode == null)
StateHasChanged();
}
/// <summary>
/// Update check status when bind default
/// </summary>
/// <param name="halfChecked"></param>
private void UpdateCheckStateDefault(bool? halfChecked = null)
{
if (halfChecked == true)
{
//If the child node is indeterminate, the parent node must is indeterminate.
this.Checked = false;
this.Indeterminate = true;
}
else if (HasChildNodes == true && !DisableCheckbox)
{
//Determines the selection status of the current node
bool hasChecked = false;
bool hasUnchecked = false;
foreach (var item in ChildNodes)
{
if (item.Indeterminate)
{
hasChecked = true;
hasUnchecked = true;
break;
}
else if (item.Checked)
{
hasChecked = true;
}
else if (!item.Checked)
{
hasUnchecked = true;
}
}
if (hasChecked && !hasUnchecked)
{
this.Checked = true;
this.Indeterminate = false;
}
else if (!hasChecked && hasUnchecked)
{
this.Checked = false;
this.Indeterminate = false;
}
else if (hasChecked && hasUnchecked)
{
this.Checked = false;
this.Indeterminate = true;
}
}
TreeComponent.AddOrRemoveCheckNode(this);
if (ParentNode != null)
ParentNode.UpdateCheckStateDefault(this.Indeterminate);
if (ParentNode == null)
StateHasChanged();
}
#endregion Checkbox
@ -621,7 +708,7 @@ namespace AntDesign
get
{
if (TreeComponent.ChildrenExpression != null)
return TreeComponent.ChildrenExpression(this) ?? new List<TItem>();
return TreeComponent.ChildrenExpression(this)?.ToList() ?? new List<TItem>();
else
return new List<TItem>();
}
@ -789,8 +876,6 @@ namespace AntDesign
#endregion Node data operation
bool _defaultBinding;
protected override void OnInitialized()
{
SetTreeNodeClassMapper();
@ -817,73 +902,22 @@ namespace AntDesign
if (TreeComponent.Selectable && TreeComponent.SelectedKeys != null)
{
this.Selected = TreeComponent.SelectedKeys.Any(k => k == this.Key);
this.SetChecked(this.Selected);
}
if (TreeComponent.Selectable && TreeComponent.SelectedKeys != null)
{
this.Selected = TreeComponent.SelectedKeys.Any(k => k == this.Key);
}
if (this.Checked)
this.SetChecked(true);
if (!TreeComponent.DefaultExpandAll)
{
if (this.Expanded)
this.OpenPropagation();
}
base.OnInitialized();
}
protected override void OnParametersSet()
{
DefaultBinding();
base.OnParametersSet();
}
private void DefaultBinding()
{
if (!_defaultBinding)
{
_defaultBinding = true;
if (this.Checked)
this.SetChecked(true);
this.SetChecked(TreeComponent?.DefaultCheckedKeys?.Any(k => k == Key) ?? false);
this.SetSelected(TreeComponent?.DefaultSelectedKeys?.Any(k => k == Key) ?? false);
if (!TreeComponent.DefaultExpandAll)
{
if (this.Expanded)
this.OpenPropagation();
if (TreeComponent.DefaultExpandedKeys != null)
{
if (TreeComponent.DefaultExpandedKeys.Contains(this.Key))
{
this.OpenPropagation();
};
}
}
}
}
private void DefaultBindingold()
{
if (!_defaultBinding)
{
_defaultBinding = true;
if (this.Checked)
this.SetChecked(true);
TreeComponent.DefaultCheckedKeys?.ForEach(k =>
{
var node = TreeComponent._allNodes.FirstOrDefault(x => x.Key == k);
if (node != null)
node.SetChecked(true);
});
TreeComponent.DefaultSelectedKeys?.ForEach(k =>
{
var node = TreeComponent._allNodes.FirstOrDefault(x => x.Key == k);
if (node != null)
node.SetSelected(true);
});
if (!TreeComponent.DefaultExpandAll)
{
if (this.Expanded)
this.OpenPropagation();
TreeComponent.DefaultExpandedKeys?.ForEach(k =>
{
var node = TreeComponent._allNodes.FirstOrDefault(x => x.Key == k);
if (node != null)
node.OpenPropagation();
});
}
}
}
}
}

View File

@ -39,11 +39,17 @@ namespace AntDesign
return JsonSerializer.Deserialize<TResponseModel>(this.Response, options);
}
public static string[] ImageExtensions { get; set; } = new[] { ".jpg", ".png", ".gif", ".ico", ".jfif", ".jpeg", ".bmp", ".tga", ".svg", ".tif", ".webp" };
public bool IsPicture()
{
string[] imageTypes = new[] { ".jpg", ".png", ".gif", ".ico",".jfif",".jpeg",".bmp",".tga",".svg",".tif" };
Ext = FileName.Substring(FileName.LastIndexOf('.'));
return imageTypes.Any(imageType => imageType.Equals(Ext, StringComparison.InvariantCultureIgnoreCase));
if (string.IsNullOrEmpty(Ext))
{
var lastIndex = FileName.LastIndexOf('.');
if (lastIndex < 0) return false;
Ext = FileName[lastIndex..];
}
return ImageExtensions.Any(imageExt => imageExt.Equals(Ext, StringComparison.InvariantCultureIgnoreCase));
}
}
}

View File

@ -47,6 +47,18 @@ $ dotnet publish -c release -o dist
$ dotnet add package AntDesign
```
### 注册依赖
`Startup.cs` 文件中注册 AntDesign 服务
```cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAntDesign();
}
```
### 引入样式
#### 使用样式与脚本

View File

@ -8,7 +8,7 @@ Following the Ant Design specification, we developed a Blazor Components library
<div class="pic-plus">
<img width="150" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
<span>+</span>
<img height="150" src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/docs/assets/blazor.svg">
<img height="150" src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/master/docs/assets/blazor.svg">
</div>
<style>
@ -49,7 +49,7 @@ WebAssembly static hosting examples:
- Run directly on [.NET MAUI](https://dotnet.microsoft.com/zh-cn/apps/maui?WT.mc_id=DT-MVP-5003987) / [WPF](https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/wpf?view=aspnetcore-6.0&WT.mc_id=DT-MVP-5003987) / [Windows Forms](https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/windows-forms?view=aspnetcore-6.0) and other Blazor Hybrid workloads.
- Run directly on [Electron](http://electron.atom.io/) and other Web standards-based environments.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> Edge / IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> Edge / IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| Edge 16 / IE 11† | 522 | 57 | 11 | 44 | Chromium 57 |
@ -77,7 +77,7 @@ We have provided the `dotnet new` template to create a [Boilerplate](https://git
- Install the template
```bash
$ dotnet new --install AntDesign.Templates::0.1.0-*
$ dotnet new --install AntDesign.Templates
```
- Create the Boilerplate project with the template
@ -101,7 +101,7 @@ Options for the template
- Go to the project folder of the application and install the Nuget package reference
```bash
$ dotnet add package AntDesign --version 0.1.0-*
$ dotnet add package AntDesign
```
- Register the services
@ -148,7 +148,7 @@ Options for the template
## 🔨 Local Development
- Install [.NET Core SDK](https://dotnet.microsoft.com/download?WT.mc_id=DT-MVP-5003987) 5.0.100 or later.
- Install [.NET Core SDK](https://dotnet.microsoft.com/download?WT.mc_id=DT-MVP-5003987) 6.0.x or later.
- Install Node.js (only for building style files and interoperable TypeScript files)
- Clone to local development
@ -161,7 +161,7 @@ Options for the template
- Visit https://localhost:5001 in your supported browser and check [local development documentation](https://github.com/ant-design-blazor/ant-design-blazor/wiki) for details.
> Visual Studio 2019 is recommended for development.
> Visual Studio 2022 is recommended for development.
## 🔗 Links
@ -169,10 +169,6 @@ Options for the template
- [Official Blazor Documentation](https://docs.microsoft.com/en-us/aspnet/core/blazor/?WT.mc_id=DT-MVP-5003987)
- [MS Learn for Blazor Tutorial](https://docs.microsoft.com/en-us/learn/modules/build-blazor-webassembly-visual-studio-code/?WT.mc_id=DT-MVP-5003987)
## 🗺 Roadmap
Check out this [issue](https://github.com/ant-design-blazor/ant-design-blazor/issues/21) to learn about our development plans for 2020.
## 🤝 Contributing
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/ant-design-blazor/ant-design-blazor/pulls)

View File

@ -8,7 +8,7 @@ title: Ant Design of Blazor
<div class="pic-plus">
<img width="150" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
<span>+</span>
<img height="150" src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/docs/assets/blazor.svg">
<img height="150" src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/master/docs/assets/blazor.svg">
</div>
<style>
@ -48,7 +48,7 @@ title: Ant Design of Blazor
- 可直接运行在 [.NET MAUI](https://dotnet.microsoft.com/zh-cn/apps/maui?WT.mc_id=DT-MVP-5003987)、[WPF](https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/wpf?view=aspnetcore-6.0&WT.mc_id=DT-MVP-5003987)、[Windows Forms](https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/windows-forms?view=aspnetcore-6.0) 等 Blazor 混合客户端环境中。
- 可直接运行在 [Electron](http://electron.atom.io/) 等基于 Web 标准的环境上
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> Edge / IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br> Edge / IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Electron |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| Edge 16 / IE 11† | 522 | 57 | 11 | 44 | Chromium 57 |
@ -76,7 +76,7 @@ title: Ant Design of Blazor
- 安装模板
```bash
$ dotnet new --install AntDesign.Templates::0.1.0-*
$ dotnet new --install AntDesign.Templates
```
- 从模板创建 Ant Design Blazor Pro 项目
@ -98,7 +98,7 @@ title: Ant Design of Blazor
- 进入应用的项目文件夹,安装 Nuget 包引用
```bash
$ dotnet add package AntDesign --version 0.1.0-*
$ dotnet add package AntDesign
```
- 在项目中注册:
@ -182,7 +182,7 @@ title: Ant Design of Blazor
- [![钉钉群](https://img.shields.io/badge/钉钉-AntBlazor-blue.svg?style=flat-square&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg0KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpY29uIiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiBmaWxsPSIjZmZmZmZmIj4NCiAgPHBhdGggZD0iTTU3My43IDI1Mi41QzQyMi41IDE5Ny40IDIwMS4zIDk2LjcgMjAxLjMgOTYuN2MtMTUuNy00LjEtMTcuOSAxMS4xLTE3LjkgMTEuMS01IDYxLjEgMzMuNiAxNjAuNSA1My42IDE4Mi44IDE5LjkgMjIuMyAzMTkuMSAxMTMuNyAzMTkuMSAxMTMuN1MzMjYgMzU3LjkgMjcwLjUgMzQxLjljLTU1LjYtMTYtMzcuOSAxNy44LTM3LjkgMTcuOCAxMS40IDYxLjcgNjQuOSAxMzEuOCAxMDcuMiAxMzguNCA0Mi4yIDYuNiAyMjAuMSA0IDIyMC4xIDRzLTM1LjUgNC4xLTkzLjIgMTEuOWMtNDIuNyA1LjgtOTcgMTIuNS0xMTEuMSAxNy44LTMzLjEgMTIuNSAyNCA2Mi42IDI0IDYyLjYgODQuNyA3Ni44IDEyOS43IDUwLjUgMTI5LjcgNTAuNSAzMy4zLTEwLjcgNjEuNC0xOC41IDg1LjItMjQuMkw1NjUgNzQzLjFoODQuNkw2MDMgOTI4bDIwNS4zLTI3MS45SDcwMC44bDIyLjMtMzguN2MuMy41LjQuOC40LjhTNzk5LjggNDk2LjEgODI5IDQzMy44bC42LTFoLS4xYzUtMTAuOCA4LjYtMTkuNyAxMC0yNS44IDE3LTcxLjMtMTE0LjUtOTkuNC0yNjUuOC0xNTQuNXoiLz4NCjwvc3ZnPg0K)](https://h5.dingtalk.com/circle/healthCheckin.html?corpId=dingf3df1949a4aa48627b0128d9a44ecb79&c5df5865-4f41-=be1b34c7-397b-&cbdbhh=qwertyuiop&origin=11) (中文)
<img src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/docs/assets/dingtalk.jpg" width="300">
<img src="https://raw.githubusercontent.com/ant-design-blazor/ant-design-blazor/master/docs/assets/dingtalk.jpg" width="300">
- 另外,我还创立了面向中文开发者的 Blazor 中文社区高手如云只讨论技术无卖课广告。可以加我微信JamesYeungMVP拉进微信群另外也有一个 QQ 群 1012762441。广告勿扰。

View File

@ -57,7 +57,7 @@
"husky": "^4.2.3",
"inquirer": "^7.1.0",
"jquery": "^3.4.1",
"jsdom": "^16.0.0",
"jsdom": "^16.5.0",
"less": "^4.1.0",
"less-plugin-clean-css": "^1.5.1",
"less-plugin-npm-import": "^2.1.0",
@ -76,4 +76,4 @@
"dependencies": {
"antblazor.js-ts.tests": "file:tests/AntDesign.Tests.Js"
}
}
}

View File

@ -22,7 +22,7 @@
#if NET5_0_OR_GREATER
var polyfillPath = "_framework/blazor.polyfill.min.js";
#else
var polyfillPath = "https://raw.githubusercontent.com/Daddoon/Blazor.Polyfill@3.0.8/Publish/Blazor.Polyfill.Publish/blazor.polyfill.min.js";
var polyfillPath = "https://raw.githubusercontent.com/Daddoon/Blazor.Polyfill/3.0.8/Publish/Blazor.Polyfill.Publish/blazor.polyfill.min.js";
#endif
#if NET6_0
var isNET6 = true;

View File

@ -1,17 +1,41 @@
@inject DrawerService DrawerService
@inject ConfirmService confirmService
<Input @bind-Value="@value" />
<br />
<br />
<Button OnClick="OpenComponent" Type="primary">Use Component</Button>
<Space Direction="DirectionVHType.Vertical">
<SpaceItem>
<Space>
<SpaceItem>
<Input @bind-Value="@value" />
</SpaceItem>
<SpaceItem>
<Button OnClick="OpenComponent" Type="primary">Use Component</Button>
</SpaceItem>
</Space>
</SpaceItem>
<SpaceItem>
<div>
<Button Type="primary" @onclick="@OpenDrawer">OpenDrawer</Button>
<Drawer Closable="true"
Visible="@visible"
Placement="right"
Title='("Basic Drawer")'
OnOpen="@OnOpen"
OnClose="@CloseDrawer">
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Drawer>
</div>
</SpaceItem>
</Space>
@code{
// #region use component
private string value = "blazor";
// use component
private async Task OpenComponent()
{
var options = new DrawerOptions()
@ -24,13 +48,13 @@
drawerRef.OnOpen = () =>
{
Console.WriteLine("OnAfterOpen");
Console.WriteLine("drawerRef OnOpen");
return Task.CompletedTask;
};
drawerRef.OnClosing = async handle =>
{
Console.WriteLine("OnAfterClosing:");
Console.WriteLine("drawerRef OnClosing");
if (await confirmService.Show("Drawer to close?", "Confirm?", ConfirmButtons.YesNo) == ConfirmResult.No)
{
@ -40,11 +64,38 @@
drawerRef.OnClosed = async result =>
{
Console.WriteLine("OnAfterClosed:" + result);
Console.WriteLine("drawerRef OnClosed, value:" + result);
if (result != null)
value = result;
await InvokeAsync(StateHasChanged);
};
}
//# endregion
// #region use <Drawer></Drawer>
bool visible = false;
void OpenDrawer()
{
this.visible = true;
}
async Task OnOpen()
{
var confirmResult = await confirmService.Show("OnOpen event: Are you sure to close the drawer?", "Question", ConfirmButtons.YesNo);
if (confirmResult == ConfirmResult.Yes)
{
this.visible = false;
StateHasChanged();
}
}
void CloseDrawer()
{
this.visible = false;
}
// #endregion
}

View File

@ -9,7 +9,26 @@ title:
Drawer 的事件处理
对于 Drawer 组件:
1. OnOpen: 在打开前执行,并可以通过参数的 `Cancel` 属性取消打开操作。
2. OnClose: 在关闭前执行,你需要通过它来控制 Drawer 组件的 `Visible` 属性。
对于 DrawerService:
1. DrawerRef.OnOpen: 在打开前执行,内部是在 `Drawe.OnOpen` 事件中调用该方法
2. DrawerRef.OnClosing: 在关闭前执行
3. DrawerRef.OnClosed: 在关闭后执行。
## en-US
Drawer event handling
Drawer event handling
For Drawer component:
1. OnOpen: Execute before opening, and you can cancel the opening operation through the `Cancel` attribute of the parameter.
2. OnClose: Before closing, you need to control the `Visible` paramter of the Drawer component through it.
For DrawerService:
1. DrawerRef.OnOpen: Execute before opening, and call this method in `Drawe.OnOpen` event internally.
2. DrawerRef.OnClosing: Execute before closing
3. DrawerRef.OnClosed: Execute after shutdown

View File

@ -31,17 +31,18 @@ tasks can be achieved more efficiently within thesame context.
| ChildContent | Subcomponent | object | - |
| MaskClosable | Clicking on the mask (area outside the Drawer) to close the Drawer or not. | boolean | true |
| MaskStyle | Style for Drawer's mask element. | object | - |
| Mask | Whether to show mask or not. | boolean | true |
| Placement | The placement of the Drawer, option could be `left` , `top`,`right`,`bottom` | string | `right` |
| WrapClassName | The class name of the container of the Drawer dialog. | string | - |
| Width | Width of the Drawer dialog. | | int |
| Width | Width of the Drawer dialog, only when placement is 'left' or 'right'. | | int |
| Height | placement is top or bottom, height of the Drawer dialog. | int | 256 |
| ZIndex | The z-index of the Drawer. | int | - |
| OffsetX | The the X coordinate offset(px), only when placement is `'left'` or `'right'`. | int | 0 |
| OffsetY | The the Y coordinate offset(px), only when placement is `'top'` or `'bottom'`. | int | 0 |
| Visible | Whether the Drawer dialog is visible or not. | boolean | - |
| Keyboard | Whether support press esc to close | boolean | true |
| OnClose | Specify a callback that will be called when a user clicks mask, close button or Cancel button. | function(e) | - |
| OnViewInit | Specify a callback that will be called before drawer displayed | function(e) | - |
| OnClose | Specify a callback that will be called when a user clicks mask, close button or Cancel button. | EventCallback | - |
| OnOpen | Specify a callback that will be called after drawer rendered | Func<Task> | - |
### DrawerService
@ -62,7 +63,7 @@ tasks can be achieved more efficiently within thesame context.
| CloseOnNavigation | Whether to close the drawer when the navigation history changes | `boolean` | `true` |
| Keyboard | Whether to support keyboard esc off | `boolean` | `true` |
| MaskStyle | Style for Drawer's mask element. | `string` | `{}` |
| BodyStyle | Body style for modal body element. Such as height, padding etc. | `string` | `{}` |
| BodyStyle | Body style for Drawer body element. Such as height, padding etc. | `string` | `{}` |
| Title | The title for Drawer. | `OneOf<RenderFragment, string>` | - |
| Width | Width of the Drawer dialog. | `int` | `256` |
| Height | Height of the Drawer dialog, only when placement is `'top'` or `'bottom'`. | `int` | `256` |

View File

@ -22,22 +22,23 @@ cover: https://gw.alipayobjects.com/zos/alicdn/7z8NJQhFb/Drawer.svg
| 参数 | 说明 | 类型 | 默认值 |
| ------------- | ------------------------------------------------------- | -------------- | ------- |
| Title | 标题 | string or slot | - |
| BodyStyle | 可用于设置 Drawer 内容部分的样式 | object | - |
| BodyStyle | 可用于设置 Drawer 内容部分的样式 | object | - |
| Closable | 是否显示右上角的关闭按钮 | boolean | true |
| ChildContent | 抽屉元素之间的子组件 | object | - |
| MaskClosable | 点击蒙层是否允许关闭 | boolean | true |
| MaskStyle | 遮罩样式 | object | - |
| Placement | 抽屉的方向,可选值为 `left` , `top`,`right`,`bottom` | string | `right` |
| Mask | 是否展示遮罩 | boolean | true |
| Placement | 抽屉的方向,可选值为 `left` , `top`,`right`,`bottom` | string | right |
| WrapClassName | 对话框外层容器的类名 | string | - |
| Width | 宽度 | string\|int | 256 |
| Width | 宽度 | string\|int | 256 |
| Height | 高度, 在 placement 为 top 或 bottom 时使用 | | int |
| ZIndex | 设置 Drawer 的 z-index | int | - |
| ZIndex | 设置 Drawer 的 z-index | int | - |
| OffsetX | X 轴方向的偏移量,只在方向为 `'left'`或`'right'` 时生效 | int | 0 |
| OffsetY | Y 轴方向的偏移量,只在方向为 `'top'`或`'bottom'` 时生效 | int | 0 |
| Visible | Drawer 是否可见 | boolean | - |
| Keyboard | 是否支持键盘 esc 关闭 | boolean | true |
| OnClose | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | - |
| OnViewInit | 抽屉显示之前回调事件 | function(e) | - |
| OnClose | 点击遮罩层或右上角叉或取消按钮的回调 | EventCallback | - |
| OnOpen | 抽屉渲染之后回调事件 | Func<Task> | - |
### DrawerService
@ -58,7 +59,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/7z8NJQhFb/Drawer.svg
| CloseOnNavigation | 导航历史变化时是否关闭抽屉组件 | `boolean` | `true` |
| Keyboard | 是否支持键盘 esc 关闭 | `boolean` | `true` |
| MaskStyle | 遮罩样式 | `string` | `{}` |
| BodyStyle | Modal body 样式 | `string` | `{}` |
| BodyStyle | Drawer body 样式 | `string` | `{}` |
| Title | 标题 | `OneOf<RenderFragment, string>` | - |
| Width | 宽度 | `int` | `256` |
| Height | 高度, 只在方向为 `'top'`或`'bottom'` 时生效 | `int` | `256` |

View File

@ -15,7 +15,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
- 需要对输入的数据类型进行校验时。
## API
### From
### Form
| 名称 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| Layout | 表单布局 | [FormLayout](https://github.com/ant-design-blazor/ant-design-blazor/blob/master/components/form/types/FormLayout.cs) | FormLayout.Horizontal |
@ -32,7 +32,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
| OnFinish | 提交事件 | EventCallback\<EditContext\> | - |
| OnFinishFailed | 提交失败(校验失败)回调事件 | EventCallback\<EditContext\> | - |
| ValidateOnChange | 是否在更改时校验 | bool | false |
### FromItem
### FormItem
| 名称 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| Label | **label** 标签的文本 | string | input组件的Display或者DisplayName特性 |

View File

@ -1,15 +1,18 @@
@inject IconService iconService;
<div class="icons-list">
<IconFont Type="icon-tuichu" />
<IconFont Type="icon-facebook" />
<IconFont Type="icon-twitter" />
<IconFont Type="icon-tuichu" />
<IconFont Type="icon-facebook" />
<IconFont Type="icon-twitter" />
</div>
@code
{
protected override async Task OnInitializedAsync()
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await iconService.CreateFromIconfontCN("//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js");
await iconService.CreateFromIconfontCN("//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js");
}
}
}
}

View File

@ -1,17 +1,20 @@
@inject IconService iconService;
<div class="icons-list">
<IconFont Type="icon-javascript" />
<IconFont Type="icon-java" />
<IconFont Type="icon-shoppingcart" />
<IconFont Type="icon-python" />
<IconFont Type="icon-javascript" />
<IconFont Type="icon-java" />
<IconFont Type="icon-shoppingcart" />
<IconFont Type="icon-python" />
</div>
@code
{
protected override async Task OnInitializedAsync()
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await iconService.CreateFromIconfontCN("//at.alicdn.com/t/font_1788044_0dwu4guekcwr.js");
await iconService.CreateFromIconfontCN("//at.alicdn.com/t/font_1788592_a5xf2bdic3u.js");
await iconService.CreateFromIconfontCN("//at.alicdn.com/t/font_1788044_0dwu4guekcwr.js");
await iconService.CreateFromIconfontCN("//at.alicdn.com/t/font_1788592_a5xf2bdic3u.js");
}
}
}

View File

@ -9,6 +9,10 @@ title:
对于使用 [iconfont.cn](http://iconfont.cn/) 的用户,通过设置 `createFromIconfontCN` 方法参数对象中的 `scriptUrl` 字段, 即可轻松地使用已有项目中的图标。
> 注意这个方法会涉及JS互操作因此需要确保在 `firstRender=true` 时调用。
## en-US
If you are using [iconfont.cn](http://iconfont.cn/), you can use the icons in your project gracefully.
If you are using [iconfont.cn](http://iconfont.cn/), you can use the icons in your project gracefully.
> Note: This method involves JS interope, so make sure you call it when 'firstRender=true'.

View File

@ -7,8 +7,12 @@ title:
## zh-CN
`@ant-design/icons@4.1.0` 以后,`scriptUrl` 可引用多个资源,用户可灵活的管理 [iconfont.cn](http://iconfont.cn/) 图标。如果资源的图标出现重名,会按照数组顺序进行覆盖。
使用`scriptUrl` 可引用多个资源,用户可灵活的管理 [iconfont.cn](http://iconfont.cn/) 图标。如果资源的图标出现重名,会按照数组顺序进行覆盖。
> 注意这个方法会涉及JS互操作因此需要确保在 `firstRender=true` 时调用。
## en-US
You can use `scriptUrl` as an array after `@ant-design/icons@4.1.0`, manage icons in one `<Icon />` from multiple [iconfont.cn](http://iconfont.cn/) resources. If icon with a duplicate name in resources, it will overrided in array order.
You can use `scriptUrl` multiple times, manage icons in one `<Icon />` from multiple [iconfont.cn](http://iconfont.cn/) resources. If icon with a duplicate name in resources, it will overrided in array order.
> Note: This method involves JS interope, so make sure you call it when 'firstRender=true'

View File

@ -1,15 +1,5 @@
@code
{
bool collapsed;
void toggle()
{
collapsed = !collapsed;
}
}
<Layout>
<Sider Collapsible Collapsed=@collapsed NoTrigger OnCollapse="OnCollapse">
<Layout>
<Sider @bind-Collapsed=@collapsed NoTrigger OnCollapse="OnCollapse">
<div class="logo" />
<Menu Theme="MenuTheme.Dark" Mode="MenuMode.Inline" DefaultSelectedKeys=@(new[]{"1"})>
<MenuItem Key="1">
@ -69,6 +59,12 @@
@code{
bool collapsed;
void toggle()
{
collapsed = !collapsed;
}
void OnCollapse(bool isCollapsed)
{

View File

@ -1,5 +0,0 @@
<h3>CustomTriggerDebug</h3>
@code {
}

View File

@ -1,12 +1,8 @@
<Layout>
<Sider Breakpoint="@BreakpointType.Lg"
CollapsedWidth="64"
OnBreakpoint="@(broken => {
Console.WriteLine($"OnBreakpoint:{broken}");
})"
OnCollapse="@(collapsed => {
Console.WriteLine($"collapsed:{collapsed}");
})">
@bind-Collapsed=@collapsed
>
<div class="logo" />
<Menu Theme="MenuTheme.Dark" Mode="MenuMode.Inline" DefaultSelectedKeys=@(new[]{"4"})>
<MenuItem Key="1">
@ -38,6 +34,10 @@
</Layout>
</Layout>
@code{
bool collapsed;
}
<style>
#components-layout-demo-responsive .logo {
height: 32px;

View File

@ -1,11 +0,0 @@
---
order: 99
title:
zh-CN: 自定义触发器 Debug
en-US: Custom trigger debug
debug: true
---
## zh-CN
修改内容前,请尝试此 Demo 查看样式是否抖动。

View File

@ -0,0 +1,44 @@
<Divider Orientation="left">Draggable</Divider>
<AntList Bordered DataSource="@data">
<Header>Header</Header>
<ChildContent Context="item">
<ListItem>
<div draggable="true" @ondrop="e=>OnDrop(e, item)" @ondragstart="e=>OnDragStart(e, item)" ondragover="event.preventDefault()">
<span><Text Mark>[ITEM]</Text></span>@item
</div>
</ListItem>
</ChildContent>
<Footer>Footer</Footer>
</AntList>
@code {
string _dragging;
void OnDrop(DragEventArgs e, string s)
{
if (!string.IsNullOrEmpty(s) && !string.IsNullOrEmpty(_dragging)) {
System.Diagnostics.Trace.WriteLine(s);
int index = data.IndexOf(s);
data.Remove(_dragging);
data.Insert(index, _dragging);
_dragging = null;
StateHasChanged();
}
}
void OnDragStart(DragEventArgs e, string s)
{
e.DataTransfer.DropEffect = "move";
e.DataTransfer.EffectAllowed = "move";
_dragging = s;
}
public List<string> data = new List<string> {
"Racing car sprays burning fuel into crowd.",
"Japanese princess to wed commoner.",
"Australian walks 100km after outback crash.",
"Man charged over missing wedding girl.",
"Los Angeles battles huge wildfires."
};
}

View File

@ -0,0 +1,12 @@
---
order: 8
title:
zh-CN: 可拖拽(Simple)
en-US: Simple Draggable
---
## zh-CN
为ListItem子组件包装一层div, 设置draggable='true', 并为之加上ondrop/ondragstart/ondragover事件处理函数.
## en-US
Add a `div` element wrapping `ListItem` child element, set `draggable='true'`, add `ondrop`/`ondragstart`/`ondragover` event handler.

View File

@ -24,6 +24,7 @@ Radio/Radio.Button
| Checked | Specifies whether the radio is selected. | boolean |
| DefaultChecked | Specifies the initial state: whether or not the radio is selected. | boolean |- |
| Disabled | Disable radio | string | - |
| RadioButton | Set to TRUE to style the radio as button group. | bool | false |
| Value | According to value for comparison, to determine whether the selected | string | - |
RadioGroup

View File

@ -24,6 +24,7 @@ Radio/Radio.Button
| Checked | 指定当前是否选中 | boolean |
| DefaultChecked | 初始是否选中 | boolean |- |
| Disabled | 禁用 Radio | string | - |
| RadioButton | 设置为 TRUE 以将radio风格设置为按钮组 | bool | false |
| Value | 根据 value 进行比较,判断是否选中 | string | - |
RadioGroup

View File

@ -8,6 +8,8 @@
<Col Span="24">
<CountDown Title="Day Level" Value="@deadline" Format="dd 天 h 小时 m 分钟 s 秒" />
</Col>
<br/>
<Button OnClick="OnReset">Reset Value</Button>
</Row>
@code
@ -18,4 +20,9 @@
{
Console.WriteLine("finished!");
}
void OnReset()
{
deadline = DateTime.Now.AddMinutes(10);
}
}

View File

@ -1,25 +1,22 @@
@using System.ComponentModel
@using AntDesign.TableModels
<Table TItem="Dictionary<string, object>" DataSource="@data" Loading="data==null" ScrollX="1500" PageSize="5">
@if (data?.Any() == true)
{
@foreach (var key in data.FirstOrDefault()?.Keys.Take(10))
{
<Column TData="object" DataIndex=@($"['{key}']") Title="@key"></Column>
}
}
<Table TItem="Dictionary<string, object>" OnChange="HanleChange" DataSource="@data" Loading="data==null" ScrollX="1500" PageSize="5" Size="TableSize.Small">
@foreach (var key in data?.FirstOrDefault()?.Keys.Take(10) ??new string[0])
{
<PropertyColumn Property=@(c=>c[key]) Title="@key"></PropertyColumn>
}
</Table>
@inject HttpClient httpClient;
@code {
List<Dictionary<string, object>> data;
List<Dictionary<string, object>> data;
string githubUrl = "https://api.github.com/repos/ant-design-blazor/ant-design-blazor/contributors?per_page=200";
string githubUrl = "https://api.github.com/repos/ant-design-blazor/ant-design-blazor/contributors?per_page=200";
protected override async Task OnInitializedAsync()
{
data = await httpClient.GetFromJsonAsync<List<Dictionary<string, object>>>(githubUrl);
}
protected async Task HanleChange(QueryModel<Dictionary<string, object>> query)
{
data = await httpClient.GetFromJsonAsync<List<Dictionary<string, object>>>(githubUrl);
}
}

View File

@ -2,7 +2,7 @@
DefaultExpandedKeys="@(new[] { "0-0-0", "0-0-1"})"
DefaultSelectedKeys="@(new[] {"0-0-0", "0-0-1" })"
DefaultCheckedKeys="@(new[] {"0-0-0", "0-0-1" })"
SelectedNodeChanged="SelectedNodeChanged"
SelectedNodeChanged="SelectedNodeChanged"
OnSelect="OnSelect"
OnCheck="OnCheck">
<TreeNode Title="parent 1" Key="0-0" TItem="string">

View File

@ -0,0 +1,5 @@
<h3>Async</h3>
@code {
}

View File

@ -3,14 +3,13 @@
@bind-Value="value"
Placeholder="Please select"
AllowClear
TreeDefaultExpandAll
OnChange="OnChange">
TreeDefaultExpandAll>
<TreeNode TItem="string" Key="parent 1" Title="parent 1">
<TreeNode TItem="string" Key="parent 1-0" title="parent 1-0">
<TreeNode TItem="string" Key="leaf1" title="leaf1" />
<TreeNode TItem="string" Key="leaf2" title="leaf2" />
<TreeNode TItem="string" Key="parent 1-0" Title="parent 1-0">
<TreeNode TItem="string" Key="leaf1" Title="leaf1" />
<TreeNode TItem="string" Key="leaf2" Title="leaf2" />
</TreeNode>
<TreeNode TItem="string" Key="parent 1-1" title="parent 1-1">
<TreeNode TItem="string" Key="parent 1-1" Title="parent 1-1">
<TreeNode TItem="string" Key="leaf3">
<TitleTemplate>
<b Style="color:#08c;">leaf3</b>
@ -22,10 +21,5 @@
@code {
private string value;
public void OnChange()
{
}
private string value="leaf3";
}

View File

@ -0,0 +1,67 @@
<TreeSelect TItem="Data"
Style="width:100%;"
DataSource="treeData"
@bind-Value="@value"
Placeholder="Please select"
AllowClear
Multiple
TreeCheckable
TreeDefaultExpandAll
ChildrenExpression="node=>node.DataItem.Children"
TitleExpression="node=>node.DataItem.Title"
KeyExpression="node=>node.DataItem.Key"
IsLeafExpression="node=>node.DataItem.Children==null">
</TreeSelect>
@code {
private string value;
Data[] treeData = new Data[]
{
new()
{
Title = "Node1",
Key="0-0",
Children = new Data[]
{
new()
{
Title = "Child Node1",
Key="0-0-0",
},
}
},
new()
{
Title = "Node2",
Key="0-1",
Children = new Data[]
{
new()
{
Title ="Child Node3",
Key="0-1-0"
},
new()
{
Title ="Child Node4",
Key="0-1-1",
},
new()
{
Title ="Child Node5",
Key="0-1-2",
}
}
}
};
public class Data : ITreeData<Data>
{
public string Key { get; set; }
public Data Value => this;
public string Title { get; set; }
public IEnumerable<Data> Children { get; set; }
}
}

View File

@ -1,17 +1,17 @@
<TreeSelect TItem="string"
Style="width:100%;"
@bind-Value="value"
@bind-Values="values"
DropdownStyle="max-height:400px;overflow:auto;"
Placeholder="Please select"
AllowClear
Multiple
TreeDefaultExpandAll
OnChange="OnValuesChange">
<TreeNode TItem="string" Key="parent 1" title="parent 1">
<TreeNode TItem="string" Key="parent 1-0" title="parent 1-0">
<TreeNode TItem="string" Key="leaf1" title="my leaf" />
<TreeNode TItem="string" Key="leaf2" title="your leaf" />
TreeDefaultExpandAll>
<TreeNode TItem="string" Key="parent 1" Title="parent 1">
<TreeNode TItem="string" Key="parent 1-0" Title="parent 1-0">
<TreeNode TItem="string" Key="leaf1" Title="my leaf" />
<TreeNode TItem="string" Key="leaf2" Title="your leaf" />
</TreeNode>
<TreeNode TItem="string" Key="parent 1-1" title="parent 1-1">
<TreeNode TItem="string" Key="parent 1-1" Title="parent 1-1">
<TreeNode TItem="string" Key="sss">
<TitleTemplate>
<b style=" color: #08c;">sss</b>
@ -21,13 +21,10 @@
</TreeNode>
</TreeSelect>
@JsonSerializer.Serialize(values);
@code {
private string value;
public void OnValuesChange(IEnumerable<string> args)
{
Console.WriteLine(string.Join(',', args));
}
private IEnumerable<string> values=new[]{"leaf1","leaf2"};
}

View File

@ -0,0 +1,33 @@
<RadioGroup @bind-Value="_placement">
<Radio RadioButton Value="Placement.TopLeft">topLeft</Radio>
<Radio RadioButton Value="Placement.TopRight">topRight</Radio>
<Radio RadioButton Value="Placement.BottomLeft">bottomLeft</Radio>
<Radio RadioButton Value="Placement.BottomRight">bottomRight</Radio>
</RadioGroup>
<br />
<br />
<TreeSelect TItem="string"
Style="width:100%;"
DropdownStyle="max-height:400px;overflow:auto;"
Placeholder="Please select"
AllowClear
Multiple
TreeDefaultExpandAll>
<TreeNode TItem="string" Value="parent 1" Title="parent 1">
<TreeNode TItem="string" Value="parent 1-0" Title="parent 1-0">
<TreeNode TItem="string" Value="leaf1" Title="leaf1" />
<TreeNode TItem="string" Value="leaf2" Title="leaf2" />
</TreeNode>
<TreeNode TItem="string" Value="parent 1-1" Title="parent 1-1">
<TreeNode TItem="string" Value="leaf3">
<TitleTemplate>
<b style=" color: #08c;">leaf3</b>
</TitleTemplate>
</TreeNode>
</TreeNode>
</TreeNode>
</TreeSelect>
@code {
Placement _placement;
}

View File

@ -1,14 +0,0 @@
---
order: 1
title:
zh-CN: 多选赋值
en-US: SetMultipleValue
---
## zh-CN
展示设置选中多个点击节点,以及默认值功能。
## en-US
The most basic usage, tell you how to use tree-select multiple selected etc.

View File

@ -1,68 +0,0 @@
<SimpleTreeSelect Multiple="true" TItem="WebMenu" @bind-Values="selectMenuId"
DataSource="@(LocalJsonData.Data)" AllowClear="true"
TitleExpression="m => m.MenuName"
KeyExpression="m => m.MenuId"
DataItemExpression="(menus, menuId) => menus.FirstOrDefault(m => m.MenuId == menuId)"
ChildrenMethodExpression="(menus, menuId) => menus.Where(m => m.ParentId == menuId).ToList()"
IsLeafExpression="(menus,m) => menus.All(m => m.ParentId != m.MenuId)"
Placeholder="Please select"
TreeDefaultExpandAll>
</SimpleTreeSelect>
select MenuId: @string.Join(',', selectMenuId ?? new[] { ""})
<Button OnClick="ChangeMenuId" OnClickStopPropagation="true">Update</Button>
@code{
private IEnumerable<string> selectMenuId = new[] { "3", "1"};
private void ChangeMenuId()
{
selectMenuId = new[] { "5", "2" };
}
public class WebMenu
{
public string MenuId { get; set; }
public string MenuHref { get; set; }
public string MenuName { get; set; }
public string Target { get; set; }
public string ParentId { get; set; }
public string Right { get; set; }
public int? OrderBy { get; set; } = 9999;
}
public static class LocalJsonData
{
private static readonly IList<WebMenu> _data;
static LocalJsonData()
{
_data = new List<WebMenu>() {
new WebMenu(){ MenuId="1", MenuName="parent 1", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="2", MenuName="parent 1-0", ParentId="1", OrderBy=10},
new WebMenu(){ MenuId="3", MenuName="leaf1", ParentId="2", OrderBy=10},
new WebMenu(){ MenuId="4", MenuName="leaf2", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="5", MenuName="parent1-1", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="6", MenuName="leaf3", ParentId="5", OrderBy=10}
};
}
public static IList<WebMenu> Data => _data;
public static IList<WebMenu> GetMenusByParentId(string parentId)
{
return _data.Where(m => m.ParentId == parentId).ToList();
}
}
}

View File

@ -1,68 +0,0 @@
<SimpleTreeSelect TItem="WebMenu" @bind-Value="selectMenuId"
DataSource="@(LocalJsonData.Data)"
TitleExpression="m => m.MenuName"
KeyExpression="m => m.MenuId"
DataItemExpression="(menus, menuId) => menus.FirstOrDefault(m => m.MenuId == menuId)"
ChildrenMethodExpression="(menus, menuId) => menus.Where(m => m.ParentId == menuId).ToList()"
IsLeafExpression="(menus,m) => menus.All(m => m.ParentId != m.MenuId)"
Placeholder="Please select"
TreeDefaultExpandAll>
</SimpleTreeSelect>
select MenuId:<Input Placeholder="current Select MenuId" @bind-Value="@selectMenuId" /><Button OnClick="ChangeMenuId" OnClickStopPropagation="true">Update</Button>
@code{
private string selectMenuId = "3";
private void ChangeMenuId()
{
selectMenuId = "5";
}
public class WebMenu
{
public string MenuId { get; set; }
public string MenuHref { get; set; }
public string MenuName { get; set; }
public string Target { get; set; }
public string ParentId { get; set; }
public string Right { get; set; }
public int? OrderBy { get; set; } = 9999;
}
public static class LocalJsonData
{
private static readonly IList<WebMenu> _data;
static LocalJsonData()
{
_data = new List<WebMenu>() {
new WebMenu(){ MenuId="1", MenuName="parent 1", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="2", MenuName="parent 1-0", ParentId="1", OrderBy=10},
new WebMenu(){ MenuId="3", MenuName="leaf1", ParentId="2", OrderBy=10},
new WebMenu(){ MenuId="4", MenuName="leaf2", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="5", MenuName="parent1-1", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="6", MenuName="leaf3", ParentId="5", OrderBy=10}
};
}
public static IList<WebMenu> Data => _data;
public static IList<WebMenu> GetMenusByParentId(string parentId)
{
return _data.Where(m => m.ParentId == parentId).ToList();
}
}
}

View File

@ -1,14 +0,0 @@
---
order: 1
title:
zh-CN: 赋值
en-US: SetValue1
---
## zh-CN
展示设置选中节点以及默认值功能。XXXXXXXXXXX
## en-US
The most basic usage, tell you how to use tree-select multiple selected etc.

View File

@ -1,66 +0,0 @@

<TreeSelect TItem="WebMenu"
Style="width:100%;"
@bind-Value="@ParentDeptId"
DataSource="@(LocalJsonData.Data)"
Placeholder="请选择"
AllowClear="false"
TitleExpression="m => m.MenuName"
KeyExpression="m => m.MenuId"
ChildrenExpression="m => LocalJsonData.GetMenusByParentId(m.MenuId)"
IsLeafExpression="(menus,m) => menus.All(m => m.ParentId != m.MenuId)"
TreeDefaultExpandAll>
</TreeSelect>
@ParentDeptId
@code{
private string ParentDeptId = "1";
public class WebMenu
{
public string MenuId { get; set; }
public string MenuHref { get; set; }
public string MenuName { get; set; }
public string Target { get; set; }
public string ParentId { get; set; }
public string Right { get; set; }
public int? OrderBy { get; set; } = 9999;
}
public static class LocalJsonData
{
private static readonly IList<WebMenu> _data;
static LocalJsonData()
{
_data = new List<WebMenu>() {
new WebMenu(){ MenuId="1", MenuName="parent 1", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="2", MenuName="parent 1-0", ParentId="1", OrderBy=10},
new WebMenu(){ MenuId="3", MenuName="leaf1", ParentId="2", OrderBy=10},
new WebMenu(){ MenuId="4", MenuName="leaf2", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="5", MenuName="parent1-1", ParentId="0", OrderBy=10},
new WebMenu(){ MenuId="6", MenuName="leaf3", ParentId="5", OrderBy=10}
};
}
public static IList<WebMenu> Data => _data;
public static IList<WebMenu> GetMenusByParentId(string parentId)
{
return _data.Where(m => m.ParentId == parentId).ToList();
}
}
}

View File

@ -0,0 +1,13 @@
<Space Direction="DirectionVHType.Vertical" Style="width: 100%">
<SpaceItem>
<TreeSelect TItem="string" Status="error" Style="width: 100%" Placeholder="Error" />
</SpaceItem>
<Space>
<TreeSelect TItem="string" Status="warning" Style="width: 100%" Multiple Placeholder="Warning multiple" />
</Space>
</Space>
@code {
}

View File

@ -0,0 +1,30 @@
<TreeSelect TItem="string"
EnableSearch
Style="width:100%;"
@bind-Value="value"
DropdownStyle="max-height:400px;overflow:auto;"
Placeholder="Please select"
AllowClear
Multiple
SuffixIcon="icon"
TreeDefaultExpandAll>
<TreeNode TItem="string" Key="parent 1" Title="parent 1">
<TreeNode TItem="string" Key="parent 1-0" Title="parent 1-0">
<TreeNode TItem="string" Key="leaf1" Title="my leaf" />
<TreeNode TItem="string" Key="leaf2" Title="your leaf" />
</TreeNode>
<TreeNode TItem="string" Key="parent 1-1" Title="parent 1-1">
<TreeNode TItem="string" Key="sss">
<TitleTemplate>
<b style=" color: #08c;">sss</b>
</TitleTemplate>
</TreeNode>
</TreeNode>
</TreeNode>
</TreeSelect>
@code {
private string value;
private RenderFragment icon =@<Icon Type="smile"></Icon>;
}

View File

@ -1,54 +1,53 @@
<TreeSelect TItem="Data"
Style="width:100%;"
DataSource="treeData"
@bind-Value="@value"
Placeholder="Please select"
AllowClear
Multiple
TitleExpression="data => data.Title"
KeyExpression="data => data.Value"
ChildrenExpression="data => data.Children "
IsLeafExpression="(menus, data) => data.Children == null "
TreeDefaultExpandAll>
<TreeSelect TItem="Data"
Style="width:100%;"
DataSource="treeData"
@bind-Value="@value"
Placeholder="Please select"
AllowClear
Multiple
TreeDefaultExpandAll
ChildrenExpression="node=>node.DataItem.Children"
TitleExpression="node=>node.DataItem.Title"
KeyExpression="node=>node.DataItem.Key"
IsLeafExpression="node=>node.DataItem.Children==null">
</TreeSelect>
@code {
private string value;
private string value;
Data[] treeData = new Data[]
{
Data[] treeData = new Data[]
{
new()
{
Title = "Node1",
Value = "0-0",
Key = "0-0",
Children = new Data[]
{
{
new()
{
Title = "Child Node1",
Value = "0-0-1",
Key = "0-0-1",
},
new()
{
Title = "Child Node2",
Value = "0-0-2",
Key = "0-0-2",
}
}
},
new()
{
Title = "Node2",
Value = "0-1",
Key = "0-1",
}
};
};
public class Data
{
public string Value { get; set; }
public string Title { get; set; }
public Data[] Children { get; set; }
}
public class Data : ITreeData<Data>
{
public string Key { get; set; }
public Data Value => this;
public string Title { get; set; }
public IEnumerable<Data> Children { get; set; }
}
}

View File

@ -0,0 +1,29 @@
<TreeSelect TItem="string"
EnableSearch
Style="width:100%;"
@bind-Value="value"
DropdownStyle="max-height:400px;overflow:auto;"
Placeholder="Please select"
AllowClear
Multiple
TreeLine
TreeDefaultExpandAll>
<TreeNode TItem="string" Key="parent 1" Title="parent 1">
<TreeNode TItem="string" Key="parent 1-0" Title="parent 1-0">
<TreeNode TItem="string" Key="leaf1" Title="my leaf" />
<TreeNode TItem="string" Key="leaf2" Title="your leaf" />
</TreeNode>
<TreeNode TItem="string" Key="parent 1-1" Title="parent 1-1">
<TreeNode TItem="string" Key="sss">
<TitleTemplate>
<b style=" color: #08c;">sss</b>
</TitleTemplate>
</TreeNode>
</TreeNode>
</TreeNode>
</TreeSelect>
@code {
private string value;
}

View File

@ -0,0 +1,72 @@
---
order: 5
debug: true
title:
zh-CN: 异步加载
en-US: Asynchronous loading
---
## zh-CN
异步加载树节点。
## en-US
Asynchronous loading tree node.
```tsx
import React, { useState } from 'react';
import { TreeSelect } from 'antd';
import type { DefaultOptionType } from 'antd/es/select';
import type { TreeSelectProps } from 'antd';
const App: React.FC = () => {
const [value, setValue] = useState<string>();
const [treeData, setTreeData] = useState<Omit<DefaultOptionType, 'label'>[]>([
{ id: 1, pId: 0, value: '1', title: 'Expand to load' },
{ id: 2, pId: 0, value: '2', title: 'Expand to load' },
{ id: 3, pId: 0, value: '3', title: 'Tree Node', isLeaf: true },
]);
const genTreeNode = (parentId: number, isLeaf = false) => {
const random = Math.random().toString(36).substring(2, 6);
return {
id: random,
pId: parentId,
value: random,
title: isLeaf ? 'Tree Node' : 'Expand to load',
isLeaf,
};
};
const onLoadData: TreeSelectProps['loadData'] = ({ id }) =>
new Promise(resolve => {
setTimeout(() => {
setTreeData(
treeData.concat([genTreeNode(id, false), genTreeNode(id, true), genTreeNode(id, true)]),
);
resolve(undefined);
}, 300);
});
const onChange = (newValue: string) => {
console.log(newValue);
setValue(newValue);
};
return (
<TreeSelect
treeDataSimpleMode
style={{ width: '100%' }}
value={value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder="Please select"
onChange={onChange}
loadData={onLoadData}
treeData={treeData}
/>
);
};
export default App;
```

View File

@ -7,8 +7,48 @@ title:
## zh-CN
最简单的用法,展示动态下拉树功能
最简单的用法。
## en-US
The most basic usage, tell you how to use tree-select etc.
The most basic usage.
```tsx
import React, { useState } from 'react';
import { TreeSelect } from 'antd';
const { TreeNode } = TreeSelect;
const App: React.FC = () => {
const [value, setValue] = useState<string | undefined>(undefined);
const onChange = (newValue: string) => {
setValue(newValue);
};
return (
<TreeSelect
showSearch
style={{ width: '100%' }}
value={value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder="Please select"
allowClear
treeDefaultExpandAll
onChange={onChange}
>
<TreeNode value="parent 1" title="parent 1">
<TreeNode value="parent 1-0" title="parent 1-0">
<TreeNode value="leaf1" title="leaf1" />
<TreeNode value="leaf2" title="leaf2" />
</TreeNode>
<TreeNode value="parent 1-1" title="parent 1-1">
<TreeNode value="leaf3" title={<b style={{ color: '#08c' }}>leaf3</b>} />
</TreeNode>
</TreeNode>
</TreeSelect>
);
};
export default App;
```

View File

@ -0,0 +1,84 @@
---
order: 3
debug: true
title:
zh-CN: 可勾选
en-US: Checkable
---
## zh-CN
使用勾选框实现多选功能。
## en-US
Multiple and checkable.
```tsx
import React, { useState } from 'react';
import { TreeSelect } from 'antd';
const { SHOW_PARENT } = TreeSelect;
const treeData = [
{
title: 'Node1',
value: '0-0',
key: '0-0',
children: [
{
title: 'Child Node1',
value: '0-0-0',
key: '0-0-0',
},
],
},
{
title: 'Node2',
value: '0-1',
key: '0-1',
children: [
{
title: 'Child Node3',
value: '0-1-0',
key: '0-1-0',
},
{
title: 'Child Node4',
value: '0-1-1',
key: '0-1-1',
},
{
title: 'Child Node5',
value: '0-1-2',
key: '0-1-2',
},
],
},
];
const App: React.FC = () => {
const [value, setValue] = useState(['0-0-0']);
const onChange = (newValue: string[]) => {
console.log('onChange ', value);
setValue(newValue);
};
const tProps = {
treeData,
value,
onChange,
treeCheckable: true,
showCheckedStrategy: SHOW_PARENT,
placeholder: 'Please select',
style: {
width: '100%',
},
};
return <TreeSelect {...tProps} />;
};
export default App;
```

View File

@ -2,13 +2,55 @@
order: 1
title:
zh-CN: 多选
en-US: multiple
en-US: Multiple Selection
---
## zh-CN
最简单的用法展示多选功能。其中RootValue用于筛选DataSources中第一级的节点
多选的树选择
## en-US
The most basic usage, tell you how to use tree-select multiple selected etc.
Multiple selection usage.
```tsx
import React, { useState } from 'react';
import { TreeSelect } from 'antd';
const { TreeNode } = TreeSelect;
const App: React.FC = () => {
const [value, setValue] = useState<string>();
const onChange = (newValue: string) => {
console.log(newValue);
setValue(newValue);
};
return (
<TreeSelect
showSearch
style={{ width: '100%' }}
value={value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder="Please select"
allowClear
multiple
treeDefaultExpandAll
onChange={onChange}
>
<TreeNode value="parent 1" title="parent 1">
<TreeNode value="parent 1-0" title="parent 1-0">
<TreeNode value="leaf1" title="my leaf" />
<TreeNode value="leaf2" title="your leaf" />
</TreeNode>
<TreeNode value="parent 1-1" title="parent 1-1">
<TreeNode value="sss" title={<b style={{ color: '#08c' }}>sss</b>} />
</TreeNode>
</TreeNode>
</TreeSelect>
);
};
export default App;
```

View File

@ -0,0 +1,67 @@
---
order: 8
debug: true
title:
zh-CN: 弹出位置
en-US: Placement
---
## zh-CN
可以通过 `placement` 手动指定弹出的位置。
## en-US
You can manually specify the position of the popup via `placement`.
```tsx
import React, { useState } from 'react';
import { TreeSelect, Radio } from 'antd';
import type { SelectCommonPlacement } from 'antd/es/_util/motion';
import type { RadioChangeEvent } from 'antd';
const { TreeNode } = TreeSelect;
const App: React.FC = () => {
const [placement, SetPlacement] = useState<SelectCommonPlacement>('topLeft');
const placementChange = (e: RadioChangeEvent) => {
SetPlacement(e.target.value);
};
return (
<>
<Radio.Group value={placement} onChange={placementChange}>
<Radio.Button value="topLeft">topLeft</Radio.Button>
<Radio.Button value="topRight">topRight</Radio.Button>
<Radio.Button value="bottomLeft">bottomLeft</Radio.Button>
<Radio.Button value="bottomRight">bottomRight</Radio.Button>
</Radio.Group>
<br />
<br />
<TreeSelect
showSearch
dropdownStyle={{ maxHeight: 400, overflow: 'auto', minWidth: 300 }}
placeholder="Please select"
dropdownMatchSelectWidth={false}
placement={placement}
allowClear
treeDefaultExpandAll
>
<TreeNode value="parent 1" title="parent 1">
<TreeNode value="parent 1-0" title="parent 1-0">
<TreeNode value="leaf1" title="leaf1" />
<TreeNode value="leaf2" title="leaf2" />
</TreeNode>
<TreeNode value="parent 1-1" title="parent 1-1">
<TreeNode value="leaf3" title={<b style={{ color: '#08c' }}>leaf3</b>} />
</TreeNode>
</TreeNode>
</TreeSelect>
</>
);
};
export default App;
```

View File

@ -1,14 +0,0 @@
---
order: 1
title:
zh-CN: 赋值
en-US: setValue
---
## zh-CN
展示设置选中节点,以及默认值功能。
## en-US
The most basic usage, tell you how to use tree-select multiple selected etc.

View File

@ -0,0 +1,34 @@
---
order: 9
debug: true
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` 为 TreeSelect 添加状态,可选 `error` 或者 `warning`
## en-US
Add status to TreeSelect with `status`, which could be `error` or `warning`.
```tsx
import React from 'react';
import { TreeSelect, Space } from 'antd';
const App: React.FC = () => (
<Space direction="vertical" style={{ width: '100%' }}>
<TreeSelect status="error" style={{ width: '100%' }} placeholder="Error" />
<TreeSelect
status="warning"
style={{ width: '100%' }}
multiple
placeholder="Warning multiple"
/>
</Space>
);
export default App;
```

View File

@ -0,0 +1,59 @@
---
order: 12
debug: true
title:
zh-CN: 后缀图标
en-US: Suffix
---
## zh-CN
最简单的用法。
## en-US
The most basic usage.
```tsx
import React, { useState } from 'react';
import { TreeSelect } from 'antd';
import { SmileOutlined } from '@ant-design/icons';
const { TreeNode } = TreeSelect;
const icon = <SmileOutlined />;
const App: React.FC = () => {
const [value, setValue] = useState<string>();
const onChange = (newValue: string) => {
console.log(newValue);
setValue(newValue);
};
return (
<TreeSelect
showSearch
suffixIcon={icon}
style={{ width: '100%' }}
value={value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder="Please select"
allowClear
treeDefaultExpandAll
onChange={onChange}
>
<TreeNode value="parent 1" title="parent 1">
<TreeNode value="parent 1-0" title="parent 1-0">
<TreeNode value="leaf1" title="my leaf" />
<TreeNode value="leaf2" title="your leaf" />
</TreeNode>
<TreeNode value="parent 1-1" title="parent 1-1">
<TreeNode value="sss" title={<b style={{ color: '#08c' }}>sss</b>} />
</TreeNode>
</TreeNode>
</TreeSelect>
);
};
export default App;
```

View File

@ -7,8 +7,8 @@ title:
## zh-CN
使用 `treeData` 把 JSON 数据直接生成树结构。
使用 `DataSource` 把 IEnumerable<T> 数据直接生成树结构。
## en-US
The tree structure can be populated using `treeData` property. This is a quick and easy way to provide the tree content.
The tree structure can be populated using `DataSource` property. This is a quick and easy way to provide the tree content.

View File

@ -0,0 +1,58 @@
---
order: 6
debug: true
title:
zh-CN: 线性样式
en-US: Show Tree Line
---
## zh-CN
通过 `treeLine` 配置线性样式。
## en-US
Use `treeLine` to show the line style.
```tsx
import React, { useState } from 'react';
import { TreeSelect, Switch, Space } from 'antd';
const { TreeNode } = TreeSelect;
const App: React.FC = () => {
const [treeLine, setTreeLine] = useState(true);
const [showLeafIcon, setShowLeafIcon] = useState(false);
return (
<Space direction="vertical">
<Switch
checkedChildren="treeLine"
unCheckedChildren="treeLine"
checked={treeLine}
onChange={() => setTreeLine(!treeLine)}
/>
<Switch
disabled={!treeLine}
checkedChildren="showLeafIcon"
unCheckedChildren="showLeafIcon"
checked={showLeafIcon}
onChange={() => setShowLeafIcon(!showLeafIcon)}
/>
<TreeSelect treeLine={treeLine && { showLeafIcon }} style={{ width: 300 }}>
<TreeNode value="parent 1" title="parent 1">
<TreeNode value="parent 1-0" title="parent 1-0">
<TreeNode value="leaf1" title="my leaf" />
<TreeNode value="leaf2" title="your leaf" />
</TreeNode>
<TreeNode value="parent 1-1" title="parent 1-1">
<TreeNode value="sss" title="sss" />
</TreeNode>
</TreeNode>
</TreeSelect>
</Space>
);
};
export default App;
```

View File

@ -7,7 +7,7 @@
@Title
</div>
<div class="code-box-description">
<Markdown>@Description</Markdown>
@Description
</div>
</section>
</div>

View File

@ -1,25 +1,44 @@
@using System.Text.RegularExpressions
@inherits AntDesignTestBase
@inherits AntDesignTestBase
@code {
[Fact]
public void InputNumber_formatter_parser_change_value()
{
//Arrange
[Fact]
public void InputNumber_formatter_parser_change_value()
{
//Arrange
#pragma warning disable CS0618 // Type or member is obsolete
JSInterop.SetupVoid(JSInteropConstants.Focus, _ => true).SetVoidResult();
#pragma warning restore CS0618 // Type or member is obsolete
AntDesign.InputNumber<double>? input = null;
Func<double, string> formatter = v => "$ " + v.ToString("n0");
Func<string, string> parser = v => Regex.Replace(v, @"\$\s?|(,*)", "");
AntDesign.InputNumber<double>? input = null;
Func<double, string> formatter = v => "$ " + v.ToString("n0");
Func<string, string> parser = v => Regex.Replace(v, @"\$\s?|(,*)", "");
var cut = Render<AntDesign.InputNumber<double>>(
@<AntDesign.InputNumber @ref="@input" Formatter="formatter" Parser="parser" DefaultValue="1000d"/>
);
//Act
cut.Find("input").Change("$ 10");
//Assert
cut.Instance.Value.Should().Be(10);
}
var cut = Render<AntDesign.InputNumber<double>>(
@<AntDesign.InputNumber @ref="@input" Formatter="formatter" Parser="parser" DefaultValue="1000d" />
);
//Act
cut.Find("input").Change("$ 10");
//Assert
cut.Instance.Value.Should().Be(10);
}
[Fact]
public void InputNumber_value_display_check_when_step_notation_is_scientific()
{
//Arrange
#pragma warning disable CS0618 // Type or member is obsolete
JSInterop.SetupVoid(JSInteropConstants.Focus, _ => true).SetVoidResult();
#pragma warning restore CS0618 // Type or member is obsolete
AntDesign.InputNumber<double>? input = null;
var cut = Render<AntDesign.InputNumber<double>>(
@<AntDesign.InputNumber @ref="@input" Step="0.00001" />
);
//Act
var inputElement = cut.Find("input");
inputElement.KeyDown(Key.Up);
//Assert
string decimalSeparator = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator;
inputElement.GetAttribute("value").Should().Be($"0{decimalSeparator}00001");
}
}