merge master into feature

This commit is contained in:
James Yeung 2022-11-14 17:43:54 +08:00
commit ff7031ffec
142 changed files with 3166 additions and 1199 deletions

View File

@ -35,6 +35,11 @@ jobs:
with:
dotnet-version: 6.0.101
- name: Setup .NET 7.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.100
- uses: actions/setup-node@v1
with:
node-version: "10.x"
@ -125,7 +130,7 @@ jobs:
run: |
npm install
sed -i s/{version}/$PACKAGE_VERSION/g ./site/AntDesign.Docs/Shared/HeaderMenu.razor
dotnet publish ./site/AntDesign.Docs.Wasm -c Release -f net6 -o cargo
dotnet publish ./site/AntDesign.Docs.Wasm -c Release -f net7 -o cargo
env:
PACKAGE_VERSION: ${{ steps.pack.outputs.package_version }}

View File

@ -1,110 +0,0 @@
name: 🔂 Surge PR Preview
on:
issue_comment:
types: [created]
jobs:
preview:
name: Preview
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/preview')
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@0.9.0
name: Prepare preview ⛏
env:
RUN_PATH: https://github.com/ant-design-blazor/ant-design-blazor/actions/runs/${{ github.run_id }}
with:
github-token: ${{ github.token }}
script: |
const REPLACE_MARK = '<!-- PREVIEW_UPDATE_COMMENT -->';
const comment = `[<img width="306" src="https://user-images.githubusercontent.com/5378891/72351368-2c979e00-371b-11ea-9652-eb4e825d745e.gif">](${process.env.RUN_PATH})`;
const wrappedComment = `
${REPLACE_MARK}
${comment}
`.trim();
let comments = await github.issues.listComments(context.issue);
// Find my comment
const updateComment = comments.data.find(({ body }) => body.includes(REPLACE_MARK));
// eslint-disable-next-line no-console
console.log('Origin comment:', updateComment);
let res;
if (!updateComment) {
res = await github.issues.createComment({...context.issue, body: wrappedComment })
} else {
res = await github.issues.updateComment({...context.issue, comment_id: updateComment.id, body: wrappedComment })
}
console.log(res);
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.100
- name: Setup .NET 6.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.100
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: "10.x"
- name: Checkout & Building ⚙
env:
PULL_NUMBER: ${{ github.event.issue.number }}
run: |
git init
git remote add origin https://github.com/ant-design-blazor/ant-design-blazor.git
git fetch origin pull/$PULL_NUMBER/head:fork
git checkout fork
npm install
dotnet build
dotnet publish ./site/AntDesign.Docs.Wasm -c Release -f net6 -o cargo
- name: Deploy Preview 🚀
env:
SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}
DEPLOY_DOMAIN: preview-${{ github.run_id }}-ant-design-blazor.surge.sh
run: |
npm install -g surge
surge --project ./cargo/wwwroot --domain $DEPLOY_DOMAIN
- uses: actions/github-script@0.9.0
name: Show preview ⛏
env:
DEPLOY_DOMAIN: https://preview-${{ github.run_id }}-ant-design-blazor.surge.sh
with:
github-token: ${{ github.token }}
script: |
const REPLACE_MARK = '<!-- PREVIEW_UPDATE_COMMENT -->';
const comment = `[<img width="306" src="https://user-images.githubusercontent.com/5378891/72400743-23dbb200-3785-11ea-9d13-1a2d92743846.png">](${process.env.DEPLOY_DOMAIN})`;
const wrappedComment = `
${REPLACE_MARK}
${comment}
`.trim();
let comments = await github.issues.listComments(context.issue);
// Find my comment
const updateComment = comments.data.find(({ body }) => body.includes(REPLACE_MARK));
// eslint-disable-next-line no-console
console.log('Origin comment:', updateComment);
let res;
if (!updateComment) {
res = await github.issues.createComment({...context.issue, body: wrappedComment })
} else {
res = await github.issues.updateComment({...context.issue, comment_id: updateComment.id, body: wrappedComment })
}
console.log(res);
# https://github.community/t5/GitHub-Actions/Workflow-is-failing-if-no-job-can-be-ran-due-to-condition/m-p/38186#M3250
always_job:
name: Always run job
runs-on: ubuntu-latest
steps:
- name: Always run
run: echo "This job is used to prevent the workflow to fail when all other jobs are skipped."

View File

@ -23,6 +23,11 @@ jobs:
with:
dotnet-version: 6.0.101
- name: Setup .NET 7.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.100
- name: Setup Node.js
uses: actions/setup-node@v1
with:
@ -39,7 +44,7 @@ jobs:
npm install
dotnet build
sed -i s/{version}/$PACKAGE_VERSION/g ./site/AntDesign.Docs/Shared/HeaderMenu.razor
dotnet publish ./site/AntDesign.Docs.Wasm -c Release -f net6 -o _site
dotnet publish ./site/AntDesign.Docs.Wasm -c Release -f net7 -o _site
env:
PACKAGE_VERSION: ${{ github.event.number }}

View File

@ -28,6 +28,11 @@ jobs:
with:
dotnet-version: 6.0.101
- name: Setup .NET 7.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.100
- uses: actions/setup-node@v1
with:
node-version: "10.x"

View File

@ -31,6 +31,11 @@ jobs:
with:
dotnet-version: 6.0.101
- name: Setup .NET 7.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.100
- uses: actions/setup-node@v1
with:
node-version: "10.x"
@ -52,7 +57,7 @@ jobs:
sed -i s/{version}/$VERSION/g ./site/AntDesign.Docs.Wasm/wwwroot/index.html
sed -i s/{version}/$VERSION/g ./site/AntDesign.Docs.Wasm/wwwroot/service-worker.published.js
dotnet build
dotnet publish ./site/AntDesign.Docs.Wasm -c Release -f net6 -o cargo -p:EnableAOT=true
dotnet publish ./site/AntDesign.Docs.Wasm -c Release -f net7 -o cargo -p:EnableAOT=true
cp -rf cargo/staticwebapp.config.json cargo/wwwroot
- name: Deploy to Azure 🚀

View File

@ -31,6 +31,11 @@ jobs:
with:
dotnet-version: 6.0.101
- name: Setup .NET 7.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.100
- uses: actions/setup-node@v1
with:
node-version: "10.x"

View File

@ -15,6 +15,67 @@ timeline: true
---
### 0.12.7
`2022-11-6`
- DatePicker
- 🐞 Fixed wrong day order in some locales and fallback to use Globalization libaray when there is no day locale. [#2855](https://github.com/ant-design-blazor/ant-design-blazor/pull/2855) [@ElderJames](https://github.com/ElderJames)
- 🐞 Fixed `smoothScrollTo` causes an infinite loop. [#2854](https://github.com/ant-design-blazor/ant-design-blazor/pull/2854) [@Alexbits](https://github.com/Alexbits)
- 🐞 fix day order in calendar header for russian locale. [#2845](https://github.com/ant-design-blazor/ant-design-blazor/pull/2845) [@ocoka](https://github.com/ocoka)
- 🐞 Fixed tab key does not confirm the value. [#2847](https://github.com/ant-design-blazor/ant-design-blazor/pull/2847) [@Alexbits](https://github.com/Alexbits)
- Core
- ✅ Improve unit tests cover for Core module. [#2821](https://github.com/ant-design-blazor/ant-design-blazor/pull/2821) [@LeaFrock](https://github.com/LeaFrock)
- ⚡️ Optimize CssSizeLength and CssStyleBuilder. [#2803](https://github.com/ant-design-blazor/ant-design-blazor/pull/2803) [@LeaFrock](https://github.com/LeaFrock)
- 🐞 Fixed Tabs support of tab bar css style and class. [#2844](https://github.com/ant-design-blazor/ant-design-blazor/pull/2844) [@ldsenow](https://github.com/ldsenow)
- 🐞 Fixed BackTop doesn't remove the dom when visible is false. [#2831](https://github.com/ant-design-blazor/ant-design-blazor/pull/2831) [@ElderJames](https://github.com/ElderJames)
- 🐞 Fixed bug where Content wouldn't render in Drawer if it was a string and not RenderFragment. [#2833](https://github.com/ant-design-blazor/ant-design-blazor/pull/2833) [@kooliokey](https://github.com/kooliokey)
- 🐞 Fixed bug where Title parameter was not being rendered. [#2830](https://github.com/ant-design-blazor/ant-design-blazor/pull/2830) [@kooliokey](https://github.com/kooliokey)
- 🐞 Fixed Slider accessibility updates with aria labels. [#2818](https://github.com/ant-design-blazor/ant-design-blazor/pull/2818) [@kooliokey](https://github.com/kooliokey)
- 🐞 Fixed Table exception during page navigation [#2797](https://github.com/ant-design-blazor/ant-design-blazor/pull/2797) [@Kyojuro27](https://github.com/Kyojuro27)
- 🐞 Fixed bug with tag color change after render not always styling properly. [#2816](https://github.com/ant-design-blazor/ant-design-blazor/pull/2816) [@kooliokey](https://github.com/kooliokey)
- 🐞 Fixed Cascader AllowClear was not working when false. [#2792](https://github.com/ant-design-blazor/ant-design-blazor/pull/2792) [@YongQuan-dotnet](https://github.com/YongQuan-dotnet)
- 🐞 Fixed AutoComplete search panel show. [#2793](https://github.com/ant-design-blazor/ant-design-blazor/pull/2793) [@lyj0309](https://github.com/lyj0309)
- 💄 Fixed Menu that class name of the expand icon for submenu. [#2796](https://github.com/ant-design-blazor/ant-design-blazor/pull/2796) [@ElderJames](https://github.com/ElderJames)
- 🐞 fix descriptions component miss div element. [#2798](https://github.com/ant-design-blazor/ant-design-blazor/pull/2798) [@Weilence](https://github.com/Weilence)
- 🐞 Fixed Upload should get error raw response. [#2858](https://github.com/ant-design-blazor/ant-design-blazor/pull/2858) [@yosheng](https://github.com/yosheng)
### 0.12.6
`2022-10-11`
- 🐞 Fixed JS event listener registration. [#2783](https://github.com/ant-design-blazor/ant-design-blazor/pull/2783) [@ElderJames](https://github.com/ElderJames)
- 🐞 Fixed Segmented that the Disabled parameter does not work on items and cannot be dynamically toggled. [#2778](https://github.com/ant-design-blazor/ant-design-blazor/pull/2778) [@ElderJames](https://github.com/ElderJames)
- 🐞 Removing the gulp task to exclude empty files. [#2779](https://github.com/ant-design-blazor/ant-design-blazor/pull/2779) [@paulsuart](https://github.com/paulsuart)
### 0.12.5
`2022-10-09`
- Datepicker
- 🐞 Fixed correct culture not applied when manual input. [#2715](https://github.com/ant-design-blazor/ant-design-blazor/pull/2715) [@Alexbits](https://github.com/Alexbits)
- 🐞 Fixed a series of issues to make Datepicker and RangePicker behave more like antd. [#2741](https://github.com/ant-design-blazor/ant-design-blazor/pull/2741) [@Alexbits](https://github.com/Alexbits)
- Fixed an issue with the OnChange event passing in an old value.
- Fixed RangePicker head not switching year.
- Fixed RangePicker selection panel display problem when both start and end in the same period.
- Fixed an issue where the end date was not highlighted when the RangePicker was selected in week mode.
- The start date is not highlighted during the end date input in the date picker with the time.
- Other minor fixes and refactorings
- Modal
- 🐞 Fixed maximizable not work when using Title. [#2750](https://github.com/ant-design-blazor/ant-design-blazor/pull/2750) [@zxyao145](https://github.com/zxyao145)
- 🐞 Fixed Confirm dialog closeable. [#2776](https://github.com/ant-design-blazor/ant-design-blazor/pull/2776) [@zxyao145](https://github.com/zxyao145)
- 🐞 Fixed Core that remove the event listener when the component is disposed. [#2738](https://github.com/ant-design-blazor/ant-design-blazor/pull/2738) [@ElderJames](https://github.com/ElderJames)
- 🐞 Fixed Radio that the disabled parameter for RadioGroup with <code class="notranslate">RadioOption&lt;TValue&gt;</code> options doesn't work. [#2744](https://github.com/ant-design-blazor/ant-design-blazor/pull/2744) [@ElderJames](https://github.com/ElderJames)
- 🐞 Fixed Table that set value for table header checkbox Disabled attribute. [#2737](https://github.com/ant-design-blazor/ant-design-blazor/pull/2737) [@YongQuan-dotnet](https://github.com/YongQuan-dotnet)
- ⚡️ Fixed Select that remove redundant CreateDeleteSelectOptions() calls in render cycles. [#2657](https://github.com/ant-design-blazor/ant-design-blazor/pull/2657) [@m-khrapunov](https://github.com/m-khrapunov)
- 🛠 Fixed gulp pipeline to include less files so they end up in /staticwebassets/less in the nuget package. [#2730](https://github.com/ant-design-blazor/ant-design-blazor/pull/2730) [@paulsuart](https://github.com/paulsuart)
### 0.12.4
`2022-09-14`

View File

@ -15,6 +15,66 @@ timeline: true
---
### 0.12.7
`2022-11-6`
- DatePicker
- 🐞 修复 部分语言文件的星期排序,缺失的语言使用 Globalization 替代。[#2855](https://github.com/ant-design-blazor/ant-design-blazor/pull/2855) [@ElderJames](https://github.com/ElderJames)
- 🐞 修复 smoothScrollTo 方法导致无限循环的问题。[#2854](https://github.com/ant-design-blazor/ant-design-blazor/pull/2854) [@Alexbits](https://github.com/Alexbits)
- 🐞 修复 俄语 的星期排序[#2845](https://github.com/ant-design-blazor/ant-design-blazor/pull/2845) [@ocoka](https://github.com/ocoka)
- 🐞 修复 tab 键不确认值的问题。[#2847](https://github.com/ant-design-blazor/ant-design-blazor/pull/2847) [@Alexbits](https://github.com/Alexbits)
- Core
- ✅ 增加核心模块的单元测试覆盖。[#2821](https://github.com/ant-design-blazor/ant-design-blazor/pull/2821) [@LeaFrock](https://github.com/LeaFrock)
- ⚡️ 优化 CssSizeLength 和CssStyleBuilder。[#2803](https://github.com/ant-design-blazor/ant-design-blazor/pull/2803) [@LeaFrock](https://github.com/LeaFrock)
- 🐞 修复 `TabBarStyle``TabBarClass`。[#2844](https://github.com/ant-design-blazor/ant-design-blazor/pull/2844) [@ldsenow](https://github.com/ldsenow)
- 🐞 修复 BackTop 在隐藏时没有真正清除 dom。[#2831](https://github.com/ant-design-blazor/ant-design-blazor/pull/2831) [@ElderJames](https://github.com/ElderJames)
- 🐞 修复 Drawer 如果 `Content` 是字符串而不是 RenderFragment则内容不会渲染的错误。[#2833](https://github.com/ant-design-blazor/ant-design-blazor/pull/2833) [@kooliokey](https://github.com/kooliokey)
- 🐞 修复 Title 属性没有渲染。[#2830](https://github.com/ant-design-blazor/ant-design-blazor/pull/2830) [@kooliokey](https://github.com/kooliokey)
- 🐞 修复 Slider 可访问性 aria labels。[#2818](https://github.com/ant-design-blazor/ant-design-blazor/pull/2818) [@kooliokey](https://github.com/kooliokey)
- 🐞 修复 Table 在页面切换时偶尔的异常[#2797](https://github.com/ant-design-blazor/ant-design-blazor/pull/2797) [@Kyojuro27](https://github.com/Kyojuro27)
- 🐞 修复 Tag 渲染后标签颜色变化不总是正确更新样式。[#2816](https://github.com/ant-design-blazor/ant-design-blazor/pull/2816) [@kooliokey](https://github.com/kooliokey)
- 🐞 修复 Cascader 当 `AllowClear` 属性为false时清除无效的问题。[#2792](https://github.com/ant-design-blazor/ant-design-blazor/pull/2792) [@YongQuan-dotnet](https://github.com/YongQuan-dotnet)
- 🐞 修复 AutoComplete 搜索时下拉列表有时候不显示。[#2793](https://github.com/ant-design-blazor/ant-design-blazor/pull/2793) [@lyj0309](https://github.com/lyj0309)
- 🐞 修复 Menu 子菜单展开图标的样式。[#2796](https://github.com/ant-design-blazor/ant-design-blazor/pull/2796) [@ElderJames](https://github.com/ElderJames)
- 🐞 修复 Descriptions 缺少的div元素。[#2798](https://github.com/ant-design-blazor/ant-design-blazor/pull/2798) [@Weilence](https://github.com/Weilence)
- 🐞 修复 Upload 返回错误时没有正确传出响应报文。[#2858](https://github.com/ant-design-blazor/ant-design-blazor/pull/2858) [@yosheng](https://github.com/yosheng)
### 0.12.6
`2022-10-11`
- 🐞 修复 JS事件监听器注册问题。[#2783](https://github.com/ant-design-blazor/ant-design-blazor/pull/2783) [@ElderJames](https://github.com/ElderJames)
- 🐞 修复 Segmented 的 Disabled 参数对项不起作用,也不能动态切换的问题。[#2778](https://github.com/ant-design-blazor/ant-design-blazor/pull/2778) [@ElderJames](https://github.com/ElderJames)
- 🐞 修复打包时 patch.less 空文件缺失。[#2779](https://github.com/ant-design-blazor/ant-design-blazor/pull/2779) [@paulsuart](https://github.com/paulsuart)
### 0.12.5
`2022-10-09`
- Datepicker
- 🐞 修复 手动输入时间时CultureInfo 格式不识别导致无法绑定的问题。[#2715](https://github.com/ant-design-blazor/ant-design-blazor/pull/2715) [@Alexbits](https://github.com/Alexbits)
- 🐞 修复一系列问题,使 Datepicker 和 RangePicker 的行为更接近 antd。[#2741](https://github.com/ant-design-blazor/ant-design-blazor/pull/2741) [@Alexbits](https://github.com/Alexbits)
- 修复 OnChange 事件传入旧值的问题。
- 修复 RangePicker 的头部无法切换年份。
- 修复 RangePicker 当开始和结束都在同一周期时,选择面板的显示问题。
- 修复 RangePicker 周选择模式时,结束日期不高亮显示的问题。
- 修复 RangePicker 在带有时间的日期选择器中输入结束日期时,开始日期不会高亮显示的问题。
- 其他一些小修复和重构
- Modal
- 🐞 修复 当使用 Title 时Maximizable 设置不生效。[#2750](https://github.com/ant-design-blazor/ant-design-blazor/pull/2750) [@zxyao145](https://github.com/zxyao145)
- 🐞 修复 Confirm 错误的响应 close icon 事件。[#2776](https://github.com/ant-design-blazor/ant-design-blazor/pull/2776) [@zxyao145](https://github.com/zxyao145)
- 🐞 修复 底层 当组件 Dispose 时移除JS事件监听器。[#2738](https://github.com/ant-design-blazor/ant-design-blazor/pull/2738) [@ElderJames](https://github.com/ElderJames)
- 🐞 修复 Radio 的 Disabled 属性在使用了 RadioOption 作为 options 的 RadioGroup 中不起作用的问题。[#2744](https://github.com/ant-design-blazor/ant-design-blazor/pull/2744) [@ElderJames](https://github.com/ElderJames)
- 🐞 修复 Table 当所有selection都为Disabled=true则头部的全选 Selection 也变为 Disable 状态。[#2737](https://github.com/ant-design-blazor/ant-design-blazor/pull/2737) [@YongQuan-dotnet](https://github.com/YongQuan-dotnet)
- ⚡️ 修复 Select 内部 CreateDeleteSelectOptions 方法的循环调用。[#2657](https://github.com/ant-design-blazor/ant-design-blazor/pull/2657) [@m-khrapunov](https://github.com/m-khrapunov)
- 🛠 修复 Gulp 脚本使LESS文件打包到NUGET包中。[#2730](https://github.com/ant-design-blazor/ant-design-blazor/pull/2730) [@paulsuart](https://github.com/paulsuart)
### 0.12.4
`2022-09-14`

View File

@ -164,7 +164,7 @@ WebAssembly 静态托管页面示例
### 本地
- 先安装 [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet/6.0?WT.mc_id=DT-MVP-5003987) 6.0.100 以上版本
- 先安装 [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet/7.0?WT.mc_id=DT-MVP-5003987) 7.0.100 以上版本
- 安装 Node.js只用于样式文件和互操作所需 TS 文件的构建)
- 克隆到本地开发

View File

@ -169,7 +169,7 @@ Click the button below to start a new workspace for development for free.
### Local
- Install [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet/6.0?WT.mc_id=DT-MVP-5003987) 6.0.100 or later.
- Install [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet/7.0?WT.mc_id=DT-MVP-5003987) 7.0.100 or later.
- Install Node.js (only for building style files and interoperable TypeScript files)
- Clone to local development

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>netstandard2.1;net5;net6</TargetFrameworks>
<TargetFrameworks>netstandard2.1;net5;net6;net7</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
@ -65,6 +65,14 @@
</PackageReference>
</ItemGroup>
<ItemGroup Condition="$(TargetFramework) == 'net7'">
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="7.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.DataAnnotations.Validation" Version="3.2.0-rc1.20223.4" />
<PackageReference Include="OneOf" Version="2.1.155" />

View File

@ -0,0 +1,6 @@
namespace System.Runtime.CompilerServices
{
public sealed class IsExternalInit
{
}
}

View File

@ -368,7 +368,7 @@ namespace AntDesign
}
}
if (_overlayTrigger != null && ShowPanel)
if (_overlayTrigger != null)
{
// if options count == 0 then close overlay
if (_isOptionsZero && _overlayTrigger.IsOverlayShow())

View File

@ -1,19 +1,22 @@
@namespace AntDesign
@inherits AntDomComponentBase
<div class="@ClassMapper.Class" @onclick="_ => OnClickHandle()" style="@Style">
<div>
<div class="@BackTopContentClassMapper.Class">
@if (ChildContent != null)
{
@ChildContent
}
else
{
<div class="@BackTopIconClassMapper.Class">
<Icon Type="@Icon" Theme="outline" />
</div>
}
@if (!_hidden)
{
<div class="@ClassMapper.Class" @onclick="OnClickHandler" style="@Style" @ref="Ref" id="@Id">
<div>
<div class="@BackTopContentClassMapper.Class">
@if (ChildContent != null)
{
@ChildContent
}
else
{
<div class="@BackTopIconClassMapper.Class">
<Icon Type="@Icon" Theme="outline" />
</div>
}
</div>
</div>
</div>
</div>
}

View File

@ -2,16 +2,12 @@
using System.Threading.Tasks;
using AntDesign.JsInterop;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace AntDesign
{
public partial class BackTop : AntDomComponentBase
{
private bool _visible = false;
[Inject]
private IDomEventListener DomEventListener { get; set; }
[Parameter]
public string Icon { get; set; } = "vertical-align-top";
@ -27,14 +23,20 @@ namespace AntDesign
[Parameter]
public string TargetSelector { get; set; }
[Parameter]
public EventCallback OnClick { get; set; }
[Inject]
private IDomEventListener DomEventListener { get; set; }
protected ClassMapper BackTopContentClassMapper { get; set; } = new ClassMapper();
protected ClassMapper BackTopIconClassMapper { get; set; } = new ClassMapper();
[Parameter]
public EventCallback OnClick { get; set; }
private bool _visible = false;
private bool _hidden = false;
protected async Task OnClickHandle()
protected async Task OnClickHandler(MouseEventArgs e)
{
if (string.IsNullOrWhiteSpace(TargetSelector))
await JsInvokeAsync<DomRect>(JSInteropConstants.BackTop);
@ -42,7 +44,7 @@ namespace AntDesign
await JsInvokeAsync<DomRect>(JSInteropConstants.BackTop, TargetSelector);
if (OnClick.HasDelegate)
await OnClick.InvokeAsync(null);
await OnClick.InvokeAsync(e);
}
protected override void OnInitialized()
@ -51,7 +53,7 @@ namespace AntDesign
base.OnInitialized();
}
protected async override Task OnFirstAfterRenderAsync()
protected override async Task OnFirstAfterRenderAsync()
{
if (string.IsNullOrWhiteSpace(TargetSelector))
{
@ -82,7 +84,25 @@ namespace AntDesign
{
JsonElement scrollInfo = await JsInvokeAsync<JsonElement>(JSInteropConstants.GetScroll);
double offset = scrollInfo.GetProperty("y").GetDouble();
_visible = offset > VisibilityHeight;
var visible = offset > VisibilityHeight;
if (visible == _visible)
return;
_visible = visible;
StateHasChanged();
if (_visible)
{
_hidden = false;
}
else
{
await Task.Delay(300);
_hidden = true;
}
StateHasChanged();
}

View File

@ -9,9 +9,9 @@
@if (HasStatusOrColor && ChildContent == null)
{
<span class="@($"ant-badge-status-dot {(StatusOrPresetColor!=null? $"ant-badge-status-{StatusOrPresetColor.ToLowerInvariant()}":"")}")" style="@DotColorStyle @Style"></span>
<span class="@DotClassMapper.Class" style="@DotColorStyle @Style"></span>
@if (!string.IsNullOrEmpty(Text))
if (!string.IsNullOrEmpty(Text))
{
<span class="ant-badge-status-text">@Text</span>
}
@ -27,9 +27,9 @@
<span class="ant-badge-status-text">@Text</span>
}
if (_finalShowSup)
@if (_finalShowSup)
{
<sup class="@CountClassMapper.Class" style="@CountStyle @DotColorStyle @Style" title="@Count">
<sup class="@CountClassMapper.Class" style="@CountStyle @DotColorStyle @Style" title="@(string.IsNullOrWhiteSpace(Title) ? Count.ToString() : Title)">
@if (!Dot && !HasStatusOrColor)
{
@if (_showOverflowCount)

View File

@ -1,4 +1,8 @@
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.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
@ -11,11 +15,14 @@ namespace AntDesign
public partial class Badge : AntDomComponentBase
{
/// <summary>
/// Customize Badge dot color
/// Customize Badge status dot color. Usage of this parameter will make the badge a status dot.
/// </summary>
[Parameter]
public string Color { get; set; }
/// <summary>
/// Set Badge status dot to a preset color. Usage of this parameter will make the badge a status dot.
/// </summary>
[Parameter]
public PresetColor? PresetColor { get; set; }
@ -37,16 +44,19 @@ namespace AntDesign
if (_count > 0)
{
this._countArray = DigitsFromInteger(_count.Value);
_countArray = DigitsFromInteger(_count.Value);
}
}
}
/// <summary>
/// Template to show in place of Count
/// </summary>
[Parameter]
public RenderFragment CountTemplate { get; set; }
/// <summary>
/// Whether to display a red dot instead of count
/// Whether to display a dot instead of count
/// </summary>
[Parameter]
public bool Dot { get; set; }
@ -87,23 +97,26 @@ namespace AntDesign
public bool ShowZero { get; set; } = false;
/// <summary>
/// Set Badge as a status dot
/// Set Badge dot to a status color. Usage of this parameter will make the badge a status dot.
/// </summary>
[Parameter]
public string Status { get; set; }
/// <summary>
/// If status is set, text sets the display text of the status dot
/// The display text next to the status dot
/// </summary>
[Parameter]
public string Text { get; set; }
/// <summary>
/// Text to show when hovering over the badge
/// Text to show when hovering over the badge. Defaults to the value of Count
/// </summary>
[Parameter]
public string Title { get; set; }
/// <summary>
/// Size of the badge
/// </summary>
[Parameter]
public string Size { get; set; }
@ -115,13 +128,19 @@ namespace AntDesign
private ClassMapper CountClassMapper { get; set; } = new ClassMapper();
private ClassMapper DotClassMapper { get; set; } = new ClassMapper();
private int[] _countArray = Array.Empty<int>();
private readonly int[] _countSingleArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
private char[] _maxNumberArray = Array.Empty<char>();
private string StatusOrPresetColor => Status.IsIn(_badgeStatusTypes) ? Status : (PresetColor.HasValue ? Enum.GetName(typeof(PresetColor), PresetColor) : "");
private string StatusOrPresetColor => Status.IsIn(_badgeStatusTypes)
? Status
: (PresetColor.HasValue
? Enum.GetName(typeof(PresetColor), PresetColor)
: "");
private bool HasStatusOrColor => !string.IsNullOrWhiteSpace(Status) || !string.IsNullOrWhiteSpace(Color) || PresetColor.HasValue;
@ -129,19 +148,31 @@ namespace AntDesign
private string DotColorStyle => (PresetColor == null && !string.IsNullOrWhiteSpace(Color) ? $"background:{Color};" : "");
private bool RealShowSup => (this.Dot && (!this.Count.HasValue || (this.Count > 0 || this.Count == 0 && this.ShowZero)))
|| this.Count > 0
|| (this.Count == 0 && this.ShowZero);
private bool RealShowSup => (Dot && (!Count.HasValue || (Count > 0 || Count == 0 && ShowZero)))
|| Count > 0
|| (Count == 0 && ShowZero);
private bool _dotEnter;
private bool _dotLeave;
private bool _finalShowSup;
private int? _count;
private int _overflowCount = 99;
private bool _showOverflowCount = false;
private static readonly string[] _badgeStatusTypes =
{
"success",
"processing",
"default",
"error",
"warning"
};
/// <summary>
/// Sets the default CSS classes.
/// </summary>
@ -166,6 +197,11 @@ namespace AntDesign
.If($"{prefixName}-zoom-leave {prefixName}-zoom-leave-active", () => _dotLeave)
.If($"{prefixName}-count-overflow", () => _showOverflowCount)
;
DotClassMapper.Clear()
.Add("ant-badge-status-dot")
.GetIf(() => $"ant-badge-status-{StatusOrPresetColor.ToLowerInvariant()}", () => !string.IsNullOrWhiteSpace(StatusOrPresetColor))
;
}
/// <summary>
@ -202,7 +238,9 @@ namespace AntDesign
}
if (showSupBefore == RealShowSup)
{
return;
}
if (RealShowSup)
{
@ -243,7 +281,7 @@ namespace AntDesign
private void GenerateMaxNumberArray()
{
this._maxNumberArray = _overflowCount.ToString(CultureInfo.InvariantCulture).ToCharArray();
_maxNumberArray = _overflowCount.ToString(CultureInfo.InvariantCulture).ToCharArray();
}
private static int[] DigitsFromInteger(int number)
@ -258,18 +296,11 @@ namespace AntDesign
}
if (n < 0)
{
digits[0] *= -1;
}
return digits;
}
private static readonly string[] _badgeStatusTypes =
{
"success",
"processing",
"default",
"error",
"warning"
};
}
}

View File

@ -50,11 +50,14 @@
</span>
}
</span>
<span class="ant-select-clear" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="ClearSelected" @onclick:stopPropagation>
<span role="img" aria-label="close-circle" class="anticon anticon-close-circle">
<svg viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>
</span>
</span>
@if (AllowClear && _selectedNodes.Count > 0)
{
<span class="ant-select-clear" unselectable="on" aria-hidden="true" style="user-select: none;" @onclick="ClearSelected" @onclick:stopPropagation>
<span role="img" aria-label="close-circle" class="anticon anticon-close-circle">
<svg viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>
</span>
</span>
}
</div>
</Unbound>
<Overlay>

View File

@ -1,6 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
namespace AntDesign
{
@ -75,26 +75,18 @@ namespace AntDesign
public CssSizeLength(string value)
{
value = value?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(value));
_stringValue = value;
_parsed = true;
if (value.StartsWith("calc", StringComparison.OrdinalIgnoreCase))
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(value);
#else
if (value == null)
{
_stringValue = value;
_value = null;
_unit = CssSizeLengthUnit.NoUnit;
return;
throw new ArgumentNullException(nameof(value));
}
var index = value
.Select((c, i) => ((char c, int i)?)(c, i))
.FirstOrDefault(x => !(x.Value.c == '.' || x.Value.c >= '0' && x.Value.c <= '9'))
?.i
?? value.Length;
if (index == 0)
#endif
value = value.Trim().ToLowerInvariant();
if (value.Length < 1)
{
// Should it throw an ArgumentException?
_stringValue = value;
_value = null;
_unit = CssSizeLengthUnit.Calc;
@ -102,33 +94,45 @@ namespace AntDesign
return;
}
_value = decimal.Parse(value.Substring(0, index), CultureInfo.InvariantCulture);
_stringValue = value;
_parsed = true;
if (index == value.Length)
if (TryResolveByPrefix(value, out var unit))
{
_unit = CssSizeLengthUnit.Px;
_stringValue = null;
if (unit == CssSizeLengthUnit.NoUnit)
{
_parsed = false;
}
_value = null;
_unit = unit;
return;
}
var unit = value.Substring(index).Trim();
if (!Enum.TryParse(unit, ignoreCase: true, out _unit))
_value = ResolveBySuffix(value, out unit);
switch (unit)
{
_unit = unit switch
{
"%" => CssSizeLengthUnit.Percent,
_ => CssSizeLengthUnit.NoUnit,
};
case CssSizeLengthUnit.NoUnit:
_parsed = false;
break;
case CssSizeLengthUnit.Px:
if (IsNumberOrDot(value[^1]))
{
_stringValue = null;
}
break;
}
_unit = unit;
}
public static implicit operator CssSizeLength(int value) => new CssSizeLength(value);
public static implicit operator CssSizeLength(int value) => new(value);
public static implicit operator CssSizeLength(double value) => new CssSizeLength(value);
public static implicit operator CssSizeLength(double value) => new(value);
public static implicit operator CssSizeLength(decimal value) => new CssSizeLength(value);
public static implicit operator CssSizeLength(decimal value) => new(value);
public static implicit operator CssSizeLength(string value) => new CssSizeLength(value);
public static implicit operator CssSizeLength(string value) => new(value);
public override bool Equals(object obj) => obj is CssSizeLength other && Equals(other);
@ -145,5 +149,66 @@ namespace AntDesign
cssSizeLength = new(value);
return cssSizeLength._parsed;
}
private static bool TryResolveByPrefix(string trimmedValue, out CssSizeLengthUnit unit)
{
if (trimmedValue.StartsWith("calc", StringComparison.OrdinalIgnoreCase))
{
unit = CssSizeLengthUnit.Calc;
return true;
}
if (!IsNumberOrDot(trimmedValue[0]))
{
unit = CssSizeLengthUnit.NoUnit;
return true;
}
unit = default;
return false;
}
private static decimal ResolveBySuffix(string trimmedValue, out CssSizeLengthUnit unit)
{
// Notice: before this method, we've made sure that at least the first char is number or dot.
// So all the logics below are based on this promise.
// Also, we don't need to handle all kinds of input ("1@1", "1@1%", "1 px", ".1.1" for example).
// Let `decimal.Parse` decides whether throws a format exception if the input is out of expect.
if (trimmedValue[^1] == '%')
{
unit = CssSizeLengthUnit.Percent;
return decimal.Parse(trimmedValue.AsSpan(0, trimmedValue.Length - 1), provider: CultureInfo.InvariantCulture);
}
if (IsNumberOrDot(trimmedValue[^1]))
{
unit = CssSizeLengthUnit.Px;
return decimal.Parse(trimmedValue, provider: CultureInfo.InvariantCulture);
}
var index = trimmedValue.Length - 2;
while (index >= 0)
{
if (IsNumberOrDot(trimmedValue[index]))
{
break;
}
index--;
}
#if NET6_0_OR_GREATER
if (!Enum.TryParse(trimmedValue.AsSpan(index + 1), ignoreCase: true, out unit))
#else
if (!Enum.TryParse(trimmedValue[(index + 1)..], ignoreCase: true, out unit))
#endif
{
unit = CssSizeLengthUnit.NoUnit;
}
return decimal.Parse(trimmedValue.AsSpan(0, index + 1), provider: CultureInfo.InvariantCulture);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsNumberOrDot(char c) => (c >= '0' && c <= '9') || c == '.';
}
}

View File

@ -0,0 +1,31 @@
// 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.Linq;
using System.Text;
namespace AntDesign.Core.Extensions
{
public static class ArrayExtensions
{
/// <summary>
/// scroll left the elements
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sourceArray"></param>
/// <param name="offset"></param>
/// <returns></returns>
public static T[] Scroll<T>(this T[] sourceArray, int offset)
{
return offset switch
{
0 => sourceArray,
> 0 => sourceArray[offset..].Concat(sourceArray[..offset]).ToArray(),
< 0 => sourceArray[^-offset..].Concat(sourceArray[..^-offset]).ToArray()
};
}
}
}

View File

@ -0,0 +1,32 @@
// 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.Core.Extensions
{
public static class ElementReferenceExtensions
{
public static string GetSelector(this ElementReference? elementReference)
{
return elementReference?.GetSelector();
}
public static string GetSelector(this ElementReference elementReference)
{
#if NET5_0_OR_GREATER
if (elementReference.Context is null)
{
return null;
}
return $"[_bl_{elementReference.Id}]";
#endif
return $"[_bl_{elementReference.Id}]";
}
}
}

View File

@ -6,27 +6,28 @@ namespace AntDesign
{
public class ClassMapper
{
public string Class => AsString();
private readonly Dictionary<Func<string>, Func<bool>> _map = new();
internal string OriginalClass { get; set; }
public string AsString()
public ClassMapper() : this(string.Empty)
{
return string.Join(" ", _map.Where(i => i.Value()).Select(i => i.Key()));
}
public override string ToString()
public ClassMapper(string originalClass)
{
return AsString();
OriginalClass = originalClass;
_map.Add(() => OriginalClass, () => !string.IsNullOrEmpty(OriginalClass));
}
private readonly Dictionary<Func<string>, Func<bool>> _map = new Dictionary<Func<string>, Func<bool>>();
public string Class => ToString();
public ClassMapper Add(string name)
{
_map.Add(() => name, () => true);
return this;
}
public string OriginalClass { get; internal set; }
[Obsolete("Use ToString() instead")]
public string AsString() => ToString();
public override string ToString() => string.Join(' ', _map.Where(i => i.Value()).Select(i => i.Key()));
public ClassMapper Add(string name) => Get(() => name);
public ClassMapper Get(Func<string> funcName)
{
@ -40,11 +41,7 @@ namespace AntDesign
return this;
}
public ClassMapper If(string name, Func<bool> func)
{
_map.Add(() => name, func);
return this;
}
public ClassMapper If(string name, Func<bool> func) => GetIf(() => name, func);
public ClassMapper Clear()
{

View File

@ -2,9 +2,7 @@
// 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
{
@ -12,7 +10,7 @@ namespace AntDesign
{
public static string GetBackgroundStyle(Color color) => _colors[color];
private static Dictionary<Color, string> _colors = new Dictionary<Color, string>()
private static readonly Dictionary<Color, string> _colors = new()
{
{ Color.None, "" },
{ Color.Red1, "background-color: #fff1f0; border-color: #fff1f0; color: rgba(0,0,0,0.85);" },

View File

@ -5,11 +5,10 @@ namespace AntDesign
{
public static class ComponentBaseExtensions
{
public static bool ParameterIsChanged<T>(this ComponentBase cmp, ParameterView parameters,
public static bool ParameterIsChanged<T>(this ComponentBase _, ParameterView parameters,
string parameterName, T value)
{
T newValue;
if (parameters.TryGetValue(parameterName, out newValue))
if (parameters.TryGetValue(parameterName, out T newValue))
{
if (!EqualityComparer<T>.Default.Equals(value, newValue))
{

View File

@ -2,34 +2,54 @@
// 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.Collections.Generic;
using System.Text;
namespace AntDesign
{
public class CssStyleBuilder
{
private readonly List<string> _styles = new();
// An example below:
// "font-size: 10px; color: red; background-color: white; "
private const string StyleKeyValuePairSplitString = ": ";
public void AddStyle(string style)
{
_styles.Add(style.Trim().TrimEnd(';'));
}
private const string StyleItemSplitString = "; ";
public void AddStyle(string styleName, string styleValue)
{
_styles.Add($"{styleName}: {styleValue}");
}
private readonly StringBuilder _stringBuilder = new();
public string Build()
public CssStyleBuilder AddStyle(string style)
{
StringBuilder totalStyle = new StringBuilder();
foreach (string style in _styles)
if (style.EndsWith(StyleItemSplitString[0]))
{
totalStyle.Append(style).Append("; ");
_stringBuilder.Append(style)
.Append(StyleItemSplitString[1]);
}
else if (style.EndsWith(StyleItemSplitString, System.StringComparison.Ordinal))
{
_stringBuilder.Append(style);
}
else
{
_stringBuilder.Append(style.Trim().TrimEnd(';'))
.Append(StyleItemSplitString);
}
return this;
}
return totalStyle.ToString();
public CssStyleBuilder AddStyle(string styleName, string styleValue)
{
_stringBuilder.Append(styleName)
.Append(StyleKeyValuePairSplitString)
.Append(styleValue)
.Append(StyleItemSplitString);
return this;
}
public string Build() => _stringBuilder.ToString();
public CssStyleBuilder Clear()
{
_stringBuilder.Clear();
return this;
}
}
}

View File

@ -268,5 +268,15 @@ namespace AntDesign
return currentDate.AddDays(value);
}
public static bool IsSameDecade(DateTime? date, DateTime? compareDate)
{
if (date is null || compareDate is null)
return false;
var num1 = Math.Floor(date.Value.Year / 10d);
var num2 = Math.Floor(compareDate.Value.Year / 10d);
return num1 == num2;
}
}
}

View File

@ -15,9 +15,9 @@ namespace AntDesign
{
private static readonly Func<T, T, T> _aggregateFunction;
private static IEnumerable<T> _valueList;
private static IEnumerable<(T Value, string Label)> _valueLabelList;
private static Type _enumType;
private static readonly IEnumerable<T> _valueList;
private static readonly IEnumerable<(T Value, string Label)> _valueLabelList;
private static readonly Type _enumType;
static EnumHelper()
{
@ -30,19 +30,21 @@ namespace AntDesign
// There is no constraint or type check for type parameter T, be sure that T is an enumeration type
public static object Combine(IEnumerable<T> enumValues)
{
if (enumValues?.Count() > 0)
if (enumValues?.Any() == true)
{
return enumValues.Aggregate(_aggregateFunction);
}
else
{
return null;
}
return null;
}
public static IEnumerable<T> Split(object enumValue)
{
return enumValue?.ToString().Split(',').Select(x => (T)Enum.Parse(_enumType, x)) ?? Array.Empty<T>();
var str = enumValue?.ToString();
if (string.IsNullOrEmpty(str))
{
return Array.Empty<T>();
}
return str.Split(',').Select(x => (T)Enum.Parse(_enumType, x)).ToArray();
}
public static IEnumerable<T> GetValueList()

View File

@ -3,15 +3,13 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
namespace AntDesign.Core.Helpers
{
public static class JsonElementHelper<TValue>
{
static Type _columnDataType;
private static readonly Type _columnDataType;
static JsonElementHelper()
{
@ -20,15 +18,15 @@ namespace AntDesign.Core.Helpers
public static object GetValue(JsonElement jsonElement)
{
if (_columnDataType == typeof(DateTime))
return jsonElement.GetDateTime();
else if (_columnDataType.IsEnum)
if (_columnDataType.IsEnum)
{
if (jsonElement.ValueKind == JsonValueKind.Number)
return Enum.Parse(THelper.GetUnderlyingType<TValue>(), jsonElement.GetInt32().ToString());
else
return Enum.Parse(THelper.GetUnderlyingType<TValue>(), jsonElement.GetString());
}
if (_columnDataType == typeof(DateTime))
return jsonElement.GetDateTime();
else if (_columnDataType == typeof(byte))
return jsonElement.GetByte();
else if (_columnDataType == typeof(decimal))
@ -54,12 +52,7 @@ namespace AntDesign.Core.Helpers
else if (_columnDataType == typeof(Guid))
return jsonElement.GetGuid();
else if (_columnDataType == typeof(bool))
{
if (jsonElement.ValueKind == JsonValueKind.True)
return true;
else
return false;
}
return jsonElement.ValueKind == JsonValueKind.True;
else
return jsonElement.GetString();
}

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace AntDesign
{
@ -12,7 +9,7 @@ namespace AntDesign
private const int DaysPerWeek = 7;
private const int HoursPerDay = 24;
public static string FromNow(DateTime time )
public static string FromNow(DateTime time)
{
var timespan = DateTime.Now - time;

View File

@ -3,9 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace AntDesign
{
@ -24,7 +22,7 @@ namespace AntDesign
{
if (value == null)
{
return default(T);
return default;
}
t = Nullable.GetUnderlyingType(t);
@ -41,7 +39,7 @@ namespace AntDesign
public static Type GetNullableType<T>()
{
Type type = typeof(T);
var type = typeof(T);
type = Nullable.GetUnderlyingType(type) ?? type;
if (type.IsValueType)
return typeof(Nullable<>).MakeGenericType(type);
@ -51,7 +49,7 @@ namespace AntDesign
public static Type GetUnderlyingType<T>()
{
Type type = typeof(T);
var type = typeof(T);
Type targetType;
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
@ -72,22 +70,21 @@ namespace AntDesign
return false;
}
switch (Type.GetTypeCode(type))
return Type.GetTypeCode(type) switch
{
case TypeCode.Byte:
case TypeCode.Decimal:
case TypeCode.Double:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
case TypeCode.Single:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
}
return false;
TypeCode.Byte
or TypeCode.Decimal
or TypeCode.Double
or TypeCode.Int16
or TypeCode.Int32
or TypeCode.Int64
or TypeCode.SByte
or TypeCode.Single
or TypeCode.UInt16
or TypeCode.UInt32
or TypeCode.UInt64 => true,
_ => false,
};
}
}
}

View File

@ -3,10 +3,13 @@ using Microsoft.AspNetCore.Components;
namespace AntDesign
{
#if NET7_0_OR_GREATER
#else
[EventHandler("onmouseleave", typeof(EventArgs))]
[EventHandler("onmouseenter", typeof(EventArgs))]
public static class EventHandlers
{
}
#endif
}

View File

@ -12,7 +12,8 @@ namespace AntDesign.JsInterop
{
public class DomEventListener : IDomEventListener
{
private Dictionary<string, IDisposable> _dotNetObjectStore = new();
private Dictionary<DomEventKey, IDisposable> _exclusiveDotNetObjectStore = new();
private Dictionary<DomEventKey, IDisposable> _sharedDotNetObjectStore = new();
private bool? _isResizeObserverSupported = null;
private readonly IJSRuntime _jsRuntime;
@ -27,20 +28,26 @@ namespace AntDesign.JsInterop
_id = Guid.NewGuid().ToString();
}
private string FormatKey(object dom, string eventName)
private DomEventKey FormatKey(object dom, string eventName)
{
var selector = dom is ElementReference eleRef ? eleRef.Id : dom.ToString();
var selector = dom is ElementReference eleRef ? eleRef.GetSelector() : dom.ToString();
if (selector.IsIn("window", "document"))
{
return $"DEL-{selector}-{eventName}";
return new DomEventKey(selector, eventName, "");
}
return $"DEL-{_id}-{selector}-{eventName}";
return new DomEventKey(selector, eventName, _id);
}
private (string dom, string eventName) ParseKey(string key)
{
var keyParts = key.Split('-');
return (keyParts[1], keyParts[2]);
}
public void AddExclusive<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false)
{
var key = FormatKey(dom, eventName);
if (_dotNetObjectStore.ContainsKey(key))
if (_exclusiveDotNetObjectStore.ContainsKey(key))
return;
var dotNetObject = DotNetObjectReference.Create(new Invoker<T>((p) =>
@ -48,33 +55,33 @@ namespace AntDesign.JsInterop
callback(p);
}));
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, dotNetObject);
_dotNetObjectStore.Add(key, dotNetObject);
_exclusiveDotNetObjectStore.Add(key, dotNetObject);
}
public void RemoveExclusive(object dom, string eventName)
{
var key = FormatKey(dom, eventName);
if (_dotNetObjectStore.TryGetValue(key, out IDisposable value))
if (_exclusiveDotNetObjectStore.TryGetValue(key, out IDisposable dotNetObject))
{
value?.Dispose();
_jsRuntime.InvokeVoidAsync(JSInteropConstants.RemoveDomEventListener, dom, eventName, dotNetObject);
}
_dotNetObjectStore.Remove(key);
_exclusiveDotNetObjectStore.Remove(key);
}
public void DisposeExclusive()
{
foreach (var (k, v) in _dotNetObjectStore)
foreach (var (key, dotNetObject) in _exclusiveDotNetObjectStore)
{
v?.Dispose();
_jsRuntime.InvokeVoidAsync(JSInteropConstants.RemoveDomEventListener, key.Selector, key.EventName, dotNetObject);
}
_dotNetObjectStore.Clear();
_exclusiveDotNetObjectStore.Clear();
}
#region SharedEventListerner
public virtual void AddShared<T>(object dom, string eventName, Action<T> callback, bool preventDefault = false)
{
string key = FormatKey(dom, eventName);
var key = FormatKey(dom, eventName);
if (!_domEventSubscriptionsStore.ContainsKey(key))
{
_domEventSubscriptionsStore[key] = new List<DomEventSubscription>();
@ -89,14 +96,16 @@ namespace AntDesign.JsInterop
}
}));
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, dotNetObject);
_sharedDotNetObjectStore.Add(key, dotNetObject);
_jsRuntime.InvokeVoidAsync(JSInteropConstants.AddDomEventListener, dom, eventName, preventDefault, dotNetObject);
}
_domEventSubscriptionsStore[key].Add(new DomEventSubscription(callback, typeof(T), _id));
}
public void RemoveShared<T>(object dom, string eventName, Action<T> callback)
{
string key = FormatKey(dom, eventName);
var key = FormatKey(dom, eventName);
if (_domEventSubscriptionsStore.ContainsKey(key))
{
var subscription = _domEventSubscriptionsStore[key].SingleOrDefault(s => s.Delegate == (Delegate)callback);
@ -114,9 +123,16 @@ namespace AntDesign.JsInterop
while (find)
{
var (key, subscription) = _domEventSubscriptionsStore.FindDomEventSubscription(_id);
if (!string.IsNullOrEmpty(key) && subscription != null)
if (key != null && subscription != null)
{
_domEventSubscriptionsStore[key].Remove(subscription);
if (_domEventSubscriptionsStore[key].Count == 0 && _sharedDotNetObjectStore.ContainsKey(key))
{
var dotNetObject = _sharedDotNetObjectStore[key];
_jsRuntime.InvokeVoidAsync(JSInteropConstants.RemoveDomEventListener, key.Selector, key.EventName, dotNetObject);
}
}
else
find = false;
@ -129,7 +145,7 @@ namespace AntDesign.JsInterop
public async ValueTask AddResizeObserver(ElementReference dom, Action<List<ResizeObserverEntry>> callback)
{
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
var key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
if (!(await IsResizeObserverSupported()))
{
Action<JsonElement> action = (je) => callback.Invoke(new List<ResizeObserverEntry> { new ResizeObserverEntry() });
@ -157,7 +173,7 @@ namespace AntDesign.JsInterop
public async ValueTask RemoveResizeObserver(ElementReference dom, Action<List<ResizeObserverEntry>> callback)
{
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
var key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
if (_domEventSubscriptionsStore.ContainsKey(key))
{
var subscription = _domEventSubscriptionsStore[key].SingleOrDefault(s => s.Delegate == (Delegate)callback);
@ -166,11 +182,13 @@ namespace AntDesign.JsInterop
_domEventSubscriptionsStore[key].Remove(subscription);
}
}
await Task.CompletedTask;
}
public async ValueTask DisposeResizeObserver(ElementReference dom)
{
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
var key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
if (await IsResizeObserverSupported())
{
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Dispose, key);
@ -180,7 +198,7 @@ namespace AntDesign.JsInterop
public async ValueTask DisconnectResizeObserver(ElementReference dom)
{
string key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
var key = FormatKey(dom.Id, nameof(JSInteropConstants.ObserverConstants.Resize));
if (await IsResizeObserverSupported())
{
await _jsRuntime.InvokeVoidAsync(JSInteropConstants.ObserverConstants.Resize.Disconnect, key);
@ -218,7 +236,7 @@ namespace AntDesign.JsInterop
private void AddEventListenerToFirstChildInternal<T>(object dom, string eventName, bool preventDefault, Action<T> callback)
{
var key = FormatKey(dom, eventName);
if (!_dotNetObjectStore.ContainsKey(key))
if (!_exclusiveDotNetObjectStore.ContainsKey(key))
{
var dotNetObject = DotNetObjectReference.Create(new Invoker<T>((p) =>
{
@ -226,7 +244,7 @@ namespace AntDesign.JsInterop
}));
_jsRuntime.InvokeAsync<string>(JSInteropConstants.AddDomEventListenerToFirstChild, dom, eventName, preventDefault, dotNetObject);
_dotNetObjectStore.Add(key, dotNetObject);
_exclusiveDotNetObjectStore.Add(key, dotNetObject);
}
}

View File

@ -1,14 +1,15 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Sockets;
namespace AntDesign.JsInterop
{
public class DomEventSubscriptionStore : ConcurrentDictionary<string, List<DomEventSubscription>>
public class DomEventSubscriptionStore : ConcurrentDictionary<DomEventKey, List<DomEventSubscription>>
{
public (string key, DomEventSubscription subscription) FindDomEventSubscription(string id)
public (DomEventKey key, DomEventSubscription subscription) FindDomEventSubscription(string id)
{
string key = string.Empty;
DomEventKey key = null;
DomEventSubscription subscription = null;
foreach (var (k, subscriptionList) in this)
@ -44,4 +45,7 @@ namespace AntDesign.JsInterop
Id = id;
}
}
public record DomEventKey(string Selector, string EventName, string ListenerId);
}

View File

@ -72,6 +72,7 @@ namespace AntDesign
public static string TriggerEvent => EventHelper.TriggerEvent;
public static string AddDomEventListener => EventHelper.AddDomEventListener;
public static string RemoveDomEventListener => EventHelper.RemoveDomEventListener;
public static string AddDomEventListenerToFirstChild => EventHelper.AddDomEventListenerToFirstChild;
public static string AddPreventKeys => EventHelper.AddPreventKeys;
public static string RemovePreventKeys => EventHelper.RemovePreventKeys;
@ -158,6 +159,7 @@ namespace AntDesign
private const string FUNC_PREFIX = JSInteropConstants.FUNC_PREFIX + "eventHelper.";
public static string TriggerEvent => $"{FUNC_PREFIX}triggerEvent";
public static string AddDomEventListener => $"{FUNC_PREFIX}addDomEventListener";
public static string RemoveDomEventListener => $"{FUNC_PREFIX}removeDomEventListener";
public static string AddDomEventListenerToFirstChild => $"{FUNC_PREFIX}addDomEventListenerToFirstChild";
public static string AddPreventKeys => $"{FUNC_PREFIX}addPreventKeys";
public static string RemovePreventKeys => $"{FUNC_PREFIX}removePreventKeys";

View File

@ -72,7 +72,8 @@ export class uploadHelper {
if (req.readyState === 4) {
// #1655 Any 2xx response code is okay
if (req.status < 200 || req.status > 299) {
instance.invokeMethodAsync(errorMethod, fileId, `{"status": ${req.status}}`);
// #2857 should get error raw response
instance.invokeMethodAsync(errorMethod, fileId, req.responseText);
return;
}
instance.invokeMethodAsync(successMethod, fileId, req.responseText);

View File

@ -23,24 +23,24 @@ export class eventHelper {
if (v instanceof Window) return 'Window';
return v;
}, ' ');
setTimeout(function () { invoker.invokeMethodAsync('Invoke', json) }, 0);
if (preventDefault === true) {
args.preventDefault();
}
};
if (element === 'window') {
if (eventName === 'resize') {
window.addEventListener(eventName, this.debounce(() => callback({ innerWidth: window.innerWidth, innerHeight: window.innerHeight }), 200, false));
} else {
window.addEventListener(eventName, callback);
}
const dom = domInfoHelper.get(element);
const key = `${eventName}-${invoker._id}`;
if (eventName === 'resize') {
dom[`e_${key}`] = this.debounce(() => callback({ innerWidth: window.innerWidth, innerHeight: window.innerHeight }), 200, false);
} else {
const dom = domInfoHelper.get(element);
if (dom) {
(dom as HTMLElement).addEventListener(eventName, callback);
}
dom[`e_${key}`] = callback;
}
dom[`i_${key}`] = invoker;
(dom as HTMLElement).addEventListener(eventName, dom[`e_${key}`]);
}
static addDomEventListenerToFirstChild(element, eventName, preventDefault, invoker) {
@ -51,6 +51,16 @@ export class eventHelper {
}
}
static removeDomEventListener(element, eventName: string, invoker) {
const dom = domInfoHelper.get(element);
const key = `${eventName}-${invoker._id}`;
if (dom) {
dom.removeEventListener(eventName, dom[`e_${key}`]);
dom[`i_${key}`].dispose();
}
}
static addPreventKeys(inputElement, keys: string[]) {
if (inputElement) {
const dom = domInfoHelper.get(inputElement);

View File

@ -12,7 +12,9 @@ export class infoHelper {
if (!element) {
element = document.body;
} else if (typeof element === 'string') {
if (element === 'document') {
if (element === 'window') {
return window;
} else if (element === 'document') {
return document;
}
element = document.querySelector(element!)

View File

@ -5,7 +5,7 @@ import * as enums from '../enums';
let cachedScrollBarSize: number | undefined = undefined;
const scrollIds = new Map<HTMLElement, number>();
export class manipulationHelper {
static addElementToBody(element) {
@ -147,27 +147,35 @@ export class manipulationHelper {
}
}
static smoothScrollTo(selector: Element | string, parentElement: HTMLElement, duration: number = 100) {
static smoothScrollTo(selector: Element | string, parentElement: HTMLElement, duration: number) {
const element = domInfoHelper.get(selector);
var to = element.offsetTop;
let to = element.offsetTop;
if (scrollIds.get(parentElement)) {
cancelAnimationFrame(scrollIds.get(parentElement)!);
}
// jump to target if duration zero
if (duration <= 0) {
window.requestAnimationFrame(() => {
parentElement.scrollTop = to;
});
scrollIds.set(
parentElement,
requestAnimationFrame(() => {
parentElement.scrollTop = to;
}),
);
return;
}
const difference = to - parentElement.scrollTop;
const perTick = (difference / duration) * 10;
const animateScroll = () => {
const tick = ((to - parentElement.scrollTop) / duration) * 10;
window.requestAnimationFrame(() => {
parentElement.scrollTop += tick;
if (parentElement.scrollTop === to) return;
duration -= 10;
animateScroll();
});
};
setTimeout(() => animateScroll(), 10);
scrollIds.set(
parentElement,
requestAnimationFrame(() => {
parentElement.scrollTop += perTick;
if (parentElement.scrollTop !== to) {
manipulationHelper.smoothScrollTo(selector, parentElement, duration - 10);
}
}),
);
}
static slideTo(targetPageY) {

View File

@ -80,8 +80,6 @@ namespace AntDesign
}
}
private TValue _cacheDuringInput;
protected void OnInput(ChangeEventArgs args, int index = 0)
{
if (index != 0)
@ -95,12 +93,15 @@ namespace AntDesign
if (!_duringManualInput)
{
_duringManualInput = true;
_cacheDuringInput = Value;
}
if (FormatAnalyzer.TryPickerStringConvert(args.Value.ToString(), out TValue changeValue, IsNullable))
{
CurrentValue = changeValue;
_cacheDuringInput = changeValue;
GetIfNotNull(changeValue, parsed =>
{
_pickerStatus[0].SelectedValue = parsed;
ChangePickerValue(parsed, index);
});
}
}
@ -109,17 +110,9 @@ namespace AntDesign
if (_openingOverlay)
return;
if (_duringManualInput)
{
if (!Value.Equals(_cacheDuringInput))
{
//reset picker to Value
CurrentValue = _cacheDuringInput;
}
_duringManualInput = false;
}
AutoFocus = false;
if (!_dropDown.IsOverlayShow())
_pickerStatus[0].SelectedValue = null;
await Task.Yield();
}
@ -131,57 +124,49 @@ namespace AntDesign
{
if (e == null) throw new ArgumentNullException(nameof(e));
var key = e.Key.ToUpperInvariant();
if (key == "ENTER" || key == "TAB" || key == "ESCAPE")
var isEnter = key == "ENTER";
var isTab = key == "TAB";
var isEscape = key == "ESCAPE";
var isOverlayShown = _dropDown.IsOverlayShow();
if (isEnter || isTab || isEscape)
{
_duringManualInput = false;
if (string.IsNullOrWhiteSpace(_inputStart.Value))
ClearValue();
else
await TryApplyInputValue();
if (key == "ESCAPE" && _dropDown.IsOverlayShow())
if (isEscape && isOverlayShown)
{
Close();
await Js.FocusAsync(_inputStart.Ref);
return;
}
if (key == "ENTER")
else if (isEnter || isTab)
{
//needed only in wasm, details: https://github.com/dotnet/aspnetcore/issues/30070
await Task.Yield();
await Js.InvokeVoidAsync(JSInteropConstants.InvokeTabKey);
if (HasTimeInput && _pickerStatus[0].SelectedValue is not null)
{
await OnOkClick();
}
else if (_pickerStatus[0].SelectedValue is not null)
{
await OnSelect(_pickerStatus[0].SelectedValue.Value, 0);
}
else if (isOverlayShown)
{
Close();
}
else if (!isTab)
{
await _dropDown.Show();
}
}
Close();
AutoFocus = false;
return;
}
if (key == "ARROWDOWN" && !_dropDown.IsOverlayShow())
else if (key == "ARROWUP")
{
if (isOverlayShown)
Close();
}
else if (!isOverlayShown)
{
await _dropDown.Show();
return;
}
if (key == "ARROWUP" && _dropDown.IsOverlayShow())
{
Close();
return;
}
}
private async Task TryApplyInputValue()
{
if (FormatAnalyzer.TryPickerStringConvert(_inputStart.Value, out TValue changeValue, IsNullable))
{
CurrentValue = changeValue;
if (OnChange.HasDelegate)
{
await OnChange.InvokeAsync(new DateTimeChangedEventArgs
{
Date = Convert.ToDateTime(changeValue, this.CultureInfo),
DateString = GetInputValue(0)
});
}
}
}
@ -196,6 +181,12 @@ namespace AntDesign
{
throw new ArgumentOutOfRangeException("DatePicker should have only single picker.");
}
if (_pickerStatus[index].SelectedValue is not null)
{
return _pickerStatus[index].SelectedValue;
}
if (_pickerStatus[0].IsValueSelected)
{
if (Value == null)
@ -213,28 +204,29 @@ namespace AntDesign
return null;
}
public override void ChangeValue(DateTime value, int index = 0)
public override void ChangeValue(DateTime value, int index = 0, bool closeDropdown = true)
{
if (index != 0)
{
throw new ArgumentOutOfRangeException("DatePicker should have only single picker.");
}
UseDefaultPickerValue[0] = false;
CurrentValue = THelper.ChangeType<TValue>(value);
if (!IsShowTime && Picker != DatePickerType.Time)
if (closeDropdown && !IsShowTime && Picker != DatePickerType.Time)
{
Close();
}
if (OnChange.HasDelegate)
var currentValue = CurrentValue is not null ?
Convert.ToDateTime(CurrentValue, CultureInfo)
: (DateTime?)null;
if (currentValue != value)
{
OnChange.InvokeAsync(new DateTimeChangedEventArgs
{
Date = value,
DateString = GetInputValue(0)
});
CurrentValue = THelper.ChangeType<TValue>(value);
_ = InvokeOnChange();
}
}
@ -273,6 +265,13 @@ namespace AntDesign
});
_dropDown.SetShouldRender(true);
OnChange.InvokeAsync(new DateTimeChangedEventArgs
{
Date = GetIndexValue(0),
DateString = GetInputValue(0)
});
}
private void GetIfNotNull(TValue value, Action<DateTime> notNullAction)
@ -303,5 +302,14 @@ namespace AntDesign
await Focus();
await OnInputClick();
}
private async Task InvokeOnChange()
{
await OnChange.InvokeAsync(new DateTimeChangedEventArgs
{
Date = GetIndexValue(0),
DateString = GetInputValue(0)
});
}
}
}

View File

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AntDesign.Core.Extensions;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
namespace AntDesign
{
@ -58,7 +56,7 @@ namespace AntDesign
}
}
private DateTime[] _pickerValuesAfterInit = new DateTime[2];
private readonly DateTime[] _pickerValuesAfterInit = new DateTime[2];
[Parameter]
public EventCallback<DateRangeChangedEventArgs> OnChange { get; set; }
@ -73,15 +71,13 @@ namespace AntDesign
DisabledDate = (date) =>
{
var array = Value as Array;
int? index = null;
if (_pickerStatus[0].IsValueSelected && _inputEnd.IsOnFocused)
if (_inputEnd.IsOnFocused && GetIndexValue(0) is not null)
{
index = 0;
}
else if (_pickerStatus[1].IsValueSelected && _inputStart.IsOnFocused)
else if (_inputStart.IsOnFocused && GetIndexValue(1) is not null)
{
index = 1;
}
@ -91,12 +87,12 @@ namespace AntDesign
return false;
}
DateTime? value = null;
GetIfNotNull(Value, index.Value, notNullValue =>
if (_pickerStatus[index.Value].SelectedValue is null)
{
value = notNullValue;
});
return false;
}
DateTime? value = GetIndexValue(index.Value);
if (value is null)
{
@ -138,13 +134,21 @@ namespace AntDesign
if (currentValue is not null)
{
if (IsShowTime)
if (index == 0 || IsShowTime)
{
PickerValues[index] = currentValue.Value;
}
else
{
PickerValues[index] = index == 0 ? currentValue.Value : GetClosingDate(currentValue.Value, -1);
var otherValue = GetIndexValue(Math.Abs(index - 1));
PickerValues[index] = Picker switch
{
DatePickerType.Year when DateHelper.IsSameDecade(currentValue, otherValue) => currentValue.Value,
DatePickerType.Week or DatePickerType.Date when DateHelper.IsSameMonth(currentValue, otherValue) => currentValue.Value,
DatePickerType.Quarter or DatePickerType.Month when DateHelper.IsSameYear(currentValue, otherValue) => currentValue.Value,
_ => GetClosingDate(currentValue.Value, -1)
};
}
}
else if (UseDefaultPickerValue[index] && DefaultPickerValue is not null)
@ -186,35 +190,22 @@ namespace AntDesign
}
}
private DateTime? _cacheDuringInput;
private DateTime _pickerValueCache;
protected void OnInput(ChangeEventArgs args, int index = 0)
{
if (args == null)
{
return;
}
var array = Value as Array;
if (!_duringManualInput)
{
_duringManualInput = true;
_cacheDuringInput = array.GetValue(index) as DateTime?;
_pickerValueCache = PickerValues[index];
}
if (FormatAnalyzer.TryPickerStringConvert(args.Value.ToString(), out DateTime changeValue, false)
&& IsValidRange(changeValue, index, array))
if (FormatAnalyzer.TryPickerStringConvert(args.Value.ToString(), out DateTime parsedValue, false))
{
array.SetValue(changeValue, index);
_cacheDuringInput = changeValue;
ChangePickerValue(changeValue, index);
if (_isNotifyFieldChanged && (Form?.ValidateOnChange == true))
{
EditContext?.NotifyFieldChanged(FieldIdentifier);
}
StateHasChanged();
_pickerStatus[index].SelectedValue = parsedValue;
ChangePickerValue(parsedValue, index);
}
}
@ -228,7 +219,13 @@ namespace AntDesign
if (e == null) throw new ArgumentNullException(nameof(e));
var key = e.Key.ToUpperInvariant();
if (key == "ENTER" || key == "TAB" || key == "ESCAPE")
var isEnter = key == "ENTER";
var isTab = key == "TAB";
var isEscape = key == "ESCAPE";
var isOverlayShown = _dropDown.IsOverlayShow();
if (isEnter || isTab || isEscape)
{
if (_duringManualInput)
{
@ -240,111 +237,57 @@ namespace AntDesign
_duringManualInput = false;
}
var input = (index == 0 ? _inputStart : _inputEnd);
if (string.IsNullOrWhiteSpace(input.Value))
{
ClearValue(index, false);
}
else if (!await TryApplyInputValue(index, input.Value))
return;
if (key == "ESCAPE" && _dropDown.IsOverlayShow())
if (isEnter || isTab)
{
if (HasTimeInput && _pickerStatus[index].SelectedValue is not null)
{
await OnOkClick();
}
else if (_pickerStatus[index].SelectedValue is not null)
{
await OnSelect(_pickerStatus[index].SelectedValue.Value, index);
}
else if (isOverlayShown)
{
if (_pickerStatus[index].SelectedValue is null && _pickerStatus[index].IsValueSelected)
{
_pickerStatus[index].SelectedValue = GetIndexValue(index);
}
if (isTab || !await SwitchFocus(index))
{
Close();
if (isTab && index == 1)
{
AutoFocus = false;
}
}
}
else if (!isTab)
{
await _dropDown.Show();
}
}
else if (isEscape && isOverlayShown)
{
Close();
await Js.FocusAsync(input.Ref);
return;
}
if (index == 1)
{
if (key != "TAB")
{
//needed only in wasm, details: https://github.com/dotnet/aspnetcore/issues/30070
await Task.Yield();
await Js.InvokeVoidAsync(JSInteropConstants.InvokeTabKey);
Close();
}
else if (!e.ShiftKey)
{
Close();
AutoFocus = false;
}
}
if (index == 0)
{
if (key == "TAB" && e.ShiftKey)
{
Close();
AutoFocus = false;
}
else if (key != "TAB")
{
await Blur(0);
await Focus(1);
}
}
return;
}
if (key == "ARROWDOWN" && !_dropDown.IsOverlayShow())
else if (key == "ARROWUP")
{
if (isOverlayShown)
{
Close();
AutoFocus = true;
}
}
else if (!isOverlayShown)
await _dropDown.Show();
return;
}
if (key == "ARROWUP" && _dropDown.IsOverlayShow())
{
Close();
await Task.Yield();
AutoFocus = true;
return;
}
}
private async Task<bool> TryApplyInputValue(int index, string inputValue)
{
if (FormatAnalyzer.TryPickerStringConvert(inputValue, out DateTime changeValue, false))
{
var array = Value as Array;
array.SetValue(changeValue, index);
var validationSuccess = await ValidateRange(index, changeValue, array);
if (OnChange.HasDelegate)
{
await OnChange.InvokeAsync(new DateRangeChangedEventArgs
{
Dates = new DateTime?[] { array.GetValue(0) as DateTime?, array.GetValue(1) as DateTime? },
DateStrings = new string[] { GetInputValue(0), GetInputValue(1) }
});
}
return validationSuccess;
}
return false;
}
private async Task<bool> ValidateRange(int index, DateTime newDate, Array array)
{
if (index == 0 && array.GetValue(1) is not null && ((DateTime)array.GetValue(1)).CompareTo(newDate) < 0)
{
ClearValue(1, false);
await Blur(0);
await Focus(1);
return false;
}
else if (index == 1)
{
if (array.GetValue(0) is not null && newDate.CompareTo((DateTime)array.GetValue(0)) < 0)
{
ClearValue(0, false);
await Blur(1);
await Focus(0);
return false;
}
else if (array.GetValue(0) is null)
{
await Blur(1);
await Focus(0);
return false;
}
}
return true;
}
private async Task OnFocus(int index)
{
@ -384,25 +327,7 @@ namespace AntDesign
{
return;
}
if (_duringManualInput)
{
var array = Value as Array;
if (!array.GetValue(index).Equals(_cacheDuringInput))
{
//reset picker to Value
if (IsNullable)
array.SetValue(_cacheDuringInput, index);
else
array.SetValue(_cacheDuringInput.GetValueOrDefault(), index);
_pickerStatus[index].IsValueSelected = !(Value is null && (DefaultValue is not null || DefaultPickerValue is not null));
ChangePickerValue(_pickerValueCache, index);
}
_duringManualInput = false;
}
_duringManualInput = false;
AutoFocus = false;
}
@ -417,6 +342,7 @@ namespace AntDesign
_value = CreateInstance();
ValueChanged.InvokeAsync(_value);
}
ResetPlaceholder();
}
/// <summary>
@ -445,17 +371,29 @@ namespace AntDesign
/// <returns></returns>
public override DateTime? GetIndexValue(int index)
{
if (Value != null)
if (_pickerStatus[index].SelectedValue is null)
{
var array = Value as Array;
var indexValue = array.GetValue(index);
var isFocused = index == 0 && _inputStart?.IsOnFocused == true ||
index == 1 && _inputEnd?.IsOnFocused == true;
if (indexValue == null)
DateTime? currentValue;
if (isFocused && (currentValue = GetValue(index)) is not null
&& _pickerStatus[Math.Abs(index - 1)].SelectedValue is not null
&& !IsValidRange(currentValue.Value, index))
{
return null;
}
}
return Convert.ToDateTime(indexValue, CultureInfo);
if (_pickerStatus[index].SelectedValue is not null)
{
return _pickerStatus[index].SelectedValue;
}
if (Value != null)
{
return GetValue(index);
}
else if (!IsTypedValueNull(DefaultValue, index, out var defaultValue))
{
@ -464,13 +402,26 @@ namespace AntDesign
return null;
}
private DateTime? GetValue(int index)
{
var array = Value as Array;
var indexValue = array.GetValue(index);
if (indexValue == null)
{
return null;
}
return Convert.ToDateTime(indexValue, CultureInfo);
}
private static bool IsTypedValueNull(TValue value, int index, out DateTime? outValue)
{
outValue = (DateTime?)(value as Array)?.GetValue(index);
return outValue == null;
}
public override void ChangeValue(DateTime value, int index = 0)
public override void ChangeValue(DateTime value, int index = 0, bool closeDropdown = true)
{
bool isValueInstantiated = Value == null;
if (isValueInstantiated)
@ -478,12 +429,20 @@ namespace AntDesign
Value = CreateInstance();
}
UseDefaultPickerValue[index] = false;
var array = Value as Array;
array.SetValue(value, index);
var currentValue = array.GetValue(index) as DateTime?;
var isValueChanged = currentValue != _pickerStatus[index].SelectedValue;
if (isValueChanged)
{
array.SetValue(value, index);
}
//if Value was just now instantiated then set the other index to existing DefaultValue
if (isValueInstantiated && IsRange && DefaultValue != null)
if (isValueInstantiated && DefaultValue != null)
{
var arrayDefault = DefaultValue as Array;
int oppositeIndex = index == 1 ? 0 : 1;
@ -492,11 +451,10 @@ namespace AntDesign
_pickerStatus[index].IsValueSelected = true;
if (!IsShowTime && Picker != DatePickerType.Time)
if (closeDropdown && !HasTimeInput)
{
_pickerStatus[index].IsNewValueSelected = true;
if (_pickerStatus[0].IsNewValueSelected && _pickerStatus[1].IsNewValueSelected)
if (_pickerStatus[0].SelectedValue is not null
&& _pickerStatus[1].SelectedValue is not null)
{
Close();
}
@ -507,13 +465,13 @@ namespace AntDesign
}
}
if (OnChange.HasDelegate)
var startDate = array.GetValue(0) as DateTime?;
var endDate = array.GetValue(1) as DateTime?;
if (isValueChanged && startDate is not null
&& endDate is not null)
{
OnChange.InvokeAsync(new DateRangeChangedEventArgs
{
Dates = new DateTime?[] { array.GetValue(0) as DateTime?, array.GetValue(1) as DateTime? },
DateStrings = new string[] { GetInputValue(0), GetInputValue(1) }
});
InvokeOnChange();
}
if (_isNotifyFieldChanged && (Form?.ValidateOnChange == true))
@ -540,6 +498,8 @@ namespace AntDesign
{
array.SetValue(default, i);
}
_pickerStatus[i].SelectedValue = null;
_pickerStatus[i].IsValueSelected = false;
PickerValues[i] = _pickerValuesAfterInit[i];
ResetPlaceholder(i);
@ -655,15 +615,5 @@ namespace AntDesign
await Focus();
await OnInputClick(0);
}
private bool IsValidRange(DateTime newValue, int newValueIndex, Array rangeValues)
{
return newValueIndex switch
{
0 when newValue > (rangeValues.GetValue(1) as DateTime?) => false,
1 when newValue < (rangeValues.GetValue(0) as DateTime?) => false,
_ => true
};
}
}
}

View File

@ -13,7 +13,7 @@ using OneOf;
namespace AntDesign
{
public class DatePickerBase<TValue> : AntInputComponentBase<TValue>, IDatePicker
public abstract class DatePickerBase<TValue> : AntInputComponentBase<TValue>, IDatePicker
{
DateTime? IDatePicker.HoverDateTime { get; set; }
private TValue _swpValue;
@ -108,10 +108,11 @@ namespace AntDesign
{
get
{
return base.CultureInfo;
return _isCultureInfoOutside ? base.CultureInfo : _locale.GetCultureInfo();
}
set
{
_isCultureInfoOutside = true;
if (!_isLocaleSetOutside &&
(
(base.CultureInfo != value && base.CultureInfo.Name != value.Name)
@ -263,7 +264,7 @@ namespace AntDesign
protected DateTime[] PickerValues { get; } = new DateTime[] { DateTime.Today, DateTime.Today };
public bool IsRange { get; set; }
public bool IsRange { get; protected set; }
protected DatePickerInput _inputStart;
protected DatePickerInput _inputEnd;
@ -281,6 +282,7 @@ namespace AntDesign
protected bool _needRefresh;
protected bool _duringManualInput;
private bool _isLocaleSetOutside;
private bool _isCultureInfoOutside;
private DatePickerLocale _locale = LocaleProvider.CurrentLocale.DatePicker;
protected bool _openingOverlay;
@ -293,6 +295,8 @@ namespace AntDesign
private readonly object _eventLock = new();
protected bool HasTimeInput => IsShowTime || Picker == DatePickerType.Time;
event EventHandler<bool> IDatePicker.OverlayVisibleChanged
{
add
@ -428,6 +432,7 @@ namespace AntDesign
protected string GetInputValue(int index = 0)
{
DateTime? tryGetValue = GetIndexValue(index);
if (tryGetValue == null)
{
return "";
@ -458,23 +463,49 @@ namespace AntDesign
return Task.CompletedTask;
}
protected virtual async Task OnSelect(DateTime date, int index)
protected virtual async Task OnSelect(DateTime date, int index, bool switchFocus = true, bool closeDropdown = true)
{
_duringManualInput = false;
// InitPicker is the finally value
if (_picker == _pickerStatus[index].InitPicker)
var isInitialPickerType = _picker == _pickerStatus[index].InitPicker;
if (isInitialPickerType)
{
ChangeValue(date, index);
_pickerStatus[index].SelectedValue = date;
if (IsRange)
{
if (!HasTimeInput)
{
if (IsValidRange(date, index))
{
var otherIndex = Math.Abs(index - 1);
if (_pickerStatus[otherIndex].SelectedValue is not null)
{
ChangeValue(_pickerStatus[otherIndex].SelectedValue.Value, otherIndex, closeDropdown);
}
ChangeValue(date, index, closeDropdown);
}
}
}
else if (!HasTimeInput)
{
ChangeValue(date, index, closeDropdown);
}
// auto focus the other input
if (IsRange && (!IsShowTime || Picker == DatePickerType.Time))
if (switchFocus)
{
await SwitchFocus(index);
}
else
{
await Focus(index);
if (IsRange && !HasTimeInput)
{
await SwitchFocus(index);
}
else
{
await Focus(index);
}
}
}
else
@ -482,7 +513,7 @@ namespace AntDesign
_picker = _prePickerStack.Pop();
}
if (!IsRange || IsShowTime)
if (!isInitialPickerType || !IsRange || IsShowTime)
{
ChangePickerValue(date, index);
}
@ -494,27 +525,34 @@ namespace AntDesign
{
var index = GetOnFocusPickerIndex();
_pickerStatus[index].IsNewValueSelected = true;
if (IsRange)
{
var otherIndex = Math.Abs(index - 1);
var otherValue = GetIndexValue(otherIndex);
if (!_pickerStatus[otherIndex].IsNewValueSelected)
if (_pickerStatus[index].SelectedValue is not null && otherValue is not null
&& IsValidRange(_pickerStatus[index].SelectedValue.Value, index))
{
var otherValue = GetIndexValue(otherIndex);
_pickerStatus[otherIndex].IsNewValueSelected = otherValue is not null
&& otherValue != _pickerStatus[otherIndex].OldValue;
if (_pickerStatus[otherIndex].SelectedValue is not null)
{
ChangeValue(_pickerStatus[otherIndex].SelectedValue.Value, otherIndex);
}
ChangeValue(_pickerStatus[index].SelectedValue.Value, index);
}
if (!(await SwitchFocus(index)))
{
Close();
}
}
else
{
Close();
return;
}
if (HasTimeInput && _pickerStatus[index].SelectedValue is not null)
{
ChangeValue(_pickerStatus[index].SelectedValue.Value, index);
}
if (!(await SwitchFocus(index)))
{
Close();
}
@ -534,26 +572,23 @@ namespace AntDesign
_swpValue = DataConvertionExtensions.Convert<DateTime?[], TValue>(new DateTime?[] { range[0], range[1] });
ChangeValue((DateTime)range[0], 0);
ChangeValue((DateTime)range[1], 1);
_pickerStatus[0].IsNewValueSelected = true;
_pickerStatus[1].IsNewValueSelected = true;
Close();
}
private async Task<bool> SwitchFocus(int index)
protected async Task<bool> SwitchFocus(int index)
{
if (index == 0 && (!_pickerStatus[1].IsNewValueSelected || Open) && !_inputEnd.IsOnFocused && !IsDisabled(1))
if (index == 0 && (_pickerStatus[1].SelectedValue is null || Open) && !_inputEnd.IsOnFocused && !IsDisabled(1))
{
await Blur(0);
await Focus(1);
}
else if (index == 1 && (!_pickerStatus[0].IsNewValueSelected || Open) && !_inputStart.IsOnFocused && !IsDisabled(0))
else if (index == 1 && (_pickerStatus[0].SelectedValue is null || Open) && !_inputStart.IsOnFocused && !IsDisabled(0))
{
await Blur(1);
await Focus(0);
}
else
{
await Focus(index); //keep focus on current input
return false;
}
@ -562,7 +597,7 @@ namespace AntDesign
return true;
}
protected virtual async Task OnBlur(int index) => await Task.Yield();
protected abstract Task OnBlur(int index);
protected void InitPicker(string picker)
{
@ -674,7 +709,7 @@ namespace AntDesign
var pickerValue = PickerValues[tempIndex];
if (index == 0 || IsShowTime || Picker == DatePickerType.Time)
if (index == 0 || HasTimeInput)
{
return pickerValue;
}
@ -692,7 +727,7 @@ namespace AntDesign
{
DatePickerType.Year => pickerValue.AddYears(offset * 10),
DatePickerType.Quarter or DatePickerType.Decade or DatePickerType.Month => pickerValue.AddYears(offset),
_ => pickerValue.AddMonths(offset),
_ => pickerValue.AddMonths(offset)
};
}
@ -703,7 +738,7 @@ namespace AntDesign
StateHasChanged();
}
public void ResetPlaceholder(int index = -1)
public void ResetPlaceholder(int rangePickerIndex = -1)
{
_placeholder.Switch(single =>
{
@ -712,18 +747,19 @@ namespace AntDesign
_placeholders[1] = placeholder;
}, arr =>
{
var rangePickerIndex = index >= 0 ? index : GetOnFocusPickerIndex();
var (startPlaceholder, endPlaceholder) = DatePickerPlaceholder.GetRangePlaceHolderByType(Picker, Locale);
var placeholder = arr.Length > rangePickerIndex ? arr[rangePickerIndex] : null;
if (placeholder is null)
if (rangePickerIndex >= 0)
{
var (startPlaceholder, endPlaceholder) = DatePickerPlaceholder.GetRangePlaceHolderByType(Picker, Locale);
placeholder = rangePickerIndex == 0 ? startPlaceholder : endPlaceholder;
var placeholder = arr.Length > rangePickerIndex ? arr[rangePickerIndex] : null;
placeholder ??= rangePickerIndex == 0 ? startPlaceholder : endPlaceholder;
_placeholders[rangePickerIndex] = placeholder;
}
else
{
_placeholders[0] = arr.Length > 0 ? arr[0] : startPlaceholder;
_placeholders[1] = arr.Length > 1 ? arr[1] : endPlaceholder;
}
_placeholders[rangePickerIndex] = placeholder;
});
}
@ -786,7 +822,7 @@ namespace AntDesign
}
private FormatAnalyzer _formatAnalyzer;
public FormatAnalyzer FormatAnalyzer => _formatAnalyzer ??= new(InternalFormat, Picker, Locale);
protected FormatAnalyzer FormatAnalyzer => _formatAnalyzer ??= new(InternalFormat, Picker, Locale, CultureInfo);
public string GetFormatValue(DateTime value, int index)
{
@ -865,18 +901,11 @@ namespace AntDesign
/// </summary>
/// <param name="value"></param>
/// <param name="index"></param>
public virtual void ChangeValue(DateTime value, int index = 0)
{
}
public abstract void ChangeValue(DateTime value, int index = 0, bool closeDropdown = true);
public virtual void ClearValue(int index = 0, bool closeDropdown = true)
{
}
public abstract void ClearValue(int index = 0, bool closeDropdown = true);
public virtual DateTime? GetIndexValue(int index)
{
return null;
}
public abstract DateTime? GetIndexValue(int index);
protected TValue SortValue(TValue value)
{
@ -908,62 +937,24 @@ namespace AntDesign
protected void InvokeInternalOverlayVisibleChanged(bool visible)
{
var index = GetOnFocusPickerIndex();
_pickerStatus[index].SelectedValue = null;
if (IsRange)
{
if (!visible && (!_pickerStatus[0].IsNewValueSelected || !_pickerStatus[1].IsNewValueSelected))
_pickerStatus[Math.Abs(index - 1)].SelectedValue = null;
if (!visible)
{
if (_pickerStatus[0].OldValue.HasValue && _pickerStatus[1].OldValue.HasValue)
{
var startDate = GetIndexValue(0);
var endDate = GetIndexValue(1);
if (startDate is not null && startDate != _pickerStatus[0].OldValue)
{
ChangeValue(_pickerStatus[0].OldValue.Value, 0);
}
if (endDate is not null && endDate != _pickerStatus[1].OldValue)
{
ChangeValue(_pickerStatus[1].OldValue.Value, 1);
}
}
else if (_pickerStatus[0].IsValueSelected || _pickerStatus[1].IsValueSelected)
{
ClearValue(-1);
}
AutoFocus = false;
ResetPlaceholder();
}
else if (visible)
else
{
_pickerStatus[0].OldValue = GetIndexValue(0);
_pickerStatus[1].OldValue = GetIndexValue(1);
_pickerStatus[index].SelectedValue = GetIndexValue(index);
}
}
else if (IsShowTime || Picker == DatePickerType.Time)
{
var index = GetOnFocusPickerIndex();
if (!_pickerStatus[index].IsNewValueSelected && !visible)
{
if (_pickerStatus[index].OldValue.HasValue)
{
ChangeValue(_pickerStatus[index].OldValue.Value, index);
}
else
{
ClearValue(index);
}
}
else if (visible)
{
_pickerStatus[index].OldValue = GetIndexValue(index);
}
}
if (visible)
{
_pickerStatus[0].IsNewValueSelected = false;
_pickerStatus[1].IsNewValueSelected = false;
}
OverlayVisibleChanged?.Invoke(this, visible);
}
@ -998,11 +989,26 @@ namespace AntDesign
}
}
internal async Task OnNowClick()
protected bool IsValidRange(DateTime newValue, int newValueIndex)
{
var pickerIndex = GetOnFocusPickerIndex();
await OnSelect(DateTime.Now, GetOnFocusPickerIndex());
_pickerStatus[pickerIndex].IsNewValueSelected = true;
var otherValue = GetIndexValue(Math.Abs(newValueIndex - 1));
if (otherValue is null)
{
return false;
}
return newValueIndex switch
{
0 when newValue > otherValue => false,
1 when newValue < otherValue => false,
_ => true
};
}
internal void OnNowClick()
{
ChangeValue(DateTime.Now, GetOnFocusPickerIndex());
Close();
}
}

View File

@ -1,4 +1,5 @@
@namespace AntDesign.Internal
@using AntDesign.Core.Extensions
@typeparam TValue
@inherits DatePickerPanelBase<TValue>
@ -58,13 +59,12 @@
{
<th></th>
}
@for (var i = (int)Locale.FirstDayOfWeek; i < Locale.Lang.ShortWeekDays.Length; i++)
@foreach (var dayName in Locale.Lang.ShortWeekDays.Scroll((int)Locale.FirstDayOfWeek))
{
<th>@(Locale.Lang.ShortWeekDays[i])</th>
}
@for (var i = 0; i < (int)Locale.FirstDayOfWeek; i++)
{
<th>@(Locale.Lang.ShortWeekDays[i])</th>
<th>
@dayName
</th>
}
</tr>
</RenderTableHeader>

View File

@ -1,3 +1,5 @@
@using AntDesign.Core.Extensions
@namespace AntDesign.Internal
@typeparam TValue
@inherits DatePickerPanelBase<TValue>
@ -39,7 +41,7 @@
ShowFooter="@ShowToday"
IsInView="date => DateHelper.IsSameMonth(date, PickerValue)"
IsToday="date => DateHelper.IsSameDay(date, DatePicker.CurrentDate)"
IsSelected="date => DateHelper.IsSameDay(date, Value)"
IsSelected="date => DateHelper.IsSameDay(date, Value) || IsRange && DateHelper.IsSameDay(date, GetIndexValue(Math.Abs(PickerIndex-1)))"
GetColTitle='date => date.ToString(Locale.Lang.DateFormat, CultureInfo)'
OnValueSelect="OnSelectDate"
GetNextColValue="date => DateHelper.AddDaysSafely(date, 1)"
@ -49,14 +51,13 @@
</RenderPickerHeader>
<RenderTableHeader>
<tr>
@for (var i = (int)Locale.FirstDayOfWeek; i < Locale.Lang.ShortWeekDays.Length; i++)
@foreach(var dayName in Locale.Lang.ShortWeekDays.Scroll((int)Locale.FirstDayOfWeek))
{
<th>@(Locale.Lang.ShortWeekDays[i])</th>
}
@for (var i = 0; i < (int)Locale.FirstDayOfWeek; i++)
{
<th>@(Locale.Lang.ShortWeekDays[i])</th>
<th>
@dayName
</th>
}
</tr>
</RenderTableHeader>
<RenderColValue Context="currentColDate">
@ -80,7 +81,7 @@
bool isValueNull = Value is null;
Func<int, int?, string> selected;
string localValue;
if (Value is null)
if (isValueNull)
{
localValue = "";
selected = (_, _) => "";

View File

@ -218,7 +218,7 @@ namespace AntDesign.Internal
}
private async Task InvokeSmoothScrollAsync(ElementReference element, ElementReference parent, int? duration)
=> await JsInvokeAsync(JSInteropConstants.SmoothScrollTo, element, parent, duration ?? 100);
=> await JsInvokeAsync(JSInteropConstants.SmoothScrollTo, element, parent, duration ?? 120);
public async ValueTask DisposeAsync()
{

View File

@ -188,7 +188,7 @@ namespace AntDesign
protected DateTime PickerValue { get => GetIndexPickerValue(PickerIndex); }
protected DateTime? Value { get => GetIndexValue(PickerIndex); }
protected DateTime? Value { get => GetIndexValue(GetPickerIndex()); }
public void PopUpPicker(string type) => ChangePickerType(type, PickerIndex);

View File

@ -6,7 +6,6 @@ namespace AntDesign.Internal
{
public string InitPicker { get; set; } = null;
public bool IsValueSelected { get; set; }
public bool IsNewValueSelected { get; set; }
public DateTime? OldValue { get; set; }
public DateTime? SelectedValue { get; set; }
}
}

View File

@ -1,7 +1,6 @@
using System;
using System;
using System.Text;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace AntDesign.Internal
{
@ -294,12 +293,12 @@ namespace AntDesign.Internal
{
if (endDate == null)
{
cls.Append($"{PrefixCls}-cell-range-start-single");
cls.Append($" {PrefixCls}-cell-range-start-single");
}
if (startDate != endDate || currentDate > hoverDateTime)
{
cls.Append($"{PrefixCls}-cell-range-start");
cls.Append($" {PrefixCls}-cell-range-start");
}
if (currentDate > hoverDateTime)

View File

@ -1,18 +1,32 @@
using System;
using System.Globalization;
using System.Text.Json.Serialization;
namespace AntDesign
{
public class DatePickerLocale
{
public DayOfWeek FirstDayOfWeek { get; set; } = DayOfWeek.Sunday;
private DayOfWeek? _firstDayOfWeek;
public DateLocale Lang { get; set; } = new DateLocale();
public DayOfWeek FirstDayOfWeek { get => _firstDayOfWeek ?? GetCultureInfo()?.DateTimeFormat.FirstDayOfWeek ?? DayOfWeek.Sunday; set => _firstDayOfWeek = value; }
public TimePickerLocale TimePickerLocale { get; set; } = new TimePickerLocale();
[JsonPropertyName("lang")]
public DateLocale Lang { get; set; } = new();
public DateLocale DateLocale { get => Lang; set => Lang = value; }
public TimePickerLocale TimePickerLocale { get; set; } = new();
internal Func<CultureInfo> GetCultureInfo { get; set; } = () => null;
}
public class DateLocale
{
private string[] _shortWeekDays;
private static string[] _defaultShortestDayNames = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };
internal Func<CultureInfo> GetCultureInfo { get; set; } = () => null;
public string Placeholder { get; set; } = "Select date";
public string YearPlaceholder { get; set; } = "Select year";
public string QuarterPlaceholder { get; set; } = "Select quarter";
@ -64,7 +78,13 @@ namespace AntDesign
public string EndQuarter { get; set; } = "End quarter";
public string QuarterSelect { get; set; } = "Select quarter";
public string Week { get; set; } = "Week";
public string[] ShortWeekDays { get; set; } = new string[] { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };
public string[] ShortWeekDays
{
get => _shortWeekDays ?? GetCultureInfo()?.DateTimeFormat.ShortestDayNames ?? _defaultShortestDayNames;
set => _shortWeekDays = value;
}
public string TimeFormat12Hour { get; set; } = "hh:mm:ss tt";
public string DateTimeFormat12Hour { get; set; } = "yyyy-MM-dd hh:mm:ss tt";
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@ -25,6 +25,7 @@ namespace AntDesign.Datepicker.Locale
private int _startPosition;
private int _separatorPrefixOffset;
private readonly DatePickerLocale _locale;
private readonly CultureInfo _cultureInfo;
public enum DateTimePartialType
{
@ -38,15 +39,15 @@ namespace AntDesign.Datepicker.Locale
AmPmDesignator
}
public FormatAnalyzer(string format, string analyzerType, DatePickerLocale locale)
public FormatAnalyzer(string format, string analyzerType, DatePickerLocale locale, CultureInfo cultureInfo)
{
_formatLength = format.Length;
_analyzerType = analyzerType;
_locale = locale;
_cultureInfo = cultureInfo;
//Quarter and Week have individual appoaches, so no need to analyze format
//if (!(_analyzerType == DatePickerType.Quarter || _analyzerType == DatePickerType.Week))
AnalyzeFormat(format);
_format = format;
}
private void AnalyzeFormat(string format)
@ -210,7 +211,7 @@ namespace AntDesign.Datepicker.Locale
&& quarter > 0 && quarter <= 4)
{
//pick first day/month of the quarter
return (true, new DateTime(year, quarter * 3 - 2, 1));
return (true, new DateTime(year, quarter * 3 - 2, 1, _cultureInfo.Calendar));
}
return (false, default);
@ -236,7 +237,7 @@ namespace AntDesign.Datepicker.Locale
return (false, default);
//pick first day of the week
var resultDate = new DateTime(year, 1, 1).AddDays(week * 7 - 7);
var resultDate = new DateTime(year, 1, 1, _cultureInfo.Calendar).AddDays(week * 7 - 7);
if (week > 1)
{
int mondayOffset = (7 + (resultDate.DayOfWeek - DayOfWeek.Monday)) % 7;
@ -267,7 +268,6 @@ namespace AntDesign.Datepicker.Locale
}
Func<string, (bool, DateTime)> _converter;
private readonly string _format;
private Func<string, (bool, DateTime)> Converter
{
@ -367,7 +367,7 @@ namespace AntDesign.Datepicker.Locale
}
if (DateTime.DaysInMonth(year, month) < day)
return false;
result = new DateTime(year, month, day, hour, minute, second);
result = new DateTime(year, month, day, hour, minute, second, 0, _cultureInfo.Calendar, DateTimeKind.Local);
return true;
}
@ -375,7 +375,7 @@ namespace AntDesign.Datepicker.Locale
{
if (IsFullString(pickerString))
{
return (true, new DateTime(_parsedMap[DateTimePartialType.Year], 1, 1));
return (true, new DateTime(_parsedMap[DateTimePartialType.Year], 1, 1, _cultureInfo.Calendar));
}
return (false, default);
}

View File

@ -84,16 +84,18 @@
@foreach (var item in row)
{
<td class="ant-descriptions-item" colspan="@item.realSpan">
<span class="ant-descriptions-item-label @(Colon ? "ant-descriptions-item-colon" : null)">
@if (item.item.TitleTemplate != null)
{
@item.item.TitleTemplate
}
else
{
@item.item.Title
}
</span>
<div class="ant-descriptions-item-container">
<span class="ant-descriptions-item-label @(Colon ? "ant-descriptions-item-colon" : null)">
@if (item.item.TitleTemplate != null)
{
@item.item.TitleTemplate
}
else
{
@item.item.Title
}
</span>
</div>
</td>
}
</tr>
@ -101,9 +103,11 @@
@foreach (var item in row)
{
<td class="ant-descriptions-item" colspan="@item.realSpan">
<span class="ant-descriptions-item-content">
@item.item.ChildContent
</span>
<div class="ant-descriptions-item-container">
<span class="ant-descriptions-item-content">
@item.item.ChildContent
</span>
</div>
</td>
}
</tr>

View File

@ -4,14 +4,13 @@
<div class="@ClassMapper.Class" @ref="@Ref" style="@_drawerStyle @InnerZIndexStyle @Style" id="@Id">
@if (Mask)
{
<div class="ant-drawer-mask" @onclick="_=>MaskClick()" style="@MaskStyle"></div>
<div class="ant-drawer-mask" @onclick="MaskClick" style="@MaskStyle"></div>
}
<div class="ant-drawer-content-wrapper @WrapClassName " style="@WrapperStyle" id="@($"ant-drawer-wrap_{Id}")">
<div class="ant-drawer-content">
<div class="ant-drawer-wrapper-body" style="@(IsLeftOrRight?"height:100%":"")">
@if (_title.Value != null || Closable)
{
<div class="@TitleClassMapper.Class" style="@HeaderStyle">
@if (_title.Value != null)
{
@ -39,7 +38,7 @@
{
@ContentTemplate
}
@if (string.IsNullOrEmpty(ContentString))
@if (!string.IsNullOrEmpty(ContentString))
{
@((MarkupString)ContentString)
}

View File

@ -1,8 +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.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Timers;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using OneOf;
namespace AntDesign
@ -194,7 +199,7 @@ namespace AntDesign
set
{
_zIndex = value;
if (_zIndex == 1000)
if (_zIndex == DefaultZIndez)
_zIndexStyle = "";
else
_zIndexStyle = $"z-index: {_zIndex};";
@ -236,18 +241,18 @@ namespace AntDesign
[Parameter]
public bool Visible
{
get => this._isOpen;
get => _isOpen;
set
{
if (this._isOpen && !value)
if (_isOpen && !value)
{
_status = ComponentStatus.Closing;
}
else if (!this._isOpen && value)
else if (!_isOpen && value)
{
_status = ComponentStatus.Opening;
}
this._isOpen = value;
_isOpen = value;
}
}
@ -306,10 +311,10 @@ namespace AntDesign
return Placement switch
{
"left" => $"translateX({this.OffsetX}px);",
"right" => $"translateX(-{this.OffsetX}px);",
"top" => $"translateY({this.OffsetY}px);",
"bottom" => $"translateY(-{this.OffsetY}px);",
"left" => $"translateX({OffsetX}px);",
"right" => $"translateX(-{OffsetX}px);",
"top" => $"translateY({OffsetY}px);",
"bottom" => $"translateY(-{OffsetY}px);",
_ => null
};
}
@ -317,9 +322,8 @@ namespace AntDesign
private const string Duration = "0.3s";
private const string Ease = "cubic-bezier(0.78, 0.14, 0.15, 0.86)";
private string _widthTransition = "";
private readonly string _transformTransition = $"transform {Duration} {Ease} 0s";
private string _heightTransition = "";
private const int DefaultZIndez = 1000;
/// <summary>
/// 设置 Drawer 是否隐藏,以及隐藏时候的位置 Offset
@ -333,7 +337,7 @@ namespace AntDesign
return null;
}
return this.Placement switch
return Placement switch
{
"left" => "translateX(-100%)",
"right" => "translateX(100%)",
@ -344,11 +348,11 @@ namespace AntDesign
}
}
private bool IsLeftOrRight => Placement == "left" || this.Placement == "right";
private bool IsLeftOrRight => Placement == "left" || Placement == "right";
private string WidthPx => this.IsLeftOrRight ? StyleHelper.ToCssPixel(this.Width) : null;
private string WidthPx => IsLeftOrRight ? StyleHelper.ToCssPixel(Width) : null;
private string HeightPx => !this.IsLeftOrRight ? StyleHelper.ToCssPixel(this.Height) : null;
private string HeightPx => !IsLeftOrRight ? StyleHelper.ToCssPixel(Height) : null;
private ClassMapper TitleClassMapper { get; set; } = new ClassMapper();
@ -367,14 +371,14 @@ namespace AntDesign
private void SetClass()
{
var prefixCls = "ant-drawer";
this.ClassMapper.Clear()
ClassMapper.Clear()
.Add(prefixCls)
.If($"{prefixCls}-open", () => _isOpen)
.If($"{prefixCls}-{Placement}", () => Placement.IsIn("top", "bottom", "right", "left"))
.If($"{prefixCls}-rtl", () => RTL)
;
this.TitleClassMapper.Clear()
TitleClassMapper.Clear()
.If("ant-drawer-header", () => _title.Value != null)
.If("ant-drawer-header-no-title", () => _title.Value == null)
;
@ -382,26 +386,17 @@ namespace AntDesign
protected override void OnInitialized()
{
this._originalPlacement = Placement;
_originalPlacement = Placement;
// TODO: remove
this.SetClass();
SetClass();
base.OnInitialized();
}
protected override void OnParametersSet()
{
this.SetClass();
if (string.IsNullOrEmpty(Placement) && Placement != _originalPlacement)
{
this._originalPlacement = Placement;
_isPlacementFirstChange = false;
if (!_isPlacementFirstChange)
{
this.TriggerPlacementChangeCycleOnce();
}
}
SetClass();
_drawerStyle = "";
@ -424,7 +419,7 @@ namespace AntDesign
_hasInvokeClosed = false;
if (string.IsNullOrWhiteSpace(Style))
{
_ = JsInvokeAsync(JSInteropConstants.DisableBodyScroll);
await JsInvokeAsync(JSInteropConstants.DisableBodyScroll);
}
else if (!_renderInCurrentContainerRegex.IsMatch(Style))
{
@ -434,7 +429,9 @@ namespace AntDesign
CalcDrawerStyle();
StateHasChanged();
await Task.Delay(3000);
_drawerStyle = !string.IsNullOrWhiteSpace(OffsetTransform) ? $"transform: {OffsetTransform};" : "";
_drawerStyle = !string.IsNullOrWhiteSpace(OffsetTransform)
? $"transform: {OffsetTransform};"
: string.Empty;
StateHasChanged();
break;
}
@ -453,33 +450,16 @@ namespace AntDesign
}
private Timer _timer;
private int _zIndex = 1000;
private int _zIndex = DefaultZIndez;
private string _zIndexStyle = "";
private void TriggerPlacementChangeCycleOnce()
{
this.PlacementChanging = true;
InvokeStateHasChanged();
_timer = new Timer()
{
AutoReset = false,
Interval = 300,
};
_timer.Elapsed += (_, args) =>
{
this.PlacementChanging = false;
InvokeStateHasChanged();
};
_timer.Start();
}
/// <summary>
/// trigger when mask is clicked
/// </summary>
/// <returns></returns>
private async Task MaskClick()
private async Task MaskClick(MouseEventArgs _)
{
if (this.MaskClosable && this.Mask && this.OnClose.HasDelegate)
if (MaskClosable && Mask && OnClose.HasDelegate)
{
await HandleClose();
}
@ -513,37 +493,16 @@ namespace AntDesign
}
}
private void CalcAnimation()
{
switch (this.Placement)
{
case "left":
case "right":
_widthTransition = $"width 0s {Ease} {Duration}";
break;
case "top":
case "bottom":
_heightTransition = $"height 0s {Ease} {Duration}";
break;
default:
break;
}
}
private void CalcDrawerStyle()
{
string style = null;
if (_status == ComponentStatus.Opened)
{
CalcAnimation();
if (string.IsNullOrWhiteSpace(_heightTransition))
{
_heightTransition += ",";
}
var widthHeightTransition = Placement is "left" or "right"
? $"width 0s {Ease} {Duration}"
: $"height 0s {Ease} {Duration}";
style = $"transition:{_transformTransition} {_heightTransition} {_widthTransition};";
style = $"transition:{_transformTransition} {widthHeightTransition};";
}
if (!string.IsNullOrWhiteSpace(OffsetTransform))

View File

@ -6,10 +6,20 @@ namespace AntDesign.Locales
{
public class Locale
{
[JsonPropertyName("locale")]
public string LocaleName { get; set; }
private CultureInfo _currentCulture;
public CultureInfo CurrentCulture => new CultureInfo(LocaleName);
internal void SetCultureInfo(string cultureName)
{
LocaleName = cultureName;
_currentCulture = new(cultureName);
this.DatePicker.GetCultureInfo = () => _currentCulture;
this.DatePicker.Lang.GetCultureInfo = () => _currentCulture;
}
[JsonPropertyName("locale")]
public string LocaleName { get; private set; }
public CultureInfo CurrentCulture => _currentCulture;
public PaginationLocale Pagination { get; set; } = new();

View File

@ -90,6 +90,7 @@ namespace AntDesign
// fallback to 'en-US'
TryGetSpecifiedLocale("en-US", out locale);
AddClonedLocale(cultureName, ref locale);
return locale;
}
@ -98,15 +99,23 @@ namespace AntDesign
return GetLocale(cultureInfo.Name);
}
private static void AddClonedLocale(string cultureName, ref Locale locale)
public static void SetLocale(string cultureName, Locale locale = null)
{
locale = JsonSerializer.Deserialize<Locale>(
JsonSerializer.Serialize(locale, new JsonSerializerOptions { IgnoreReadOnlyProperties = true }));
locale.LocaleName = cultureName;
_localeCache.TryAdd(cultureName, locale);
var culture = CultureInfo.GetCultureInfo(cultureName);
if (culture != null)
{
CultureInfo.DefaultThreadCurrentUICulture = culture;
}
if (locale != null)
{
locale.SetCultureInfo(cultureName);
_localeCache.AddOrUpdate(cultureName, locale, (name, original) => locale);
}
}
public static bool TryGetSpecifiedLocale(string cultureName, out Locale locale)
private static bool TryGetSpecifiedLocale(string cultureName, out Locale locale)
{
if (!_availableResources.ContainsKey(cultureName)) return _localeCache.TryGetValue(cultureName, out locale);
locale = _localeCache.GetOrAdd(cultureName, key =>
@ -123,25 +132,18 @@ namespace AntDesign
};
serializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
var result = JsonSerializer.Deserialize<Locale>(content, serializerOptions);
result.LocaleName = key;
result.SetCultureInfo(key);
return result;
});
return true;
}
public static void SetLocale(string cultureName, Locale locale = null)
private static void AddClonedLocale(string cultureName, ref Locale locale)
{
var culture = CultureInfo.GetCultureInfo(cultureName);
if (culture != null)
{
CultureInfo.DefaultThreadCurrentUICulture = culture;
}
if (locale != null)
{
_localeCache.AddOrUpdate(cultureName, locale, (name, original) => locale);
}
locale = JsonSerializer.Deserialize<Locale>(
JsonSerializer.Serialize(locale, new JsonSerializerOptions { IgnoreReadOnlyProperties = true }));
locale.SetCultureInfo(cultureName);
_localeCache.TryAdd(cultureName, locale);
}
private static string GetParentCultureName(string name)

View File

@ -64,7 +64,7 @@
"quarterSelect": "Vybrat čtvrtletí",
"week": "Týden",
"monthFormat": "MMM",
"shortWeekDays": [ "Po", "Út", "St", "Čt", "Pá", "So", "Ne" ]
"shortWeekDays": [ "Ne", "Po", "Út", "St", "Čt", "Pá", "So" ]
},
"timePickerLocale": {
"placeholder": "Vybrat čas",

View File

@ -56,7 +56,7 @@
"quarterSelect": "Quartal wählen",
"week": "Woche",
"monthFormat": "MMM",
"shortWeekDays": [ "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" ]
"shortWeekDays": [ "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa" ]
},
"timePickerLocale": { "placeholder": "Zeit auswählen" }
},

View File

@ -56,7 +56,7 @@
"quarterSelect": "Select quarter",
"week": "Week",
"monthFormat": "MMM",
"shortWeekDays": [ "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su" ]
"shortWeekDays": [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ]
},
"timePickerLocale": { "placeholder": "Select time" }
},

View File

@ -64,7 +64,7 @@
"quarterSelect": "Selecteer kwartaal",
"week": "Week",
"monthFormat": "MMM",
"shortWeekDays": [ "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo" ]
"shortWeekDays": [ "Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za" ]
},
"timePickerLocale": {
"placeholder": "Selecteer tijd",

View File

@ -64,7 +64,7 @@
"quarterSelect": "Selecteer kwartaal",
"week": "Week",
"monthFormat": "MMM",
"shortWeekDays": [ "Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo" ]
"shortWeekDays": [ "Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za" ]
},
"timePickerLocale": {
"placeholder": "Selecteer tijd",

View File

@ -64,7 +64,7 @@
"quarterSelect": "Wybierz kwartał",
"week": "Tydzień",
"monthFormat": "MMMM",
"shortWeekDays": [ "Pn", "Wt", "Śr", "Cz", "Pt", "So", "Nd" ]
"shortWeekDays": [ "Nd", "Pn", "Wt", "Śr", "Cz", "Pt", "So" ]
},
"timePickerLocale": {
"placeholder": "Wybierz godzinę"

View File

@ -64,7 +64,7 @@
"quarterSelect": "Выберите четверть",
"week": "Неделя",
"monthFormat": "MMM",
"shortWeekDays": [ "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс" ]
"shortWeekDays": [ "Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб" ]
},
"timePickerLocale": {
"placeholder": "Выберите время",

View File

@ -42,7 +42,8 @@
"nextDecade": "下一年代",
"previousCentury": "上一世紀",
"nextCentury": "下一世紀",
"monthFormat": "M月"
"monthFormat": "M月",
"shortWeekDays": [ "日", "一", "二", "三", "四", "五", "六" ]
},
"timePickerLocale": { "placeholder": "請選擇時間" }
},

View File

@ -81,7 +81,7 @@ else if (RootMenu.InlineCollapsed || RootMenu.InternalMode == MenuMode.Horizonta
string prefixCls = $"{RootMenu.PrefixCls}-submenu";
if (RootMenu.PrefixCls.Contains("dropdown"))
{
return @<span class="@(prefixCls)-arrow">
return @<span class="@(prefixCls)-expand-icon">
<span role="img" aria-label="right" class="anticon anticon-right @(prefixCls)-arrow-icon">
<svg viewBox="64 64 896 896" focusable="false" class="" data-icon="right" width="1em" height="1em" fill="currentColor" aria-hidden="true">
<path d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"></path>

View File

@ -80,7 +80,7 @@ namespace AntDesign
ClassMapper
.Clear()
.Add(prefixCls)
.Get(() => $"{prefixCls}-{(Parent is null ? RootMenu?.InternalMode : MenuMode.Vertical)}")
.Get(() => $"{prefixCls}-{RootMenu?.InternalMode}")
.If($"{prefixCls}-disabled", () => Disabled)
.If($"{prefixCls}-selected", () => _isSelected)
.If($"{prefixCls}-open", () => {

View File

@ -126,7 +126,7 @@ namespace AntDesign
/// <summary>
/// The modal dialog's title of RenderFragment
/// </summary>
public RenderFragment TitleTemplate { get; set; }
public RenderFragment TitleTemplate { get; set; } = null;
/// <summary>
/// Width of the modal dialog

View File

@ -61,6 +61,7 @@ namespace AntDesign
Footer = null,
ClassName = confirmOptions.ClassName,
Closable = false
};
config.ClassName = "ant-modal-confirm ant-modal-confirm-" + confirmOptions.ConfirmType;

View File

@ -18,7 +18,7 @@
{
@foreach (var radio in Options.AsT1)
{
<Radio TValue="TValue" Value="radio.Value" Disabled="radio.Disabled">@radio.Label</Radio>
<Radio Value="radio.Value">@radio.Label</Radio>
}
}
}

View File

@ -9,17 +9,39 @@ using System.Text;
using System.Threading.Tasks;
using AntDesign.JsInterop;
using Microsoft.AspNetCore.Components;
using OneOf;
namespace AntDesign
{
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TValue))]
#endif
public partial class Segmented<TValue> : AntDomComponentBase
{
[Parameter]
public TValue DefaultValue { get; set; }
public TValue DefaultValue
{
get => _defaultValue;
set
{
_defaultValueSet = true;
_defaultValue = value;
}
}
[Parameter]
public bool Disabled { get; set; }
public bool Disabled
{
get => _disabled;
set
{
if (_disabled == value)
return;
_disabled = value;
_items.ForEach(item => item.MarkStateHasChanged());
}
}
[Parameter]
public EventCallback<TValue> OnChange { get; set; }
@ -65,11 +87,15 @@ namespace AntDesign
get => _value;
set
{
if (value != null && !value.Equals(_value))
{
_value = value;
ChangeValue(_value);
}
if (_value is null && value is null)
return;
if (_value?.Equals(value) == true)
return;
_value = value;
_valueSet = true;
ChangeValue(_value);
}
}
@ -101,6 +127,10 @@ namespace AntDesign
private bool _firstRendered;
private TValue _value;
private bool _valueSet;
private bool _disabled;
private TValue _defaultValue;
private bool _defaultValueSet;
protected override void OnInitialized()
{
@ -114,7 +144,7 @@ namespace AntDesign
.If($"{PrefixCls}-rtl", () => RTL)
;
if (DefaultValue != null)
if (_defaultValueSet)
{
_value = DefaultValue;
ChangeValue(_value);
@ -134,9 +164,14 @@ namespace AntDesign
_optionsChanged = true;
}
if (_value == null && _optionValues?.Any() == true)
if (!_valueSet && _optionValues?.Any() == true)
{
_value = _optionValues[0];
_valueSet = true;
}
if (_valueSet)
{
ChangeValue(_value);
}
}
@ -156,28 +191,23 @@ namespace AntDesign
if (_firstRendered && _optionsChanged)
{
_optionsChanged = false;
ChangeValue(_value, true);
await GetItemElememt();
ChangeValue(_value);
}
await base.OnAfterRenderAsync(firstRender);
}
private void ChangeValue(TValue value)
private void ChangeValue(TValue value, bool noAnimation = false)
{
if (Disabled)
{
return;
}
var item = _items.FirstOrDefault(x => x.Value.Equals(value));
if (item != null)
{
Select(item);
Select(item, noAnimation);
}
}
internal async void Select(SegmentedItem<TValue> item)
internal async void Select(SegmentedItem<TValue> item, bool noAnimation = false)
{
_items[_activeIndex].SetSelected(false);
_value = item.Value;
@ -192,8 +222,12 @@ namespace AntDesign
await ValueChanged.InvokeAsync(_value);
}
await ThumbAnimation(item);
if (!noAnimation)
{
await ThumbAnimation(item);
}
_activeIndex = item.Index;
_items.ForEach(x => x.SetSelected(false));
item.SetSelected(true);
}
@ -214,7 +248,6 @@ namespace AntDesign
_sliding = true;
_slidingCls = "ant-segmented-thumb ant-segmented-thumb-motion ant-segmented-thumb-motion-appear ant-segmented-thumb-motion-appear-start";
_slidingStyle = $"transform: translateX({_itemRefs[_items[_activeIndex].Id].OffsetLeft}px); width: {_itemRefs[_items[_activeIndex].Id].ClientWidth}px;";
_activeIndex = item.Index;
StateHasChanged();
await Task.Delay(100);

View File

@ -42,22 +42,24 @@ namespace AntDesign
ClassMapper.Add(PrefixCls)
.If($"{PrefixCls}-selected", () => _selected)
.If($"{PrefixCls}-disabled", () => Disabled)
.If($"{PrefixCls}-disabled", () => Disabled || Parent?.Disabled == true)
;
Parent?.AddItem(this);
}
internal void SetSelected(bool selected)
protected internal void SetSelected(bool selected)
{
_selected = selected;
StateHasChanged();
}
protected internal void MarkStateHasChanged() => StateHasChanged();
private void OnClick()
{
if (Disabled)
if (Disabled || Parent.Disabled)
{
return;
}

View File

@ -8,6 +8,6 @@ namespace AntDesign
{
public class SelectLocale
{
public string NotFoundContent { get; set; }
public string NotFoundContent { get; set; } = "Not Found";
}
}

View File

@ -17,7 +17,7 @@
{
@foreach (SliderMark mark in Marks)
{
<span class="ant-slider-dot @IsActiveMark(mark.Key)" style=@($"{(Vertical ? "bottom" : "left")}: {SetMarkPosition(mark.Key)};")></span>
<span class="ant-slider-dot @(IsActiveMark(mark.Key) ? "ant-slider-dot-active" : string.Empty)" style=@($"{(Vertical ? "bottom" : "left")}: {SetMarkPosition(mark.Key)};")></span>
}
}
</div>
@ -27,40 +27,39 @@
{
<Tooltip Title="@TipFormatter(_leftValue)" Style="display: inline;" Visible="_tooltipLeftVisible" ArrowPointAtCenter="true" OverlayClassName="ant-slider-tooltip" Placement="@TooltipPlacement" @ref="_toolTipLeft">
<Unbound>
<div tabindex="0" class="ant-slider-handle ant-slider-handle-1" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100" style="@_leftHandleStyle" @ref="@context.Current" @onmousedown="(e) => OnMouseDownEdge(e, false)">
<div tabindex="0" class="ant-slider-handle ant-slider-handle-1" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow=@(Math.Round(LeftValue, 2)) aria-valuemin=@(Math.Round(Min, 2)) aria-valuemax=@(Math.Round(Max, 2)) style="@_leftHandleStyle" @ref="@context.Current" @onmousedown="(e) => OnMouseDownEdge(e, false)">
</div>
</Unbound>
</Tooltip>
<Tooltip Title="@TipFormatter(_rightValue)" Style="display: inline;" Visible="_tooltipRightVisible" ArrowPointAtCenter="true" OverlayClassName="ant-slider-tooltip" Placement="@TooltipPlacement" @ref="_toolTipRight">
<Unbound>
<div tabindex="0" class="ant-slider-handle ant-slider-handle-2" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100" style="@_rightHandleStyle" @ref="@context.Current" @onmousedown="(e) => OnMouseDownEdge(e, true)">
<div tabindex="0" class="ant-slider-handle ant-slider-handle-2" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow=@(Math.Round(RightValue, 2)) aria-valuemin=@(Math.Round(Min, 2)) aria-valuemax=@(Math.Round(Max, 2)) style="@_rightHandleStyle" @ref="@context.Current" @onmousedown="(e) => OnMouseDownEdge(e, true)">
</div>
</Unbound>
</Tooltip>
}
else
{
<div tabindex="0" class="ant-slider-handle ant-slider-handle-1" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100" style="@_leftHandleStyle" @ref="_leftHandle" @onmousedown="(e) => OnMouseDownEdge(e, false)">
<div tabindex="0" class="ant-slider-handle ant-slider-handle-1" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow=@(Math.Round(LeftValue, 2)) aria-valuemin=@(Math.Round(Min, 2)) aria-valuemax=@(Math.Round(Max, 2)) style="@_leftHandleStyle" @ref="_leftHandle" @onmousedown="(e) => OnMouseDownEdge(e, false)">
</div>
<div tabindex="0" class="ant-slider-handle ant-slider-handle-2" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100" style="@_rightHandleStyle" @ref="_rightHandle" @onmousedown="(e) => OnMouseDownEdge(e, true)">
<div tabindex="0" class="ant-slider-handle ant-slider-handle-2" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow=@(Math.Round(RightValue, 2)) aria-valuemin=@(Math.Round(Min, 2)) aria-valuemax=@(Math.Round(Max, 2)) style="@_rightHandleStyle" @ref="_rightHandle" @onmousedown="(e) => OnMouseDownEdge(e, true)">
</div>
}
}
else
{
@if (HasTooltip)
{
<Tooltip Title="@TipFormatter(_rightValue)" Style="display: inline;" Visible="_tooltipRightVisible" ArrowPointAtCenter="true" OverlayClassName="ant-slider-tooltip" Placement="@TooltipPlacement" @ref="_toolTipRight">
<Unbound>
<div tabindex="0" class="ant-slider-handle" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100" style="@_rightHandleStyle" @ref="@context.Current" @onmousedown="(e)=>OnMouseDownEdge(e, true)">
<div tabindex="0" class="ant-slider-handle" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow=@(Math.Round(RightValue, 2)) aria-valuemin=@(Math.Round(Min, 2)) aria-valuemax=@(Math.Round(Max, 2)) style="@_rightHandleStyle" @ref="@context.Current" @onmousedown="(e)=>OnMouseDownEdge(e, true)">
</div>
</Unbound>
</Tooltip>
}
else
{
<div tabindex="0" class="ant-slider-handle" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100" style="@_rightHandleStyle" @ref="_rightHandle" @onmousedown="(e)=>OnMouseDownEdge(e, true)">
<div tabindex="0" class="ant-slider-handle" role="slider" aria-disabled="@Disabled.ToString()" aria-valuenow=@(Math.Round(RightValue, 2)) aria-valuemin=@(Math.Round(Min, 2)) aria-valuemax=@(Math.Round(Max, 2)) style="@_rightHandleStyle" @ref="_rightHandle" @onmousedown="(e)=>OnMouseDownEdge(e, true)">
</div>
}
}
@ -69,7 +68,7 @@
{
@foreach (SliderMark mark in Marks)
{
<span class="ant-slider-mark-text ant-slider-mark-text-active" style=@($"{(Vertical ? "bottom" : "left")}: {SetMarkPosition(mark.Key)}; {(Vertical ? "margin-bottom: -50%;" : "transform: translateX(-50%);")}" + mark.Style)>@mark.Value</span>
<span class="ant-slider-mark-text @(IsActiveMark(mark.Key) ? "ant-slider-mark-text-active" : string.Empty)" style=@($"{(Vertical ? "bottom" : "left")}: {SetMarkPosition(mark.Key)}; {(Vertical ? "margin-bottom: -50%;" : "transform: translateX(-50%);")}" + mark.Style)>@mark.Value</span>
}
}
</div>

View File

@ -420,27 +420,13 @@ namespace AntDesign
TValue defaultValue;
if (Range)
{
//if (typeof(T) == typeof((int, int)))
//{
// defaultValue = parameters.GetValueOrDefault(nameof(DefaultValue), DataConvertionExtensions.Convert<(int, int), T>((0, 0)));
//}
//else
//{
defaultValue = parameters.GetValueOrDefault(nameof(DefaultValue), DataConvertionExtensions.Convert<(double, double), TValue>((0, 0)));
//}
LeftValue = DataConvertionExtensions.Convert<TValue, (double, double)>(defaultValue).Item1;
RightValue = DataConvertionExtensions.Convert<TValue, (double, double)>(defaultValue).Item2;
}
else
{
//if (typeof(T) == typeof(int))
//{
// defaultValue = parameters.GetValueOrDefault(nameof(DefaultValue), DataConvertionExtensions.Convert<int, T>(0));
//}
//else
//{
defaultValue = parameters.GetValueOrDefault(nameof(DefaultValue), DataConvertionExtensions.Convert<double, TValue>(0));
//}
RightValue = DataConvertionExtensions.Convert<TValue, double>(defaultValue);
}
}
@ -477,6 +463,7 @@ namespace AntDesign
ClassMapper.Clear()
.Add(PreFixCls)
.If($"{PreFixCls}-disabled", () => Disabled)
.If($"{PreFixCls}-horizontal", () => !Vertical)
.If($"{PreFixCls}-vertical", () => Vertical)
.If($"{PreFixCls}-with-marks", () => Marks != null)
.If($"{PreFixCls}-rtl", () => RTL);
@ -527,23 +514,15 @@ namespace AntDesign
throw new ArgumentOutOfRangeException(nameof(Step), "Must greater than 0.");
}
if (Step != null && (Max - Min) / Step % 1 != 0)
var minMaxStepComparison = (Max - Min) / Step % 1;
if (Step != null && (minMaxStepComparison < 0 || minMaxStepComparison >= 0.1))
{
throw new ArgumentOutOfRangeException(nameof(Step), $"Must be divided by ({Max} - {Min}).");
}
}
private async void OnMouseDown(MouseEventArgs args)
private void OnMouseDown(MouseEventArgs args)
{
//// _sliderDom = await JsInvokeAsync<DomRect>(JSInteropConstants.GetBoundingClientRect, _slider);
//_sliderDom = await JsInvokeAsync<Element>(JSInteropConstants.GetDomInfo, _slider);
//decimal x = (decimal)args.ClientX;
//decimal y = (decimal)args.ClientY;
//_mouseDown = !Disabled
// && _sliderDom.clientLeft <= x && x <= _sliderDom.clientLeft + _sliderDom.clientWidth
// && _sliderDom.clientTop <= y && y <= _sliderDom.clientTop + _sliderDom.clientHeight;
_mouseDown = !Disabled;
}
@ -743,13 +722,8 @@ namespace AntDesign
return Formatter.ToPercentWithoutBlank((key - Min) / MinMaxDelta);
}
private string IsActiveMark(double key)
{
bool active = (Range && key >= LeftValue && key <= RightValue)
|| (!Range && key <= RightValue);
return active ? "ant-slider-dot-active" : string.Empty;
}
private bool IsActiveMark(double key) => (Range && key >= LeftValue && key <= RightValue)
|| (!Range && key <= RightValue);
private double GetNearestStep(double value)
{

View File

@ -152,7 +152,7 @@ namespace AntDesign
}
}
private static readonly EventCallbackFactory _callbackFactory = new EventCallbackFactory();
private static readonly EventCallbackFactory _callbackFactory = new();
private bool _filterOpened;
@ -454,9 +454,9 @@ namespace AntDesign
{
foreach (var filter in filterModel.Filters)
{
if (filter.Value is JsonElement)
if (filter.Value is JsonElement jsonElement)
{
filter.Value = JsonElementHelper<TData>.GetValue((JsonElement)filter.Value);
filter.Value = JsonElementHelper<TData>.GetValue(jsonElement);
}
}

View File

@ -197,8 +197,9 @@ namespace AntDesign
fixedWidths = fixedWidths.Append($"{(CssSizeLength)Table.ScrollBarWidth}");
}
cssStyleBuilder.AddStyle("position", "sticky");
cssStyleBuilder.AddStyle(Fixed, $"{(fixedWidths.Any() ? $"calc({string.Join(" + ", fixedWidths) })" : "0px")}");
cssStyleBuilder
.AddStyle("position", "sticky")
.AddStyle(Fixed, $"{(fixedWidths.Any() ? $"calc({string.Join(" + ", fixedWidths)})" : "0px")}");
return cssStyleBuilder.Build();
}

View File

@ -30,7 +30,7 @@ else if (IsHeader && HeaderColSpan != 0)
<th class="@ClassMapper.Class ant-table-selection-column" style="@FixedStyle @HeaderStyle" @key="@Key" colspan="@HeaderColSpan">
@if (Type == "checkbox")
{
<Checkbox Checked="Table.AllSelected" CheckedChange="OnCkeckedChange" Indeterminate="Indeterminate" />
<Checkbox Checked="Table.AllSelected" CheckedChange="OnCheckedChange" Indeterminate="Indeterminate" Disabled="IsHeaderDisabled" />
}
</th>
}
@ -51,11 +51,11 @@ else if (!IsHeader && RowSpan != 0 && ColSpan != 0)
{
@if (Type == "checkbox")
{
<Checkbox Checked="RowData.Selected" Disabled="Disabled" CheckedChange="OnCkeckedChange" />
<Checkbox Checked="RowData.Selected" Disabled="Disabled" CheckedChange="OnCheckedChange" />
}
else if (Type == "radio")
{
<Radio Checked="RowData.Selected" Disabled="Disabled" CheckedChange="OnCkeckedChange" TValue="bool" />
<Radio Checked="RowData.Selected" Disabled="Disabled" CheckedChange="OnCheckedChange" TValue="bool" />
}
}
</td>

View File

@ -29,7 +29,9 @@ namespace AntDesign
//private int[] _selectedIndexes;
private void OnCkeckedChange(bool selected)
private bool IsHeaderDisabled => RowSelections.All(x => x.Disabled);
private void OnCheckedChange(bool selected)
{
if (IsHeader)
{

View File

@ -8,10 +8,11 @@ using AntDesign.Core.HashCodes;
using AntDesign.JsInterop;
using AntDesign.TableModels;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
namespace AntDesign
{
#if NET6_0
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
@ -162,6 +163,9 @@ namespace AntDesign
[Inject]
private IDomEventListener DomEventListener { get; set; }
[Inject]
private ILogger<Table<TItem>> Logger { get; set; }
public ColumnContext ColumnContext { get; set; }
private IEnumerable<TItem> _showItems;
@ -398,6 +402,11 @@ namespace AntDesign
_shouldRender = true;
}
if (HidePagination)
{
PageSize = _total;
}
if (!_preventRender)
{
if (_outerSelectedRows != null)
@ -632,14 +641,21 @@ namespace AntDesign
public async ValueTask DisposeAsync()
{
if (!_isReloading)
try
{
if (ScrollY != null || ScrollX != null)
if (!_isReloading)
{
await JsInvokeAsync(JSInteropConstants.UnbindTableScroll, _tableBodyRef);
if (ScrollY != null || ScrollX != null)
{
await JsInvokeAsync(JSInteropConstants.UnbindTableScroll, _tableBodyRef);
}
}
DomEventListener?.Dispose();
}
catch (Exception ex)
{
Logger.LogError("AntDesign: an exception was thrown at Table `DisposeAsync` method.", ex);
}
DomEventListener?.Dispose();
}
bool ITable.RowExpandable(RowData rowData)

View File

@ -12,7 +12,7 @@ namespace AntDesign
public string FilterReset { get; set; } = "Reset";
public string FilterEmptyText { get; set; }
public string FilterEmptyText { get; set; } = "";
public string SelectAll { get; set; } = "Select current page";

View File

@ -4,7 +4,7 @@
<CascadingValue Value="this" IsFixed="@true">
<div class="@ClassMapper.Class" style="@Style" id="@Id">
<!--Tab bar-->
<div role="tablist" class="ant-tabs-nav">
<div role="tablist" class="@_tabBarClassMapper.Class" style="@TabBarStyle">
@if (TabBarExtraContentLeft != null)
{

View File

@ -97,6 +97,12 @@ namespace AntDesign
[Parameter]
public string TabBarStyle { get; set; }
/// <summary>
/// Tab bar css class
/// </summary>
[Parameter]
public string TabBarClass { get; set; }
/// <summary>
/// Position of tabs
/// </summary>
@ -185,6 +191,7 @@ namespace AntDesign
private readonly ClassMapper _inkClassMapper = new ClassMapper();
private readonly ClassMapper _contentClassMapper = new ClassMapper();
private readonly ClassMapper _tabsNavWarpPingClassMapper = new ClassMapper();
private readonly ClassMapper _tabBarClassMapper = new ClassMapper();
private readonly List<TabPane> _panes = new List<TabPane>();
private readonly List<TabPane> _tabs = new List<TabPane>();
@ -236,6 +243,9 @@ namespace AntDesign
.If("ant-tabs-nav-wrap-ping-left", () => NavWrapPingLeft)
.If("ant-tabs-nav-wrap-ping-right", () => NavWrapPingRight);
_tabBarClassMapper
.Add($"{PrefixCls}-nav")
.If(TabBarClass, () => !string.IsNullOrWhiteSpace(TabBarClass));
}
protected override Task OnFirstAfterRenderAsync()

View File

@ -51,7 +51,9 @@ namespace AntDesign
{
if (_color != value)
{
_color = value;
_color = string.IsNullOrWhiteSpace(value)
? "default"
: value.ToLowerInvariant();
_isPresetColor = IsPresetColor(_color);
_isCustomColor = !_isPresetColor; //if it's not a preset color, we can assume that the input is a HTML5 color or Hex or RGB value
}
@ -59,6 +61,7 @@ namespace AntDesign
}
[Parameter]
[Obsolete($"Use {nameof(Color)} instead by passing the string of the enum value")]
public PresetColor? PresetColor
{
get
@ -85,6 +88,7 @@ namespace AntDesign
public string Icon { get; set; }
[Parameter]
[Obsolete("Parameter is not used and does not affect functionality")]
public bool NoAnimation { get; set; }
/// <summary>
@ -112,11 +116,10 @@ namespace AntDesign
[Parameter]
public bool Visible { get; set; } = true;
private bool _isPresetColor;
private bool _isPresetColor = true;
private bool _isCustomColor;
private bool _closed;
private string _color;
private string _color = "default";
private string _style;
protected override void OnParametersSet()
@ -133,15 +136,11 @@ namespace AntDesign
private static bool IsPresetColor(string color)
{
if (string.IsNullOrEmpty(color))
{
return false;
}
bool result = Regex.IsMatch(color, "^(pink|red|yellow|orange|cyan|green|blue|purple|geekblue|magenta|volcano|gold|lime|success|processing|error|warning|default)(-inverse)?$");
return result;
return Regex.IsMatch(color, "^(pink|red|yellow|orange|cyan|green|blue|purple|geekblue|magenta|volcano|gold|lime|success|processing|error|warning|default)(-inverse)?$");
}
private string _prefix = "ant-tag";
private void UpdateClassMap()
{
this.ClassMapper.Add(_prefix)
@ -157,7 +156,7 @@ namespace AntDesign
private string GetStyle()
{
StringBuilder styleBuilder = new StringBuilder();
var styleBuilder = new StringBuilder();
styleBuilder.Append(Style);

View File

@ -2,15 +2,15 @@
{
public class TransferLocale
{
public string NotFoundContent { get; set; }
public string NotFoundContent { get; set; } = "Not Found";
public string SearchPlaceholder { get; set; } = "Search here";
public string ItemUnit { get; set; } = "item";
public string ItemsUnit { get; set; } = "items";
public string Remove { get; set; }
public string SelectCurrent { get; set; }
public string RemoveCurrent { get; set; }
public string SelectAll { get; set; }
public string RemoveAll { get; set; }
public string SelectInvert { get; set; }
public string Remove { get; set; } = "Remove";
public string SelectCurrent { get; set; } = "Select current";
public string RemoveCurrent { get; set; } = "Remove current";
public string SelectAll { get; set; } = "Select All";
public string RemoveAll { get; set; } = "Remove All";
public string SelectInvert { get; set; } = "Select Invert";
}
}

View File

@ -9,6 +9,7 @@ namespace AntDesign
public TreeEventArgs(Tree<TItem> tree) { Tree = tree; }
public TreeEventArgs(Tree<TItem> tree, TreeNode<TItem> node) { Tree = tree; Node = node; }
public TreeEventArgs(Tree<TItem> tree, TreeNode<TItem> node, MouseEventArgs originalEvent) { Tree = tree; Node = node; OriginalEvent = originalEvent; }
public TreeEventArgs(Tree<TItem> tree, TreeNode<TItem> node, MouseEventArgs originalEvent, bool dropBelow ) { Tree = tree; Node = node; OriginalEvent = originalEvent; DropBelow = dropBelow; }
public Tree<TItem> Tree { get; set; }
public TreeNode<TItem> Node { get; set; }
@ -22,5 +23,10 @@ namespace AntDesign
/// 原生事件
/// </summary>
public MouseEventArgs OriginalEvent { get; set; }
/// <summary>
/// Whether to drop dragged node as a sibling (below) or as a child of target node.
/// </summary>
public bool DropBelow { get; set; }
}
}

View File

@ -158,7 +158,7 @@ namespace AntDesign
TreeComponent.DragItem.DragMoveInto(SelfNode);
if (TreeComponent.OnDrop.HasDelegate)
TreeComponent.OnDrop.InvokeAsync(new TreeEventArgs<TItem>(TreeComponent, TreeComponent.DragItem) { TargetNode = SelfNode });
TreeComponent.OnDrop.InvokeAsync(new TreeEventArgs<TItem>(TreeComponent, TreeComponent.DragItem, e, SelfNode.DragTargetBottom) { TargetNode = SelfNode });
}
/// <summary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -30,7 +30,7 @@ Following the Ant Design specification, we developed a Blazor Components library
- 💕 Supports WebAssembly-based client-side and SignalR-based server-side UI event interaction.
- 🎨 Supports Progressive Web Applications (PWA).
- 🛡 Build with C#, a multi-paradigm static language for an efficient development experience.
- ⚙️ .NET Standard 2.1/.NET 5 based, with direct reference to the rich .NET ecosystem.
- ⚙️ .NET Standard 2.1 ~ .NET 7 based, with direct reference to the rich .NET ecosystem.
- 🎁 Seamless integration with existing ASP.NET Core MVC and Razor Pages projects.
## 🌈 Online Examples
@ -41,8 +41,8 @@ WebAssembly static hosting examples:
## 🖥 Environment Support
- .NET Core 3.1 / .NET 5 / .NET 6
- Blazor WebAssembly 3.2 / .NET 5 / .NET 6 Release
- .NET Core 3.1 / .NET 5 / .NET 6 / .NET 7
- Blazor WebAssembly 3.2 / .NET 5 / .NET 6 / .NET 7 Release
- Supports two-way binding on the server side
- Supports WebAssembly static file deployment
- Support 4 major browsers engines, and Internet Explorer 11+ ([Blazor Server](https://docs.microsoft.com/en-us/aspnet/core/blazor/supported-platforms?view=aspnetcore-6.0&WT.mc_id=DT-MVP-5003987) only)
@ -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) 6.0.x or later.
- Install [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet/7.0?WT.mc_id=DT-MVP-5003987) 7.0.x or later.
- Install Node.js (only for building style files and interoperable TypeScript files)
- Clone to local development

View File

@ -40,8 +40,8 @@ title: Ant Design of Blazor
## 🖥 支持环境
- 兼容 .NET Core 3.1 / .NET 5 / .NET 6
- 最新支持 Blazor WebAssembly 6.0 正式版
- 兼容 .NET Core 3.1 / .NET 5 / .NET 6 / .NET 7
- 最新支持 Blazor WebAssembly 7.0 正式版
- 支持服务端双向绑定
- 支持 WebAssembly 静态文件部署
- 主流 4 款现代浏览器,以及 Internet Explorer 11+ (使用 [Blazor Server](https://docs.microsoft.com/zh-cn/aspnet/core/blazor/supported-platforms?view=aspnetcore-6.0&WT.mc_id=DT-MVP-5003987)
@ -145,7 +145,7 @@ title: Ant Design of Blazor
## 🔨 本地开发
- 先安装 [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet/5.0?WT.mc_id=DT-MVP-5003987) 5.0.100 以上版本
- 先安装 [.NET Core SDK](https://dotnet.microsoft.com/download/dotnet/7.0?WT.mc_id=DT-MVP-5003987) 7.0.100 以上版本
- 安装 Node.js只用于样式文件和互操作所需 TS 文件的构建)
- 克隆到本地开发

View File

@ -21,8 +21,8 @@
"url": "https://github.com/ElderJames/ant-design-blazor/issues"
},
"scripts": {
"start": "dotnet watch -p ./site/AntDesign.Docs.Server run -f net6",
"start:wasm": "dotnet watch -p ./site/AntDesign.Docs.Wasm run -f net6",
"start": "dotnet watch -p ./site/AntDesign.Docs.Server run -f net7",
"start:wasm": "dotnet watch -p ./site/AntDesign.Docs.Wasm run -f net7",
"build:lib": "gulp build:library",
"build:doc": "gulp build:preview --max-old-space-size=81920",
"preinstall": "dotnet tool restore",

View File

@ -177,6 +177,10 @@
transform: rotate(405deg);
}
}
.loading-progress-text:after {
content: var(--blazor-load-percentage-text, "Loading");
}
</style>
<div style="display: flex; justify-content: center; align-items: center; flex-direction: column; min-height: 420px; height: 100%;">
<img src="logo.png" alt="logo" width="256">
@ -191,7 +195,7 @@
</div>
</div>
<div style="display: flex; justify-content: center; align-items: center;">
<img src="logo.png" width="32" style="margin-right: 8px;"> Ant Design of Blazor
<div class="loading-progress-text"></div>
</div>
</div>
</div>

View File

@ -40,12 +40,21 @@ task('library:copy-libs-js', () => {
return src([join(buildConfig.publishDir, '*.js*')]).pipe(dest(join(buildConfig.componentsDir, 'wwwroot/js')));
});
task('library:copy-libs-less', () => {
// Copy all the less files, excluding the src folder which would duplicate some files.
return src([
join(buildConfig.publishDir, '**/*.less'),
'!' + join(buildConfig.publishDir, 'src', '**/*.less')
])
.pipe(dest(join(buildConfig.componentsDir, 'wwwroot/less')));
});
task(
'build:library',
series(
'clean',
'library:mkdir-dir',
parallel('library:scripts', 'library:compile-less'),
parallel('library:copy-libs-css', 'library:copy-libs-js'),
parallel('library:copy-libs-css', 'library:copy-libs-js', 'library:copy-libs-less'),
)
);

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6</TargetFrameworks>
<TargetFrameworks>net7</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SolutionDir)node_modules') ">
@ -38,8 +38,8 @@
<Target Name="RunCli" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug'">
<Copy SourceFiles="@(DocFiles)" DestinationFolder="$(ProjectDir)\wwwroot\docs\%(RecursiveDir)" ContinueOnError="true" />
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet run -f net6 --project $(CLIPath) demo2json $(ProjectDir)/Demos $(ProjectDir)/wwwroot/meta" />
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet run -f net6 --project $(CLIPath) menu2json $(ProjectDir)/Demos $(ProjectDir)/wwwroot/docs $(ProjectDir)/wwwroot/meta" />
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet run -f net6 --project $(CLIPath) docs2html $(ProjectDir)/wwwroot/docs ./site/AntDesign.Docs/wwwroot/docs" />
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet run -f net7 --project $(CLIPath) demo2json $(ProjectDir)/Demos $(ProjectDir)/wwwroot/meta" />
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet run -f net7 --project $(CLIPath) menu2json $(ProjectDir)/Demos $(ProjectDir)/wwwroot/docs $(ProjectDir)/wwwroot/meta" />
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet run -f net7 --project $(CLIPath) docs2html $(ProjectDir)/wwwroot/docs ./site/AntDesign.Docs/wwwroot/docs" />
</Target>
</Project>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net6</TargetFrameworks>
<TargetFrameworks>net7</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
@ -29,6 +29,12 @@
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7'">
<PackageReference Include="BlazorPolyfill.Server">
<Version>6.0.100.1</Version>
</PackageReference>
</ItemGroup>
<Target Name="CopyDocs" BeforeTargets="Build">
<Copy SourceFiles="@(DocFiles)" DestinationFolder="$(ProjectDir)\wwwroot\docs\%(RecursiveDir)" ContinueOnError="true" />
</Target>

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFrameworks>net6</TargetFrameworks>
<TargetFrameworks>net7</TargetFrameworks>
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
<BlazorWebAssemblyLoadAllGlobalizationData>false</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
<PropertyGroup Condition="$(EnableAOT) == 'true'">
@ -17,6 +17,13 @@
<PackageReference Include="Microsoft.AspNetCore.Components.Web.Extensions" Version="5.0.0-preview9.20513.1" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework) == 'net7'">
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web.Extensions" Version="5.0.0-preview9.20513.1" />
</ItemGroup>
<ItemGroup>
<EmscriptenEnvVars Include="PYTHONUTF8=1" />
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />

View File

@ -173,6 +173,10 @@
transform: rotate(405deg);
}
}
.loading-progress-text:after {
content: var(--blazor-load-percentage-text, "Loading");
}
</style>
<div style="display: flex; justify-content: center; align-items: center; flex-direction: column; min-height: 420px; height: 100%;">
<img src="/logo.png" alt="logo" width="256">
@ -187,7 +191,7 @@
</div>
</div>
<div style="display: flex; justify-content: center; align-items: center;">
<img src="/logo.png" width="32" style="margin-right: 8px;"> Ant Design of Blazor
<div class="loading-progress-text"></div>
</div>
</div>
</div>

View File

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6</TargetFramework>
<TargetFramework>net7</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.0" />
</ItemGroup>
<ItemGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>net6</TargetFrameworks>
<TargetFrameworks>net6;net7</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

View File

@ -17,15 +17,16 @@ Badge normally appears in proximity to notifications or user avatars with eye-ca
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| Color | Customize Badge dot color | string | - | |
| Count | Number to show in badge | ReactNode | | |
| Color | Customize Badge status dot color. Usage of this parameter will make the badge a status dot. | string | - | |
| Count | Number to show in badge | int | | |
| CountTemplate | Template to show in place of Count| RenderFragment | | |
| Dot | Whether to display a red dot instead of `count` | boolean | `false` | |
| Offset | set offset of the badge dot, like`[x, y]` | `[number, number]` | - | |
| Offset | Set offset of the badge dot, like`[x, y]` | `[number, number]` | - | |
| OverflowCount | Max count to show | number | 99 | |
| ShowZero | Whether to show badge when `count` is zero | boolean | `false` | |
| Status | Set Badge as a status dot | `success` \| `processing` \| `default` \| `error` \| `warning` | `''` | |
| Status | Set Badge dot to a status color. Usage of this parameter will make the badge a status dot. | `success` \| `processing` \| `default` \| `error` \| `warning` | `''` | |
| Size | If `count` is set, `size` sets the size of badge | `default` \| `small` | - | |
| Text | If `status` is set, `text` sets the display text of the status `dot` | string | `''` | |
| Text | The display text next to the status dot | string | `''` | |
| Title | Text to show when hovering over the badge | string | `count` | |

View File

@ -18,15 +18,16 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/6%26GF9WHwvY/Badge.svg
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| Color | 自定义小圆点的颜色 | string | - | |
| Count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | ReactNode | | |
| Color | 自定义徽章状态点颜色。使用此参数将使徽章成为状态点。 | string | - | |
| Count | 徽章中显示的编号 | int | | |
| CountTemplate | 代替计数显示的模板 | RenderFragment | | |
| Dot | 不展示数字,只有一个小红点 | boolean | false | |
| Offset | 设置状态点的位置偏移,格式为 `[x, y]` | `[number, number]` | - | |
| OverflowCount | 展示封顶的数字值 | number | 99 | |
| ShowZero | 当数值为 0 时,是否展示 Badge | boolean | false | |
| Status | 设置 Badge 为状态点 | `success` \| `processing` \| `default` \| `error` \| `warning` | '' | |
| Status | 将徽章点设置为状态颜色。使用此参数将使徽章成为状态点。 | `success` \| `processing` \| `default` \| `error` \| `warning` | '' | |
| Size | 在设置了 `count` 的前提下有效,设置小圆点的大小 | `default` \| `small` | - | |
| Text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | '' | |
| Text | 状态点旁边显示文本 | string | '' | |
| Title | 设置鼠标放在状态点上时显示的文字 | string | `count` | |
### Badge.Ribbon

View File

@ -1,60 +1,27 @@
@using System.Globalization
<Row>
<Col Span="12">
<Text>en-US</Text>
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Date" Locale="@EN" CultureInfo="CiEn" />
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Week" Locale="@EN" CultureInfo="CiEn" />
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Month" Locale="@EN" CultureInfo="CiEn" />
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Quarter" Locale="@EN" CultureInfo="CiEn" />
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Year" Locale="@EN" CultureInfo="CiEn" />
<br />
<Text>Japanese with Era</Text>
<br />
<DatePicker TValue="DateTime?" Locale="jaJP.DatePicker" />
<br />
</Col>
<Col Span="12">
<Text>zh-CN</Text>
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Date" Locale="@CN" CultureInfo="CiCn" />
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Week" Locale="@CN" CultureInfo="CiCn" />
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Month" Locale="@CN" CultureInfo="CiCn" />
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Quarter" Locale="@CN" CultureInfo="CiCn" />
<br />
<DatePicker TValue="DateTime?" Picker="@DatePickerType.Year" Locale="@CN" CultureInfo="CiCn" />
<br />
<Text>Chinese</Text>
<br />
<DatePicker TValue="DateTime?" Locale="CN" CultureInfo="@CultureInfo.GetCultureInfo("zh-CN")" />
<br />
</Col>
</Row>
@code{
CultureInfo CiEn = CultureInfo.GetCultureInfo("en-US");
CultureInfo CiCn = CultureInfo.GetCultureInfo("zh-CN");
@code {
AntDesign.Locales.Locale jaJP;
DatePickerLocale EN = new DatePickerLocale
{
FirstDayOfWeek = DayOfWeek.Monday,
Lang = new DateLocale
{
YearFormat = "yyyy",
MonthFormat = "MMM",
DateSelect = "Select date",
WeekSelect = "Select week",
MonthSelect = "Select month",
YearSelect = "Select year",
QuarterSelect = "Select quarter",
Today = "Today"
}
};
DatePickerLocale CN = new DatePickerLocale
{
FirstDayOfWeek = DayOfWeek.Sunday,
Lang = new DateLocale
FirstDayOfWeek = DayOfWeek.Sunday, // it's Monday by default in built-in locale.
DateLocale = new DateLocale
{
YearFormat = "yyyy年",
MonthFormat = "M月",
@ -66,4 +33,13 @@
Today = "今天"
}
};
protected override void OnInitialized()
{
// update an exist locale
jaJP = LocaleProvider.GetLocale("ja-JP");
jaJP.DatePicker.DateLocale.DateFormat = "ggy年M月d日";
jaJP.CurrentCulture.DateTimeFormat.Calendar = new JapaneseCalendar();
}
}

Some files were not shown because too many files have changed in this diff Show More