mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-11-29 18:48:50 +08:00
docs: dynaimc route for markdown docs
This commit is contained in:
parent
7c92f8eb14
commit
2087205bfc
1
.gitignore
vendored
1
.gitignore
vendored
@ -344,3 +344,4 @@ package-lock.json
|
||||
/components/wwwroot/js/ant-design-blazor.js
|
||||
/components/wwwroot/js/ant-design-blazor.js.map
|
||||
/site/AntBlazor.Docs/wwwroot/css/docs.css
|
||||
/site/**/wwwroot/docs
|
||||
|
@ -20,7 +20,7 @@
|
||||
<PackageLicenseExpression></PackageLicenseExpression>
|
||||
<Authors>James Yeung</Authors>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/ElderJames/ant-design-blazor/master/logo.svg?sanitize=true</PackageIconUrl>
|
||||
<RootDir>..\</RootDir>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
@ -33,23 +33,23 @@
|
||||
<PackageReference Include="OneOf" Version="2.1.151" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(RootDir)node_modules') ">
|
||||
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SolutionDir)node_modules') ">
|
||||
<!-- Ensure Node.js is installed -->
|
||||
<Exec Command="node --version" ContinueOnError="true">
|
||||
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
|
||||
</Exec>
|
||||
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
|
||||
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
|
||||
<Exec WorkingDirectory="$(RootDir)" Command="npm install" />
|
||||
<Exec WorkingDirectory="$(SolutionDir)" Command="npm install" />
|
||||
</Target>
|
||||
|
||||
<Target Name="DebugRunGulp" BeforeTargets="DebugEnsureNodeEnv" Condition=" '$(Configuration)' == 'Debug' And Exists('$(RootDir)node_modules') ">
|
||||
<Exec WorkingDirectory="$(RootDir)" Command="npm run gulp:components" />
|
||||
<Target Name="DebugRunGulp" BeforeTargets="DebugEnsureNodeEnv" Condition=" '$(Configuration)' == 'Debug' And Exists('$(SolutionDir)node_modules') ">
|
||||
<Exec WorkingDirectory="$(SolutionDir)" Command="npm run gulp:components" />
|
||||
</Target>
|
||||
|
||||
<Target Name="PublishRunGulp" AfterTargets="ComputeFilesToPublish">
|
||||
<Exec WorkingDirectory="$(RootDir)" Command="npm install" />
|
||||
<Exec WorkingDirectory="$(RootDir)" Command="npm run gulp:components" />
|
||||
<Exec WorkingDirectory="$(SolutionDir)" Command="npm install" />
|
||||
<Exec WorkingDirectory="$(SolutionDir)" Command="npm run gulp:components" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -82,10 +82,11 @@ namespace AntBlazor
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
public override void Dispose()
|
||||
{
|
||||
// To avoid leaking memory, it's important to detach any event handlers in Dispose()
|
||||
NavigationManger.LocationChanged -= OnLocationChanged;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
private void UpdateCssClass()
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="pic-plus">
|
||||
<img width="150" src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg">
|
||||
<span>+</span>
|
||||
<img height="150" src="./assets/blazor.svg">
|
||||
<img height="150" src="/docs/assets/blazor.svg">
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@ -126,7 +126,7 @@
|
||||
|
||||
如果您在使用的过程中碰到问题,可以通过 [钉钉群](https://h5.dingtalk.com/circle/healthCheckin.html?corpId=dingccf128388c3ea40eda055e4784d35b88&2f46=c9b80ba5&origin=11) 寻求帮助,同时我们也鼓励资深用户通过下面的途径给新人提供帮助。
|
||||
|
||||
<img src="./assets/dingtalk.jpg" width="300">
|
||||
<img src="/docs/assets/dingtalk.jpg" width="300">
|
||||
|
||||
## ☀️ 授权协议
|
||||
|
@ -22,4 +22,12 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AntBlazor.Docs\AntBlazor.Docs.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DocFiles Include="$(SolutionDir)docs\**\*.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyDocs" BeforeTargets="Build">
|
||||
<Copy SourceFiles="@(DocFiles)" DestinationFolder="$(ProjectDir)\wwwroot\docs\%(RecursiveDir)" ContinueOnError="true" />
|
||||
</Target>
|
||||
</Project>
|
@ -8,4 +8,12 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AntBlazor.Docs\AntBlazor.Docs.csproj" />
|
||||
</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>
|
@ -1,13 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AntBlazor.Docs.ServerApp
|
||||
{
|
||||
@ -25,4 +17,4 @@ namespace AntBlazor.Docs.ServerApp
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@
|
||||
<IsPackable>true</IsPackable>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
|
||||
<RootDir>..\..\</RootDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -17,22 +16,26 @@
|
||||
<ProjectReference Include="..\..\components\AntBlazor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(RootDir)node_modules') ">
|
||||
<ItemGroup>
|
||||
<Content Include="$(SolutionDir)docs\**" LinkBase="docs" />
|
||||
</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">
|
||||
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
|
||||
</Exec>
|
||||
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
|
||||
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
|
||||
<Exec WorkingDirectory="$(RootDir)" Command="npm install" />
|
||||
<Exec WorkingDirectory="$(SolutionDir)" Command="npm install" />
|
||||
</Target>
|
||||
|
||||
<Target Name="DebugRunGulp" BeforeTargets="DebugEnsureNodeEnv" Condition=" '$(Configuration)' == 'Debug' And Exists('$(RootDir)node_modules') ">
|
||||
<Exec WorkingDirectory="$(RootDir)" Command="npm run gulp:docs" />
|
||||
<Target Name="DebugRunGulp" BeforeTargets="DebugEnsureNodeEnv" Condition=" '$(Configuration)' == 'Debug' And Exists('$(SolutionDir)node_modules') ">
|
||||
<Exec WorkingDirectory="$(SolutionDir)" Command="npm run gulp:docs" />
|
||||
</Target>
|
||||
|
||||
<Target Name="PublishRunGulp" AfterTargets="ComputeFilesToPublish">
|
||||
<Exec WorkingDirectory="$(RootDir)" Command="npm install" />
|
||||
<Exec WorkingDirectory="$(RootDir)" Command="npm run gulp:docs" />
|
||||
<Exec WorkingDirectory="$(SolutionDir)" Command="npm install" />
|
||||
<Exec WorkingDirectory="$(SolutionDir)" Command="npm run gulp:docs" />
|
||||
</Target>
|
||||
</Project>
|
@ -1,26 +1,27 @@
|
||||
|
||||
@inject HttpClient HttpClient
|
||||
@page "/docs/{fileName}"
|
||||
|
||||
<Markdown>
|
||||
@markdown
|
||||
</Markdown>
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<article class="markdown">
|
||||
<Markdown docUrl="@docUrl"></Markdown>
|
||||
</article>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter] public string fileName { get; set; }
|
||||
|
||||
string docUrl => "https://raw.githubusercontent.com/NG-ZORRO/ng-zorro-antd/master/docs/introduce.zh-CN.md"; //fileName;
|
||||
string docUrl { get; set; }
|
||||
|
||||
string markdown = "";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(docUrl))
|
||||
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
markdown = await this.HttpClient.GetStringAsync(docUrl);
|
||||
var baseUrl = NavigationManager.ToAbsoluteUri(NavigationManager.BaseUri);
|
||||
docUrl = new Uri(baseUrl, $"docs/{fileName}.md").ToString();
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
|
||||
<Markdown>
|
||||
<p align="center">
|
||||
<a href="https://yangshunjie.com/ant-design-blazor/">
|
||||
<img src="https://raw.githubusercontent.com/ElderJames/ant-design-blazor/master/logo.svg?sanitize=true">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h1 align="center">Ant Design Blazor</h1>
|
||||
|
||||
<div align="center">
|
||||
An enterprise-class UI components based on Ant Design and Blazor.
|
||||
</div>
|
||||
</Markdown>
|
@ -71,21 +71,17 @@ namespace AntBlazor.Docs.Routing
|
||||
private void Refresh()
|
||||
{
|
||||
var relativeUri = NavigationManager.ToBaseRelativePath(_location);
|
||||
var parameters = ParseQueryString(relativeUri);
|
||||
|
||||
if (relativeUri.IndexOf('?') > -1)
|
||||
{
|
||||
relativeUri = relativeUri.Substring(0, relativeUri.IndexOf('?'));
|
||||
}
|
||||
|
||||
var segments = relativeUri.Trim().Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
var matchResult = RouteManager.Match(segments);
|
||||
var matchResult = RouteManager.Match(relativeUri);
|
||||
|
||||
if (matchResult.IsMatch)
|
||||
{
|
||||
var routeData = new RouteData(
|
||||
matchResult.MatchedRoute.Handler,
|
||||
parameters);
|
||||
var routeData = new RouteData(matchResult.MatchedRoute.PageType, matchResult.MatchedRoute.Parameters);
|
||||
|
||||
_renderHandle.Render(Found(routeData));
|
||||
}
|
||||
@ -94,21 +90,5 @@ namespace AntBlazor.Docs.Routing
|
||||
_renderHandle.Render(NotFound);
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, object> ParseQueryString(string uri)
|
||||
{
|
||||
var querystring = new Dictionary<string, object>();
|
||||
|
||||
foreach (string kvp in uri.Substring(uri.IndexOf("?") + 1).Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (kvp != "" && kvp.Contains("="))
|
||||
{
|
||||
var pair = kvp.Split('=');
|
||||
querystring.Add(pair[0], pair[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return querystring;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
namespace AntBlazor.Docs.Routing
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AntBlazor.Docs.Routing
|
||||
{
|
||||
public class MatchResult
|
||||
{
|
||||
|
@ -1,28 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AntBlazor.Docs.Routing
|
||||
{
|
||||
public class Route
|
||||
{
|
||||
public string[] UriSegments { get; set; }
|
||||
public Type Handler { get; set; }
|
||||
public Type PageType { get; set; }
|
||||
|
||||
public MatchResult Match(string[] segments)
|
||||
internal RouteTemplate Template { get; set; }
|
||||
|
||||
public string[] UnusedRouteParameterNames { get; set; }
|
||||
|
||||
public Dictionary<string, object> Parameters { get; set; }
|
||||
|
||||
public MatchResult Match(string[] segments, string relativeUri)
|
||||
{
|
||||
if (segments.Length != UriSegments.Length)
|
||||
{
|
||||
return MatchResult.NoMatch();
|
||||
}
|
||||
var parameters = ParseQueryString(relativeUri);
|
||||
|
||||
for (var i = 0; i < UriSegments.Length; i++)
|
||||
if (Template != null)
|
||||
{
|
||||
if (string.Compare(segments[i], UriSegments[i], StringComparison.OrdinalIgnoreCase) != 0)
|
||||
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 ??= new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
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)
|
||||
{
|
||||
parameters ??= new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
foreach (var name in UnusedRouteParameterNames)
|
||||
{
|
||||
parameters[name] = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.Parameters = parameters;
|
||||
|
||||
return MatchResult.Match(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (segments.Length != UriSegments.Length)
|
||||
{
|
||||
return MatchResult.NoMatch();
|
||||
}
|
||||
|
||||
for (var i = 0; i < UriSegments.Length; i++)
|
||||
{
|
||||
if (string.Compare(segments[i], UriSegments[i], StringComparison.OrdinalIgnoreCase) != 0)
|
||||
{
|
||||
return MatchResult.NoMatch();
|
||||
}
|
||||
}
|
||||
this.Parameters ??= new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
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 MatchResult.Match(this);
|
||||
return querystring;
|
||||
}
|
||||
}
|
||||
}
|
112
site/AntBlazor.Docs/Routing/RouteConstraint.cs
Normal file
112
site/AntBlazor.Docs/Routing/RouteConstraint.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
|
||||
namespace AntBlazor.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AntBlazor.core.Internal;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntBlazor.Docs.Routing
|
||||
@ -23,29 +24,65 @@ namespace AntBlazor.Docs.Routing
|
||||
if (pageType.FullName == null)
|
||||
continue;
|
||||
|
||||
var newRoute = new Route
|
||||
{
|
||||
UriSegments = pageType.FullName.Substring(pageType.FullName.IndexOf("Pages", StringComparison.Ordinal) + 6).Split('.'),
|
||||
Handler = pageType
|
||||
};
|
||||
var uriSegments = pageType.FullName.Substring(pageType.FullName.IndexOf("Pages", StringComparison.Ordinal) + 6).Split('.');
|
||||
|
||||
routesList.Add(newRoute);
|
||||
var routeAttributes = pageType.GetCustomAttributes<RouteAttribute>(inherit: false);
|
||||
|
||||
if (!routeAttributes.Any())
|
||||
{
|
||||
var newRoute = new Route
|
||||
{
|
||||
UriSegments = uriSegments,
|
||||
PageType = pageType,
|
||||
};
|
||||
routesList.Add(newRoute);
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
UriSegments = uriSegments,
|
||||
PageType = pageType,
|
||||
Template = parsedTemplate,
|
||||
UnusedRouteParameterNames = unusedRouteParameterNames
|
||||
};
|
||||
|
||||
routesList.Add(newRoute);
|
||||
}
|
||||
}
|
||||
|
||||
Routes = routesList.ToArray();
|
||||
}
|
||||
|
||||
public MatchResult Match(string[] segments)
|
||||
public MatchResult Match(string relativeUri)
|
||||
{
|
||||
var segments = relativeUri.Trim().Split('/', StringSplitOptions.RemoveEmptyEntries).Select(Uri.UnescapeDataString).ToArray();
|
||||
if (segments.Length == 0)
|
||||
{
|
||||
var indexRoute = Routes.SingleOrDefault(x => x.Handler.FullName != null && x.Handler.FullName.ToLower().EndsWith("index"));
|
||||
return MatchResult.Match(indexRoute);
|
||||
var indexRoute = Routes.SingleOrDefault(x => x.PageType.FullName != null && x.PageType.FullName.ToLower().EndsWith("index"));
|
||||
|
||||
if (indexRoute != null)
|
||||
{
|
||||
return MatchResult.Match(indexRoute);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
var matchResult = route.Match(segments);
|
||||
var matchResult = route.Match(segments, relativeUri);
|
||||
|
||||
if (matchResult.IsMatch)
|
||||
{
|
||||
@ -55,5 +92,13 @@ namespace AntBlazor.Docs.Routing
|
||||
|
||||
return MatchResult.NoMatch();
|
||||
}
|
||||
|
||||
private static string[] GetParameterNames(RouteTemplate routeTemplate)
|
||||
{
|
||||
return routeTemplate.Segments
|
||||
.Where(s => s.IsParameter)
|
||||
.Select(s => s.Value)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
21
site/AntBlazor.Docs/Routing/RouteTemplate.cs
Normal file
21
site/AntBlazor.Docs/Routing/RouteTemplate.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace AntBlazor.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; }
|
||||
}
|
||||
}
|
95
site/AntBlazor.Docs/Routing/TemplateParser.cs
Normal file
95
site/AntBlazor.Docs/Routing/TemplateParser.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using AntBlazor.Docs.Routing;
|
||||
|
||||
namespace AntBlazor.core.Internal
|
||||
{
|
||||
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[segment.Length - 1] == '}')
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid template '{template}'. Missing '{{' in parameter segment '{segment}'.");
|
||||
}
|
||||
templateSegments[i] = new TemplateSegment(originalTemplate, segment, isParameter: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (segment[segment.Length - 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);
|
||||
}
|
||||
}
|
||||
}
|
63
site/AntBlazor.Docs/Routing/TemplateSegment.cs
Normal file
63
site/AntBlazor.Docs/Routing/TemplateSegment.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace AntBlazor.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@ namespace AntBlazor.Docs
|
||||
{
|
||||
services.AddAntBlazor();
|
||||
services.AddSingleton<RouteManager>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,11 @@
|
||||
{
|
||||
var baseUrl = NavigationManager.ToAbsoluteUri(NavigationManager.BaseUri);
|
||||
MenuItems = await HttpClient.GetJsonAsync<MenuItem[]>(new Uri(baseUrl,"_content/AntBlazor.Docs/menu.json"));
|
||||
var defaultUrl = MenuItems.FirstOrDefault(x => x.Default);
|
||||
if (defaultUrl!=null)
|
||||
{
|
||||
NavigationManager.NavigateTo(defaultUrl.Url);
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
@using Markdig
|
||||
@inherits ComponentBase
|
||||
@inject HtmlRenderService HtmlRenderService
|
||||
@inject HttpClient HttpClient
|
||||
|
||||
@((MarkupString)html)
|
||||
|
||||
@ -19,6 +20,11 @@
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
|
||||
if (!string.IsNullOrEmpty(docUrl))
|
||||
{
|
||||
markdown = await this.HttpClient.GetStringAsync(docUrl);
|
||||
}
|
||||
|
||||
if (ChildContent != null)
|
||||
{
|
||||
markdown = await HtmlRenderService.RenderAsync(ChildContent);
|
||||
|
@ -1,10 +1,10 @@
|
||||
[
|
||||
//{
|
||||
// "title": "Ant Design of Blazor",
|
||||
// "type": "menuItem",
|
||||
// "url": "docs/introduce",
|
||||
// "default": true
|
||||
//},
|
||||
{
|
||||
"title": "Ant Design of Blazor",
|
||||
"type": "menuItem",
|
||||
"url": "docs/introduce",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"title": "Components",
|
||||
"type": "subMenu",
|
||||
|
Loading…
Reference in New Issue
Block a user