docs: add blazor webapp hosting project (#3642)
* 测试上传 * 有js报错 * remove ConventionRouter * fix assets and js interop --------- Co-authored-by: James Yeung <shunjiey@hotmail.com>
This commit is contained in:
Родитель
3f9443b229
Коммит
f893456a6e
|
@ -45,6 +45,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntDesign.TestApp.Server",
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AntDesign.TestApp.Client", "tests\AntDesign.TestApp\Client\AntDesign.TestApp.Client.csproj", "{5ADDB648-9417-4FE9-8A7D-0D4BF2DD3EC5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntDesign.Docs.WebApp", "site\AntDesign.Docs.WebApp\AntDesign.Docs.WebApp\AntDesign.Docs.WebApp.csproj", "{B888A244-7049-4065-937B-9881C45BA0DB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntDesign.Docs.WebApp.Client", "site\AntDesign.Docs.WebApp\AntDesign.Docs.WebApp.Client\AntDesign.Docs.WebApp.Client.csproj", "{8572A4DA-1852-4816-A876-D2E1CC96481A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -99,6 +103,14 @@ Global
|
|||
{5ADDB648-9417-4FE9-8A7D-0D4BF2DD3EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5ADDB648-9417-4FE9-8A7D-0D4BF2DD3EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5ADDB648-9417-4FE9-8A7D-0D4BF2DD3EC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B888A244-7049-4065-937B-9881C45BA0DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B888A244-7049-4065-937B-9881C45BA0DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B888A244-7049-4065-937B-9881C45BA0DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B888A244-7049-4065-937B-9881C45BA0DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8572A4DA-1852-4816-A876-D2E1CC96481A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8572A4DA-1852-4816-A876-D2E1CC96481A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8572A4DA-1852-4816-A876-D2E1CC96481A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8572A4DA-1852-4816-A876-D2E1CC96481A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -117,6 +129,8 @@ Global
|
|||
{606789E3-AFE9-4489-9963-2B06A701D6B6} = {DFD13180-D1BF-44DA-BEBE-4A54EFDEFFE2}
|
||||
{32A251DB-FF95-4325-B80F-0E3B0FA5CBC5} = {606789E3-AFE9-4489-9963-2B06A701D6B6}
|
||||
{5ADDB648-9417-4FE9-8A7D-0D4BF2DD3EC5} = {606789E3-AFE9-4489-9963-2B06A701D6B6}
|
||||
{B888A244-7049-4065-937B-9881C45BA0DB} = {752F5AE8-BA08-4C41-B9B2-D2ED12727E63}
|
||||
{8572A4DA-1852-4816-A876-D2E1CC96481A} = {752F5AE8-BA08-4C41-B9B2-D2ED12727E63}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E124DDCB-1F8D-4F96-BF41-D87019D0A412}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
|
||||
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\AntDesign.Docs\AntDesign.Docs.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,20 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using AntDesign;
|
||||
|
||||
if (!CultureInfo.CurrentCulture.Name.IsIn("en-US", "zh-CN"))
|
||||
{
|
||||
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
|
||||
CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
|
||||
}
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
|
||||
builder.Services.AddSingleton(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
|
||||
builder.Services.AddAntDesignDocs();
|
||||
await builder.Build().RunAsync();
|
|
@ -0,0 +1,9 @@
|
|||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using AntDesign.Docs.WebApp.Client
|
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AntDesign.Docs.WebApp.Client\AntDesign.Docs.WebApp.Client.csproj" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Watch Include="$(SolutionDir)**\*.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DocFiles Include="$(SolutionDir)docs\**\*.*"></DocFiles>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyDocs" BeforeTargets="Build">
|
||||
<Copy SourceFiles="@(DocFiles)" DestinationFolder="$(ProjectDir)\wwwroot\docs\%(RecursiveDir)" ContinueOnError="true" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="/" />
|
||||
|
||||
<link rel="icon" href="logo.png" type="image/x-icon" />
|
||||
<link rel="preconnect" href="https://C9UTAZSOWW-dsn.algolia.net" crossorigin />
|
||||
<link rel="stylesheet" href="@("https://fastly.jsdelivr.net/npm/@docsearch/css@3")" />
|
||||
<link href="_content/AntDesign.Docs/css/default.css" rel="stylesheet" />
|
||||
<link antblazor-css />
|
||||
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="AntDesign.Docs.WebApp.styles.css" />
|
||||
|
||||
<HeadOutlet @rendermode="InteractiveAuto" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app>
|
||||
<Routes @rendermode="InteractiveAuto" />
|
||||
</app>
|
||||
|
||||
<script src="_content/AntDesign.Charts/g2plot.js"></script>
|
||||
<script src="_content/AntDesign.Charts/ant-design-charts-blazor.js"></script>
|
||||
@* <script src="_content/AntDesign/js/ant-design-blazor.js"></script> *@
|
||||
<script src="_content/AntDesign.Docs/js/prism.js"></script>
|
||||
<script>
|
||||
window.XPrism = {};
|
||||
window.XPrism.highlight = function (code, language) {
|
||||
return Prism.highlight(code, Prism.languages[language], language);
|
||||
}
|
||||
|
||||
window.XPrism.highlightAll = function () {
|
||||
Prism.highlightAll();
|
||||
}
|
||||
</script>
|
||||
<script src="@("https://fastly.jsdelivr.net/npm/@docsearch/js@3")"></script>
|
||||
<script src="_content/AntDesign.Docs/js/docsearch.js"></script>
|
||||
|
||||
<script antblazor-js></script>
|
||||
@* <script src="/blazor.web.js" autostart="false"></script> *@
|
||||
<script src="/blazor.web.patched.js" autostart="false"></script>
|
||||
<script>
|
||||
Blazor.start({
|
||||
webAssembly: {
|
||||
loadBootResource: function (type, name, defaultUri, integrity) {
|
||||
if (type === 'dotnetjs' && name === 'dotnet.js') {
|
||||
return '/dotnet.patched.js';
|
||||
}
|
||||
return `_framework/${name}`;
|
||||
},
|
||||
configureRuntime: (builder) => {
|
||||
|
||||
builder.withConfig({
|
||||
maxParallelDownloads: 2, // Adjust this to change the WebAssembly resource throttling amount
|
||||
});
|
||||
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,36 @@
|
|||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() =>
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using AntDesign.Docs.WebApp
|
||||
@using AntDesign.Docs.WebApp.Client
|
||||
@using AntDesign.Docs.WebApp.Components
|
|
@ -0,0 +1,48 @@
|
|||
// 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 AntDesign.Docs.WebApp.Components;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents()
|
||||
.AddInteractiveWebAssemblyComponents();
|
||||
|
||||
builder.Services.AddSingleton(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"}
|
||||
}
|
||||
});
|
||||
builder.Services.AddAntDesignDocs();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseWebAssemblyDebugging();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseAntiforgery();
|
||||
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode()
|
||||
.AddInteractiveWebAssemblyRenderMode()
|
||||
.AddAdditionalAssemblies(typeof(AntDesign.Docs.WebApp.Client._Imports).Assembly, typeof(AntDesign.Docs.App).Assembly);
|
||||
|
||||
app.Run();
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:15888",
|
||||
"sslPort": 44354
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"applicationUrl": "http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"applicationUrl": "https://localhost:7193;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
h1:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.valid.modified:not([type=checkbox]) {
|
||||
outline: 1px solid #26b050;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
outline: 1px solid #e50000;
|
||||
}
|
||||
|
||||
.validation-message {
|
||||
color: #e50000;
|
||||
}
|
||||
|
||||
.blazor-error-boundary {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||
padding: 1rem 1rem 1rem 3.7rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.blazor-error-boundary::after {
|
||||
content: "An error has occurred."
|
||||
}
|
||||
|
||||
.darker-border-checkbox.form-check-input {
|
||||
border-color: #929292;
|
||||
}
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 9.2 KiB |
|
@ -1,14 +1,2 @@
|
|||
<ConfigProvider>
|
||||
<ConventionRouter AppAssembly="@typeof(MainLayout).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<Result Status="404" />
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</ConventionRouter>
|
||||
|
||||
<AntContainer />
|
||||
</ConfigProvider>
|
||||
|
||||
<Routes />
|
|
@ -5,6 +5,12 @@
|
|||
@page "/experimental/{name}"
|
||||
@page "/experimental"
|
||||
|
||||
@page "/{locale}/components"
|
||||
@page "/{locale}/components/{name}"
|
||||
@page "/{locale}/charts/{name}"
|
||||
@page "/{locale}/charts"
|
||||
@page "/{locale}/experimental/{name}"
|
||||
@page "/{locale}/experimental"
|
||||
<section class="main-container main-container-component">
|
||||
<article>
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace AntDesign.Docs.Pages
|
|||
{
|
||||
public partial class Components : ComponentBase, IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public string Locale { get; set; }
|
||||
[Parameter]
|
||||
public string Name { get; set; }
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
@page "/docs/{fileName}"
|
||||
@page "/docs"
|
||||
@page "/{locale}/docs/{fileName}"
|
||||
@page "/{locale}/docs"
|
||||
|
||||
<section class="main-container">
|
||||
<article class="markdown">
|
||||
@if (_file != null)
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace AntDesign.Docs.Pages
|
|||
{
|
||||
public partial class Docs : ComponentBase, IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public string Locale { get; set; }
|
||||
[Parameter] public string FileName { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
@layout BlankLayout
|
||||
|
||||
@page "/"
|
||||
@page "/zh-CN/"
|
||||
@page "/en-US/"
|
||||
|
||||
<PageTitle>Ant Design of Blazor</PageTitle>
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ namespace AntDesign.Docs.Pages
|
|||
{
|
||||
public partial class Index : ComponentBase, IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public string Locale { get; set; }
|
||||
private Recommend[] _recommends = { };
|
||||
|
||||
private Product[] _products = { };
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
@using System.Reflection
|
||||
@using AntDesign.Docs.Localization
|
||||
@using System.Globalization
|
||||
|
||||
<ConfigProvider>
|
||||
<Router AppAssembly="typeof(App).Assembly" OnNavigateAsync="OnNavigateAsync">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" />
|
||||
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||
</Found>
|
||||
</Router>
|
||||
<AntContainer />
|
||||
</ConfigProvider>
|
||||
|
||||
@inject ILanguageService LanguageService;
|
||||
@inject NavigationManager NavigationManager;
|
||||
@code{
|
||||
async Task OnNavigateAsync(NavigationContext navigationContext)
|
||||
{
|
||||
var relativeUri = navigationContext.Path;
|
||||
var currentCulture = LanguageService.CurrentCulture;
|
||||
|
||||
var segment = relativeUri.IndexOf('/') > 0 ? relativeUri.Substring(0, relativeUri.IndexOf('/')) : relativeUri;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(segment))
|
||||
{
|
||||
NavigationManager.NavigateTo($"{currentCulture.Name}/{relativeUri}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (segment.IsIn("zh-CN", "en-US"))
|
||||
{
|
||||
LanguageService.SetLanguage(CultureInfo.GetCultureInfo(segment));
|
||||
}
|
||||
else if (currentCulture.Name.IsIn("zh-CN", "en-US"))
|
||||
{
|
||||
NavigationManager.NavigateTo($"{currentCulture.Name}/{relativeUri}");
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo($"en-US/{relativeUri}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.Core.Extensions;
|
||||
using AntDesign.Docs.Localization;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace AntDesign.Docs.Routing
|
||||
{
|
||||
public class ConventionRouter : IComponent, IHandleAfterRender, IDisposable
|
||||
{
|
||||
private RenderHandle _renderHandle;
|
||||
private bool _navigationInterceptionEnabled;
|
||||
private string _location;
|
||||
|
||||
[Inject] private NavigationManager NavigationManager { get; set; }
|
||||
[Inject] private INavigationInterception NavigationInterception { get; set; }
|
||||
[Inject] private RouteManager RouteManager { get; set; }
|
||||
[Inject] private ILanguageService LanguageService { get; set; }
|
||||
[Inject] private IJSRuntime JsRuntime { get; set; }
|
||||
|
||||
[Parameter] public RenderFragment NotFound { get; set; }
|
||||
[Parameter] public RenderFragment<RouteData> Found { get; set; }
|
||||
|
||||
[Parameter] public Assembly AppAssembly { get; set; }
|
||||
|
||||
[Parameter] public string DefaultUrl { get; set; }
|
||||
|
||||
public void Attach(RenderHandle renderHandle)
|
||||
{
|
||||
_renderHandle = renderHandle;
|
||||
_location = NavigationManager.Uri;
|
||||
NavigationManager.LocationChanged += HandleLocationChanged;
|
||||
}
|
||||
|
||||
public Task SetParametersAsync(ParameterView parameters)
|
||||
{
|
||||
parameters.SetParameterProperties(this);
|
||||
|
||||
if (Found == null)
|
||||
{
|
||||
throw new InvalidOperationException($"The {nameof(ConventionRouter)} component requires a value for the parameter {nameof(Found)}.");
|
||||
}
|
||||
|
||||
if (NotFound == null)
|
||||
{
|
||||
throw new InvalidOperationException($"The {nameof(ConventionRouter)} component requires a value for the parameter {nameof(NotFound)}.");
|
||||
}
|
||||
|
||||
RouteManager.Initialise(AppAssembly);
|
||||
|
||||
try
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// In the server prerendering mode, it will throw an expection.
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task OnAfterRenderAsync()
|
||||
{
|
||||
if (!_navigationInterceptionEnabled)
|
||||
{
|
||||
_navigationInterceptionEnabled = true;
|
||||
await NavigationInterception.EnableNavigationInterceptionAsync();
|
||||
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
NavigationManager.LocationChanged -= HandleLocationChanged;
|
||||
}
|
||||
|
||||
private void HandleLocationChanged(object sender, LocationChangedEventArgs args)
|
||||
{
|
||||
_location = args.Location;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
var relativeUri = NavigationManager.ToBaseRelativePath(_location);
|
||||
var hash = string.Empty;
|
||||
|
||||
if (relativeUri.IndexOf('#') >= 0)
|
||||
{
|
||||
hash = relativeUri.Substring(relativeUri.IndexOf('#'), relativeUri.Length - relativeUri.IndexOf('#'));
|
||||
relativeUri = relativeUri.Substring(0, relativeUri.IndexOf('#'));
|
||||
}
|
||||
|
||||
var currentCulture = LanguageService.CurrentCulture;
|
||||
|
||||
var segment = relativeUri.IndexOf('/') > 0 ? relativeUri.Substring(0, relativeUri.IndexOf('/')) : relativeUri;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(segment))
|
||||
{
|
||||
NavigationManager.NavigateTo($"{currentCulture.Name}/{relativeUri}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (segment.IsIn("zh-CN", "en-US"))
|
||||
{
|
||||
LanguageService.SetLanguage(CultureInfo.GetCultureInfo(segment));
|
||||
}
|
||||
else if (currentCulture.Name.IsIn("zh-CN", "en-US"))
|
||||
{
|
||||
NavigationManager.NavigateTo($"{currentCulture.Name}/{relativeUri}");
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo($"en-US/{relativeUri}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var matchResult = RouteManager.Match(relativeUri);
|
||||
|
||||
if (matchResult.IsMatch)
|
||||
{
|
||||
var routeData = new RouteData(matchResult.MatchedRoute.PageType, matchResult.MatchedRoute.Parameters);
|
||||
|
||||
_renderHandle.Render(Found(routeData));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(DefaultUrl))
|
||||
{
|
||||
NavigationManager.NavigateTo($"{currentCulture}/{DefaultUrl}");
|
||||
}
|
||||
|
||||
_renderHandle.Render(NotFound);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(hash))
|
||||
{
|
||||
if (JsRuntime.IsBrowser())
|
||||
{
|
||||
((IJSInProcessRuntime)JsRuntime).InvokeVoid(JSInteropConstants.ScrollTo, $"{hash}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Task.Run(() => JsRuntime.InvokeVoidAsync(JSInteropConstants.ScrollTo, $"{hash}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
namespace AntDesign.Docs.Routing
|
||||
{
|
||||
public class MatchResult
|
||||
{
|
||||
public bool IsMatch { get; set; }
|
||||
public Route MatchedRoute { get; set; }
|
||||
|
||||
public MatchResult(bool isMatch, Route matchedRoute)
|
||||
{
|
||||
IsMatch = isMatch;
|
||||
MatchedRoute = matchedRoute;
|
||||
}
|
||||
|
||||
public static MatchResult Match(Route matchedRoute)
|
||||
{
|
||||
return new MatchResult(true, matchedRoute);
|
||||
}
|
||||
|
||||
public static MatchResult NoMatch()
|
||||
{
|
||||
return new MatchResult(false, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AntDesign.Docs.Routing
|
||||
{
|
||||
public class Route
|
||||
{
|
||||
public Type PageType { get; set; }
|
||||
|
||||
internal RouteTemplate Template { get; set; }
|
||||
|
||||
public string[] UnusedRouteParameterNames { get; set; }
|
||||
|
||||
public Dictionary<string, object> Parameters { get; set; }
|
||||
|
||||
public MatchResult Match(string[] segments, string relativeUri)
|
||||
{
|
||||
Dictionary<string, object> parameters = []; //ParseQueryString(relativeUri);
|
||||
|
||||
if (Template.Segments.Length != segments.Length)
|
||||
{
|
||||
return MatchResult.NoMatch();
|
||||
}
|
||||
|
||||
for (var i = 0; i < Template.Segments.Length; i++)
|
||||
{
|
||||
var segment = Template.Segments[i];
|
||||
var pathSegment = segments[i];
|
||||
if (!segment.Match(pathSegment, out var matchedParameterValue))
|
||||
{
|
||||
return MatchResult.NoMatch();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (segment.IsParameter)
|
||||
{
|
||||
parameters[segment.Value] = matchedParameterValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In addition to extracting parameter values from the URL, each route entry
|
||||
// also knows which other parameters should be supplied with null values. These
|
||||
// are parameters supplied by other route entries matching the same handler.
|
||||
if (UnusedRouteParameterNames.Length > 0)
|
||||
{
|
||||
foreach (var name in UnusedRouteParameterNames)
|
||||
{
|
||||
parameters[name] = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.Parameters = parameters;
|
||||
|
||||
return MatchResult.Match(this);
|
||||
}
|
||||
|
||||
private Dictionary<string, object> ParseQueryString(string uri)
|
||||
{
|
||||
// Parameters will be lazily initialized.
|
||||
Dictionary<string, object> querystring = null;
|
||||
|
||||
foreach (string kvp in uri.Substring(uri.IndexOf("?", StringComparison.Ordinal) + 1).Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (kvp != "" && kvp.Contains("="))
|
||||
{
|
||||
var pair = kvp.Split('=');
|
||||
querystring ??= new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
querystring.Add(pair[0], pair[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return querystring;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
|
||||
namespace AntDesign.Docs.Routing
|
||||
{
|
||||
internal abstract class RouteConstraint
|
||||
{
|
||||
// note: the things that prevent this cache from growing unbounded is that
|
||||
// we're the only caller to this code path, and the fact that there are only
|
||||
// 8 possible instances that we create.
|
||||
//
|
||||
// The values passed in here for parsing are always static text defined in route attributes.
|
||||
private static readonly ConcurrentDictionary<string, RouteConstraint> _cachedConstraints
|
||||
= new ConcurrentDictionary<string, RouteConstraint>();
|
||||
|
||||
public abstract bool Match(string pathSegment, out object convertedValue);
|
||||
|
||||
public static RouteConstraint Parse(string template, string segment, string constraint)
|
||||
{
|
||||
if (string.IsNullOrEmpty(constraint))
|
||||
{
|
||||
throw new ArgumentException($"Malformed segment '{segment}' in route '{template}' contains an empty constraint.");
|
||||
}
|
||||
|
||||
if (_cachedConstraints.TryGetValue(constraint, out var cachedInstance))
|
||||
{
|
||||
return cachedInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newInstance = CreateRouteConstraint(constraint);
|
||||
if (newInstance != null)
|
||||
{
|
||||
// We've done to the work to create the constraint now, but it's possible
|
||||
// we're competing with another thread. GetOrAdd can ensure only a single
|
||||
// instance is returned so that any extra ones can be GC'ed.
|
||||
return _cachedConstraints.GetOrAdd(constraint, newInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Unsupported constraint '{constraint}' in route '{template}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RouteConstraint CreateRouteConstraint(string constraint)
|
||||
{
|
||||
switch (constraint)
|
||||
{
|
||||
case "bool":
|
||||
return new TypeRouteConstraint<bool>(bool.TryParse);
|
||||
|
||||
case "datetime":
|
||||
return new TypeRouteConstraint<DateTime>((string str, out DateTime result)
|
||||
=> DateTime.TryParse(str, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
|
||||
|
||||
case "decimal":
|
||||
return new TypeRouteConstraint<decimal>((string str, out decimal result)
|
||||
=> decimal.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out result));
|
||||
|
||||
case "double":
|
||||
return new TypeRouteConstraint<double>((string str, out double result)
|
||||
=> double.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out result));
|
||||
|
||||
case "float":
|
||||
return new TypeRouteConstraint<float>((string str, out float result)
|
||||
=> float.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out result));
|
||||
|
||||
case "guid":
|
||||
return new TypeRouteConstraint<Guid>(Guid.TryParse);
|
||||
|
||||
case "int":
|
||||
return new TypeRouteConstraint<int>((string str, out int result)
|
||||
=> int.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out result));
|
||||
|
||||
case "long":
|
||||
return new TypeRouteConstraint<long>((string str, out long result)
|
||||
=> long.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out result));
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TypeRouteConstraint<T> : RouteConstraint
|
||||
{
|
||||
public delegate bool TryParseDelegate(string str, out T result);
|
||||
|
||||
private readonly TryParseDelegate _parser;
|
||||
|
||||
public TypeRouteConstraint(TryParseDelegate parser)
|
||||
{
|
||||
_parser = parser;
|
||||
}
|
||||
|
||||
public override bool Match(string pathSegment, out object convertedValue)
|
||||
{
|
||||
if (_parser(pathSegment, out var result))
|
||||
{
|
||||
convertedValue = result;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
convertedValue = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AntDesign.Docs.Localization;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.Docs.Routing
|
||||
{
|
||||
public class RouteManager
|
||||
{
|
||||
public Route[] Routes { get; private set; }
|
||||
|
||||
private readonly ILanguageService _languageService;
|
||||
|
||||
public RouteManager(ILanguageService languageService)
|
||||
{
|
||||
_languageService = languageService;
|
||||
}
|
||||
|
||||
public void Initialise(Assembly appAssembly)
|
||||
{
|
||||
var pageComponentTypes = appAssembly
|
||||
.ExportedTypes
|
||||
.Where(t => t.Namespace != null && (t.IsSubclassOf(typeof(ComponentBase))
|
||||
&& t.Namespace.Contains(".Pages")));
|
||||
|
||||
var routesList = new List<Route>();
|
||||
foreach (var pageType in pageComponentTypes)
|
||||
{
|
||||
if (pageType.FullName == null)
|
||||
continue;
|
||||
|
||||
var uriSegments = pageType.FullName.Substring(pageType.FullName.IndexOf("Pages", StringComparison.Ordinal) + 6).Split('.');
|
||||
|
||||
var routeAttributes = pageType.GetCustomAttributes<RouteAttribute>(inherit: false);
|
||||
|
||||
if (!routeAttributes.Any())
|
||||
{
|
||||
routeAttributes = new[] { new RouteAttribute($"/{string.Join("/", uriSegments)}"), };
|
||||
}
|
||||
|
||||
var templates = routeAttributes.Select(t => t.Template).ToArray();
|
||||
var parsedTemplates = templates.Select(TemplateParser.ParseTemplate).ToArray();
|
||||
var allRouteParameterNames = parsedTemplates
|
||||
.SelectMany(GetParameterNames)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
foreach (var parsedTemplate in parsedTemplates)
|
||||
{
|
||||
var unusedRouteParameterNames = allRouteParameterNames
|
||||
.Except(GetParameterNames(parsedTemplate), StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
var newRoute = new Route
|
||||
{
|
||||
PageType = pageType,
|
||||
Template = parsedTemplate,
|
||||
UnusedRouteParameterNames = unusedRouteParameterNames
|
||||
};
|
||||
|
||||
routesList.Add(newRoute);
|
||||
}
|
||||
}
|
||||
|
||||
Routes = routesList.ToArray();
|
||||
}
|
||||
|
||||
public MatchResult Match(string relativeUri)
|
||||
{
|
||||
var originalUri = relativeUri;
|
||||
|
||||
if (relativeUri.IndexOf('?') > -1)
|
||||
{
|
||||
relativeUri = relativeUri.Substring(0, relativeUri.IndexOf('?'));
|
||||
}
|
||||
|
||||
var segments = relativeUri.Trim().Split('/', StringSplitOptions.RemoveEmptyEntries).Select(Uri.UnescapeDataString).ToArray();
|
||||
|
||||
if (segments.Length == 0)
|
||||
{
|
||||
var indexRoute = Routes.SingleOrDefault(x => x.PageType.FullName != null && x.PageType.FullName.ToLower().EndsWith("index"));
|
||||
|
||||
if (indexRoute != null)
|
||||
{
|
||||
return MatchResult.Match(indexRoute);
|
||||
}
|
||||
}
|
||||
|
||||
if (segments[0] == _languageService.CurrentCulture.Name)
|
||||
{
|
||||
segments = segments[1..];
|
||||
}
|
||||
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
var matchResult = route.Match(segments, originalUri);
|
||||
|
||||
if (matchResult.IsMatch)
|
||||
{
|
||||
return matchResult;
|
||||
}
|
||||
}
|
||||
|
||||
return MatchResult.NoMatch();
|
||||
}
|
||||
|
||||
private static string[] GetParameterNames(RouteTemplate routeTemplate)
|
||||
{
|
||||
return routeTemplate.Segments
|
||||
.Where(s => s.IsParameter)
|
||||
.Select(s => s.Value)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
namespace AntDesign.Docs.Routing
|
||||
{
|
||||
[DebuggerDisplay("{TemplateText}")]
|
||||
internal class RouteTemplate
|
||||
{
|
||||
public RouteTemplate(string templateText, TemplateSegment[] segments)
|
||||
{
|
||||
TemplateText = templateText;
|
||||
Segments = segments;
|
||||
}
|
||||
|
||||
public string TemplateText { get; }
|
||||
|
||||
public TemplateSegment[] Segments { get; }
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace AntDesign.Docs.Routing
|
||||
{
|
||||
internal class TemplateParser
|
||||
{
|
||||
public static readonly char[] InvalidParameterNameCharacters =
|
||||
new char[] { '*', '?', '{', '}', '=', '.' };
|
||||
|
||||
internal static RouteTemplate ParseTemplate(string template)
|
||||
{
|
||||
var originalTemplate = template;
|
||||
template = template.Trim('/');
|
||||
if (template == "")
|
||||
{
|
||||
// Special case "/";
|
||||
return new RouteTemplate("/", Array.Empty<TemplateSegment>());
|
||||
}
|
||||
|
||||
var segments = template.Split('/');
|
||||
var templateSegments = new TemplateSegment[segments.Length];
|
||||
for (int i = 0; i < segments.Length; i++)
|
||||
{
|
||||
var segment = segments[i];
|
||||
if (string.IsNullOrEmpty(segment))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid template '{template}'. Empty segments are not allowed.");
|
||||
}
|
||||
|
||||
if (segment[0] != '{')
|
||||
{
|
||||
if (segment[^1] == '}')
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid template '{template}'. Missing '{{' in parameter segment '{segment}'.");
|
||||
}
|
||||
templateSegments[i] = new TemplateSegment(originalTemplate, segment, isParameter: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (segment[^1] != '}')
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid template '{template}'. Missing '}}' in parameter segment '{segment}'.");
|
||||
}
|
||||
|
||||
if (segment.Length < 3)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid template '{template}'. Empty parameter name in segment '{segment}' is not allowed.");
|
||||
}
|
||||
|
||||
var invalidCharacter = segment.IndexOfAny(InvalidParameterNameCharacters, 1, segment.Length - 2);
|
||||
if (invalidCharacter != -1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid template '{template}'. The character '{segment[invalidCharacter]}' in parameter segment '{segment}' is not allowed.");
|
||||
}
|
||||
|
||||
templateSegments[i] = new TemplateSegment(originalTemplate, segment.Substring(1, segment.Length - 2), isParameter: true);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < templateSegments.Length; i++)
|
||||
{
|
||||
var currentSegment = templateSegments[i];
|
||||
if (!currentSegment.IsParameter)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = i + 1; j < templateSegments.Length; j++)
|
||||
{
|
||||
var nextSegment = templateSegments[j];
|
||||
if (!nextSegment.IsParameter)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.Equals(currentSegment.Value, nextSegment.Value, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid template '{template}'. The parameter '{currentSegment}' appears multiple times.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new RouteTemplate(template, templateSegments);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace AntDesign.Docs.Routing
|
||||
{
|
||||
internal class TemplateSegment
|
||||
{
|
||||
public TemplateSegment(string template, string segment, bool isParameter)
|
||||
{
|
||||
IsParameter = isParameter;
|
||||
|
||||
if (!isParameter || segment.IndexOf(':') < 0)
|
||||
{
|
||||
Value = segment;
|
||||
Constraints = Array.Empty<RouteConstraint>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var tokens = segment.Split(':');
|
||||
if (tokens[0].Length == 0)
|
||||
{
|
||||
throw new ArgumentException($"Malformed parameter '{segment}' in route '{template}' has no name before the constraints list.");
|
||||
}
|
||||
|
||||
Value = tokens[0];
|
||||
Constraints = tokens.Skip(1)
|
||||
.Select(token => RouteConstraint.Parse(template, segment, token))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// The value of the segment. The exact text to match when is a literal.
|
||||
// The parameter name when its a segment
|
||||
public string Value { get; }
|
||||
|
||||
public bool IsParameter { get; }
|
||||
|
||||
public RouteConstraint[] Constraints { get; }
|
||||
|
||||
public bool Match(string pathSegment, out object matchedParameterValue)
|
||||
{
|
||||
if (IsParameter)
|
||||
{
|
||||
matchedParameterValue = pathSegment;
|
||||
|
||||
foreach (var constraint in Constraints)
|
||||
{
|
||||
if (!constraint.Match(pathSegment, out matchedParameterValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
matchedParameterValue = null;
|
||||
return string.Equals(Value, pathSegment, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
using System.Reflection;
|
||||
using AntDesign.Docs.Highlight;
|
||||
using AntDesign.Docs.Localization;
|
||||
using AntDesign.Docs.Routing;
|
||||
using AntDesign.Docs.Services;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
|
@ -11,7 +10,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
public static IServiceCollection AddAntDesignDocs(this IServiceCollection services)
|
||||
{
|
||||
services.AddAntDesign();
|
||||
services.AddSingleton<RouteManager>();
|
||||
services.AddScoped<DemoService>();
|
||||
services.AddScoped<IconListService>();
|
||||
services.AddSingleton<ILanguageService>(new InAssemblyLanguageService(Assembly.GetExecutingAssembly()));
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace AntDesign.Docs.Shared
|
|||
|
||||
private DemoMenuItem[] _menuItems = { };
|
||||
|
||||
private bool _firstRender;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
@ -46,12 +48,17 @@ namespace AntDesign.Docs.Shared
|
|||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_firstRender = true;
|
||||
await JsInterop.InvokeVoidAsync("window.DocSearch.init", CurrentLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnLanguageChanged(object sender, CultureInfo culture)
|
||||
{
|
||||
if (!_firstRender)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_menuItems = await DemoService.GetMenuAsync();
|
||||
await JsInterop.InvokeVoidAsync("window.DocSearch.init", culture.Name);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.JSInterop
|
||||
@using AntDesign
|
||||
@using AntDesign.Docs.Routing
|
||||
@using AntDesign.Docs.Pages
|
||||
@using AntDesign.Docs.Shared
|
||||
@using AntDesign.Docs.Localization
|
||||
|
|
Загрузка…
Ссылка в новой задаче