Skip to content

Commit 7e94c34

Browse files
committed
Merge branch 'rel-1.7'
2 parents c4b462b + 2813062 commit 7e94c34

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+588
-373
lines changed

Documentation/Blazorise.Docs.Compiler/ApiDocsGenerator/ComponentsApiDocsGenerator.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@ public class ComponentsApiDocsGenerator
3737

3838
readonly string[] skipMethods = ["Dispose", "DisposeAsync", "Equals", "GetHashCode", "GetType", "MemberwiseClone", "ToString", "GetEnumerator"];
3939

40+
readonly SearchHelper searchHelper;
41+
4042
#endregion
4143

4244
#region Constructors
4345

4446
public ComponentsApiDocsGenerator()
4547
{
48+
searchHelper = new SearchHelper();
4649
var aspnetCoreAssemblyName = typeof( Microsoft.AspNetCore.Components.ParameterAttribute ).Assembly.GetName().Name;
4750
aspNetCoreComponentsAssembly = AppDomain.CurrentDomain
4851
.GetAssemblies()
@@ -202,7 +205,14 @@ private IEnumerable<ComponentInfo> GetComponentsInfo( Compilation compilation, I
202205
Properties: parameterProperties,
203206
InheritsFromChain: typeQualification.NamedTypeSymbols ?? [],
204207
Category: typeQualification.Category,
205-
Subcategory: typeQualification.Subcategory
208+
Subcategory: typeQualification.Subcategory,
209+
SearchUrl: searchHelper.GetSearchUrl( type ),
210+
Summary:
211+
$""""
212+
$"""
213+
{StringHelpers.ExtractFromXmlComment( type, ExtractorParts.Summary )}
214+
"""
215+
""""
206216
);
207217
}
208218
}
@@ -294,7 +304,8 @@ private static string GenerateComponentsApiSource( Compilation compilation, Immu
294304

295305
ApiDocsForComponent comp = new( type: componentType, typeName: componentTypeName,
296306
properties: propertiesData, methods: methodsData,
297-
inheritsFromChain: component.InheritsFromChain.Select( type => type.ToStringWithGenerics() ), component.Category, component.Subcategory );
307+
inheritsFromChain: component.InheritsFromChain.Select( type => type.ToStringWithGenerics() ),
308+
component.Category, component.Subcategory, component.SearchUrl, component.Summary );
298309

299310
return comp;
300311
} );
@@ -344,9 +355,11 @@ public class ComponentApiSource_ForNamespace_{{assemblyName.Replace( ".", "_" )}
344355
},
345356
new List<Type>{
346357
{{comp.InheritsFromChain.Select( x => $"typeof({x})" ).StringJoin( "," )}}
347-
}
358+
},
359+
{{comp.Summary}}
348360
349361
{{( comp.Category is null ? "" : $""","{comp.Category}" {( comp.Subcategory is null ? "" : $""", "{comp.Subcategory}" """ )} """ )}}
362+
{{( string.IsNullOrWhiteSpace( comp.SearchUrl ) ? "" : $""", searchUrl:"{comp.SearchUrl}" """ )}}
350363
)},
351364
352365
""";

Documentation/Blazorise.Docs.Compiler/ApiDocsGenerator/Dtos/ApiDocsForComponent.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ public ApiDocsForComponent( string type, string typeName,
1515
IEnumerable<ApiDocsForComponentMethod> methods,
1616
IEnumerable<string> inheritsFromChain,
1717
string category,
18-
string subcategory
18+
string subcategory,
19+
string searchUrl,
20+
string summary
1921
)
2022
{
2123
Type = type;
@@ -25,8 +27,14 @@ string subcategory
2527
InheritsFromChain = inheritsFromChain;
2628
Category = category;
2729
Subcategory = subcategory;
30+
SearchUrl = searchUrl;
31+
Summary = summary;
2832
}
2933

34+
public string SearchUrl { get; }
35+
36+
public string Summary { get; set; }
37+
3038
public string Type { get; }
3139

3240
public string TypeName { get; }

Documentation/Blazorise.Docs.Compiler/ApiDocsGenerator/Dtos/ComponentInfo.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
namespace Blazorise.Docs.Compiler.ApiDocsGenerator.Dtos;
55

6-
public record ComponentInfo( INamedTypeSymbol Type, IEnumerable<IPropertySymbol> Properties, IEnumerable<IMethodSymbol> PublicMethods, IEnumerable<INamedTypeSymbol> InheritsFromChain, string Category, string Subcategory )
6+
public record ComponentInfo( INamedTypeSymbol Type, IEnumerable<IPropertySymbol> Properties, IEnumerable<IMethodSymbol> PublicMethods, IEnumerable<INamedTypeSymbol> InheritsFromChain, string Category, string Subcategory, string SearchUrl, string Summary )
77
{
88
public INamedTypeSymbol Type { get; } = Type;
99
public IEnumerable<IPropertySymbol> Properties { get; } = Properties;
1010
public IEnumerable<IMethodSymbol> PublicMethods { get; } = PublicMethods;
1111
public IEnumerable<INamedTypeSymbol> InheritsFromChain { get; } = InheritsFromChain;
1212
public string Category { get; set; } = Category;
1313
public string Subcategory { get; set; } = Subcategory;
14+
public string SearchUrl { get; set; } = SearchUrl;
15+
public string Summary { get; set; } = Summary;
1416
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#region Using directives
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using Microsoft.CodeAnalysis;
6+
#endregion
7+
8+
namespace Blazorise.Docs.Compiler.ApiDocsGenerator.Helpers;
9+
10+
public class SearchHelper
11+
{
12+
/// <summary>
13+
/// Resolves the appropriate search URL for a given type based on its source file location.
14+
/// The resolution is driven by a mapping of file path segments to base documentation URLs,
15+
/// combined with configurable strategies that define how the final URL should be constructed
16+
/// (e.g., appending the type name or using the directory name).
17+
/// </summary>
18+
/// <param name="typeSymbol">The type symbol representing the component or class to resolve the URL for.</param>
19+
/// <returns>The resolved documentation search URL, or <c>null</c> if no matching path was found.</returns>
20+
public string GetSearchUrl( INamedTypeSymbol typeSymbol )
21+
{
22+
foreach ( var syntaxRef in typeSymbol.DeclaringSyntaxReferences )//the symbol can be in multiple files (partial classes), this kinda ignores the case when different location would break the search
23+
{
24+
string filePath = syntaxRef.SyntaxTree.FilePath;
25+
string normalizedFilePath = Path.GetFullPath( filePath ).Replace( Path.DirectorySeparatorChar, '/' );
26+
27+
var link = sortedPathToSearchUrlsMap.FirstOrDefault( entry => normalizedFilePath.Contains( entry.Segment ) );
28+
if ( link is null )
29+
continue;
30+
31+
return link.Strategy switch
32+
{
33+
PathResolverStrategy.DirectoryNameToKebabCase => Path.Combine( link.SearchUrlBase, ToKebabCase( Path.GetFileName( Path.GetDirectoryName( normalizedFilePath ) ) ?? "" ) ),
34+
PathResolverStrategy.Default => link.SearchUrlBase,
35+
_ => link.SearchUrlBase
36+
};
37+
}
38+
39+
return null;
40+
}
41+
42+
// Match the most specific (longest) path segment first.
43+
// For example, a file in "Source/Blazorise/Themes/Colors/ThemeColor.cs"
44+
// will match the segment "Source/Blazorise/Themes/Colors/" over "Source/Blazorise/Themes/"
45+
readonly List<SearchPathMapping> sortedPathToSearchUrlsMap = PathToSearchUrlsMap
46+
.OrderByDescending( entry => entry.Segment.Length )
47+
.ToList();
48+
49+
static readonly List<SearchPathMapping> PathToSearchUrlsMap =
50+
[
51+
new( "Source/Blazorise/Components/", "docs/components/" , PathResolverStrategy.DirectoryNameToKebabCase),
52+
new( "Source/Blazorise/Themes/Colors/", "docs/theming/api/colors" ),
53+
new( "Source/Blazorise/Themes/Options/", "docs/theming/api/options" ),
54+
new( "Source/Blazorise/Themes/Palettes/", "docs/theming/api/palettes" ),
55+
new( "Source/Blazorise/Themes/", "docs/theming/api" )
56+
];
57+
58+
public enum PathResolverStrategy
59+
{
60+
Default, // Use the base URL only
61+
DirectoryNameToKebabCase // Append the containing folder name to the base URL // Append the type name to the base URL
62+
}
63+
64+
//FileEdit -> file-edit
65+
static string ToKebabCase( string input ) =>
66+
string.Concat(
67+
input.Select( ( c, i ) =>
68+
char.IsUpper( c )
69+
? ( i > 0 ? "-" : "" ) + char.ToLowerInvariant( c )
70+
: c.ToString()
71+
) );
72+
}
73+
74+
public record SearchPathMapping( string Segment, string SearchUrlBase, SearchHelper.PathResolverStrategy Strategy = SearchHelper.PathResolverStrategy.Default );

Documentation/Blazorise.Docs.Compiler/ApiDocsGenerator/Helpers/StringHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ internal static string GetSimplifiedTypeName( ITypeSymbol typeSymbol, bool witho
128128
if ( typeSymbol is INamedTypeSymbol genericType && genericType.IsGenericType )
129129
{
130130
var baseName = typeMap.ContainsKey( genericType.Name ) ? typeMap[genericType.Name] : genericType.Name;
131-
if ( withoutGenerics )
131+
if ( withoutGenerics || genericType.Name.Contains( "ValueTuple" ) )
132132
return baseName;
133133
var typeArguments = string.Join( ", ", genericType.TypeArguments.Select( x => GetSimplifiedTypeName( x ) ) );
134134
return $"{baseName}<{typeArguments}>";
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Collections.Generic;
2+
3+
namespace Blazorise.Docs.Server.Infrastructure;
4+
5+
static class PermanentRedirects
6+
{
7+
public static readonly Dictionary<string, string> Map = new()
8+
{
9+
["/docs/components/date"] = "/docs/components/date-edit",
10+
["/docs/components/file"] = "/docs/components/file-edit",
11+
["/docs/components/memo"] = "/docs/components/memo-edit",
12+
["/docs/components/numeric"] = "/docs/components/numeric-edit",
13+
["/docs/components/text"] = "/docs/components/text-edit",
14+
["/docs/components/time"] = "/docs/components/time-edit",
15+
["/docs/components/color"] = "/docs/components/color-edit",
16+
["/docs/components/dragdrop"] = "/docs/components/drag-drop",
17+
};
18+
}

Documentation/Blazorise.Docs.Server/Startup.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#region Using directives
12
using System;
23
using System.IO.Compression;
34
using System.Linq;
@@ -10,16 +11,19 @@
1011
using Blazorise.Docs.Options;
1112
using Blazorise.Docs.Server.Infrastructure;
1213
using Blazorise.Docs.Services;
14+
using Blazorise.Docs.Services.Search;
1315
using Blazorise.FluentValidation;
1416
using Blazorise.Icons.FontAwesome;
1517
using Blazorise.RichTextEdit;
1618
using FluentValidation;
1719
using Microsoft.AspNetCore.Builder;
1820
using Microsoft.AspNetCore.Hosting;
21+
using Microsoft.AspNetCore.Http;
1922
using Microsoft.AspNetCore.ResponseCompression;
2023
using Microsoft.Extensions.Configuration;
2124
using Microsoft.Extensions.DependencyInjection;
2225
using Microsoft.Extensions.Hosting;
26+
#endregion
2327

2428
namespace Blazorise.Docs.Server;
2529

@@ -71,7 +75,7 @@ public void ConfigureServices( IServiceCollection services )
7175
services.AddMemoryCache();
7276
services.AddScoped<Shared.Data.EmployeeData>();
7377
services.AddScoped<Shared.Data.CountryData>();
74-
services.AddScoped<Shared.Data.PageEntryData>();
78+
services.AddSingleton<SearchEntriesProvider>();
7579

7680
var emailOptions = Configuration.GetSection( "Email" ).Get<EmailOptions>();
7781
services.AddSingleton<IEmailOptions>( serviceProvider => emailOptions );
@@ -140,5 +144,20 @@ public void Configure( WebApplication app )
140144
app.MapGet( "/sitemap.txt", SeoGenerator.GenerateSitemap );
141145
app.MapGet( "/sitemap.xml", SeoGenerator.GenerateSitemapXml );
142146
app.MapGet( "/feed.rss", SeoGenerator.GenerateRssFeed );
147+
148+
//permanent redirects
149+
app.Use( async ( context, next ) =>
150+
{
151+
var path = context.Request.Path.Value?.ToLowerInvariant();
152+
153+
if ( path is not null && PermanentRedirects.Map.TryGetValue( path, out var newPath ) )
154+
{
155+
context.Response.StatusCode = StatusCodes.Status301MovedPermanently;
156+
context.Response.Headers.Location = newPath;
157+
return;
158+
}
159+
160+
await next();
161+
} );
143162
}
144163
}

Documentation/Blazorise.Docs/Components/ComponentApiDocs.razor

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@
4747
/// Defines the name of the subcategory, or a sub folder within the Blazorise source that will be part of the search critera.
4848
/// </summary>
4949
[Parameter] public string Subcategory { get; set; }
50-
50+
51+
/// <summary>
52+
/// Defines the specific component to be used
53+
/// </summary>
54+
[Parameter] public ApiDocsForComponent ApiDocsForComponent { get; set; }
5155

5256
IReadOnlyList<ApiDocsForComponent> apiDocsForComponents;
5357
List<ApiDocsForComponent> apiDocsForComponentsMethods;
@@ -56,7 +60,9 @@
5660

5761
protected override void OnInitialized()
5862
{
59-
apiDocsForComponents = Category is not null
63+
apiDocsForComponents = ApiDocsForComponent is not null
64+
? [ApiDocsForComponent]
65+
: Category is not null
6066
? ComponentsApiDocsSource.Instance.Components.Where( x => x.Value.Category == Category && x.Value.Subcategory == Subcategory ).Select( x => x.Value ).ToList()
6167
: ComponentTypes.Select( x => ComponentsApiDocsSource.Instance.Components.GetValueOrDefault( x ) ).Where( x => x is not null ).ToList();
6268
hasComponentTypes = apiDocsForComponents.Any();

Documentation/Blazorise.Docs/Layouts/DocsLayout.razor

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@using Blazorise.Docs.Services
2+
@using Blazorise.Docs.Models
23
@inherits BaseDocsLayout
34

45
<Layout Sider Class="b-docs">
@@ -19,14 +20,14 @@
1920
<BarItem Margin="Margin.Is2.OnAll">
2021
<Autocomplete TItem="PageEntry"
2122
TValue="string"
22-
Data="@SearchEntries"
23+
Data="@SearchMenuProvider.Entries"
2324
FreeTyping
2425
TextField="@(( item ) => item.Name)"
2526
ValueField="@(( item ) => item.Url)"
2627
@bind-SelectedText="selectedSearchText"
2728
Placeholder="Search..."
2829
Filter="AutocompleteFilter.Contains"
29-
CustomFilter="@(( item, searchValue ) => item.Name.IndexOf( searchValue, 0, StringComparison.CurrentCultureIgnoreCase ) >= 0 )"
30+
CustomFilter="@(( item, searchValue ) => item.Name.IndexOf( searchValue, 0, StringComparison.CurrentCultureIgnoreCase ) >= 0)"
3031
SelectedValueChanged="@ComponentSearchSelectedValueChanged">
3132
<NotFoundContent>
3233
Sorry... @context was not found! :(
@@ -98,16 +99,16 @@
9899
<BarDropdownItem To="docs/components/carousel">Carousel</BarDropdownItem>
99100
<BarDropdownItem To="docs/components/check">Check</BarDropdownItem>
100101
<BarDropdownItem To="docs/components/close-button">Close Button</BarDropdownItem>
101-
<BarDropdownItem To="docs/components/color">Color Edit</BarDropdownItem>
102+
<BarDropdownItem To="docs/components/color-edit">Color Edit</BarDropdownItem>
102103
<BarDropdownItem To="docs/components/color-picker">Color Picker</BarDropdownItem>
103-
<BarDropdownItem To="docs/components/date">Date Edit</BarDropdownItem>
104+
<BarDropdownItem To="docs/components/date-edit">Date Edit</BarDropdownItem>
104105
<BarDropdownItem To="docs/components/date-picker">Date Picker</BarDropdownItem>
105106
<BarDropdownItem To="docs/components/divider">Divider</BarDropdownItem>
106-
<BarDropdownItem To="docs/components/dragdrop">Drag Drop</BarDropdownItem>
107+
<BarDropdownItem To="docs/components/drag-drop">Drag Drop</BarDropdownItem>
107108
<BarDropdownItem To="docs/components/dropdown">Dropdown</BarDropdownItem>
108109
<BarDropdownItem To="docs/components/field">Field</BarDropdownItem>
109110
<BarDropdownItem To="docs/components/figure">Figure</BarDropdownItem>
110-
<BarDropdownItem To="docs/components/file">File Edit</BarDropdownItem>
111+
<BarDropdownItem To="docs/components/file-edit">File Edit</BarDropdownItem>
111112
<BarDropdownItem To="docs/components/file-picker">File Picker</BarDropdownItem>
112113
<BarDropdownItem To="docs/components/focus-trap">FocusTrap</BarDropdownItem>
113114
<BarDropdownItem To="docs/components/highlighter">Highlighter</BarDropdownItem>
@@ -117,10 +118,10 @@
117118
<BarDropdownItem To="docs/components/link">Link</BarDropdownItem>
118119
<BarDropdownItem To="docs/components/list-group">List Group</BarDropdownItem>
119120
<BarDropdownItem To="docs/components/input-mask">Input Mask</BarDropdownItem>
120-
<BarDropdownItem To="docs/components/memo">Memo Edit</BarDropdownItem>
121+
<BarDropdownItem To="docs/components/memo-edit">Memo Edit</BarDropdownItem>
121122
<BarDropdownItem To="docs/components/modal">Modal</BarDropdownItem>
122123
<BarDropdownItem To="docs/components/offcanvas">Offcanvas</BarDropdownItem>
123-
<BarDropdownItem To="docs/components/numeric">Numeric Edit</BarDropdownItem>
124+
<BarDropdownItem To="docs/components/numeric-edit">Numeric Edit</BarDropdownItem>
124125
<BarDropdownItem To="docs/components/numeric-picker">Numeric Picker</BarDropdownItem>
125126
<BarDropdownItem To="docs/components/pagination">Pagination</BarDropdownItem>
126127
<BarDropdownItem To="docs/components/progress">Progress</BarDropdownItem>
@@ -136,13 +137,13 @@
136137
<BarDropdownItem To="docs/components/switch">Switch</BarDropdownItem>
137138
<BarDropdownItem To="docs/components/tab">Tab</BarDropdownItem>
138139
<BarDropdownItem To="docs/components/table">Table</BarDropdownItem>
139-
<BarDropdownItem To="docs/components/time">Time Edit</BarDropdownItem>
140+
<BarDropdownItem To="docs/components/time-edit">Time Edit</BarDropdownItem>
140141
<BarDropdownItem To="docs/components/time-picker">Time Picker</BarDropdownItem>
141142
<BarDropdownItem To="docs/components/toast" Flex="Flex.JustifyContent.Between">
142143
<DocsNewFeatureBadge>Toast</DocsNewFeatureBadge>
143144
</BarDropdownItem>
144145
<BarDropdownItem To="docs/components/tooltip">Tooltip</BarDropdownItem>
145-
<BarDropdownItem To="docs/components/text">Text Edit</BarDropdownItem>
146+
<BarDropdownItem To="docs/components/text-edit">Text Edit</BarDropdownItem>
146147
<BarDropdownItem To="docs/components/typography">Typography</BarDropdownItem>
147148
<BarDropdownItem To="docs/components/validation">Validation</BarDropdownItem>
148149
</BarDropdownMenu>
@@ -391,7 +392,8 @@
391392
{
392393
<RouterTabs />
393394
}
394-
else{
395+
else
396+
{
395397
@Body
396398
}
397399
</Column>
@@ -405,5 +407,5 @@
405407
</Layout>
406408
</Layout>
407409
@code {
408-
RenderFragment customIcon =@<Image Source="/img/logos/blazorise-small.svg" Text="Small Blazorise logo" Style="width:32px; height: 32px" />;
410+
RenderFragment customIcon =@<Image Source="/img/logos/blazorise-small.svg" Text="Small Blazorise logo" Style="width:32px; height: 32px" />;
409411
}

0 commit comments

Comments
 (0)