docs: refactor the layout closer to the official document (#100)

* docs: add cli for building demo structured file

* docs: cli improvement

* docs: generate demo page

* fix: style of components page

* docs: add prism to mack source code highlight

* feat: add menu2json command for cli

* fix: markdown highlight

* feat: fetch menu data from cli output files

* fix: cli

* docs: add avatar demo

* docs: add button demos

* docs: add component-scope style

* docs: fix style

* docs: add badge demos

* fix: rebase conflict

* docs: refactor layout

* docs: fix navigation

* docs: fix rebase conflict

* docs: add AntAffix for sider menu

* docs: fix affix error

* docs: fix rebase confilct
This commit is contained in:
James Yeung 2020-05-10 15:42:02 +08:00 committed by GitHub
parent 0e8669a982
commit 9351a35072
207 changed files with 5426 additions and 2249 deletions

View File

@ -168,9 +168,9 @@ csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = false:error
csharp_style_var_when_type_is_apparent = false:error
csharp_style_var_elsewhere = false:error
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = false:none

1
.gitignore vendored
View File

@ -346,3 +346,4 @@ package-lock.json
/site/AntBlazor.Docs/wwwroot/css/docs.css
/site/**/wwwroot/docs
/components/AntBlazor.xml
/site/AntBlazor.Docs/wwwroot/meta

View File

@ -23,6 +23,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntBlazor.Docs.Server", "si
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntBlazor.Docs.Wasm", "site\AntBlazor.Docs.Wasm\AntBlazor.Docs.Wasm.csproj", "{3F5C09C7-81EB-461C-8E0E-538D3FBAB931}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{D34F1DE5-ECF7-4CF0-8325-B7A38F41D376}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntBlazor.Docs.Build.CLI", "site\AntBlazor.Docs.Build.CLI\AntBlazor.Docs.Build.CLI.csproj", "{5A765767-0766-433D-B78D-97D8F29CA6A6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "hosts", "hosts", "{752F5AE8-BA08-4C41-B9B2-D2ED12727E63}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -49,6 +55,10 @@ Global
{3F5C09C7-81EB-461C-8E0E-538D3FBAB931}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F5C09C7-81EB-461C-8E0E-538D3FBAB931}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F5C09C7-81EB-461C-8E0E-538D3FBAB931}.Release|Any CPU.Build.0 = Release|Any CPU
{5A765767-0766-433D-B78D-97D8F29CA6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5A765767-0766-433D-B78D-97D8F29CA6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5A765767-0766-433D-B78D-97D8F29CA6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5A765767-0766-433D-B78D-97D8F29CA6A6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -56,9 +66,10 @@ Global
GlobalSection(NestedProjects) = preSolution
{0D3995E9-EBD9-4BBB-9E6C-3A3DE8C4611A} = {C60BCE84-4AF4-4393-8D3E-1B69E29549C1}
{E2E6E4E8-2156-4B55-8164-40349D86330B} = {C60BCE84-4AF4-4393-8D3E-1B69E29549C1}
{7FD7BCA7-058C-468C-BD8B-8496074651FD} = {C60BCE84-4AF4-4393-8D3E-1B69E29549C1}
{B8761D43-B253-49FC-B144-A6CCB061951B} = {C60BCE84-4AF4-4393-8D3E-1B69E29549C1}
{3F5C09C7-81EB-461C-8E0E-538D3FBAB931} = {C60BCE84-4AF4-4393-8D3E-1B69E29549C1}
{7FD7BCA7-058C-468C-BD8B-8496074651FD} = {752F5AE8-BA08-4C41-B9B2-D2ED12727E63}
{B8761D43-B253-49FC-B144-A6CCB061951B} = {752F5AE8-BA08-4C41-B9B2-D2ED12727E63}
{3F5C09C7-81EB-461C-8E0E-538D3FBAB931} = {752F5AE8-BA08-4C41-B9B2-D2ED12727E63}
{5A765767-0766-433D-B78D-97D8F29CA6A6} = {D34F1DE5-ECF7-4CF0-8325-B7A38F41D376}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E124DDCB-1F8D-4F96-BF41-D87019D0A412}

View File

@ -23,17 +23,6 @@
<ItemGroup>
<!-- The nuget package icon -->
<None Include="date-picker\internal\AntDatePickerDatePanel.razor" />
<None Include="date-picker\internal\AntDatePickerDatetimePanel.razor" />
<None Include="date-picker\internal\AntDatePickerDecadePanel.razor" />
<None Include="date-picker\internal\AntDatePickerFooter.razor" />
<None Include="date-picker\internal\AntDatePickerHeader.razor" />
<None Include="date-picker\internal\AntDatePickerInput.razor" />
<None Include="date-picker\internal\AntDatePickerMonthPanel.razor" />
<None Include="date-picker\internal\AntDatePickerPanelChooser.razor" />
<None Include="date-picker\internal\AntDatePickerQuarterPanel.razor" />
<None Include="date-picker\internal\AntDatePickerTemplate.razor" />
<None Include="date-picker\internal\AntDatePickerYearPanel.razor" />
<None Include="logo.png" Pack="true" PackagePath="" />
</ItemGroup>
@ -44,6 +33,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ardalis.SmartEnum" Version="1.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="3.1.2" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">

View File

@ -15,6 +15,7 @@ namespace AntBlazor
private bool _affixed;
private bool _rendered;
private bool _listened;
private bool Affixed
{
get => _affixed;
@ -31,6 +32,7 @@ namespace AntBlazor
}
}
}
private ElementReference _ref;
private ElementReference _childRef;
private string _hiddenStyle;
@ -53,6 +55,9 @@ namespace AntBlazor
[Parameter]
public uint? OffsetTop { get; set; } = 0;
[Parameter]
public string ContainerSelector { get; set; } = "#BodyContainer";
/// <summary>
/// Specifies the scrollable area DOM node
/// </summary>
@ -65,7 +70,7 @@ namespace AntBlazor
[Parameter]
public EventCallback<bool> OnChange { get; set; }
#endregion
#endregion Parameters
protected override void OnInitialized()
{
@ -84,13 +89,12 @@ namespace AntBlazor
{
if (_listened)
{
await RenderAffixAsync();
}
else
{
DomEventService.AddEventListener("#BodyContainer", "scroll", OnScroll);
DomEventService.AddEventListener("#BodyContainer", "resize", OnWindowResize);
DomEventService.AddEventListener(ContainerSelector, "scroll", OnScroll);
DomEventService.AddEventListener(ContainerSelector, "resize", OnWindowResize);
if (!string.IsNullOrEmpty(Target.Id))
{
DomEventService.AddEventListener(Target, "scroll", OnScroll);
@ -130,7 +134,7 @@ namespace AntBlazor
private async Task RenderAffixAsync()
{
DomRect domRect = await JsInvokeAsync<DomRect>(JSInteropConstants.getBoundingClientRect, _ref);
DomRect bodyContainerRect = await JsInvokeAsync<DomRect>(JSInteropConstants.getBoundingClientRect, "#BodyContainer");
DomRect bodyContainerRect = await JsInvokeAsync<DomRect>(JSInteropConstants.getBoundingClientRect, ContainerSelector);
DomRect containerRect;
if (string.IsNullOrEmpty(Target.Id))
{

View File

@ -16,4 +16,10 @@
@Text
</span>
}
else if (ChildContent != null)
{
<span class="ant-avatar-string" @ref="TextEl" style="@_textStyles">
@ChildContent
</span>
}
</span>

View File

@ -9,6 +9,9 @@ namespace AntBlazor
{
public partial class AntAvatar : AntDomComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string Shape { get; set; } = null;

View File

@ -1,38 +1,49 @@
@namespace AntBlazor
@inherits AntDomComponentBase
<span class="@ClassMapper.Class" style="@Style" @ref="Ref" Id="@Id">
<span class="@ClassMapper.Class" @ref="Ref" id="@Id">
@if (ChildContent != null)
{
@ChildContent
@if (CountTemplate != null)
{
<span role="img" class="ant-scroll-number-custom-component">@CountTemplate</span>
}
@if ((CountNumber == 0 && ShowZero) || CountNumber > 0)
{
<sup class="@($"ant-badge-count {Class}")" style="@Style" title="@(Title ?? DisplayCount)">@DisplayCount</sup>
}
@if (Dot && CountNumber != 0 || (Dot && ShowZero))
{
<sup class="ant-scroll-number ant-badge-dot" style="@Style"></sup>
}
}
else
{
@if (!string.IsNullOrEmpty(Status) && badgeStatusTypes.Contains(Status))
{
<span class="@($"ant-badge-status-dot ant-badge-status-{Status}")"></span>
}
@if (!string.IsNullOrEmpty(Color))
{
if (badgePresetColors.Contains(Color))
{
<span class="@($"ant-badge-status-dot ant-badge-status-{Color}")"></span>
}
else
{
<span class="ant-badge-status-dot" style="background: @Color"></span>
}
@if (ChildContent != null)
{
@ChildContent
@if ((Count == 0 && ShowZero) || Count > 0)
{
<sup class="@($"ant-badge-count {Class}")" style="@Style" title="@DisplayCount">@DisplayCount</sup>
}
@if (Dot && Count != 0 || (Dot && ShowZero))
{
<sup class="ant-scroll-number ant-badge-dot" style="@Style"></sup>
}
}
else
{
@if (!string.IsNullOrEmpty(Status))
{
<span class="@($"ant-badge-status-dot ant-badge-status-{Status}")"></span>
}
@if (!string.IsNullOrEmpty(Color))
{
<span class="@($"ant-badge-status-dot ant-badge-status-{Color}")"></span>
}
@if (!string.IsNullOrEmpty(Text))
{
<span class="ant-badge-status-text">@Text</span>
}
@if ((Count == 0 && ShowZero) || Count > 0)
{
<sup class="@($"ant-scroll-number ant-badge-count ant-badge-multiple-words {Class}")" style="@Style" title="@DisplayCount">@DisplayCount</sup>
}
}
}
</span>
@if (!string.IsNullOrEmpty(Text))
{
<span class="ant-badge-status-text">@Text</span>
}
@if ((CountNumber == 0 && ShowZero) || CountNumber > 0)
{
<sup class="@($"ant-scroll-number ant-badge-count ant-badge-multiple-words {Class}")" style="@Style" title="@DisplayCount">@DisplayCount</sup>
}
}
</span>

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components;
using OneOf;
using System;
namespace AntBlazor
@ -14,11 +15,11 @@ namespace AntBlazor
[Parameter]
public string Color { get; set; }
/// <summary>
/// Number to show in badge
/// </summary>
[Parameter]
public int? Count { get; set; }
[Parameter] public OneOf<int?, RenderFragment> Count { get; set; }
/// <summary>
/// Whether to display a red dot instead of count
@ -26,12 +27,14 @@ namespace AntBlazor
[Parameter]
public bool Dot { get; set; } = false;
/// <summary>
/// Set offset of the badge dot, like[x, y]
/// </summary>
[Parameter]
public Tuple<int, int> Offset { get; set; }
/// <summary>
/// Max count to show
/// </summary>
@ -44,30 +47,35 @@ namespace AntBlazor
[Parameter]
public bool ShowZero { get; set; } = false;
/// <summary>
/// Set Badge as a status dot
/// </summary>
[Parameter]
public string Status { get; set; }
/// <summary>
/// If status is set, text sets the display text of the status dot
/// </summary>
[Parameter]
public string Text { get; set; }
/// <summary>
/// Text to show when hovering over the badge
/// </summary>
[Parameter]
public string Title { get; set; }
/// <summary>
/// Wrapping this item.
/// </summary>
[Parameter]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Sets the default CSS classes.
/// </summary>
@ -84,9 +92,14 @@ namespace AntBlazor
/// <summary>
/// Shows the Count and takes into account the OverFlowCount.
/// </summary>
protected string DisplayCount => Count > OverflowCount ? $"{OverflowCount}+"
: Count == 0 && !ShowZero ? string.Empty
: Count.ToString();
protected string DisplayCount => CountNumber > OverflowCount ? $"{OverflowCount}+"
: CountNumber == 0 && !ShowZero ? string.Empty
: CountNumber.ToString();
protected int? CountNumber { get; set; }
protected RenderFragment CountTemplate { get; set; }
/// <summary>
/// Startup code
@ -96,6 +109,7 @@ namespace AntBlazor
base.OnInitialized();
if (!string.IsNullOrEmpty(Color) && !string.IsNullOrEmpty(Status))
throw new ArgumentException($"You cannot provide a {nameof(Status)} and a {nameof(Color)}, choose one.");
SetClassMap();
}
@ -106,6 +120,40 @@ namespace AntBlazor
{
base.OnParametersSet();
SetClassMap();
Count.Switch(_count =>
{
this.CountNumber = _count;
}, _template =>
{
this.CountTemplate = _template;
});
}
private string[] badgePresetColors =
{
"pink",
"red",
"yellow",
"orange",
"cyan",
"green",
"blue",
"purple",
"geekblue",
"magenta",
"volcano",
"gold",
"lime"
};
private string[] badgeStatusTypes =
{
"success",
"processing",
"default",
"error",
"warning"
};
}
}

View File

@ -35,6 +35,9 @@ namespace AntBlazor
[Parameter]
public bool Disabled { get; set; }
[Parameter]
public bool Danger { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
@ -45,7 +48,7 @@ namespace AntBlazor
public IList<AntIcon> Icons { get; set; } = new List<AntIcon>();
public AntNavLink Link { get; set; }
//public AntNavLink Link { get; set; }
protected string IconStyle { get; set; }
@ -63,6 +66,7 @@ namespace AntBlazor
ClassMapper.Clear()
.Add("ant-btn")
.If($"{prefixName}-{this.Type}", () => !string.IsNullOrEmpty(Type))
.If($"{prefixName}-danger", () => Danger)
.If($"{prefixName}-{Shape}", () => !string.IsNullOrEmpty(Shape))
.If($"{prefixName}-{sizeMap[this.Size]}", () => sizeMap.ContainsKey(Size))
.If($"{prefixName}-loading", () => Loading)
@ -76,10 +80,10 @@ namespace AntBlazor
protected override void OnInitialized()
{
base.OnInitialized();
if (Link != null && string.IsNullOrEmpty(this.Type))
{
this.Type = AntButtonType.Link;
}
//if (Link != null && string.IsNullOrEmpty(this.Type))
//{
// this.Type = AntButtonType.Link;
//}
SetClassMap();
}
@ -100,10 +104,10 @@ namespace AntBlazor
protected async Task OnClick(MouseEventArgs args)
{
if (Link != null)
{
NavigationManger.NavigateTo(Link.Href);
}
//if (Link != null)
//{
// NavigationManger.NavigateTo(Link.Href);
//}
if (Onclick.HasDelegate)
{

View File

@ -7,29 +7,29 @@ namespace AntBlazor
{
public enum AntDirectionVHIType
{
vertical,
horizontal,
inline
Vertical,
Horizontal,
Inline
}
public enum AntDirectionVHType
{
vertical,
horizontal
Vertical,
Horizontal
}
public enum AntFourDirectionType
{
top,
bottom,
left,
right
Top,
Bottom,
Left,
Right
}
public enum AntAlignType
{
top,
middle,
bottom
Top,
Middle,
Bottom
}
}
}

View File

@ -12,7 +12,7 @@ namespace AntBlazor
public string AsString()
{
return string.Join(" ", map.Where(i => i.Value()).Select(i => i.Key()));
return string.Join(" ", _map.Where(i => i.Value()).Select(i => i.Key()));
}
public override string ToString()
@ -20,39 +20,39 @@ namespace AntBlazor
return AsString();
}
private Dictionary<Func<string>, Func<bool>> map = new Dictionary<Func<string>, Func<bool>>();
private readonly Dictionary<Func<string>, Func<bool>> _map = new Dictionary<Func<string>, Func<bool>>();
public ClassMapper Add(string name)
{
map.Add(() => name, () => true);
_map.Add(() => name, () => true);
return this;
}
public ClassMapper Get(Func<string> funcName)
{
map.Add(funcName, () => true);
_map.Add(funcName, () => true);
return this;
}
public ClassMapper GetIf(Func<string> funcName, Func<bool> func)
{
map.Add(funcName, func);
_map.Add(funcName, func);
return this;
}
public ClassMapper If(string name, Func<bool> func)
{
map.Add(() => name, func);
_map.Add(() => name, func);
return this;
}
public ClassMapper Clear()
{
map.Clear();
_map.Clear();
map.Add(() => OriginalClass, () => !string.IsNullOrEmpty(OriginalClass));
_map.Add(() => OriginalClass, () => !string.IsNullOrEmpty(OriginalClass));
return this;
}
}
}
}

View File

@ -20,4 +20,4 @@ namespace Microsoft.Extensions.DependencyInjection
return services;
}
}
}
}

View File

@ -69,12 +69,6 @@ namespace AntBlazor
await base.OnInitializedAsync();
}
protected override async Task OnParametersSetAsync()
{
//await this.setGutterStyle();
await base.OnParametersSetAsync();
}
private async Task SetGutterStyle()
{
string breakPoint = null;

View File

@ -13,8 +13,7 @@
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public bool HasSider { get; set; }
private bool _hasSider;
protected override void OnInitialized()
@ -22,7 +21,13 @@
base.OnInitialized();
ClassMapper
.Add("ant-layout")
.If("ant-layout-has-sider", () => HasSider);
.If("ant-layout-has-sider", () => _hasSider);
}
internal void SetSider(AntSider sider)
{
_hasSider = true;
InvokeStateHasChanged();
}
}

View File

@ -1,4 +1,5 @@
using System.Collections;
using System;
using System.Collections;
using System.Threading.Tasks;
using AntBlazor.JsInterop;
using Microsoft.AspNetCore.Components;
@ -56,6 +57,8 @@ namespace AntBlazor
private string FlexSetting => $"0 0 {WidthSetting}";
public event Action<bool> OnCollapsed;
private string InternalStyle =>
$@"flex:{FlexSetting};
max-width:{WidthSetting};
@ -86,6 +89,8 @@ namespace AntBlazor
protected override async Task OnInitializedAsync()
{
Layout?.SetSider(this);
DomEventService.AddEventListener<object>("window", "resize", async _ => await WatchMatchMedia());
await base.OnInitializedAsync();
}
@ -100,6 +105,7 @@ namespace AntBlazor
{
this.Collapsed = !this.Collapsed;
await OnCollapsedChange.InvokeAsync(Collapsed);
OnCollapsed?.Invoke(this.Collapsed);
}
public async Task WatchMatchMedia()
@ -107,10 +113,11 @@ namespace AntBlazor
if (string.IsNullOrEmpty(Breakpoint))
return;
var matchBelow = await JsInvokeAsync<bool>(JSInteropConstants.matchMedia, $"(max-width: {_dimensionMap[Breakpoint]})");
bool matchBelow = await JsInvokeAsync<bool>(JSInteropConstants.matchMedia, $"(max-width: {_dimensionMap[Breakpoint]})");
this.Below = matchBelow;
this.Collapsed = matchBelow;
await this.OnCollapsedChange.InvokeAsync(matchBelow);
OnCollapsed?.Invoke(this.Collapsed);
}
}
}

View File

@ -19,7 +19,7 @@ namespace AntBlazor
[Parameter] public IEnumerable<object> DataSource { get; set; }
[Parameter] public AntDirectionVHType ItemLayout { get; set; } = AntDirectionVHType.horizontal;
[Parameter] public AntDirectionVHType ItemLayout { get; set; } = AntDirectionVHType.Horizontal;
[Parameter] public bool Loading { get; set; } = false;
@ -57,9 +57,11 @@ namespace AntBlazor
case "large":
sizeCls = "lg";
break;
case "small":
sizeCls = "sm";
break;
default:
break;
}
@ -70,7 +72,7 @@ namespace AntBlazor
.Add($"{_prefixName}-split")
.Add($"{_prefixName}-bordered")
.If($"{_prefixName}-{sizeCls}", () => !string.IsNullOrEmpty(sizeCls))
.If($"{_prefixName}-vertical", () => ItemLayout == AntDirectionVHType.vertical)
.If($"{_prefixName}-vertical", () => ItemLayout == AntDirectionVHType.Vertical)
.If($"{_prefixName}-loading", () => (Loading))
.If($"{_prefixName}-grid", () => true)
.If($"{_prefixName}-something-after-last-item", () => false);

View File

@ -16,13 +16,13 @@ namespace AntBlazor
// todo: list
[Parameter] public string Actions { get; set; }
[Parameter] public AntDirectionVHIType ItemLayout { get; set; } = AntDirectionVHIType.horizontal;
[Parameter] public AntDirectionVHIType ItemLayout { get; set; } = AntDirectionVHIType.Horizontal;
[Parameter] public RenderFragment ChildContent { get; set; }
public bool IsVerticalAndExtra()
{
return this.ItemLayout == AntDirectionVHIType.vertical && this.Extra != null;
return this.ItemLayout == AntDirectionVHIType.Vertical && this.Extra != null;
}
protected override void OnInitialized()

View File

@ -1,8 +1,8 @@
@namespace AntBlazor
@namespace AntBlazor
@inherits AntDomComponentBase
<CascadingValue Value="this">
<ul class="@ClassMapper.Class" @ref="Ref" style="@Style" id="@Id">
<ul class="@ClassMapper.Class" style="@Style" id="@Id" direction="ltr" role="menu">
@ChildContent
</ul>
</CascadingValue>
</CascadingValue>

View File

@ -1,96 +1,148 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace AntBlazor
{
public partial class AntMenu : AntDomComponentBase
{
[CascadingParameter(Name = nameof(PrefixCls))]
public string PrefixCls { get; set; } = "ant-menu";
private const string PrefixCls = "ant-menu";
[CascadingParameter]
public AntSider Parent { get; set; }
[Parameter]
public AntMenuTheme Theme { get; set; } = AntMenuTheme.Light;
[Parameter]
public AntMenuMode Mode { get; set; } = AntMenuMode.Inline;
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public int InlineIndent { get; set; } = 24;
public EventCallback<AntSubMenu> OnSubmenuClicked { get; set; }
[Parameter]
public string Theme { get; set; } = "light";//'light' | 'dark' = 'light';
public EventCallback<AntMenuItem> OnMenuItemClicked { get; set; }
[Parameter]
public AntDirectionVHIType Mode { get; set; } = AntDirectionVHIType.vertical;
public bool Accordion { get; set; }
[Parameter]
public bool InDropDown { get; set; } = false;
public bool Selectable { get; set; } = true;
[Parameter]
public bool InlineCollapsed { get; set; } = false;
public bool Collapsed { get; set; }
[Parameter]
public bool Selectable { get; set; }
private AntMenuMode _initialMode;
internal AntMenuMode InternalMode { get; private set; }
private bool _collapsed;
[Parameter]
public EventCallback<AntMenuItem> Click { get; set; }
public List<AntSubMenu> Submenus { get; set; } = new List<AntSubMenu>();
public List<AntMenuItem> MenuItems { get; set; } = new List<AntMenuItem>();
internal readonly IList<AntMenuItem> MenuItems = new List<AntMenuItem>();
internal readonly IList<AntSubMenu> SubMenus = new List<AntSubMenu>();
private IList<AntSubMenu> _openedSubMenus = new List<AntSubMenu>();
public bool IsInDropDown { get; set; }
private AntDirectionVHIType _cacheMode;
protected override void OnInitialized()
public void SelectItem(AntMenuItem item)
{
base.OnInitialized();
SetClassMap();
IsInDropDown = InDropDown;
_cacheMode = Mode;
foreach (AntMenuItem menuitem in MenuItems.Where(x => x != item))
{
menuitem.Deselect();
}
if (!item.IsSelected)
{
item.Select();
}
if (OnMenuItemClicked.HasDelegate)
OnMenuItemClicked.InvokeAsync(item);
}
private void SetClassMap()
public void SelectSubmenu(AntSubMenu menu)
{
if (Accordion)
{
foreach (AntSubMenu item in Submenus.Where(x => x != menu && x != menu.Parent))
{
item.Close();
}
}
if (menu.IsOpen)
{
menu.Close();
}
else
{
menu.Open();
}
if (OnSubmenuClicked.HasDelegate)
OnSubmenuClicked.InvokeAsync(menu);
StateHasChanged();
}
private void SetClass()
{
ClassMapper.Add(PrefixCls)
.Add($"{PrefixCls}-root")
.Add($"{PrefixCls}-{Theme}")
.Add($"{PrefixCls}-{Mode}")
.If($"{PrefixCls}-inline-collapsed", () => InlineCollapsed);
.Add($"{PrefixCls}-{InternalMode}")
.If($"{PrefixCls}-inline-collapsed", () => _collapsed)
.If($"{PrefixCls}-unselectable", () => !Selectable);
}
protected override async Task OnParametersSetAsync()
protected override void OnInitialized()
{
base.OnInitialized();
if (InternalMode != AntMenuMode.Inline && _collapsed)
throw new ArgumentException($"{nameof(AntMenu)} in the {Mode} mode cannot be {nameof(Collapsed)}");
_initialMode = Mode;
InternalMode = Mode;
if (Parent != null)
{
Parent.OnCollapsed += Update;
}
SetClass();
}
protected override void OnParametersSet()
{
base.OnParametersSet();
await UpdateInLineCollapse();
if (Parent == null)
{
this._collapsed = Collapsed;
}
Update(_collapsed);
}
private async Task UpdateInLineCollapse()
public void Update(bool collapsed)
{
if (MenuItems.Any())
this._collapsed = collapsed;
if (collapsed)
{
if (InlineCollapsed)
InternalMode = AntMenuMode.Vertical;
foreach (AntSubMenu item in Submenus)
{
_openedSubMenus = this.SubMenus.Where(x => x.Open).ToList();
foreach (var antSubMenu in this.SubMenus)
{
await antSubMenu.SetOpenState(false);
}
item.Close();
}
}
else
{
InternalMode = _initialMode;
}
}
this.Mode = AntDirectionVHIType.vertical;
}
else
{
foreach (var subMenu in _openedSubMenus)
{
await subMenu.SetOpenState(false);
}
_openedSubMenus.Clear();
this.Mode = this._cacheMode;
}
StateHasChanged();
public void Dispose()
{
if (Parent != null)
{
Parent.OnCollapsed -= Update;
}
}
}

View File

@ -1,8 +1,8 @@
@namespace AntBlazor
@namespace AntBlazor
@inherits AntDomComponentBase
<CascadingValue Value=this>
<li class="@ClassMapper.Class" @ref="Ref" style="@Style" id="@Id" @onclick="()=> Menu.Click.InvokeAsync(this)">
<CascadingValue Value="this">
<li class="@ClassMapper.Class" role="menuitem" style="@Style" @onclick="HandleOnClick" @key="Key">
@ChildContent
</li>
</CascadingValue>
</CascadingValue>

View File

@ -1,80 +1,67 @@
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace AntBlazor
{
public partial class AntMenuItem : AntDomComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
[CascadingParameter] public AntMenu RootMenu { get; set; }
[CascadingParameter] public AntSubMenu ParentMenu { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public string Key { get; set; }
[Parameter] public bool Disabled { get; set; }
[Parameter] public EventCallback<MouseEventArgs> OnClicked { get; set; }
[Parameter]
public bool Disabled { get; set; } = false;
public bool IsSelected { get; private set; }
[Parameter]
public bool Selected { get; set; } = false;
[Parameter]
public int? PaddingLeft { get; set; }
[Parameter]
public bool MatchRouterExact { get; set; } = false;
[Parameter]
public bool MatchRouter { get; set; } = false;
[CascadingParameter]
public AntMenu Menu { get; set; }
[CascadingParameter]
public AntSubMenu SubMenu { get; set; }
private readonly int _originalPadding = 0;
private void SetClassMap()
private void SetClass()
{
string prefixName = $"{Menu.PrefixCls}-item";
ClassMapper.Clear()
.Add(prefixName)
.If($"{prefixName}-selected", () => Selected)
.If($"{prefixName}-disabled", () => Disabled);
}
string prefixCls = "ant-menu-item";
internal void SelectedChanged(bool value)
{
this.Selected = value;
ClassMapper.Add(prefixCls)
.If($"{prefixCls}-selected", () => IsSelected)
.If($"{prefixCls}-disabled", () => Disabled);
}
protected override void OnInitialized()
{
SetClass();
base.OnInitialized();
if (this is AntMenuItem item)
{
Menu?.MenuItems.Add(item);
SubMenu?.Items.Add(item);
}
int? padding = null;
if (Menu.Mode == AntDirectionVHIType.inline)
{
if (PaddingLeft != null)
{
padding = PaddingLeft;
}
else
{
int level = SubMenu?.Level + 1 ?? 1;
padding = level * this.Menu.InlineIndent;
}
}
RootMenu.MenuItems.Add(this);
}
if (padding != null)
{
Style += $"padding-left:{padding}px;";
}
public async Task HandleOnClick(MouseEventArgs args)
{
if (!RootMenu.Selectable)
return;
SetClassMap();
RootMenu.SelectItem(this);
if (OnClicked.HasDelegate)
await OnClicked.InvokeAsync(args);
if (ParentMenu == null)
return;
if (RootMenu.Mode != AntMenuMode.Inline)
{
await ParentMenu?.Collapse();
}
}
public void Select()
{
IsSelected = true;
}
public void Deselect()
{
IsSelected = false;
}
}
}

View File

@ -1,38 +1,21 @@
@namespace AntBlazor
@namespace AntBlazor
@inherits AntDomComponentBase
<li class="@ClassMapper.Class" @ref="Ref" style="@Style" id="@Id">
<div class="@titleClassMapper.Class">
@Title
<li class="ant-menu-item-group" style="@Style" >
<div class="ant-menu-item-group-title">
@if (Title.Value != null)
{
@if (Title.IsT0)
{
@Title.AsT0
}
else if (Title.IsT1)
{
@Title.AsT1
}
}
</div>
@ChildContent
</li>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string Title { get; set; }
[CascadingParameter]
public AntMenu Menu { get; set; }
private readonly ClassMapper titleClassMapper = new ClassMapper();
protected override void OnInitialized()
{
base.OnInitialized();
this.titleClassMapper
.If("ant-dropdown-menu-item-group-title", () => !Menu.IsInDropDown)
.If("ant-menu-item-group-title", () => !Menu.IsInDropDown)
;
this.ClassMapper
.If("ant-dropdown-menu-item-group", () => Menu.IsInDropDown)
.If("ant-menu-item-group", () => !Menu.IsInDropDown);
}
}
<ul class="ant-menu-item-group-list">
@ChildContent
</ul>
</li>

View File

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Components;
using OneOf;
namespace AntBlazor
{
public partial class AntMenuItemGroup : AntDomComponentBase
{
private static string _prefixCls = "ant-menu-item-group";
protected override void OnInitialized()
{
ClassMapper.Add(_prefixCls);
}
[Parameter]
public OneOf<string, RenderFragment> Title { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using Ardalis.SmartEnum;
namespace AntBlazor
{
public sealed class AntMenuMode : SmartEnum<AntMenuMode>
{
public static readonly AntMenuMode Vertical = new AntMenuMode(nameof(Vertical).ToLower(), 1);
public static readonly AntMenuMode Horizontal = new AntMenuMode(nameof(Horizontal).ToLower(), 2);
public static readonly AntMenuMode Inline = new AntMenuMode(nameof(Inline).ToLower(), 3);
private AntMenuMode(string name, int value) : base(name, value)
{
}
}
}

View File

@ -0,0 +1,14 @@
using Ardalis.SmartEnum;
namespace AntBlazor
{
public sealed class AntMenuTheme : SmartEnum<AntMenuTheme>
{
public static readonly AntMenuTheme Light = new AntMenuTheme(nameof(Light).ToLowerInvariant(), 1);
public static readonly AntMenuTheme Dark = new AntMenuTheme(nameof(Dark).ToLowerInvariant(), 2);
private AntMenuTheme(string name, int value) : base(name, value)
{
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Routing;
@ -10,11 +11,9 @@ namespace AntBlazor
public class AntNavLink : AntDomComponentBase
{
private const string DefaultActiveClass = "active";
private bool _isActive;
private string _hrefAbsolute;
private string _class;
internal string Href => _hrefAbsolute;
/// <summary>
@ -47,6 +46,9 @@ namespace AntBlazor
[CascadingParameter]
public AntMenuItem MenuItem { get; set; }
[CascadingParameter]
public AntMenu Menu { get; set; }
[CascadingParameter]
public AntButton Button { get; set; }
@ -57,10 +59,10 @@ namespace AntBlazor
{
// We'll consider re-rendering on each location change
NavigationManger.LocationChanged += OnLocationChanged;
if (Button != null)
{
Button.Link = this;
}
//if (Button != null)
//{
// Button.Link = this;
//}
}
/// <inheritdoc />
@ -72,18 +74,18 @@ namespace AntBlazor
{
href = Convert.ToString(obj, CultureInfo.CurrentCulture);
}
_hrefAbsolute = href == null ? null : NavigationManger.ToAbsoluteUri(href).AbsoluteUri;
_isActive = ShouldMatch(NavigationManger.Uri);
_class = (string)null;
if (Attributes != null && Attributes.TryGetValue("class", out obj))
{
_class = Convert.ToString(obj, CultureInfo.CurrentCulture);
}
UpdateCssClass();
MenuItem?.SelectedChanged(_isActive);
if (MenuItem != null && _isActive)
{
Menu?.SelectItem(MenuItem);
}
}
/// <inheritdoc />
@ -108,7 +110,10 @@ namespace AntBlazor
{
_isActive = shouldBeActiveNow;
UpdateCssClass();
MenuItem?.SelectedChanged(_isActive);
if (MenuItem != null && _isActive)
{
Menu?.SelectItem(MenuItem);
}
StateHasChanged();
}
}
@ -119,13 +124,11 @@ namespace AntBlazor
{
return true;
}
if (Match == NavLinkMatch.Prefix
&& IsStrictlyPrefixWithSeparator(currentUriAbsolute, _hrefAbsolute))
&& IsStrictlyPrefixWithSeparator(currentUriAbsolute, _hrefAbsolute))
{
return true;
}
return false;
}
@ -135,7 +138,6 @@ namespace AntBlazor
{
return true;
}
if (currentUriAbsolute.Length == _hrefAbsolute.Length - 1)
{
// Special case: highlight links to http://host/path/ even if you're
@ -147,12 +149,11 @@ namespace AntBlazor
// for http://host/vdir as they do for host://host/vdir/ as it's no
// good to display a blank page in that case.
if (_hrefAbsolute[^1] == '/'
&& _hrefAbsolute.StartsWith(currentUriAbsolute, StringComparison.Ordinal))
&& _hrefAbsolute.StartsWith(currentUriAbsolute, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
@ -161,18 +162,16 @@ namespace AntBlazor
if (builder != null)
{
builder.OpenElement(0, "a");
builder.AddMultipleAttributes(1, Attributes);
builder.AddAttribute(2, "class", CssClass);
builder.AddContent(3, ChildContent);
builder.CloseElement();
}
}
private static string CombineWithSpace(string str1, string str2)
=> str1 == null ? str2
: (str2 == null ? str1 : $"{str1} {str2}");
=> str1 == null ? str2
: (str2 == null ? str1 : $"{str1} {str2}");
private static bool IsStrictlyPrefixWithSeparator(string value, string prefix)
{
@ -180,15 +179,15 @@ namespace AntBlazor
if (value.Length > prefixLength)
{
return value.StartsWith(prefix, StringComparison.Ordinal)
&& (
// Only match when there's a separator character either at the end of the
// prefix or right after it.
// Example: "/abc" is treated as a prefix of "/abc/def" but not "/abcdef"
// Example: "/abc/" is treated as a prefix of "/abc/def" but not "/abcdef"
prefixLength == 0
|| !char.IsLetterOrDigit(prefix[prefixLength - 1])
|| !char.IsLetterOrDigit(value[prefixLength])
);
&& (
// Only match when there's a separator character either at the end of the
// prefix or right after it.
// Example: "/abc" is treated as a prefix of "/abc/def" but not "/abcdef"
// Example: "/abc/" is treated as a prefix of "/abc/def" but not "/abcdef"
prefixLength == 0
|| !char.IsLetterOrDigit(prefix[prefixLength - 1])
|| !char.IsLetterOrDigit(value[prefixLength])
);
}
else
{

View File

@ -1,60 +1,16 @@
@namespace AntBlazor
@namespace AntBlazor
@inherits AntDomComponentBase
<CascadingValue Value=this>
<li class="@ClassMapper.Class" @ref="Ref" style="@Style" id="@Id">
<div class="@TitleDivClass.Class" style="@titleStyle"
@onmouseover="()=>SetMouseEnterState(true)"
@onmouseout="()=>SetMouseEnterState(false)"
@onclick="()=> ClickSubMenuTitle()">
@if (Icon != null)
{
<AntIcon Type="@Icon"></AntIcon>
}
<span>@((MarkupString)Title)</span>
@if (Menu.IsInDropDown)
{
<span class="ant-dropdown-menu-submenu-arrow">
<AntIcon Type="right" Class="anticon-right ant-dropdown-menu-submenu-arrow-icon"></AntIcon>
</span>
}
else
{
<i class="ant-menu-submenu-arrow"></i>
}
</div>
@if (Menu.Mode == AntDirectionVHIType.inline)
{
<ul class="@MenuClassName ant-menu ant-menu-inline ant-menu-sub" style="transition: height 0.5s; @submenuStyle">
@ChildContent
</ul>
}
</li>
@if (Open && Menu.Mode != AntDirectionVHIType.inline)
{
<div style="position: absolute; top: 0px; left: 0px; width: 100%;">
<div class="ant-menu-submenu ant-menu-submenu-popup @PopupClassMapper.Class"
style="left:@(_element.offsetLeft+_element.clientWidth+10)px; top:@(_element.offsetTop)px; @(IsMouseHover?"":"display: none;")"
@onmouseout="()=>SetMouseEnterState(false)"
@onmouseover="()=>SetMouseEnterState(true)"
@onmouseout:stopPropagation
@onmouseover:stopPropagation>
<ul class="@MenuClassName @PopupUlClassMapper.Class ant-menu-submenu-content" style="@(IsMouseHover?"":"display: none;")">
@ChildContent
</ul>
</div>
</div>
}
</CascadingValue>
@code {
string titleStyle => Menu.Mode == AntDirectionVHIType.inline ?
$"padding-left:{(PaddingLeft ?? Level * Menu.InlineIndent)}px" : "";
string submenuStyle => Open ? "" : "height: 0px; overflow: hidden;";
}
<li class="@ClassMapper.Class" role="menuitem" @key="Key" @onmouseover="HandleMouseOver" @onmouseout="HandleMouseOut" style="position:relative;@Style">
<div class="ant-menu-submenu-title" role="button" @onclick="HandleOnTitleClick" aria-haspopup="true">
<span>
@Title.Value
</span>
<i class="ant-menu-submenu-arrow"></i>
</div>
<ul direction="ltr" class="@SubMenuMapper.Class" role="menu" aria-expanded="@IsOpen">
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
</ul>
</li>

View File

@ -1,141 +1,102 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using OneOf;
using System;
using System.Linq;
using System.Threading.Tasks;
using AntBlazor.JsInterop;
using Microsoft.AspNetCore.Components;
namespace AntBlazor
{
public partial class AntSubMenu : AntDomComponentBase
{
public int Level { get; set; } = 1;
private const string PrefixCls = "ant-menu-submenu";
[CascadingParameter]
protected AntMenu Menu { get; set; }
internal IList<AntMenuItem> Items { get; set; } = new List<AntMenuItem>();
public AntMenu RootMenu { get; set; }
[CascadingParameter]
protected AntSubMenu ParentSubMenu { get; set; }
public AntSubMenu Parent { get; set; }
[Parameter]
public OneOf<string, RenderFragment> Title { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string MenuClassName { get; set; }
public string Key { get; set; }
[Parameter]
public int? PaddingLeft { get; set; }
public bool Disabled { get; set; }
[Parameter]
public string Title { get; set; }
public EventCallback<MouseEventArgs> OnTitleClicked { get; set; }
[Parameter]
public string Icon { get; set; }
private ClassMapper SubMenuMapper { get; } = new ClassMapper();
[Parameter]
public bool Open { get; set; } = false;
public bool IsOpen { get; private set; }
[Parameter]
public bool Disabled { get; set; } = false;
[Parameter]
public EventCallback<bool> OpenChange { get; set; }
protected ClassMapper TitleDivClass { get; } = new ClassMapper();
protected ClassMapper PopupClassMapper { get; } = new ClassMapper();
protected ClassMapper PopupUlClassMapper { get; } = new ClassMapper();
private string _placement = "rightTop";
private bool _isChildMenuSelected = false;
protected bool IsMouseHover { get; set; } = false;
private bool _hasOpened;
private Element _element = new Element();
private void SetClassMap()
private void SetClass()
{
this._isChildMenuSelected = this.Items.Any(x => x.Selected);
ClassMapper.Add(PrefixCls)
.Add($"{PrefixCls}-{RootMenu.InternalMode}")
.If($"{PrefixCls}-open", () => IsOpen);
string prefixName = Menu.IsInDropDown ? "ant-dropdown-menu-submenu" : "ant-menu-submenu";
ClassMapper.Clear()
.Add(prefixName)
.If($"{prefixName}-disabled", () => Disabled)
.If($"{prefixName}-open", () => this.Open)
.If($"{prefixName}-selected", () => _isChildMenuSelected)
.Add($"{prefixName}-{Menu.Mode}")
.If($"{prefixName}-active", () => IsMouseHover && !Disabled)
;
SubMenuMapper.Add("ant-menu")
.Add("ant-menu-sub")
.Add($"ant-menu-{(RootMenu.InternalMode == AntMenuMode.Horizontal ? AntMenuMode.Vertical : RootMenu.InternalMode)}")
.If($"ant-menu-submenu-popup", () => RootMenu.InternalMode != AntMenuMode.Inline)
.If($"ant-menu-disabled", () => Disabled)
.If($"ant-menu-hidden", () => !IsOpen);
}
private async Task HandleOnTitleClick(MouseEventArgs args)
{
RootMenu.SelectSubmenu(this);
if (OnTitleClicked.HasDelegate)
await OnTitleClicked.InvokeAsync(args);
}
private void HandleMouseOver(MouseEventArgs args)
{
if (RootMenu.InternalMode == AntMenuMode.Inline)
return;
IsOpen = true;
}
private void HandleMouseOut(MouseEventArgs args)
{
if (RootMenu.InternalMode == AntMenuMode.Inline)
return;
IsOpen = false;
}
public async Task Collapse()
{
await Task.Delay(300);
IsOpen = false;
StateHasChanged();
}
protected override void OnInitialized()
{
if (this is AntSubMenu subMenu)
{
this.Menu?.SubMenus.Add(subMenu);
}
this.Level = this.ParentSubMenu?.Level + 1 ?? 1;
this._hasOpened = Open;
this.SetClassMap();
base.OnInitialized();
PopupClassMapper.Clear()
.If("ant-menu-light", () => Menu.Theme == "light")
.If("ant-menu-dark", () => Menu.Theme == "dark")
.If("ant-menu-submenu-placement-bottomLeft", () => Menu.Mode == AntDirectionVHIType.horizontal)
.If("ant-menu-submenu-placement-rightTop",
() => Menu.Mode == AntDirectionVHIType.vertical && _placement == "rightTop")
.If("ant-menu-submenu-placement-leftTop",
() => Menu.Mode == AntDirectionVHIType.vertical && _placement == "leftTop")
;
PopupUlClassMapper.Clear()
.If("ant-dropdown-menu", () => Menu.IsInDropDown)
.If("ant-menu", () => !Menu.IsInDropDown)
.If("ant-dropdown-menu-vertical", () => Menu.IsInDropDown)
.If("ant-menu-vertical", () => !Menu.IsInDropDown)
.If("ant-dropdown-menu-sub", () => Menu.IsInDropDown)
.If("ant-menu-sub", () => !Menu.IsInDropDown)
;
TitleDivClass
.If("ant-dropdown-menu-submenu-title", () => Menu.IsInDropDown)
.If("ant-menu-submenu-title", () => !Menu.IsInDropDown)
;
RootMenu.Submenus.Add(this);
}
protected async Task SetMouseEnterState(bool value)
public void Close()
{
if (IsMouseHover != value)
{
this.IsMouseHover = value;
this.SetClassMap();
this._element = await JsInvokeAsync<Element>(JSInteropConstants.getDomInfo, Ref);
}
IsOpen = false;
}
protected async Task ClickSubMenuTitle()
public void Open()
{
if (Menu.Mode == AntDirectionVHIType.inline && !Menu.IsInDropDown && !this.Disabled)
IsOpen = true;
if (Parent != null)
{
this.Open = !this.Open;
await OpenChange.InvokeAsync(this.Open);
this.SetClassMap();
Parent.Open();
}
}
public async Task SetOpenState(bool value)
{
this.Open = value;
this._hasOpened = value;
await OpenChange.InvokeAsync(this.Open);
StateHasChanged();
}
}
}

View File

@ -1,4 +1,7 @@
# Ant Design of Blazor [![Github Stars](https://img.shields.io/github/stars/ant-design-blazor/ant-design-blazor?style=social)](https://github.com/ant-design-blazor/ant-design-blazor)
---
order: 0
title: Ant Design of Blazor
---
Following the Ant Design specification, we developed a Blazor Components library `ant-design-blazor` that contains a set of high quality components and demos for building rich, interactive user interfaces.

View File

@ -1,4 +1,7 @@
# Ant Design of Blazor [![Github Stars](https://img.shields.io/github/stars/ant-design-blazor/ant-design-blazor?style=social)](https://github.com/ant-design-blazor/ant-design-blazor)
---
order: 0
title: Ant Design of Blazor
---
这里是 Ant Design 的 Blazor 实现,开发和服务于企业级后台产品。
@ -32,12 +35,12 @@
## 🌈 在线示例
由 WebAssembly 构建,托管在 [GitHub Pages](https://ant-design-blazor.github.io/) ([Gitee Pages 镜像](http://ant-design-blazor.gitee.io/))
由 WebAssembly 构建,托管在 Gitee Pages http://ant-design-blazor.gitee.io/
## 🖥 支持环境
- .NET Core 3.1
- Blazor WebAssembly 3.2 RC 1
- Blazor WebAssembly 3.2 rc 1
- 支持服务端双向绑定
- 支持 WebAssembly 静态文件部署
- 主流 4 款现代浏览器,以及 Internet Explorer 11+ (使用 [Blazor Server](https://angular.io/guide/browser-support)

View File

@ -228,6 +228,17 @@
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/AntBlazor/js/ant-design-blazor.js"></script>
<script src="_content/AntBlazor.Docs/js/prism.js"></script>
<script>
window.antBlazor.Prism = {};
window.antBlazor.Prism.highlight = function (code, language) {
return Prism.highlight(code, Prism.languages[language], language);
}
window.antBlazor.Prism.highlightAll = function () {
Prism.highlightAll();
}
</script>
<script>navigator.serviceWorker.register('service-worker.js');</script>
</body>

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\DemoComponent.cs" Link="Shared\DemoComponent.cs" />
<Compile Include="..\Shared\MenuItem.cs" Link="Shared\MenuItem.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
<PackageReference Include="YamlDotnet" Version="8.1.0" />
<PackageReference Include="Markdig" Version="0.18.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,23 @@
using System.Collections.Generic;
using Microsoft.Extensions.CommandLineUtils;
namespace AntBlazor.Docs.Build.CLI
{
public class AppCommandResolver : IAppCommandResolver
{
private readonly IEnumerable<IAppCommand> _appCommands;
public AppCommandResolver(IEnumerable<IAppCommand> appCommands)
{
_appCommands = appCommands;
}
public void Resolve(CommandLineApplication application)
{
foreach (var command in _appCommands)
{
application.Command(command.Name, command.Execute);
}
}
}
}

View File

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Encodings.Web;
using System.Text.Json;
using AntBlazor.Docs.Build.CLI.Extensions;
using AntBlazor.Docs.Build.CLI.Utils;
using Microsoft.Extensions.CommandLineUtils;
namespace AntBlazor.Docs.Build.CLI.Command
{
public class GenerateDemoJsonCommand : IAppCommand
{
public string Name => "demo2json";
public void Execute(CommandLineApplication command)
{
command.Description = "Generate json file of demos";
command.HelpOption();
CommandArgument directoryArgument = command.Argument(
"source", "[Required] The directory of demo files.");
CommandArgument outputArgument = command.Argument(
"output", "[Required] The directory where the json file to output");
command.OnExecute(() =>
{
string source = directoryArgument.Value;
string output = outputArgument.Value;
if (string.IsNullOrEmpty(source) || !Directory.Exists(source))
{
Console.WriteLine("Invalid source.");
return 1;
}
if (string.IsNullOrEmpty(output))
{
output = "./";
}
string demoDirectory = Path.Combine(Directory.GetCurrentDirectory(), source);
GenerateFiles(demoDirectory, output);
return 0;
});
}
private void GenerateFiles(string demoDirectory, string output)
{
DirectoryInfo demoDirectoryInfo = new DirectoryInfo(demoDirectory);
if (demoDirectoryInfo.Attributes != FileAttributes.Directory)
{
Console.WriteLine("{0} is not a directory", demoDirectory);
return;
}
IList<Dictionary<string, DemoComponent>> componentList = null;
foreach (FileSystemInfo component in demoDirectoryInfo.GetFileSystemInfos())
{
if (!(component is DirectoryInfo componentDirectory)) continue;
FileSystemInfo docDir = componentDirectory.GetFileSystemInfos("doc")[0];
FileSystemInfo demoDir = componentDirectory.GetFileSystemInfos("demo")[0];
Dictionary<string, DemoComponent> componentDic = new Dictionary<string, DemoComponent>();
foreach (FileSystemInfo docItem in (docDir as DirectoryInfo).GetFileSystemInfos())
{
string language = docItem.Name.Replace("index.", "").Replace(docItem.Extension, "");
string content = File.ReadAllText(docItem.FullName);
(Dictionary<string, string> Meta, string Content) docData = DocParser.ParseDemoDoc(content);
componentDic.Add(language, new DemoComponent()
{
Title = docData.Meta["title"],
SubTitle = docData.Meta.TryGetValue("subtitle", out string subtitle) ? subtitle : null,
Type = docData.Meta["type"],
Doc = docData.Content
});
}
foreach (IGrouping<string, FileSystemInfo> demo in (demoDir as DirectoryInfo).GetFileSystemInfos().GroupBy(x => x.Name.Replace(x.Extension, "").ToLower()))
{
List<FileSystemInfo> showCaseFiles = demo.ToList();
FileSystemInfo razorFile = showCaseFiles.FirstOrDefault(x => x.Extension == ".razor");
FileSystemInfo descriptionFile = showCaseFiles.FirstOrDefault(x => x.Extension == ".md");
string code = razorFile != null ? File.ReadAllText(razorFile.FullName) : null;
(DescriptionYaml Meta, string Style, Dictionary<string, string> Descriptions) descriptionContent = descriptionFile != null ? DocParser.ParseDescription(File.ReadAllText(descriptionFile.FullName)) : default;
foreach (KeyValuePair<string, string> title in descriptionContent.Meta.Title)
{
string language = title.Key;
List<DemoItem> list = componentDic[language].DemoList ??= new List<DemoItem>();
list.Add(new DemoItem()
{
Title = title.Value,
Order = descriptionContent.Meta.Order,
Code = code,
Description = descriptionContent.Descriptions[title.Key],
Name = demo.Key,
Style = descriptionContent.Style,
Type = $"{demoDirectoryInfo.Name}.{component.Name}.{demoDir.Name}.{razorFile.Name.Replace(razorFile.Extension, "")}"
});
}
}
componentList ??= new List<Dictionary<string, DemoComponent>>();
componentList.Add(componentDic);
}
if (componentList == null)
return;
IEnumerable<IGrouping<string, KeyValuePair<string, DemoComponent>>> componentI18N = componentList
.SelectMany(x => x).GroupBy(x => x.Key);
foreach (IGrouping<string, KeyValuePair<string, DemoComponent>> componentDic in componentI18N)
{
IEnumerable<DemoComponent> components = componentDic.Select(x => x.Value);
string json = JsonSerializer.Serialize(components, new JsonSerializerOptions()
{
WriteIndented = true,
IgnoreNullValues = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
string configFileDirectory = Path.Combine(Directory.GetCurrentDirectory(), output);
if (!Directory.Exists(configFileDirectory))
{
Directory.CreateDirectory(configFileDirectory);
}
string configFilePath = Path.Combine(configFileDirectory, $"demo.{componentDic.Key}.json");
Console.WriteLine(json);
if (File.Exists(configFilePath))
{
File.Delete(configFilePath);
}
File.WriteAllText(configFilePath, json);
Console.WriteLine("Generate demo file to {0}", configFilePath);
}
}
}
}

View File

@ -0,0 +1,254 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Encodings.Web;
using System.Text.Json;
using AntBlazor.Docs.Build.CLI.Extensions;
using AntBlazor.Docs.Build.CLI.Utils;
using Microsoft.Extensions.CommandLineUtils;
namespace AntBlazor.Docs.Build.CLI.Command
{
public class GenerateMenuJsonCommand : IAppCommand
{
public string Name => "menu2json";
public void Execute(CommandLineApplication command)
{
command.Description = "Generate json file for menu";
command.HelpOption();
CommandArgument demoDirArgument = command.Argument(
"demoDir", "[Required] The directory of docs files.");
CommandArgument docsDirArgument = command.Argument(
"docsDir", "[Required] The directory of docs files.");
CommandArgument outputArgument = command.Argument(
"output", "[Required] The directory where the json file to output");
command.OnExecute(() =>
{
string demoDir = demoDirArgument.Value;
string docsDir = docsDirArgument.Value;
string output = outputArgument.Value;
if (string.IsNullOrEmpty(demoDir) || !Directory.Exists(demoDir))
{
Console.WriteLine("Invalid demoDir.");
return 1;
}
if (string.IsNullOrEmpty(docsDir) || !Directory.Exists(docsDir))
{
Console.WriteLine("Invalid docsDir.");
return 1;
}
if (string.IsNullOrEmpty(output))
{
output = "./";
}
string demoDirectory = Path.Combine(Directory.GetCurrentDirectory(), demoDir);
string docsDirectory = Path.Combine(Directory.GetCurrentDirectory(), docsDir);
GenerateFiles(demoDirectory, docsDirectory, output);
return 0;
});
}
private void GenerateFiles(string demoDirectory, string docsDirectory, string output)
{
DirectoryInfo demoDirectoryInfo = new DirectoryInfo(demoDirectory);
if (demoDirectoryInfo.Attributes != FileAttributes.Directory)
{
Console.WriteLine("{0} is not a directory", demoDirectory);
return;
}
DirectoryInfo docsDirectoryInfo = new DirectoryInfo(docsDirectory);
if (docsDirectoryInfo.Attributes != FileAttributes.Directory)
{
Console.WriteLine("{0} is not a directory", docsDirectory);
return;
}
IList<Dictionary<string, DemoComponent>> componentList = null;
IList<Dictionary<string, MenuItem>> menuList = null;
Dictionary<string, int> sortMap = new Dictionary<string, int>()
{
["General"] = 0,
["通用"] = 0,
["Layout"] = 1,
["布局"] = 1,
["Navigation"] = 2,
["导航"] = 2,
["Data Entry"] = 3,
["数据录入"] = 3,
["Data Display"] = 4,
["数据展示"] = 4,
["Feedback"] = 5,
["反馈"] = 5,
["Localization"] = 6,
["Other"] = 7,
["其他"] = 7
};
foreach (FileSystemInfo component in demoDirectoryInfo.GetFileSystemInfos())
{
if (!(component is DirectoryInfo componentDirectory))
continue;
Dictionary<string, DemoComponent> componentDic = new Dictionary<string, DemoComponent>();
FileSystemInfo docDir = componentDirectory.GetFileSystemInfos("doc")[0];
foreach (FileSystemInfo docItem in (docDir as DirectoryInfo).GetFileSystemInfos())
{
string language = docItem.Name.Replace("index.", "").Replace(docItem.Extension, "");
string content = File.ReadAllText(docItem.FullName);
(Dictionary<string, string> Meta, string Content) docData = DocParser.ParseDemoDoc(content);
componentDic.Add(language, new DemoComponent()
{
Title = docData.Meta["title"],
SubTitle = docData.Meta.TryGetValue("subtitle", out string subtitle) ? subtitle : null,
Type = docData.Meta["type"],
Doc = docData.Content,
});
}
componentList ??= new List<Dictionary<string, DemoComponent>>();
componentList.Add(componentDic);
}
if (componentList == null)
return;
List<Dictionary<string, MenuItem>> componentMenuList = new List<Dictionary<string, MenuItem>>();
IEnumerable<KeyValuePair<string, DemoComponent>> componentI18N = componentList
.SelectMany(x => x);
foreach (IGrouping<string, KeyValuePair<string, DemoComponent>> group in componentI18N.GroupBy(x => x.Value.Type))
{
Dictionary<string, MenuItem> menu = new Dictionary<string, MenuItem>();
foreach (IGrouping<string, KeyValuePair<string, DemoComponent>> component in group.GroupBy(x => x.Key))
{
menu.Add(component.Key, new MenuItem()
{
Order = sortMap[group.Key],
Title = group.Key,
Type = "itemGroup",
Children = group.Select(x => new MenuItem()
{
Title = x.Value.Title,
SubTitle = x.Value.SubTitle,
Url = $"components/{x.Value.Title.ToLower()}",
Type = "menuItem"
}).ToArray()
});
}
componentMenuList.Add(menu);
}
foreach (FileSystemInfo docItem in docsDirectoryInfo.GetFileSystemInfos())
{
if (docItem.Extension != ".md")
continue;
string[] segments = docItem.Name.Split('.');
if (segments.Length != 3)
continue;
string language = segments[1];
string content = File.ReadAllText(docItem.FullName);
Dictionary<string, string> docData = DocParser.ParseHeader(content);
menuList ??= new List<Dictionary<string, MenuItem>>();
menuList.Add(new Dictionary<string, MenuItem>()
{
[language] = new MenuItem()
{
Order = int.TryParse(docData["order"], out int order) ? order : 0,
Title = docData["title"],
Url = $"docs/{segments[0]}",
Type = "menuItem"
}
});
}
if (menuList == null)
return;
IEnumerable<IGrouping<string, KeyValuePair<string, MenuItem>>> menuI18N = menuList
.SelectMany(x => x).GroupBy(x => x.Key);
IEnumerable<IGrouping<string, KeyValuePair<string, MenuItem>>> componentMenuI18N = componentMenuList
.SelectMany(x => x).GroupBy(x => x.Key);
foreach (IGrouping<string, KeyValuePair<string, MenuItem>> menuGroup in menuI18N)
{
var children = menuGroup.Select(x => x.Value).OrderBy(x => x.Order).ToArray();
List<MenuItem> menu = new List<MenuItem>
{
new MenuItem()
{
Order = 0,
Title = menuGroup.Key == "zh-CN" ? "文档" : "Docs",
Type = "subMenu",
Url = "docs",
Children = children
}
};
var components = componentMenuI18N.Where(x => x.Key == menuGroup.Key)
.SelectMany(x => x)
.Select(x => x.Value)
.OrderBy(x => x.Order)
.ToArray();
menu.Add(new MenuItem()
{
Order = 999,
Title = menuGroup.Key == "zh-CN" ? "组件" : "Components",
Type = "subMenu",
Url = "components",
Children = components.ToArray()
});
string json = JsonSerializer.Serialize(menu, new JsonSerializerOptions()
{
WriteIndented = true,
IgnoreNullValues = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
string configFileDirectory = Path.Combine(Directory.GetCurrentDirectory(), output);
if (!Directory.Exists(configFileDirectory))
{
Directory.CreateDirectory(configFileDirectory);
}
string configFilePath = Path.Combine(configFileDirectory, $"menu.{menuGroup.Key}.json");
Console.WriteLine(json);
if (File.Exists(configFilePath))
{
File.Delete(configFilePath);
}
File.WriteAllText(configFilePath, json);
Console.WriteLine("Generate demo file to {0}", configFilePath);
}
}
}
}

View File

@ -0,0 +1,39 @@
using AntBlazor.Docs.Build.CLI.Extensions;
using Microsoft.Extensions.CommandLineUtils;
namespace AntBlazor.Docs.Build.CLI
{
public class CommandLineApplicationFactory
{
private readonly IAppCommandResolver _appCommandResolver;
public CommandLineApplicationFactory(IAppCommandResolver appCommandResolver)
{
_appCommandResolver = appCommandResolver;
}
public CommandLineApplication Create()
{
var app = new CommandLineApplication
{
Name = "ant-design-blazor",
FullName = "Ant Design Blazor Command Line Tool",
Description =
"Generate demo structured file for Docs."
};
app.HelpOption();
app.VersionOptionFromAssemblyAttributes(typeof(Program).Assembly);
app.OnExecute(() =>
{
app.ShowHelp();
return 2;
});
_appCommandResolver.Resolve(app);
return app;
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Reflection;
using Microsoft.Extensions.CommandLineUtils;
namespace AntBlazor.Docs.Build.CLI.Extensions
{
internal static class CommandLineApplicationExtensions
{
public static CommandOption HelpOption(this CommandLineApplication app)
=> app.HelpOption("-?|-h|--help");
public static CommandOption VerboseOption(this CommandLineApplication app)
=> app.Option("-v|--verbose", "Show verbose output", CommandOptionType.NoValue, inherited: true);
public static void OnExecute(this CommandLineApplication app, Action action)
=> app.OnExecute(() =>
{
action();
return 0;
});
public static void VersionOptionFromAssemblyAttributes(this CommandLineApplication app, Assembly assembly)
=> app.VersionOption("--version", GetInformationalVersion(assembly));
private static string GetInformationalVersion(Assembly assembly)
{
var attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
var versionAttribute = attribute == null
? assembly.GetName().Version.ToString()
: attribute.InformationalVersion;
return versionAttribute;
}
}
}

View File

@ -0,0 +1,13 @@
using System.Diagnostics;
namespace AntBlazor.Docs.Build.CLI.Extensions
{
public static class ProcessExtensions
{
public static void Exec(this Process process, string command)
{
if (!process.HasExited)
process.StandardInput.WriteLine(command);
}
}
}

View File

@ -0,0 +1,11 @@
using Microsoft.Extensions.CommandLineUtils;
namespace AntBlazor.Docs.Build.CLI
{
public interface IAppCommand
{
string Name { get; }
void Execute(CommandLineApplication command);
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.Extensions.CommandLineUtils;
namespace AntBlazor.Docs.Build.CLI
{
public interface IAppCommandResolver
{
void Resolve(CommandLineApplication application);
}
}

View File

@ -0,0 +1,51 @@
using System;
using AntBlazor.Docs.Build.CLI.Command;
using AntBlazor.Docs.Build.CLI.Utils;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.DependencyInjection;
namespace AntBlazor.Docs.Build.CLI
{
internal class Program
{
private static int Main(string[] args)
{
return new Program().Run(args);
}
private readonly IServiceProvider _serviceProvider;
public Program()
{
_serviceProvider = ConfigureServices();
}
private int Run(string[] args)
{
try
{
var app = _serviceProvider.GetRequiredService<CommandLineApplication>();
return app.Execute(args);
}
catch (Exception e)
{
Console.WriteLine($"An error occurred. {e.Message}");
return 1;
}
}
private IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<CommandLineApplicationFactory>();
services.AddSingleton(p => p.GetRequiredService<CommandLineApplicationFactory>().Create());
services.AddSingleton<IAppCommandResolver, AppCommandResolver>();
services.AddSingleton<PlatformInformationArbiter>();
services.AddSingleton<DirectoryProvider>();
services.AddSingleton<ShellProcessFactory>();
services.AddSingleton<IAppCommand, GenerateDemoJsonCommand>();
services.AddSingleton<IAppCommand, GenerateMenuJsonCommand>();
return services.BuildServiceProvider();
}
}
}

View File

@ -0,0 +1,9 @@
{
"profiles": {
"AntBlazor.Docs.Build.CLI": {
"commandName": "Project",
"commandLineArgs": "demo2json ../../../../AntBlazor.Docs/Demos ./"
//"commandLineArgs": "menu2json ../../../../AntBlazor.Docs/Demos ../../../../../docs ./"
}
}
}

View File

@ -0,0 +1,19 @@
using System;
namespace AntBlazor.Docs.Build.CLI.Utils
{
public static class ConsoleUtils
{
public static void WriteLine(string message, ConsoleColor foregroundColor)
{
var currentForegroundColor = Console.ForegroundColor;
Console.ForegroundColor = foregroundColor;
Console.WriteLine(message);
Console.ForegroundColor = currentForegroundColor;
}
public static void WriteWelcome()
{
}
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.IO;
namespace AntBlazor.Docs.Build.CLI.Utils
{
public class DirectoryProvider
{
private readonly PlatformInformationArbiter _platformInformation;
public string TmpDirectory => _platformInformation.GetValue(
() => Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), "AppData\\Local\\Temp"),
() => "/tmp",
() => "/tmp",
() => "/tmp");
public string DotnetDirectory => _platformInformation.GetValue(
() => Path.Combine("C:\\Progra~1", "dotnet"),
() => Path.Combine("/usr/local/share", "dotnet"),
() => Path.Combine("/usr/local/share", "dotnet"),
() => Path.Combine("/usr/local/share", "dotnet"));
public string AgentPath => "skyapm.agent.aspnetcore";
public string AdditonalDepsRootDirectory => Path.Combine(DotnetDirectory, "x64", "additionalDeps");
public string StoreDirectory => Path.Combine(DotnetDirectory, "store");
public DirectoryProvider(PlatformInformationArbiter platformInformation)
{
_platformInformation = platformInformation;
}
public string GetAdditonalDepsPath(string additonalName, string frameworkVersion)
{
return Path.Combine(GetAdditonalDepsDirectory(additonalName), "shared", "Microsoft.NETCore.App", frameworkVersion);
}
public string GetAdditonalDepsDirectory(string additonalName)
{
return Path.Combine(AdditonalDepsRootDirectory, additonalName);
}
}
}

View File

@ -0,0 +1,143 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Markdig;
using Markdig.Extensions.Yaml;
using Markdig.Renderers;
using Markdig.Syntax;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace AntBlazor.Docs.Build.CLI.Utils
{
public class DocParser
{
public static (Dictionary<string, string> Meta, string Content) ParseDemoDoc(string input)
{
var pipeline = new MarkdownPipelineBuilder()
.UseYamlFrontMatter()
.Build();
StringWriter writer = new StringWriter();
var renderer = new HtmlRenderer(writer);
pipeline.Setup(renderer);
MarkdownDocument document = Markdown.Parse(input, pipeline);
var yamlBlock = document.Descendants<YamlFrontMatterBlock>().FirstOrDefault();
Dictionary<string, string> meta = null;
if (yamlBlock != null)
{
string yaml = input.Substring(yamlBlock.Span.Start, yamlBlock.Span.Length).Trim('-');
meta = new Deserializer().Deserialize<Dictionary<string, string>>(yaml);
}
renderer.Render(document);
writer.Flush();
string html = writer.ToString();
return (meta, html);
}
public static (DescriptionYaml Meta, string Style, Dictionary<string, string> Descriptions) ParseDescription(string input)
{
var pipeline = new MarkdownPipelineBuilder()
.UseYamlFrontMatter()
.Build();
MarkdownDocument document = Markdown.Parse(input, pipeline);
var yamlBlock = document.Descendants<YamlFrontMatterBlock>().FirstOrDefault();
DescriptionYaml meta = null;
if (yamlBlock != null)
{
string yaml = input.Substring(yamlBlock.Span.Start, yamlBlock.Span.Length).Trim('-');
meta = Deserializer.FromValueDeserializer(new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance).BuildValueDeserializer())
.Deserialize<DescriptionYaml>(yaml);
}
var isAfterENHeading = false;
var isStyleBlock = false;
var zhPart = "";
var enPart = "";
var stylePart = "";
for (int i = yamlBlock?.Line ?? 0; i < document.Count; i++)
{
var block = document[i];
if (block is YamlFrontMatterBlock)
continue;
if (block is HeadingBlock heading && heading.Level == 2 && heading.Inline.FirstChild.ToString() == "en-US")
{
isAfterENHeading = true;
}
if (block is HtmlBlock htmlBlock && htmlBlock.Type == HtmlBlockType.ScriptPreOrStyle)
{
isStyleBlock = true;
}
if (block is HeadingBlock h && h.Level == 2)
continue;
using StringWriter writer = new StringWriter();
var renderer = new HtmlRenderer(writer);
var blockHtml = renderer.Render(block);
if (!isAfterENHeading)
{
zhPart += blockHtml;
}
else if (isStyleBlock)
{
stylePart += blockHtml;
}
else
{
enPart += blockHtml;
}
}
stylePart = stylePart.Replace("<style>", "").Replace("</style>", "");
return (meta, stylePart, new Dictionary<string, string>() { ["zh-CN"] = zhPart, ["en-US"] = enPart });
}
public static Dictionary<string, string> ParseHeader(string input)
{
var pipeline = new MarkdownPipelineBuilder()
.UseYamlFrontMatter()
.Build();
StringWriter writer = new StringWriter();
var renderer = new HtmlRenderer(writer);
pipeline.Setup(renderer);
MarkdownDocument document = Markdown.Parse(input, pipeline);
var yamlBlock = document.Descendants<YamlFrontMatterBlock>().FirstOrDefault();
Dictionary<string, string> meta = null;
if (yamlBlock != null)
{
string yaml = input.Substring(yamlBlock.Span.Start, yamlBlock.Span.Length).Trim('-');
meta = new Deserializer().Deserialize<Dictionary<string, string>>(yaml);
}
return meta;
}
}
public class DescriptionYaml
{
public int Order { get; set; }
public Dictionary<string, string> Iframe { get; set; }
public Dictionary<string, string> Title { get; set; }
public bool Debug { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Runtime.InteropServices;
namespace AntBlazor.Docs.Build.CLI.Utils
{
public class PlatformInformationArbiter
{
public T GetValue<T>(Func<T> windowsValueProvider, Func<T> linuxValueProvider, Func<T> osxValueProvider, Func<T> defaultValueProvider)
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? windowsValueProvider()
: RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
? linuxValueProvider()
: RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
? osxValueProvider()
: defaultValueProvider();
}
public void Invoke(Action windowsValueProvider, Action linuxValueProvider, Action osxValueProvider, Action defaultValueProvider)
{
var invoker = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? windowsValueProvider
: RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
? linuxValueProvider
: RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
? osxValueProvider
: defaultValueProvider;
invoker();
}
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Diagnostics;
namespace AntBlazor.Docs.Build.CLI.Utils
{
public class ShellProcess
{
private readonly object _lock = new object();
private readonly Process _process;
private bool _isError;
private int _exitCode;
public int ExitCode
{
get
{
lock (_lock)
{
return _isError ? 1 : _exitCode;
}
}
}
public ShellProcess(string name, string argument)
{
var processStartInfo = new ProcessStartInfo
{
FileName = name,
CreateNoWindow = true,
ErrorDialog = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
if (!string.IsNullOrEmpty(argument))
{
processStartInfo.Arguments = argument;
}
_process = new Process { StartInfo = processStartInfo };
}
public void Exec(string command)
{
if (!_isError)
{
_process.StandardInput.WriteLine(command);
}
}
public ShellProcess Start()
{
_process.Start();
_process.OutputDataReceived += ProcessOnOutputDataReceived;
_process.ErrorDataReceived += ProcessOnErrorDataReceived;
_process.BeginOutputReadLine();
_process.BeginErrorReadLine();
return this;
}
public void Close()
{
_process.StandardInput.WriteLine("exit");
_process.WaitForExit();
_exitCode = _process.ExitCode;
_process.OutputDataReceived -= ProcessOnOutputDataReceived;
_process.ErrorDataReceived -= ProcessOnErrorDataReceived;
_process.Dispose();
}
private void ProcessOnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (string.IsNullOrEmpty(e.Data))
{
return;
}
if (e.Data.StartsWith("Cloning into"))
{
Console.WriteLine(e.Data);
return;
}
ConsoleUtils.WriteLine(e.Data, ConsoleColor.Yellow);
lock (_lock)
{
_isError = true;
}
}
private void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
Console.WriteLine(e.Data);
}
}
}

View File

@ -0,0 +1,16 @@
namespace AntBlazor.Docs.Build.CLI.Utils
{
public class ShellProcessFactory
{
public ShellProcess Create(string name, string argument = null)
{
return new ShellProcess(name, argument).Start();
}
public int Release(ShellProcess process)
{
process.Close();
return process.ExitCode;
}
}
}

View File

@ -20,5 +20,16 @@
<script src="_framework/blazor.server.js"></script>
<script src="_content/AntBlazor/js/ant-design-blazor.js"></script>
<script src="_content/AntBlazor.Docs/js/prism.js"></script>
<script>
window.antBlazor.Prism = {};
window.antBlazor.Prism.highlight = function (code, language) {
return Prism.highlight(code, Prism.languages[language], language);
}
window.antBlazor.Prism.highlightAll = function () {
Prism.highlightAll();
}
</script>
</body>
</html>

View File

@ -1,3 +1,6 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -21,6 +24,15 @@ namespace AntBlazor.Docs.Server
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddTransient(sp => new HttpClient()
{
DefaultRequestHeaders =
{
// Use to call the github API on server side
{"User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36 Edg/81.0.416.68"}
}
});
services.AddAntBlazorDocs();
}
@ -50,4 +62,4 @@ namespace AntBlazor.Docs.Server
});
}
}
}
}

View File

@ -26,6 +26,10 @@
<SolutionDir>../../</SolutionDir>
</PropertyGroup>
<ItemGroup>
<Content Include="$(SolutionDir)scripts\gh-pages\**" LinkBase="gh-pages" />
</ItemGroup>
<ItemGroup>
<DocFiles Include="$(SolutionDir)docs\**\*.*" />
</ItemGroup>

View File

@ -9,7 +9,6 @@
<base href="/" />
<link rel="icon" href="logo.png" type="image/x-icon">
<link href="_content/AntBlazor.Docs/css/docs.css" rel="stylesheet">
<!--<link href="manifest.json" rel="manifest" />-->
</head>
<body>
@ -199,7 +198,17 @@
</app>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/AntBlazor/js/ant-design-blazor.js"></script>
<!--<script>navigator.serviceWorker.register('service-worker.js');</script>-->
<script src="_content/AntBlazor.Docs/js/prism.js"></script>
<script>
window.antBlazor.Prism = {};
window.antBlazor.Prism.highlight = function (code, language) {
return Prism.highlight(code, Prism.languages[language], language);
}
window.antBlazor.Prism.highlightAll = function () {
Prism.highlightAll();
}
</script>
</body>
</html>

View File

@ -19,8 +19,6 @@ namespace AntBlazor.Docs.WasmHost
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -46,9 +44,8 @@ namespace AntBlazor.Docs.WasmHost
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
}
}
}
}

View File

@ -28,6 +28,11 @@
<EmbeddedResource Include="Resources\*.yml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\DemoComponent.cs" Link="Services\DemoComponent.cs" />
<Compile Include="..\Shared\MenuItem.cs" Link="Services\MenuItem.cs" />
</ItemGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SolutionDir)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
@ -42,6 +47,17 @@
<Exec WorkingDirectory="$(SolutionDir)" Command="npm run gulp:docs" />
</Target>
<PropertyGroup>
<CLIProjectDir>$(SolutionDir)site/AntBlazor.Docs.Build.CLI</CLIProjectDir>
<CLIPath>$(CLIProjectDir)/bin/Debug/netcoreapp3.1/AntBlazor.Docs.Build.CLI.dll</CLIPath>
</PropertyGroup>
<Target Name="RunCli" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug'">
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet build $(CLIProjectDir)" />
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet $(CLIPath) demo2json $(ProjectDir)Demos $(ProjectDir)wwwroot/meta" />
<Exec WorkingDirectory="$(SolutionDir)" Command="dotnet $(CLIPath) menu2json $(ProjectDir)Demos docs $(ProjectDir)wwwroot/meta" />
</Target>
<Target Name="PublishRunGulp" AfterTargets="ComputeFilesToPublish">
<Exec WorkingDirectory="$(SolutionDir)" Command="npm install" />
<Exec WorkingDirectory="$(SolutionDir)" Command="npm run gulp:docs" />

View File

@ -1,4 +1,4 @@
<ConventionRouter AppAssembly="@typeof(MainLayout).Assembly" DefaultUrl="docs/introduce">
<ConventionRouter AppAssembly="@typeof(MainLayout).Assembly" DefaultUrl="docs/introduce">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>

View File

@ -0,0 +1,19 @@
<AntAlert Type="@AntAlertType.Warning"
Message="Warning Text"
Banner
Closable />
<AntAlert Type="@AntAlertType.Warning"
Message="Warning Text"
Description="Very long warning text warning text text text text text text text"
Banner
Closable />
<AntAlert Type="@AntAlertType.Warning"
Message="Warning Text Without Icon"
Banner
ShowIcon="false" />
<AntAlert Type="@AntAlertType.Error"
Message="Error Text"
Banner />

View File

@ -0,0 +1 @@
<AntAlert Type="@AntAlertType.Success" Message="Success Text" />

View File

@ -0,0 +1,17 @@
<AntAlert Type="@AntAlertType.Warning"
Message="Warning Text Warning Text Warning Text Warning Text Warning Text Warning Text Warning Text"
Closable
OnClose="LogSomething" />
<AntAlert Type="@AntAlertType.Error"
Message="Error Text"
Description="Error Description Error Description Error Description Error Description Error Description Error Description"
Closable
OnClose="LogSomething" />
@code{
private void LogSomething()
{
Console.WriteLine("Logging Something...");
}
}

View File

@ -0,0 +1 @@
<AntAlert Message="Info Text" Type="@AntAlertType.Info" CloseText="Close Now" Closable/>

View File

@ -0,0 +1,15 @@
<AntAlert Message="Success Text"
Description="Success Description Success Description Success Description"
Type="@AntAlertType.Success" />
<AntAlert Message="Info Text"
Description="Info Description Info Description Info Description Info Description"
Type="@AntAlertType.Info" />
<AntAlert Message="Warning Text"
Description="Warning Description Warning Description Warning Description Warning Description"
Type="@AntAlertType.Warning" />
<AntAlert Message="Error Text"
Description="Error Description Error Description Error Description Error Description"
Type="@AntAlertType.Error" />

View File

@ -0,0 +1,35 @@
<AntAlert Type="@AntAlertType.Success"
Message="Success Tips"
ShowIcon />
<AntAlert Type="@AntAlertType.Info"
Message="Informational Notes"
ShowIcon />
<AntAlert Type="@AntAlertType.Warning"
Message="Warning"
ShowIcon />
<AntAlert Type="@AntAlertType.Error"
Message="Error"
ShowIcon />
<AntAlert Type="@AntAlertType.Success"
Message="Success Tips"
Description="Detailed description and advice about successful copywriting."
ShowIcon />
<AntAlert Type="@AntAlertType.Info"
Message="Informational Notes"
Description="Additional description and information about copywriting."
ShowIcon />
<AntAlert Type="@AntAlertType.Warning"
Message="Warning"
Description="This is a warning notice about copywriting."
ShowIcon />
<AntAlert Type="@AntAlertType.Error"
Message="Error"
Description="This is an error message about copywriting."
ShowIcon />

View File

@ -0,0 +1,7 @@
<AntAlert Message="Success Text" Type="@AntAlertType.Success" />
<AntAlert Message="Info Text" Type="@AntAlertType.Info" />
<AntAlert Message="Warning Text" Type="@AntAlertType.Warning" />
<AntAlert Message="Error Text" Type="@AntAlertType.Error" />

View File

@ -0,0 +1,18 @@
---
order: 6
iframe:
height: 220
title:
zh-CN: 顶部公告
en-US: Banner
---
## zh-CN
页面顶部通告形式,默认有图标且 `nzType``'warning'`
## en-US
Display Alert as a banner at top of page.

View File

@ -0,0 +1,20 @@
---
order: 0
title:
zh-CN: 基本
en-US: Basic
---
## zh-CN
最简单的用法,适用于简短的警告提示。
## en-US
The simplest usage for short messages.
<style>
.code-box-demo .ant-alert {
margin-bottom: 16px;
}
</style>

View File

@ -0,0 +1,15 @@
---
order: 2
title:
zh-CN: 可关闭的警告提示
en-US: Closable
---
## zh-CN
显示关闭按钮,点击可关闭警告提示。
## en-US
To show close button.

View File

@ -0,0 +1,14 @@
---
order: 5
title:
zh-CN: 自定义关闭
en-US: Customized Close Text
---
## zh-CN
可以自定义关闭,自定义的内容会替换原先的关闭按钮。
## en-US
Replace the default icon with customized content.

View File

@ -0,0 +1,15 @@
---
order: 3
title:
zh-CN: 含有辅助性文字介绍
en-US: Description
---
## zh-CN
含有辅助性文字介绍的警告提示。
## en-US
Additional description for alert message.

View File

@ -0,0 +1,16 @@
---
order: 4
title:
zh-CN: 图标
en-US: Icon
---
## zh-CN
可口的图标让信息类型更加醒目。
## en-US
Decent icon make information more clear and more friendly.

View File

@ -0,0 +1,16 @@
---
order: 1
title:
zh-CN: 四种样式
en-US: More types
---
## zh-CN
共有四种样式 `success`、`info`、`warning`、`error`。
## en-US
There are 4 types of Alert: `success`, `info`, `warning`, `error`.

View File

@ -0,0 +1,13 @@
---
category: Components
type: Feedback
title: Alert
---
Alert component for feedback.
## When To Use
- When you need to show alert messages to users.
- When you need a persistent static container which is closable by user actions.

View File

@ -0,0 +1,13 @@
---
category: Components
subtitle: 警告提示
type: 反馈
title: Alert
---
警告提示,展现需要关注的信息。
## 何时使用
- 当某个页面需要向用户显示警告的信息时。
- 非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。

View File

@ -0,0 +1,12 @@
<div>
<span class="avatar-item">
<AntBadge Count="1">
<AntAvatar Shape="square" Icon="user" />
</AntBadge>
</span>
<span>
<AntBadge Dot>
<AntAvatar Shape="square" Icon="user"/>
</AntBadge>
</span>
</div>

View File

@ -0,0 +1,14 @@
<div>
<div>
<AntAvatar Size="64" Icon="user" />
<AntAvatar Size="large" Icon="user" />
<AntAvatar Icon="user" />
<AntAvatar Size="small" Icon="user" />
</div>
<div>
<AntAvatar Shape="square" Size="64" Icon="user" />
<AntAvatar Shape="square" Size="large" Icon="user" />
<AntAvatar Shape="square" Icon="user" />
<AntAvatar Shape="square" Size="small" Icon="user" />
</div>
</div>

View File

@ -0,0 +1,28 @@
<div>
<AntAvatar Style="@($"background-color: {color}; vertical-align: middle;")" Size="large">
@user
</AntAvatar>
<AntButton
Size="small"
Style="margin:0 16px; vertical-align: middle;"
Onclick="_=>changeUser()"
>
Change
</AntButton>
</div>
@code
{
private static string[] userList = {"U", "Lucy", "Tom", "Edward"};
private static string[] colorList = {"#f56a00", "#7265e6", "#ffbf00", "#00a2ae"};
private string user { get; set; } = userList[0];
private string color { get; set; } = colorList[0];
private void changeUser()
{
var index = Array.IndexOf(userList, user);
user = index < userList.Length - 1 ? userList[index + 1] : userList[0];
color = index < colorList.Length - 1 ? colorList[index + 1] : colorList[0];
}
}

View File

@ -0,0 +1,8 @@
<div>
<AntAvatar Icon="user" />
<AntAvatar>U</AntAvatar>
<AntAvatar>USER</AntAvatar>
<AntAvatar Src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
<AntAvatar Style="color: #f56a00; background-color: #fde3cf; ">U</AntAvatar>
<AntAvatar Style="background-color: #87d068" Icon="user" />
</div>

View File

@ -0,0 +1,16 @@
---
order: 3
title:
zh-CN: 带徽标的头像
en-US: With Badge
---
## zh-CN
通常用于消息提示。
## en-US
Usually used for messages remind.

View File

@ -0,0 +1,15 @@
---
order: 0
title:
zh-CN: 基本
en-US: Basic
---
## zh-CN
头像有三种尺寸,两种形状可选。
## en-US
Three sizes and two shapes are available.

View File

@ -0,0 +1,14 @@
---
order: 2
title:
zh-CN: 自动调整字符大小
en-US: Autoset Font Size
---
## zh-CN
对于字符型的头像,当字符串较长时,字体大小可以根据头像宽度自动调整。
## en-US
For letter type Avatar, when the letters are too long to display, the font size can be automatically adjusted according to the width of the Avatar.

View File

@ -0,0 +1,14 @@
---
order: 1
title:
zh-CN: 类型
en-US: Type
---
## zh-CN
支持三种类型图片、Icon 以及字符,其中 Icon 和字符型可以自定义图标颜色及背景色。
## en-US
Image, Icon and letter are supported, and the latter two kinds avatar can have custom colors and background colors.

View File

@ -0,0 +1,7 @@
---
category: Components
type: Data Display
title: Avatar
---
Avatars can be used to represent people or objects. It supports images, `Icon`s, or letters.

View File

@ -0,0 +1,8 @@
---
category: Components
subtitle: 头像
type: 数据展示
title: Avatar
---
用来代表用户或事物,支持图片、图标或字符展示。

View File

@ -0,0 +1,15 @@
<div>
<AntBadge Count="5">
<a href="#" class="head-example" />
</AntBadge>
<AntBadge Count="0" ShowZero>
<a href="#" class="head-example" />
</AntBadge>
@{
RenderFragment count = @<AntIcon Type="clock-circle" Fill="#f5222d"/>;
}
<AntBadge Count="@(count)">
<a href="#" class="head-example" />
</AntBadge>
</div>

View File

@ -0,0 +1,45 @@
<div>
<div>
<AntBadge Count="@count">
<a href="#" class="head-example" />
</AntBadge>
<AntButtonGroup>
<AntButton @onclick="_=>decline()">
<AntIcon Type="minus"></AntIcon>
</AntButton>
<AntButton @onclick="_=>increase()">
<AntIcon Type="plus"></AntIcon>
</AntButton>
</AntButtonGroup>
</div>
<div style="margin-top: 10px">
<AntBadge Dot="@show">
<a href="#" class="head-example" />
</AntBadge>
<AntSwitch OnChange="onChange" Checked="@show" />
</div>
</div>
@code {
int count = 5;
bool show = true;
void increase()
{
count++;
}
void decline()
{
count--;
if (count < 0)
{
count = 0;
}
}
void onChange(bool show)
{
this.show = show;
}
}

View File

@ -0,0 +1,47 @@
<div>
<h4 style="margin-bottom: 16px">Presets:</h4>
<div>
@foreach (var color in colors)
{
<div key="@color">
<AntBadge Color="@color" Text="@color" />
</div>
}
</div>
<h4 style="margin:16px 0;">Custom:</h4>
<div>
<AntBadge Color="#f50" Text="#f50" />
<br />
<AntBadge Color="#2db7f5" Text="#2db7f5" />
<br />
<AntBadge Color="#87d068" Text="#87d068" />
<br />
<AntBadge Color="#108ee9" Text="#108ee9" />
</div>
</div>
<style>
.ant-tag {
margin-bottom: 8px;
}
</style>
@code
{
string[] colors =
{
"pink",
"red",
"yellow",
"orange",
"cyan",
"green",
"blue",
"purple",
"geekblue",
"magenta",
"volcano",
"gold",
"lime",
};
}

View File

@ -0,0 +1,11 @@
<div>
<AntBadge Dot>
<AntIcon Type="notification"/>
</AntBadge>
<AntBadge Count="0" Dot>
<AntIcon Type="notification" />
</AntBadge>
<AntBadge Dot>
<a href="#">Link something</a>
</AntBadge>
</div>

View File

@ -0,0 +1,5 @@
<a href="#">
<AntBadge Count="5">
<span class="head-example" />
</AntBadge>
</a>

View File

@ -0,0 +1,13 @@
<div>
<AntBadge Count="25" />
<AntBadge Count="4" Class="site-badge-count-4" />
<AntBadge Class="site-badge-count-109" Count="109" Style="background-color: #52c41a" />
</div>
<style>
.site-badge-count-4 .ant-badge-count {
background-color: #fff;
color: #999;
box-shadow: 0 0 0 1px #d9d9d9 inset;
}
</style>

View File

@ -0,0 +1,14 @@
<div>
<AntBadge Count="99">
<a href="#" class="head-example" />
</AntBadge>
<AntBadge Count="100">
<a href="#" class="head-example" />
</AntBadge>
<AntBadge Count="99" OverflowCount="10">
<a href="#" class="head-example" />
</AntBadge>
<AntBadge Count="1000" OverflowCount="999">
<a href="#" class="head-example" />
</AntBadge>
</div>

View File

@ -0,0 +1,17 @@
<div>
<AntBadge Status="success" />
<AntBadge Status="error" />
<AntBadge Status="default" />
<AntBadge Status="processing" />
<AntBadge Status="warning" />
<br />
<AntBadge Status="success" Text="Success" />
<br />
<AntBadge Status="error" Text="Error" />
<br />
<AntBadge Status="default" Text="Default" />
<br />
<AntBadge Status="processing" Text="Processing" />
<br />
<AntBadge Status="warning" Text="Warning" />
</div>

View File

@ -0,0 +1,5 @@
<div>
<AntBadge Count="5" Title="Custom hover text">
<a href="#" class="head-example" />
</AntBadge>
</div>

View File

@ -0,0 +1,35 @@
---
order: 0
title:
zh-CN: 基本
en-US: Basic
---
## zh-CN
简单的徽章展示,当 `count``0` 时,默认不显示,但是可以使用 `showZero` 修改为显示。
## en-US
Simplest Usage. Badge will be hidden when `count` is `0`, but we can use `showZero` to show it.
<style>
.ant-badge:not(.ant-badge-not-a-wrapper) {
margin-right: 20px;
}
.ant-badge.ant-badge-rtl:not(.ant-badge-not-a-wrapper) {
margin-right: 0;
margin-left: 20px;
}
.head-example {
width: 42px;
height: 42px;
border-radius: 2px;
background: #eee;
display: inline-block;
vertical-align: middle;
}
[data-theme="dark"] .head-example {
background: rgba(255,255,255,.12);
}
</style>

View File

@ -0,0 +1,15 @@
---
order: 4
title:
zh-CN: 动态
en-US: Dynamic
---
## zh-CN
展示动态变化的效果。
## en-US
The count will be animated as it changes.

View File

@ -0,0 +1,14 @@
---
order: 8
title:
zh-CN: 多彩徽标
en-US: Colorful Badge
---
## zh-CN
我们添加了多种预设色彩的徽标样式,用作不同场景使用。如果预设值不能满足你的需求,可以设置为具体的色值。
## en-US
We preset a series of colorful Badge styles for use in different situations. You can also set it to a hex color string for custom color.

View File

@ -0,0 +1,23 @@
---
order: 3
title:
zh-CN: 讨嫌的小红点
en-US: Red badge
---
## zh-CN
没有具体的数字。
## en-US
This will simply display a red badge, without a specific count. If count equals 0, it won't display the dot.
<style>
.anticon-notification {
width: 16px;
height: 16px;
line-height: 16px;
font-size: 16px;
}
</style>

View File

@ -0,0 +1,14 @@
---
order: 3
title:
zh-CN: 可点击
en-US: Clickable
---
## zh-CN
用 a 标签进行包裹即可。
## en-US
The badge can be wrapped with `a` tag to make it linkable.

View File

@ -0,0 +1,30 @@
---
order: 1
title:
zh-CN: 独立使用
en-US: Standalone
---
## zh-CN
不包裹任何元素即是独立使用,可自定样式展现。
> 在右上角的 badge 则限定为红色。
## en-US
Used in standalone when children is empty.
<style>
.ant-badge-not-a-wrapper:not(.ant-badge-status) {
margin-right: 8px;
}
.ant-badge.ant-badge-rtl:not(.ant-badge-not-a-wrapper) {
margin-right: 0;
margin-left: 20px;
}
[data-theme="dark"] .site-badge-count-4 .ant-badge-count {
background-color: #141414;
box-shadow: 0 0 0 1px #434343 inset;
}
</style>

View File

@ -0,0 +1,15 @@
---
order: 2
title:
zh-CN: 封顶数字
en-US: Overflow Count
---
## zh-CN
超过 `overflowCount` 的会显示为 `${overflowCount}+`,默认的 `overflowCount``99`
## en-US
`${overflowCount}+` is displayed when count is larger than `overflowCount`. The default value of `overflowCount` is `99`.

View File

@ -0,0 +1,15 @@
---
order: 6
title:
zh-CN: 状态点
en-US: Status
---
## zh-CN
用于表示状态的小圆点。
## en-US
Standalone badge with status.

View File

@ -0,0 +1,37 @@
---
order: 100
title:
zh-CN: 自定义标题
en-US: Title
debug: true
---
## zh-CN
设置鼠标放在状态点上时显示的文字。
## en-US
The badge will display `title` when hovered over, instead of `count`.
<style>
.ant-badge:not(.ant-badge-not-a-wrapper) {
margin-right: 20px;
}
.ant-badge.ant-badge-rtl:not(.ant-badge-not-a-wrapper) {
margin-right: 0;
margin-left: 20px;
}
.head-example {
width: 42px;
height: 42px;
border-radius: 2px;
background: #eee;
display: inline-block;
}
[data-theme="dark"] .head-example {
background: rgba(255,255,255,.12);
}
</style>

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