doc(Website): use web app template (#2386)

* refactor: 更新 App/Routes 组件

* chore: 更新 Program 文件

* refactor: 临时取消登录集成

* refactor: 增加 MVC 路由表

* chore: 更新路由配置

* chore: 合并工程

* refactor: 修改命名空间

* chore: 更新 nginx 配置文件

* chore: remove  dist folder

* doc: 更正单词拼写错误

* chore: 更新解决方案

* refactor: 更新命名空间

* chore: 更新命名空间

* chore: 更新命名空间

* chore: 整理命名空间

* chore: 更新命名空间

* chore: 更正脚本路径

* chore: 精简依赖

* refactor: 禁用授权

* refactor: 移除渲染方式

* refactor: 更改为服务器端渲染模式

* chore: 更新字典

* doc: 重构代码

* refactor: 增加 BootstrapBlazorRoot 组件

* doc: 更新 responsive 脚本

---------

Co-authored-by: zhangpeihang <948869991@qq.com>
This commit is contained in:
Argo Zhang 2023-11-17 10:24:58 +08:00 committed by GitHub
parent 14a46b0f43
commit 55957c39a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
666 changed files with 67662 additions and 312 deletions

View File

@ -26,14 +26,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "windows", "windows", "{4A52
scripts\windows\push.ps1 = scripts\windows\push.ps1
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dist", "dist", "{63E887C6-4610-4ED3-91F4-62EFDE7F442F}"
ProjectSection(SolutionItems) = preProject
dist\.gitattributes = dist\.gitattributes
dist\.nojekyll = dist\.nojekyll
dist\.spa = dist\.spa
dist\404.html = dist\404.html
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wasm", "wasm", "{B84D315E-967D-4FBF-9B72-1F3128155CB0}"
ProjectSection(SolutionItems) = preProject
scripts\wasm\sync.cmd = scripts\wasm\sync.cmd
@ -56,8 +48,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Markdown",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.TableExport", "src\Extensions\Components\BootstrapBlazor.TableExport\BootstrapBlazor.TableExport.csproj", "{5ED0DD16-1583-4FC3-B2E9-FE5DBA98BD47}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Shared", "src\BootstrapBlazor.Shared\BootstrapBlazor.Shared.csproj", "{C63F35FD-FE14-4517-9457-9DA43F0DCB9E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.Server", "src\BootstrapBlazor.Server\BootstrapBlazor.Server.csproj", "{1ED371F3-2B28-4B2D-91B8-0C00DA42CB65}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.WebAssembly.ClientHost", "src\Wasm\BootstrapBlazor.WebAssembly.ClientHost\BootstrapBlazor.WebAssembly.ClientHost.csproj", "{0556D9AB-8673-4248-8817-4D99F4DCC568}"
@ -155,10 +145,6 @@ Global
{5ED0DD16-1583-4FC3-B2E9-FE5DBA98BD47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5ED0DD16-1583-4FC3-B2E9-FE5DBA98BD47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5ED0DD16-1583-4FC3-B2E9-FE5DBA98BD47}.Release|Any CPU.Build.0 = Release|Any CPU
{C63F35FD-FE14-4517-9457-9DA43F0DCB9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C63F35FD-FE14-4517-9457-9DA43F0DCB9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C63F35FD-FE14-4517-9457-9DA43F0DCB9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C63F35FD-FE14-4517-9457-9DA43F0DCB9E}.Release|Any CPU.Build.0 = Release|Any CPU
{1ED371F3-2B28-4B2D-91B8-0C00DA42CB65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1ED371F3-2B28-4B2D-91B8-0C00DA42CB65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1ED371F3-2B28-4B2D-91B8-0C00DA42CB65}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -296,7 +282,6 @@ Global
{337FABF3-4318-408E-8544-C0F20D0197A1} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
{EEB9751A-5C06-4725-8037-FA9C0F140018} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
{5ED0DD16-1583-4FC3-B2E9-FE5DBA98BD47} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
{C63F35FD-FE14-4517-9457-9DA43F0DCB9E} = {A2182155-43ED-44C1-BF6F-1B70EBD2DFFE}
{1ED371F3-2B28-4B2D-91B8-0C00DA42CB65} = {A2182155-43ED-44C1-BF6F-1B70EBD2DFFE}
{0556D9AB-8673-4248-8817-4D99F4DCC568} = {C8E79F4C-8C55-4E13-96B5-3D2BD6A07B74}
{FFFD2EB7-AE88-4DAD-A825-528B2CEFB4B5} = {C8E79F4C-8C55-4E13-96B5-3D2BD6A07B74}

View File

@ -3,7 +3,6 @@
"path": "BootstrapBlazor.sln",
"projects": [
"src\\BootstrapBlazor.Server\\BootstrapBlazor.Server.csproj",
"src\\BootstrapBlazor.Shared\\BootstrapBlazor.Shared.csproj",
"src\\BootstrapBlazor\\BootstrapBlazor.csproj",
"test\\UniTest.Sass\\UniTest.Sass.csproj",
"test\\UnitTest\\UnitTest.csproj"

63
dist/.gitattributes vendored
View File

@ -1,63 +0,0 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* binary
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

0
dist/.nojekyll vendored
View File

2
dist/.spa vendored
View File

@ -1,2 +0,0 @@
This file is used to enable gitee pages' spa mode.
https://gitee.com/help/articles/4237

40
dist/404.html vendored
View File

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Single Page Apps for GitHub Pages</title>
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// https://github.com/rafrex/spa-github-pages
// Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
// ----------------------------------------------------------------------
// This script takes the current url and converts the path and query
// string into just a query string, and then redirects the browser
// to the new url with only a query string and hash fragment,
// e.g. http://www.foo.tld/one/two?a=b&c=d#qwe, becomes
// http://www.foo.tld/?p=/one/two&q=a=b~and~c=d#qwe
// Note: this 404.html file must be at least 512 bytes for it to work
// with Internet Explorer (it is currently > 512 bytes)
// If you're creating a Project Pages site and NOT using a custom domain,
// then set segmentCount to 1 (enterprise users may need to set it to > 1).
// This way the code will only replace the route part of the path, and not
// the real directory in which the app resides, for example:
// https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
// https://username.github.io/repo-name/?p=/one/two&q=a=b~and~c=d#qwe
// Otherwise, leave segmentCount as 0.
var segmentCount = 0;
var l = window.location;
l.replace(
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
l.pathname.split('/').slice(0, 1 + segmentCount).join('/') + '/?p=/' +
l.pathname.slice(1).split('/').slice(segmentCount).join('/').replace(/&/g, '~and~') +
(l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
l.hash
);
</script>
</head>
<body>
</body>
</html>

View File

@ -1,72 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Bootstrap Blazor 演示网站</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<meta content="bootstrap,blazor,wasm,webassembly,UI,netcore,web,assembly" name="Keywords">
<base href="/bootstrapblazor/">
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="icon-512.png">
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="style/loading.css">
<link rel="stylesheet" href="_content/BootstrapBlazor/lib/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="_content/BootstrapBlazor/lib/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="_content/BootstrapBlazor/lib/chartjs/Chart.min.css">
<link rel="stylesheet" href="_content/BootstrapBlazor/lib/summernote/summernote-bs4.min.css">
<link rel="stylesheet" href="_content/BootstrapBlazor/css/bootstrap.blazor.css">
<link rel="stylesheet" href="_content/BootstrapBlazor.Shared/lib/highlight/vs.css">
<link rel="stylesheet" href="_content/BootstrapBlazor.Shared/css/site.css">
</head>
<body class="overflow-hidden">
<app></app>
<div class="loader" id="loading">
<div class="logo">
<div class="one common"></div>
<div class="two common"></div>
<div class="three common"></div>
<div class="four common"></div>
<div class="five common"></div>
<div class="six common"></div>
<div class="seven common"></div>
<div class="eight common"></div>
</div>
<div class="intro">
<img src="_content/BootstrapBlazor.Shared/images/brand.png" />
<span>精彩即将呈现</span>
</div>
<div class="bar">
<div class="progress"></div>
</div>
</div>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss"><i class="fa-solid fa-xmark"></i></a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script>navigator.serviceWorker.register('service-worker.js');</script>
<script src="_content/BootstrapBlazor/lib/jquery/jquery.min.js"></script>
<script src="_content/BootstrapBlazor/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="_content/BootstrapBlazor/lib/chartjs/Chart.bundle.min.js"></script>
<script src="_content/BootstrapBlazor/lib/summernote/summernote-bs4.min.js"></script>
<script src="_content/BootstrapBlazor/lib/summernote/summernote-zh-CN.min.js"></script>
<script src="_content/BootstrapBlazor/lib/slimscroll/jquery.slimscroll.min.js"></script>
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.js"></script>
<script src="_content/BootstrapBlazor.Shared/lib/highlight/highlight.min.js"></script>
<script src="_content/BootstrapBlazor.Shared/js/common.js"></script>
</body>
</html>

View File

@ -38,3 +38,8 @@ xmark
Func
Prev
Textarea
rendermode
motronic
webassembly
netcore
oscs

View File

@ -1,3 +1,16 @@
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
upstream blazor {
server localhost:50853;

View File

@ -2,7 +2,7 @@
@using Azure.AI.OpenAI
@using BootstrapBlazor.Server.OAuth;
@inherits BootstrapModuleComponentBase
@attribute [JSModuleAutoLoader("./AIChat/Chats.razor.js")]
@attribute [JSModuleAutoLoader("AIChat/Chats.razor.js")]
<h3>@Localizer["ChatsTitle"]</h3>

View File

@ -5,6 +5,27 @@
<UserSecretsId>dd866c36-9a9b-4dda-bce0-44c91d3094cc</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<Content Remove="docs.json" />
<Content Remove="Locales\en.json" />
<Content Remove="Locales\zh.json" />
<Content Remove="menus.json" />
<Content Remove="topology.json" />
<Content Remove="versionconfig.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="docs.json" />
<EmbeddedResource Include="Locales\en.json" />
<EmbeddedResource Include="Locales\zh.json" />
<EmbeddedResource Include="topology.json" />
</ItemGroup>
<ItemGroup>
<None Include="menus.json" />
<None Include="versionconfig.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BootstrapBlazor.Middleware" Version="7.*" />
<PackageReference Include="Longbow.Logging" Version="6.0.6" />
@ -12,17 +33,48 @@
<PackageReference Include="Longbow.GitHubAuth" Version="7.0.1" />
<PackageReference Include="Longbow.OAuth" Version="7.0.0" />
<PackageReference Include="Longbow.Tasks" Version="5.*" />
<PackageReference Include="BootstrapBlazor.AzureOpenAI" Version="7.0.0" />
<PackageReference Include="BootstrapBlazor.BaiduSpeech" Version="7.1.0" />
<PackageReference Include="BootstrapBlazor.BaiduOcr" Version="7.1.1" />
<PackageReference Include="BootstrapBlazor.BarCode" Version="7.1.5" />
<PackageReference Include="BootstrapBlazor.Bluetooth" Version="7.1.0" />
<PackageReference Include="BootstrapBlazor.Chart" Version="7.6.1" />
<PackageReference Include="BootstrapBlazor.CherryMarkdown" Version="7.2.1" />
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="7.0.0" />
<PackageReference Include="BootstrapBlazor.Dock" Version="7.0.12" />
<PackageReference Include="BootstrapBlazor.FileViewer" Version="7.0.3" />
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="7.5.0" />
<PackageReference Include="BootstrapBlazor.Gantt" Version="7.0.0" />
<PackageReference Include="BootstrapBlazor.Html2Pdf" Version="7.2.0" />
<PackageReference Include="BootstrapBlazor.Live2DDisplay" Version="7.0.1" />
<PackageReference Include="BootstrapBlazor.Markdown" Version="7.2.2" />
<PackageReference Include="BootstrapBlazor.MaterialDesign" Version="7.0.0" />
<PackageReference Include="BootstrapBlazor.MaterialDesign.Extensions" Version="7.4.1" />
<PackageReference Include="BootstrapBlazor.OnScreenKeyboard" Version="7.0.1" />
<PackageReference Include="BootstrapBlazor.PdfReader" Version="7.2.0" />
<PackageReference Include="BootstrapBlazor.SignaturePad" Version="7.0.3" />
<PackageReference Include="BootstrapBlazor.Splitting" Version="7.0.0" />
<PackageReference Include="BootstrapBlazor.SummerNote" Version="7.3.4" />
<PackageReference Include="BootstrapBlazor.TableExport" Version="7.6.1" />
<PackageReference Include="BootstrapBlazor.Topology" Version="7.4.6" />
<PackageReference Include="BootstrapBlazor.VideoPlayer" Version="7.0.4" />
<PackageReference Include="BootstrapBlazor.MouseFollower" Version="7.0.0" />
<PackageReference Include="BootstrapBlazor.WebAPI" Version="7.5.3" />
<PackageReference Include="BootstrapBlazor.MindMap" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BootstrapBlazor.Shared\BootstrapBlazor.Shared.csproj" />
<ProjectReference Include="..\BootstrapBlazor\BootstrapBlazor.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="BootstrapBlazor.Components" />
<Using Include="BootstrapBlazor.Shared.Common" />
<Using Include="BootstrapBlazor.Shared.Components" />
<Using Include="BootstrapBlazor.Shared.Samples" />
<Using Include="BootstrapBlazor.Server.Components.Components" />
<Using Include="BootstrapBlazor.Server.Components.Layout" />
<Using Include="BootstrapBlazor.Server.Data" />
<Using Include="BootstrapBlazor.Server.Extensions" />
<Using Include="BootstrapBlazor.Server.Services" />
<Using Include="Microsoft.AspNetCore.Components" />
<Using Include="Microsoft.Extensions.Localization" />
<Using Include="System.ComponentModel.DataAnnotations" />
</ItemGroup>

View File

@ -0,0 +1,13 @@
<div class="table-attr">
<h4>@Title</h4>
<Table TItem="AttributeItem" Items="Items">
<TableColumns>
<TableColumn @bind-Field="@context.Name" />
<TableColumn @bind-Field="@context.Description" TextWrap="true" />
<TableColumn @bind-Field="@context.Type" />
<TableColumn @bind-Field="@context.ValueList" TextWrap="true" />
<TableColumn @bind-Field="@context.DefaultValue" />
</TableColumns>
</Table>
</div>

View File

@ -0,0 +1,37 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public sealed partial class AttributeTable
{
[Inject]
[NotNull]
private IStringLocalizer<AttributeTable>? Localizer { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public string? Title { get; set; }
/// <summary>
///
/// </summary>
[Parameter] public IEnumerable<AttributeItem>? Items { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
Title ??= Localizer[nameof(Title)];
}
}

View File

@ -0,0 +1 @@
<img alt="logo" class="bb-icon" src="./images/logo.png" />

View File

@ -0,0 +1,8 @@
.bb-icon {
width: 42px;
height: auto;
border-radius: var(--bs-border-radius);
background-color: var(--bs-info);
border: solid 1px #fff;
margin-right: 1rem;
}

View File

@ -0,0 +1,78 @@
@inject IOptionsMonitor<WebsiteOptions> WebsiteOption
<Reconnector>
<ReconnectingTemplate>
<div class="connection-mask"></div>
<div class="connection-body">
<div class="row g-3">
@RenderBootstrapBlazor
<div class="col-12 col-sm-5">
<h5>Reconnector 组件</h5>
<div class="mb-2"><b>正在尝试重新连接服务器</b></div>
<div class="mb-2">服务器正在更新新版本,稍等一会儿即可提供服务,或者 <kbd>F12</kbd> 打开 <b>Developer tools</b> 查看 <b>控制台</b> 是否有错误输出,请扫描左侧二维码加群与管理员联系</div>
</div>
<div class="col-12 col-sm-2">
<div class="d-flex align-items-center justify-content-center h-100">
<a href="javascript:window.Blazor.reconnect()" class="btn btn-primary">重新连接</a>
</div>
</div>
</div>
</div>
</ReconnectingTemplate>
<ReconnectFailedTemplate>
<div class="connection-mask"></div>
<div class="connection-body">
<div class="row g-3">
@RenderBootstrapBlazor
<div class="col-12 col-sm-5">
<h5>Reconnector 组件</h5>
<div class="mb-2"><b>与服务器连接失败</b></div>
<div class="mb-2">请确认网络是否正常,或者 <kbd>F12</kbd> 打开 <b>Developer tools</b> 查看 <b>控制台</b> 是否有错误输出,请扫描左侧二维码加群与管理员联系</div>
</div>
<div class="col-12 col-sm-2">
<div class="d-flex flex-column align-items-center justify-content-center h-100">
<a href="javascript:window.Blazor.reconnect()" class="btn btn-primary mb-2">重新连接</a>
<a href="javascript:location.reload()" class="btn btn-info">重新加载</a>
</div>
</div>
</div>
</div>
</ReconnectFailedTemplate>
<ReconnectRejectedTemplate>
<div class="connection-mask"></div>
<div class="connection-body">
<div class="row g-3">
@RenderBootstrapBlazor
<div class="col-12 col-sm-5">
<h5>Reconnector 组件</h5>
<div class="mb-2"><b>服务器拒绝连接</b></div>
<div class="mb-2">所有的连接尝试都被拒绝了,这很有可能是由于网络问题或者服务器问题引起的,请扫描左侧二维码加群与管理员联系</div>
</div>
<div class="col-12 col-sm-2">
<div class="d-flex flex-column align-items-center justify-content-center h-100">
<a href="javascript:location.reload()" class="btn btn-info">重新加载</a>
</div>
</div>
</div>
</div>
</ReconnectRejectedTemplate>
</Reconnector>
@code {
private string TemplateUrl => $"{WebsiteOption.CurrentValue.BootstrapBlazorLink}/wikis/%E9%A1%B9%E7%9B%AE%E6%A8%A1%E6%9D%BF%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B";
RenderFragment RenderBootstrapBlazor =>
@<div class="col-12 col-sm-5">
<h5>Bootstrap Blazor UI 组件库</h5>
<div class="d-flex">
<div class="flex-fill">
<div class="mb-2">一套基于 <b>Bootstrap</b> 样式的企业级 <b>Blazor UI</b> 组件库,支持 Server 与 WebAssembly</div>
<div class="mb-2">适配移动端支持各种主流浏览器以及移动端,适配 <b>ABP</b>,同时支持 <b>NET5/NET6/NET7</b></div>
<div class="mb-2"></div>
<div>已提供项目模板方便快速上手 <a class="connection-link" href="@TemplateUrl" target="_blank">项目模板</a></div>
</div>
<img altt="QQ" src="./images/QQGroup@2x.png" alt="QQGroup" />
<div class="connection-body-tail d-none d-sm-block"></div>
</div>
</div>;
}

View File

@ -0,0 +1,4 @@
<div>
<h4 class="mt-3">CSS Variables</h4>
@ChildContent
</div>

View File

@ -0,0 +1,17 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// CSS 组件
/// </summary>
public partial class CSS
{
/// <summary>
/// 获得/设置 子内容
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
}

View File

@ -0,0 +1,12 @@
<div class="calendar-day" @onclick="OnClickCell">
<div>@Value.CellValue.Day</div>
@if (Value.CellValue.Month == Value.CalendarValue.Month)
{
<div class="calendar-body">
@foreach (var tag in Crews)
{
<div class="@tag.Color">@tag.Name @tag.Value</div>
}
</div>
}
</div>

View File

@ -0,0 +1,56 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class CalendarCrewCell
{
/// <summary>
/// 获得/设置 单元格值
/// </summary>
[Parameter]
[NotNull]
public CalendarCellValue? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public EventCallback<CalendarCellValue> ValueChanged { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public List<Crew>? Crews { get; set; }
[Inject]
[NotNull]
private DialogService? DialogService { get; set; }
private async Task OnClickCell()
{
await DialogService.Show(new DialogOption()
{
Title = $"明细查看 - {Value.CellValue:yyyy-MM-dd}",
Component = BootstrapDynamicComponent.CreateComponent<CalendarCrewDialogBody>(new Dictionary<string, object?>()
{
[nameof(Value)] = Value,
[nameof(Crews)] = Crews
}),
OnCloseAsync = async () =>
{
if (ValueChanged.HasDelegate)
{
await ValueChanged.InvokeAsync(Value);
}
}
});
}
}

View File

@ -0,0 +1,3 @@
.calendar-day {
--bb-calendar-cell-height: 101px;
}

View File

@ -0,0 +1,19 @@
<div>
@foreach (var crew in Crews)
{
<div class="crew d-flex">
<div class="flex-fill @crew.Color">
@crew.Name
</div>
<input type="text" readonly value="@crew.Value" />
<div class="actions">
<span @onclick="() => OnUpdateValue(crew, 1)">
<i class="fa-solid fa-plus"></i>
</span>
<span @onclick="() => OnUpdateValue(crew, -1)">
<i class="fa-solid fa-minus"></i>
</span>
</div>
</div>
}
</div>

View File

@ -0,0 +1,32 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class CalendarCrewDialogBody
{
/// <summary>
/// 获得/设置 单元格值
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public CalendarCellValue? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public List<Crew>? Crews { get; set; }
private static void OnUpdateValue(Crew crew, int interval)
{
crew.Value += interval;
}
}

View File

@ -0,0 +1,35 @@
.actions {
border: 1px solid var(--bs-purple);
border-radius: var(--bs-border-radius);
padding: 1px 1rem;
}
.actions > span {
cursor: pointer;
border-radius: 50%;
width: 20px;
height: 20px;
display: inline-flex;
justify-content: center;
align-items: center;
transition: background-color .3s linear;
}
.actions > span:hover {
background-color: var(--bs-purple);
color: #fff;
}
.actions > span:not(:last-child) {
margin-right: .5rem;
}
.crew:not(:last-child) {
margin-bottom: .5rem;
}
.crew > input {
max-width: 2rem;
margin-right: 1rem;
border: none;
}

View File

@ -0,0 +1,7 @@
<div class="d-flex fw-bold mb-1">
<span class="text-success flex-fill">@Timestamp</span>
<span>共 @TotalCount 个提交</span>
</div>
<div>提交作者: <span class="text-info fw-bold">@Author</span></div>
<div>分支名称: <span class="text-info fw-bold">@Branch</span></div>
<div>提交信息: <a href="@Url" target="_blank">@Message</a></div>

View File

@ -0,0 +1,49 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class CommitItem
{
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
[EditorRequired]
public GiteePostBody? Item { get; set; }
private string? Author { get; set; }
private string? Timestamp { get; set; }
private string? Message { get; set; }
private string? Url { get; set; }
private string? Branch { get; set; }
private string? TotalCount { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
var commit = Item.HeadCommit;
TotalCount = Item.Commits?.Count.ToString() ?? "1";
if (commit != null)
{
Timestamp = commit.Timestamp.ToString("yyyy-MM-dd HH:mm:ss");
Author = commit.Author.Name;
Message = commit.Message;
Url = commit.Url;
Branch = Item.GetBranchName();
}
}
}

View File

@ -0,0 +1,8 @@
<div class="@ClassString">
<a href="@Url">
<div class="card">
<div class="card-header">@Text</div>
<div class="card-body"><img alt="url" src="@ImageUrl" /></div>
</div>
</a>
</div>

View File

@ -0,0 +1,60 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public sealed partial class ComponentCard
{
private string ImageUrl => $"./images/{Image}";
private string? ClassString => CssBuilder.Default("col-12 col-sm-6 col-md-4 col-lg-3")
.AddClass("d-none", IsHide)
.Build();
/// <summary>
/// 获得/设置 Header 文字
/// </summary>
[Parameter]
public string Text { get; set; } = "未设置";
/// <summary>
/// 获得/设置 组件图片
/// </summary>
[Parameter]
public string Image { get; set; } = "Divider.svg";
/// <summary>
/// 获得/设置 链接地址
/// </summary>
[Parameter]
public string? Url { get; set; }
[CascadingParameter]
private List<string>? ComponentNames { get; set; }
[CascadingParameter]
private ComponentCategory? Parent { get; set; }
[CascadingParameter]
private string? SearchText { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
ComponentNames?.Add(Text);
Parent?.Add(this);
}
/// <summary>
///
/// </summary>
internal bool IsHide => !string.IsNullOrEmpty(SearchText) && !Text.Contains(SearchText, StringComparison.OrdinalIgnoreCase);
}

View File

@ -0,0 +1,21 @@
<div class="@ClassString">
<h3>
<span>@Text</span>
<RenderTemplate>
<Badge IsPill="true" Color="Color.Success">@CardCount</Badge>
</RenderTemplate>
</h3>
@if (!string.IsNullOrEmpty(Desc))
{
<h4>@Desc</h4>
}
<div class="coms-demo">
<div class="row g-3">
<CascadingValue Value="this" IsFixed="true">
@ChildContent
</CascadingValue>
</div>
</div>
</div>

View File

@ -0,0 +1,55 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public sealed partial class ComponentCategory
{
/// <summary>
///
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public string? Text { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public string? Desc { get; set; }
private List<ComponentCard> Cards { get; } = new List<ComponentCard>();
internal void Add(ComponentCard card) => Cards.Add(card);
private int CardCount => Cards.Where(c => !c.IsHide).Count();
private bool IsRendered { get; set; }
private string? ClassString => CssBuilder.Default("coms-cate")
.AddClass("d-none", IsRendered && CardCount == 0)
.Build();
/// <summary>
///
/// </summary>
/// <param name="firstRender"></param>
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
if (firstRender)
{
IsRendered = true;
}
}
}

View File

@ -0,0 +1,46 @@
.coms-cate {
position: relative;
}
.coms-cate:not(:first-child) {
margin-top: 1rem;
}
.coms-cate ::deep .badge {
position: absolute;
top: 0;
margin-left: 1rem;
font-size: .65rem;
}
.coms-demo {
margin-top: 1rem;
}
.coms-demo ::deep .card {
width: 100%;
height: 100%;
transition: box-shadow .3s linear;
}
.coms-demo ::deep .card img {
max-width: calc(100%);
}
.coms-demo ::deep .card .card-header {
overflow: hidden;
white-space: nowrap;
}
.coms-demo ::deep .card .card-body {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
}
.coms-demo ::deep a {
width: calc(100%);
height: calc(100%);
color: inherit;
}

View File

@ -0,0 +1,16 @@
<ul>
@foreach (var item in Items)
{
<ContextMenuTrigger WrapperTag="li" ContextItem="item" >
<div>@item</div>
</ContextMenuTrigger>
}
</ul>
@code {
private List<string> Items { get; } = new List<string>() { "Test1", "Test2", "Test3" };
[CascadingParameter]
[NotNull]
private ContextMenuZone? Zone { get; set; }
}

View File

@ -0,0 +1,14 @@
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}

View File

@ -0,0 +1,13 @@
@inherits BootstrapComponentBase
<div @attributes="@AdditionalAttributes" class="@ClassString">
<span>@Label</span>
<Select Value="@SelectedCulture" OnSelectedItemChanged="@SetCulture">
<Options>
@foreach (var kv in BootstrapOptions.CurrentValue.GetSupportedCultures())
{
<SelectOption Text="@GetDisplayName(kv)" Value="@kv.Name" />
}
</Options>
</Select>
</div>

View File

@ -0,0 +1,98 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using Microsoft.Extensions.Options;
using System.Globalization;
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class CultureChooser
{
[Inject]
[NotNull]
private IOptionsMonitor<BootstrapBlazorOptions>? BootstrapOptions { get; set; }
[Inject]
[NotNull]
private IOptionsMonitor<WebsiteOptions>? WebsiteOption { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<CultureChooser>? Localizer { get; set; }
[Inject]
[NotNull]
private NavigationManager? NavigationManager { get; set; }
private string? ClassString => CssBuilder.Default("culture-selector")
.AddClassFromAttributes(AdditionalAttributes)
.Build();
private string SelectedCulture { get; set; } = CultureInfo.CurrentUICulture.Name;
[NotNull]
private string? Label { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
Label ??= Localizer[nameof(Label)];
}
private async Task SetCulture(SelectedItem item)
{
if (OperatingSystem.IsBrowser())
{
var cultureName = item.Value;
if (cultureName != CultureInfo.CurrentCulture.Name)
{
await JSRuntime.SetCulture(cultureName);
var culture = new CultureInfo(cultureName);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
}
}
else
{
// 使用 api 方式 适用于 Server-Side 模式
if (SelectedCulture != item.Value)
{
var culture = item.Value;
var uri = new Uri(NavigationManager.Uri).GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
var query = $"?culture={Uri.EscapeDataString(culture)}&redirectUri={Uri.EscapeDataString(uri)}";
// use a path that matches your culture redirect controller from the previous steps
NavigationManager.NavigateTo("/Culture/SetCulture" + query, forceLoad: true);
}
}
}
private static string GetDisplayName(CultureInfo culture)
{
string? ret;
if (OperatingSystem.IsBrowser())
{
ret = culture.Name switch
{
"zh-CN" => "中文(中国)",
"en-US" => "English (United States)",
_ => ""
};
}
else
{
ret = culture.NativeName;
}
return ret;
}
}

View File

@ -0,0 +1,18 @@
.culture-selector {
display: flex;
align-items: center;
}
.culture-selector span {
margin: 0;
color: var(--bs-navbar-color);
opacity: 0.85;
}
.culture-selector ::deep .select {
width: 210px;
}
.culture-selector ::deep .dropdown-menu {
--bs-dropdown-link-active-bg: #7532f9;
}

View File

@ -0,0 +1,50 @@
@inherits FilterBase
<Select Items="@Items" @bind-Value="@Value" OnSelectedItemChanged="_ => OnFilterValueChanged()"></Select>
@code {
private int Value = 10;
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
if (TableFilter != null) TableFilter.ShowMoreButton = false;
Items = new SelectedItem[]
{
new SelectedItem { Value = "10", Text = "大于 10" },
new SelectedItem { Value = "50", Text = "大于 50" },
new SelectedItem { Value = "100", Text = "大于 100" }
};
}
/// <summary>
/// 重置过滤条件方法
/// </summary>
public override void Reset()
{
Value = 10;
StateHasChanged();
}
/// <summary>
/// 生成过滤条件方法
/// </summary>
/// <returns></returns>
public override FilterKeyValueAction GetFilterConditions()
{
var filter = new FilterKeyValueAction() { Filters = new() };
filter.Filters.Add(new FilterKeyValueAction()
{
FieldKey = FieldKey,
FieldValue = Value,
FilterAction = FilterAction.GreaterThan
});
return filter;
}
}

View File

@ -0,0 +1,8 @@
<div class="row g-3">
<div class="col-12 col-sm-6">
<Select TValue="string" Items="@Items3" OnSelectedItemChanged="@OnCascadeBindSelectClick" />
</div>
<div class="col-12 col-sm-6">
<Select TValue="string" Items="@Items2" />
</div>
</div>

View File

@ -0,0 +1,50 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class CustomerSelectDialog
{
private IEnumerable<SelectedItem>? Items2;
private readonly IEnumerable<SelectedItem> Items3 = new SelectedItem[]
{
new SelectedItem ("", "请选择 ..."),
new SelectedItem ("Beijing", "北京"),
new SelectedItem ("Shanghai", "上海")
};
/// <summary>
/// 级联绑定菜单
/// </summary>
/// <param name="item"></param>
private async Task OnCascadeBindSelectClick(SelectedItem item)
{
// 模拟异步通讯获取数据
await Task.Delay(100);
if (item.Value == "Beijing")
{
Items2 = new SelectedItem[]
{
new SelectedItem("1","朝阳区"),
new SelectedItem("2","海淀区"),
};
}
else if (item.Value == "Shanghai")
{
Items2 = new SelectedItem[]
{
new SelectedItem("1","静安区"),
new SelectedItem("2","黄浦区"),
};
}
else
{
Items2 = Enumerable.Empty<SelectedItem>();
}
StateHasChanged();
}
}

View File

@ -0,0 +1,77 @@
@inject ToastService Toast
<p>同时通过传递数据主键获取数据后再显示的例子组件</p>
<p>传递的参数主键为:@DataPrimaryId</p>
<p>通过传递参数获取的数据为:</p>
@if (Model != null)
{
<ValidateForm Model="@Model" OnValidSubmit="@OnValidSubmit">
<EditorForm TModel="Foo">
<FieldItems>
<EditorItem @bind-Field="@context.Id" Editable="false"></EditorItem>
<EditorItem @bind-Field="@context.Hobby" Items="@Hobbys"></EditorItem>
</FieldItems>
</EditorForm>
<div class="form-footer mt-3">
<DialogCloseButton></DialogCloseButton>
<Button Color="Color.Primary" ButtonType="ButtonType.Submit" Icon="fa-solid fa-floppy-disk" Text="保存"></Button>
</div>
</ValidateForm>
}
@code {
[Inject]
[NotNull]
private IStringLocalizer<Foo>? Localizer { get; set; }
[CascadingParameter(Name = "BodyContext")]
private object? DataPrimaryId { get; set; }
[CascadingParameter]
[NotNull]
private Modal? Dialog { get; set; }
private Foo? Model { get; set; }
[NotNull]
private List<Foo>? Items { get; set; }
[NotNull]
private IEnumerable<SelectedItem>? Hobbys { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
Items = Foo.GenerateFoo(Localizer);
Hobbys = Foo.GenerateHobbies(Localizer);
}
protected override void OnParametersSet()
{
base.OnParametersSet();
if (DataPrimaryId is int primaryId)
{
Model = Items.FirstOrDefault(i => i.Id == primaryId);
}
}
private async Task OnValidSubmit(EditContext model)
{
// do someting like save model into db
// 关闭弹窗
// close Dialog
await Dialog.Close();
// 显示 Toast 提示弹窗
await Toast.Show(new ToastOption() { Title = "保存数据", Content = "保存成功4 秒后自动关闭" });
}
}

View File

@ -0,0 +1,29 @@
@inherits IdComponentBase
<article class="demo-block">
<AnchorLink Text="@Title" Id="@Name" TooltipText="@TooltipText" />
<p>@(new MarkupString(Introduction))</p>
<h5>@Localizer["SubTitle"]</h5>
<div class="card">
<div class="card-body">
@ChildContent
</div>
@if (ShowCode)
{
<div class="card-footer">
<div class="card-footer-code collapse" id="@Id">
<LazyLoad OnLoadConditionCheckAsync="OnLoadConditionCheckAsync">
<Pre BlockName="@Name" ShowToolbar="true" CodeFile="@CodeFile"></Pre>
</LazyLoad>
</div>
<a class="card-footer-control collapsed" href="#@Id" data-bs-toggle="collapse" role="button" aria-label="show code" @onclick="ShowPreCode">
<i class="fa-solid fa-caret-up"></i>
<span class="card-text"></span>
</a>
</div>
}
</div>
</article>

View File

@ -0,0 +1,75 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public sealed partial class DemoBlock
{
/// <summary>
/// 获得/设置 组件 Title 属性
/// </summary>
[Parameter]
[NotNull]
public string? Title { get; set; }
/// <summary>
/// 获得/设置 组件说明信息
/// </summary>
[Parameter]
public string Introduction { get; set; } = "未设置";
/// <summary>
/// 获得/设置 组件内容
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
/// <summary>
/// 获得/设置 是否显示代码块 默认 true 显示
/// </summary>
[Parameter]
public bool ShowCode { get; set; } = true;
/// <summary>
/// 获得/设置 Tooltip 提示信息文本
/// </summary>
[Parameter]
public string? TooltipText { get; set; }
/// <summary>
/// 获得/设置 友好链接锚点名称
/// </summary>
[Parameter]
public string? Name { get; set; }
[CascadingParameter(Name = "RazorFileName")]
private string? CodeFile { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<DemoBlock>? Localizer { get; set; }
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{
base.OnParametersSet();
Title ??= Localizer[nameof(Title)];
TooltipText ??= Localizer[nameof(TooltipText)];
}
private bool _showPreCode;
private void ShowPreCode()
{
_showPreCode = true;
}
private Task<bool> OnLoadConditionCheckAsync() => Task.FromResult(_showPreCode);
}

View File

@ -0,0 +1,116 @@
.card {
transition: all .3s linear;
}
.card:hover {
box-shadow: 0 0 8px 0 rgba(232,237,250,.6), 0 2px 4px 0 rgba(232,237,250,.5);
}
.card:hover .card-footer-control i {
margin-left: -1.5rem;
margin-left: -72px;
}
.card:hover .card-footer-control .card-text {
margin-left: 1rem;
}
.card:hover .card-footer-control .card-text:before {
opacity: 1;
}
.card-footer {
background-color: transparent;
}
.card-footer-code {
margin: -.5rem -1rem 0 -1rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
.card-footer-code.show + a {
margin-top: .5rem;
}
.card-footer-code .loading {
padding: .5rem;
}
.card-footer-code ::deep code {
border: none;
}
.card-footer-code ::deep .pre-code {
margin: .5rem;
}
.card-footer-control {
text-align: center;
color: #d3dce6;
display: block;
}
.card-footer-control:hover {
color: #409eff;
}
.card-footer-control i {
transition: .3s linear;
}
.card-footer-control.collapsed i {
transform: rotate(180deg);
}
.card-footer-control .card-text {
margin-left: 1.5rem;
position: absolute;
transition: all .3s linear;
}
.card-footer-control .card-text:before {
content: "Hide Code";
opacity: 0;
transition: opacity .3s linear;
}
.card-footer-control.collapsed .card-text:before {
content: "Show Code";
}
.demo-block > ::deep .anchor-link {
font-weight: var(--bb-font-weight);
color: var(--bb-title-color);
font-size: var(--bb-sub-font-size);
margin-top: 1rem;
margin-bottom: .5rem;
}
::deep .table-cell .progress {
height: 6px;
margin-top: 9px;
margin-bottom: 10px;
}
::deep .chart .btn i + span {
display: none;
}
::deep .ul-demo {
margin-bottom: 0;
}
::deep .ul-demo li:not(:last-child) {
margin-bottom: 0.25rem;
}
@media (min-width: 768px) {
::deep .chart {
max-width: 740px;
}
::deep .chart .btn i + span {
display: inline;
}
}

View File

@ -0,0 +1,6 @@
<div>@Parameter</div>
@code {
[CascadingParameter(Name = "BodyContext")]
private object? Parameter { get; set; }
}

View File

@ -0,0 +1,3 @@
<p>@((MarkupString)Localizer["Info"].Value)</p>
<Button OnClick="OnClick" Text="@Localizer["ButtonText"]" />

View File

@ -0,0 +1,29 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// DemoTabItem 组件
/// </summary>
public partial class DemoTabItem
{
[Inject]
[NotNull]
private IStringLocalizer<DemoTabItem>? Localizer { get; set; }
/// <summary>
/// OnSetTitle 回调方法
/// </summary>
[Parameter]
public Func<string, Task>? OnSetTitle { get; set; }
private async Task OnClick()
{
if (OnSetTitle != null)
{
await OnSetTitle(DateTime.Now.ToString("mm:ss"));
}
}
}

View File

@ -0,0 +1,3 @@
<p>@((MarkupString)Localizer["Info"].Value)</p>
<Button OnClick="OnClick" Text="@Localizer["ButtonText"]" />

View File

@ -0,0 +1,24 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// DemoTabItem 组件
/// </summary>
public partial class DemoTabItemSetText
{
[CascadingParameter]
[NotNull]
private TabItem? TabItem { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<DemoTabItem>? Localizer { get; set; }
private void OnClick()
{
TabItem.SetHeader(DateTime.Now.ToString("mm:ss"));
}
}

View File

@ -0,0 +1,28 @@
@using BootstrapBlazor.Server.Components.Samples.Table
<div class="row form-inline g-3">
<div class="col-12 col-sm-6">
<BootstrapInput @bind-Value="Model.Name" />
</div>
<div class="col-12 col-sm-6">
<BootstrapInput @bind-Value="Model.Address" />
</div>
<div class="col-12 col-sm-6">
<Select @bind-Value="Model.Education" />
</div>
<div class="col-12 col-sm-6">
<Display Value="@EducationDesc" ShowLabel="true" DisplayText="@Localizer["TablesEditTemplateDisplayLabel"]" />
</div>
</div>
@code {
[Parameter]
[NotNull]
public Foo? Model { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<TablesEdit>? Localizer { get; set; }
private string? EducationDesc => Model.Education == EnumEducation.Primary ? Localizer["TablesEditTemplateDisplayDetail1"] : Localizer["TablesEditTemplateDisplayDetail2"];
}

View File

@ -0,0 +1 @@
标题栏下拉框选中值:@Value

View File

@ -0,0 +1,29 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class DialogBodyFoo
{
private string? Value { get; set; }
private List<SelectedItem> Items { get; } = new(new[]
{
new SelectedItem("beijing", "北京"),
new SelectedItem("shanghai", "上海")
});
/// <summary>
///
/// </summary>
public Task UpdateAsync(string val)
{
Value = Items.First(i => i.Value == val).Text;
StateHasChanged();
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,16 @@
<h3>无限弹窗示例 @Title</h3>
<Tab>
<TabItem Text="用户管理">
<div>我是用户管理</div>
<Button Text="弹窗" OnClick="@OnClickButton" />
</TabItem>
<TabItem Text="菜单管理">
<div>我是菜单管理</div>
<Button Text="弹窗" OnClick="@OnClickButton" />
</TabItem>
<TabItem Text="角色管理">
<div>我是角色管理</div>
<Button Text="弹窗" OnClick="@OnClickButton" />
</TabItem>
</Tab>

View File

@ -0,0 +1,26 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class DialogDemo
{
[Inject]
[NotNull]
private DialogService? DialogService { get; set; }
private string Title { get; } = DateTime.Now.ToString();
private Task OnClickButton() => DialogService.Show(new DialogOption()
{
Title = "Pop-up",
IsDraggable = true,
IsKeyboard = true,
IsBackdrop = true,
Component = BootstrapDynamicComponent.CreateComponent<DialogDemo>()
});
}

View File

@ -0,0 +1,4 @@
<h5 class="modal-title flex-fill">自定义 Header</h5>
<div class="me-2">
<Select TValue="string" Items="Items" Value="Value" OnSelectedItemChanged="OnSelectedItemChanged" style="width: 120px;" />
</div>

View File

@ -0,0 +1,49 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class DialogHeaderFoo
{
[NotNull]
private IEnumerable<SelectedItem>? Items { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public string? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public Func<string, Task>? OnValueChanged { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
Items = new[]
{
new SelectedItem("beijing", "北京"),
new SelectedItem("shanghai", "上海")
};
}
private async Task OnSelectedItemChanged(SelectedItem item)
{
Value = item.Value;
if (OnValueChanged != null)
{
await OnValueChanged(Value);
}
}
}

View File

@ -0,0 +1,17 @@
<div class="row form-inline g-3">
<div class="col-12 col-sm-6">
<BootstrapInput @bind-Value="Value.Name" ShowLabel="true" DisplayText="姓名" />
</div>
<div class="col-12 col-sm-6">
<BootstrapInput @bind-Value="Value.Count" ShowLabel="true" DisplayText="数量" />
</div>
</div>
@code {
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public Foo? Value { get; set; }
}

View File

@ -0,0 +1,14 @@
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<Button class="btn btn-primary" OnClick="IncrementCount">Click me</Button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
throw new Exception("Custom Exception");
}
}

View File

@ -0,0 +1,17 @@
@inject IStringLocalizer<EventTable> Localizer
<div class="table-attr">
<h4>@Localizer["Title"]</h4>
<Table TItem="EventItem" Items="Items">
<TableColumns>
<TableColumn @bind-Field="@context.Name" />
<TableColumn @bind-Field="@context.Description" TextWrap="true" />
<TableColumn @bind-Field="@context.Type" />
</TableColumns>
</Table>
</div>
@code {
[Parameter] public IEnumerable<EventItem>? Items { get; set; }
}

View File

@ -0,0 +1,47 @@
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts = new WeatherForecast[0];
/// <summary>
///
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}

View File

@ -0,0 +1,14 @@
<div class="row form-inline g-3">
<div class="col-12 col-sm-6">
<BootstrapInput @bind-Value="Value.Name" ShowLabel="true" DisplayText="姓名" />
</div>
<div class="col-12 col-sm-6">
<DateTimeRange @bind-Value="Value.SearchDate" ShowLabel="true" DisplayText="时间" />
</div>
<div class="col-12 col-sm-6">
<Select @bind-Value="Value.Education" ShowLabel="true" DisplayText="学历" PlaceHolder="全部" />
</div>
<div class="col-12 col-sm-6">
<Select @bind-Value="Value.Count" ShowLabel="true" DisplayText="数量" Items="CountItems" />
</div>
</div>

View File

@ -0,0 +1,35 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class FooSearch
{
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public FooSearchModel? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public EventCallback<FooSearchModel> ValueChanged { get; set; }
/// <summary>
///
/// </summary>
public List<SelectedItem> CountItems { get; } = new List<SelectedItem>()
{
new SelectedItem("", "全部"),
new SelectedItem("1", "小于 30"),
new SelectedItem("2", "大于等于 30 小于 70"),
new SelectedItem("3", "大于等于 70 小于 100")
};
}

View File

@ -0,0 +1 @@
<RadioList IsButton="true" @bind-Value="FormRowType" Items="Items" AdditionalAttributes="AdditionalAttributes" />

View File

@ -0,0 +1,61 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using BootstrapBlazor.Server.Components.Samples;
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// 内部组件
/// </summary>
public partial class FormInlineSwitch
{
/// <summary>
/// 获得/设置 用户自定义属性
/// </summary>
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? AdditionalAttributes { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<Rows>? LocalizerRows { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public RowType Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public EventCallback<RowType> ValueChanged { get; set; }
[NotNull]
private IEnumerable<SelectedItem>? Items { get; set; }
private RowType FormRowType
{
get => Value; set
{
if (Value != value)
{
Value = value;
if (ValueChanged.HasDelegate)
{
ValueChanged.InvokeAsync(Value);
}
}
}
}
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
Items = Enum.GetNames<RowType>().Select(i => new SelectedItem(i, LocalizerRows[i]));
}
}

View File

@ -0,0 +1,3 @@
<div class="d-none d-xl-flex flex-xl-fill justify-content-xl-end px-3">
<Search PlaceHolder="@Localizer["Search"]" IsSelectAllTextOnEnter="true" IsSelectAllTextOnFocus="true" IsLikeMatch="true" Items="@ComponentItems" SearchButtonText="" OnSearch="OnSearch" OnSelectedItemChanged="OnSelectedItemChanged" SearchButtonColor="Color.None" class="btn-search" />
</div>

View File

@ -0,0 +1,57 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using Microsoft.Extensions.Options;
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// Pre 组件
/// </summary>
public partial class GlobalSearch
{
[Inject]
[NotNull]
private IStringLocalizer<GlobalSearch>? Localizer { get; set; }
[Inject]
[NotNull]
private IOptionsMonitor<WebsiteOptions>? WebsiteOption { get; set; }
[Inject]
[NotNull]
private NavigationManager? NavigationManager { get; set; }
[Inject]
[NotNull]
private MenuService? MenuService { get; set; }
[NotNull]
private List<string>? ComponentItems { get; set; }
private IEnumerable<MenuItem> Menus => MenuService.GetMenus().SelectMany(i => i.Items).Where(i => !string.IsNullOrEmpty(i.Url));
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnInitialized()
{
ComponentItems = Menus.Select(i => i.Text!).ToList();
}
private Task OnSearch(string searchText)
{
if (!string.IsNullOrEmpty(searchText))
{
var item = Menus.FirstOrDefault(i => i.Text!.Contains(searchText, StringComparison.OrdinalIgnoreCase));
if (item != null && !string.IsNullOrEmpty(item.Url))
{
NavigationManager.NavigateTo(item.Url);
}
}
return Task.CompletedTask;
}
private Task OnSelectedItemChanged(string searchText) => OnSearch(searchText);
}

View File

@ -0,0 +1,44 @@
@inherits WebSiteModuleComponentBase
@attribute [JSModuleAutoLoader("Components/Header.razor.js")]
<header class="navbar-header navbar navbar-expand navbar-dark flex-column flex-md-row px-3">
<div class="header-img navbar-brand">
<BBLogo />
<span>Bootstrap Blazor</span>
</div>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" href="index">@HomeText</a>
</li>
<li class="nav-item">
<a class="nav-link" href="introduction">@IntroductionText</a>
</li>
<li class="nav-item">
<a class="nav-link" href="components">@ComponentsText</a>
</li>
<li class="nav-item">
<a class="nav-link" href="practice">实战</a>
</li>
</ul>
</div>
<GlobalSearch />
<CultureChooser class="flex-md-fill flex-xl-grow-0 justify-content-md-end" />
<ul class="navbar-nav ms-3 d-none d-lg-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/ArgoZhang/BootstrapBlazor" target="_blank">
<img alt="git" src="./images/git.svg" />
</a>
</li>
<li class="nav-item">
<a class="nav-link p-2" href="@WebsiteOption.CurrentValue.BootstrapBlazorLink" target="_blank">
<img alt="gitee" src="./images/gitee.svg" />
</a>
</li>
<li class="nav-item">
<FullScreenButton class="nav-link p-2" TooltipText="点击切换全屏模式" />
</li>
</ul>
<a class="btn btn-bd-download d-none d-lg-inline-block mb-3 mb-md-0 ms-md-3" target="_blank" href="@DownloadUrl">@DownloadText</a>
</header>

View File

@ -0,0 +1,48 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using Microsoft.Extensions.Options;
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// Header 组件
/// </summary>
public partial class Header
{
[Inject]
[NotNull]
private IOptionsMonitor<WebsiteOptions>? WebsiteOption { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<Header>? Localizer { get; set; }
[NotNull]
private string? HomeText { get; set; }
[NotNull]
private string? IntroductionText { get; set; }
[NotNull]
private string? ComponentsText { get; set; }
[NotNull]
private string? DownloadText { get; set; }
private string DownloadUrl => $"{WebsiteOption.CurrentValue.BootstrapBlazorLink}/repository/archive/main.zip";
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
DownloadText ??= Localizer[nameof(DownloadText)];
HomeText ??= Localizer[nameof(HomeText)];
IntroductionText ??= Localizer[nameof(IntroductionText)];
ComponentsText ??= Localizer[nameof(ComponentsText)];
}
}

View File

@ -0,0 +1,68 @@
header {
--bb-violet-rgb: 112.520718,44.062154,249.437846;
background-color: transparent;
background-image: linear-gradient(to bottom, rgba(var(--bb-violet-rgb), 1), rgba(var(--bb-violet-rgb), 0.95));
box-shadow: 0 0.5rem 1rem rgba(0,0,0,.05), inset 0 -1px 0 rgba(0,0,0,.1);
font-size: 1rem;
transition: transform .3s ease;
}
.header-img {
display: flex;
align-items: center;
padding: 0;
margin-right: 0;
}
::deep .btn-search .btn {
--bs-btn-bg: #8759ff;
--bs-btn-color: #fff;
--bs-btn-hover-bg: #7d53eb;
--bs-btn-hover-color: var(--bs-btn-color);
--bs-btn-hover-border-color: var(--bs-btn-hover-bg);
--bs-btn-active-bg: var(--bs-btn-hover-bg);
--bs-btn-active-color: var(--bs-btn-hover-color);
--bs-btn-active-border-color: var(--bs-btn-hover-border-color);
}
::deep .btn-search .form-control {
--bs-border-color: #fff;
--bb-border-focus-color: var(--bs-border-color);
--bb-border-hover-color: var(--bs-border-color);
z-index: 6;
}
::deep .btn-fs {
margin-top: 2px;
}
.btn-bd-download {
font-weight: 600;
color: #ffe484;
border-color: #ffe484;
}
.btn-bd-download:active,
.btn-bd-download:hover {
color: #2a2730;
background-color: #ffe484;
border-color: #ffe484;
}
.nav-link img {
height: 24px;
width: auto;
}
@media (min-width: 768px) {
.navbar-header {
position: sticky;
top: 0;
z-index: 1050;
height: var(--bs-header-height);
}
.modal-open .navbar-header {
z-index: 1040;
}
}

View File

@ -0,0 +1,20 @@
import EventHandler from "../../_content/BootstrapBlazor/modules/event-handler.js?v=$version"
export function init() {
const scrollTop = () => (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop
let prevScrollTop = 0;
EventHandler.on(document, 'scroll', () => {
const items = document.querySelectorAll('.navbar-header, .coms-search')
const currentScrollTop = scrollTop()
if (currentScrollTop > prevScrollTop) {
items.forEach(item => item.classList.add('hide'))
} else {
items.forEach(item => item.classList.remove('hide'))
}
prevScrollTop = currentScrollTop
})
}
export function dispose() {
EventHandler.off(document, 'scroll')
}

View File

@ -0,0 +1,90 @@
@using Microsoft.Extensions.DependencyInjection
@inject PackageVersionService VersionManager
@inject IStringLocalizer<InstallContent> Localizer
<h3>@Title</h3>
<h4>@Localizer["Heading"]</h4>
<p class="mt-3">@Localizer["P1"]</p>
<ul>
<li><code>visual studio 2019</code> @Localizer["P2"] <code>visual studio 2022</code></li>
<li><code>net5</code> @Localizer["P3"] <code>net6</code></li>
</ul>
<div><code>BootstrapBlazor</code> @Localizer["P4"] <code>net5/net6</code></div>
<h4>@Localizer["P5"]</h4>
<p>@Localizer["P6"] <a href="template">[@Localizer["P7"]]</a> @Localizer["P8"]</p>
<h4>@Localizer["P9"]</h4>
<h5 class="mb-3">@Localizer["P10"]</h5>
<div class="code-label mb-2">1. @Localizer["P11"]</div>
<div class="code-label mb-2">2. @Localizer["P12"]</div>
<div class="code-label mb-2">3. @Localizer["P13"] <b>Blazor App</b> @Localizer["P14"] <b>@Localizer["P15"]</b>, @Localizer["P16"] <b>Create</b></div>
<img alt="install" src="./images/create-new-application.png" style="border-radius: 6px;" class="d-none d-sm-block mb-2" />
@ChooseTemplate
<h5 class="mb-3">@Localizer["P17"]</h5>
<div class="code-label mb-2">1. @Localizer["P18"] <b>nuget.org</b> @Localizer["P19"] <code>BootstrapBlazor</code></div>
<div class="mb-2">@Localizer["P20"] <b>Manage Nuget Packages</b></div>
<Pre @key="@Version" class="no-highlight mb-2">dotnet add package BootstrapBlazor --version @Version</Pre>
<img alt="install" src="./images/manage-nuget-packages-for-server-app.png" style="border-radius: 6px;" class="d-none d-sm-block mb-2" />
<div class="code-label mt-3 mb-2">2. @Localizer["P21"]</div>
<img alt="install" src="./images/nuget_install.png" style="width: 1000px; border-radius: 6px;" class="d-none d-sm-block mb-2" />
<div class="code-label mt-3 mb-2">3. @Localizer["P22"]</div>
<div class="mb-2">@Localizer["P23"]</div>
@SheetTemplate
<Tips>
<div>@((MarkupString)Localizer["Tips2"].Value)</div>
</Tips>
<Pre class="mb-2">&lt;head&gt;
...
<b>
&lt;!-- @Localizer["P24"] !--&gt;
&lt;link href="_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css" rel="stylesheet"&gt;
&lt;link href="_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css" rel="stylesheet"&gt;
</b>
...
&lt;link href="css/site.css" rel="stylesheet"&gt;
&lt;link href="BlazorApp1.styles.css" rel="stylesheet"&gt;
&lt;/head&gt;</Pre>
<div class="code-label my-2">4. @Localizer["P25"]</div>
@ScriptsTemplate
<Pre class="mb-2">&lt;body&gt;
...
&lt;!-- @Localizer["P26"] !--&gt;
<b>&lt;script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"&gt;&lt;/script&gt;</b>
...
&lt;script src="_framework/blazor.server.js"&gt;&lt;/script&gt;
&lt;/body&gt;</Pre>
<div class="code-label mb-2">5. @Localizer["P27"]</div>
@ServicesTemplate
<div class="code-label mb-2">6. @Localizer["P28"]</div>
<div class="mb-2">@Localizer["P29"] <code>~/_Imports.razor</code> @Localizer["P30"] <code>Razor</code> @Localizer["P31"]</div>
<Pre class="mb-2"><b>@@using BootstrapBlazor.Components</b></Pre>
<div class="code-label mb-2">7. @Localizer["P32"] <code>BootstrapBlazorRoot</code> @Localizer["P33"] <code>~/App.razor</code> @Localizer["P34"]</div>
<Pre class="mb-2">&lt;BootstrapBlazorRoot&gt;
&lt;Router AppAssembly="@@typeof(App).Assembly"&gt;
&lt;Found Context="routeData"&gt;
&lt;PageTitle&gt;Title&lt;/PageTitle&gt;
&lt;RouteView RouteData="@@routeData" DefaultLayout="@@typeof(MainLayout)" /&gt;
&lt;FocusOnNavigate RouteData="@@routeData" Selector="h1" /&gt;
&lt;/Found&gt;
&lt;NotFound&gt;
&lt;PageTitle&gt;Not found&lt;/PageTitle&gt;
&lt;LayoutView Layout="@@typeof(MainLayout)"&gt;
&lt;p&gt; @Localizer["P35"] ...&lt;/p&gt;
&lt;/LayoutView&gt;
&lt;/NotFound&gt;
&lt;/Router&gt;
&lt;/BootstrapBlazorRoot&gt;</Pre>
<h5 class="mb-3">@Localizer["P36"]</h5>
<div class="mb-2">@Localizer["P37"] <code>BootstrapBlazor</code> @Localizer["P38"]</div>
<div class="code-label mb-2">1. @Localizer["P39"] <code>Button</code> @Localizer["P40"]</div>
<Pre class="mb-2">&lt;Button Color="Color.Primary" Icon="fa-solid fa-font-awesome" Text="@Localizer["P41"]" /&gt;</Pre>
<div class="code-label mb-2">2. @Localizer["P42"] <b>Visual studio 2022</b> @Localizer["P43"] <kbd>F5</kbd> @Localizer["P44"]</div>
<img alt="install" src="./images/preview.png" style="border-radius: 6px;" class="d-none d-sm-block" />

View File

@ -0,0 +1,67 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public sealed partial class InstallContent
{
/// <summary>
/// 获得/设置 版本号字符串
/// </summary>
private string Version { get; set; } = "latest";
/// <summary>
///
/// </summary>
[Parameter]
public string Title { get; set; } = "服务器端 Blazor 安装教程";
/// <summary>
///
/// </summary>
[Parameter]
public string HostFile { get; set; } = "Pages/_Host.cshtml";
/// <summary>
///
/// </summary>
[Parameter]
public RenderFragment? ChooseTemplate { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public RenderFragment? SheetTemplate { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public RenderFragment? ScriptsTemplate { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public RenderFragment? ServicesTemplate { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
/// <summary>
/// OnInitializedAsync 方法
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
Version = await VersionManager.GetVersionAsync();
}
}

View File

@ -0,0 +1,13 @@
<div class="table-attr">
<h4>@Title</h4>
<Table TItem="MethodItem" Items="Items">
<TableColumns>
<TableColumn @bind-Field="@context.Name" />
<TableColumn @bind-Field="@context.Description" TextWrap="true" />
<TableColumn @bind-Field="@context.Parameters" />
<TableColumn @bind-Field="@context.ReturnValue" />
</TableColumns>
</Table>
</div>

View File

@ -0,0 +1,38 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public sealed partial class MethodTable
{
[Inject]
[NotNull]
private IStringLocalizer<MethodTable>? Localizer { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public string? Title { get; set; }
/// <summary>
///
/// </summary>
[Parameter] public IEnumerable<MethodItem>? Items { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
Title ??= Localizer[nameof(Title)];
}
}

View File

@ -0,0 +1,36 @@
@inject IStringLocalizer<PackageTips> Localizer
<div class="info mb-2">@((MarkupString)Localizer["Tips", Name].Value)</div>
<div class="code-label mb-2">.NET CLI</div>
<Pre class="no-highlight mb-2">dotnet add package @Name</Pre>
<div class="code-label mb-2">PackageReference</div>
<Pre @key="@Version" class="no-highlight mb-2">&lt;PackageReference Include="@Name" Version="@Version" /&gt;</Pre>
<div class="code-label mb-2">Package Manager</div>
<Pre class="no-highlight">Install-Package @Name</Pre>
@code {
[Inject]
[NotNull]
private PackageVersionService? VersionManager { get; set; }
private string Version { get; set; } = "fetching";
/// <summary>
/// 获得/设置 Package 名称
/// </summary>
[Parameter]
[NotNull]
[EditorRequired]
public string? Name { get; set; }
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override async Task OnInitializedAsync()
{
Version = await VersionManager.GetVersionAsync(Name.ToLower());
}
}

View File

@ -0,0 +1,21 @@
@inherits WebSiteModuleComponentBase
@attribute [JSModuleAutoLoader("Components/Pre.razor.js")]
<div @attributes="@AdditionalAttributes" class="@ClassString" id="@Id" data-bb-title="@CopiedText">
<p class="loading">@LoadingText</p>
@if (Loaded)
{
<pre><code>@ChildContent</code></pre>
@if (CanCopy)
{
@if (ShowToolbar)
{
<div class="btn-group">
<Button TooltipPlacement="Placement.Top" TooltipText="@PlusTooltipTitle" TooltipTrigger="hover" Icon="fa-solid fa-plus" class="btn-plus"></Button>
<Button TooltipPlacement="Placement.Top" TooltipText="@MinusTooltipTitle" TooltipTrigger="hover" Icon="fa-solid fa-minus" class="btn-minus"></Button>
</div>
}
<Button TooltipPlacement="Placement.Top" TooltipText="@TooltipTitle" TooltipTrigger="hover" class="btn-copy">Copy</Button>
}
}
</div>

View File

@ -0,0 +1,191 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using System.Text.RegularExpressions;
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// Pre 组件
/// </summary>
public partial class Pre
{
private bool Loaded { get; set; }
private bool CanCopy { get; set; }
/// <summary>
/// 获得 样式集合
/// </summary>
/// <returns></returns>
private string? ClassString => CssBuilder.Default("pre-code")
.AddClass("loaded", Loaded)
.AddClassFromAttributes(AdditionalAttributes)
.Build();
[Inject]
[NotNull]
private CodeSnippetService? CodeSnippetService { get; set; }
/// <summary>
/// 获得/设置 子组件 CodeFile 为空时生效
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
/// <summary>
/// 获得/设置 代码段的标题
/// </summary>
[Parameter]
public string? BlockName { get; set; }
/// <summary>
/// 获得/设置 示例代码片段 默认 null 未设置
/// </summary>
[Parameter]
public string? CodeFile { get; set; }
/// <summary>
/// 获得/设置 是否显示工具按钮组
/// </summary>
[Parameter]
public bool ShowToolbar { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<Pre>? Localizer { get; set; }
private string? LoadingText { get; set; }
private string? TooltipTitle { get; set; }
private string? PlusTooltipTitle { get; set; }
private string? MinusTooltipTitle { get; set; }
private string? CopiedText { get; set; }
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{
LoadingText ??= Localizer[nameof(LoadingText)];
TooltipTitle ??= Localizer[nameof(TooltipTitle)];
PlusTooltipTitle ??= Localizer[nameof(PlusTooltipTitle)];
MinusTooltipTitle ??= Localizer[nameof(MinusTooltipTitle)];
CopiedText ??= Localizer[nameof(CopiedText)];
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <returns></returns>
protected override async Task OnParametersSetAsync()
{
if (ChildContent == null)
{
await GetCodeAsync();
}
else
{
Loaded = true;
CanCopy = true;
}
}
/// <summary>
/// OnAfterRender 方法
/// </summary>
/// <param name="firstRender"></param>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (Loaded)
{
await InvokeVoidAsync("highlight", Id);
}
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <returns></returns>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, CopiedText);
private async Task GetCodeAsync()
{
if (!string.IsNullOrEmpty(CodeFile))
{
var code = await CodeSnippetService.GetCodeAsync(CodeFile);
if (!string.IsNullOrEmpty(code))
{
code = FindCodeSnippetByName(code);
ChildContent = builder =>
{
builder.AddContent(0, code);
};
}
CanCopy = !string.IsNullOrEmpty(code) && !code.StartsWith("Error: ");
}
else
{
ChildContent = builder =>
{
builder.AddContent(0, "网站改版中 ... Refactoring website. Coming soon ...");
};
CanCopy = false;
}
Loaded = true;
}
private string FindCodeSnippetByName(string code)
{
var content = code;
if (!string.IsNullOrEmpty(BlockName))
{
var regex = new Regex($"<DemoBlock [\\s\\S]*? Name=\"{BlockName}\">([\\s\\S]*?)</DemoBlock>");
var match = regex.Match(content);
if (match.Success && match.Groups.Count == 2)
{
content = match.Groups[1].Value.Replace("\r\n", "\n").Replace("\n ", "\n").TrimStart('\n');
}
// 移除 ignore 节点
regex = IgnoreRegex();
var matchCollection = regex.Matches(content);
matchCollection.ToList().ForEach(m =>
{
content = content.Replace(m.Value, "").TrimStart('\n');
});
// 移除 ConsoleLogger
regex = ConsoleLoggerRegex();
match = regex.Match(content);
if (match.Success)
{
content = content.Replace(match.Value, "").TrimStart('\n');
}
// 移除 Tips
regex = TipsRegex();
match = regex.Match(content);
if (match.Success)
{
content = content.Replace(match.Value, "").TrimStart('\n');
}
}
return content.TrimEnd('\n');
}
[GeneratedRegex("<section ignore[ \\s\\S]*?>[\\s\\S]*?</section>")]
private static partial Regex IgnoreRegex();
[GeneratedRegex("<ConsoleLogger [\\s\\S]* />")]
private static partial Regex ConsoleLoggerRegex();
[GeneratedRegex("<Tips[\\s\\S]*>[\\s\\S]*?</Tips>")]
private static partial Regex TipsRegex();
}

View File

@ -0,0 +1,48 @@
.pre-code {
position: relative;
border: 1px solid var(--bs-border-color);
border-radius: var(--bs-border-radius);
overflow: hidden;
}
.pre-code .loading {
padding: .5rem 1rem;
}
.pre-code.loaded > pre > code {
display: none;
}
::deep .btn-primary {
position: absolute;
top: .65rem;
right: 1.5rem;
font-size: 65%;
--bs-btn-color: var(--bs-primary);
--bs-btn-bg: #fff;
--bs-btn-padding-y: .25rem;
--bs-btn-padding-x: .5rem;
}
::deep .btn-group {
position: absolute;
top: 0;
right: 3rem;
}
::deep .btn-group .btn-primary {
position: relative;
}
code {
line-height: 1.8;
font-size: 0.75rem;
padding: 10px 65px 10px 16px;
display: block;
white-space: pre-wrap;
-webkit-font-smoothing: auto;
}
.no-highlight code {
color: var(--bs-code-color);
}

View File

@ -0,0 +1,88 @@
import { copy, getDescribedElement, addLink, addScript, getHeight } from "../../_content/BootstrapBlazor/modules/utility.js?v=$version"
import EventHandler from "../../_content/BootstrapBlazor/modules/event-handler.js?v=$version"
export async function init(id, title) {
const el = document.getElementById(id);
if (el === null) {
return
}
await addScript('./lib/highlight/highlight.min.js')
await addScript('./lib/highlight/cshtml-razor.min.js')
await addLink('./lib/highlight/vs.min.css')
const preElement = el.querySelector('pre')
const code = el.querySelector('pre > code')
if (preElement) {
EventHandler.on(el, 'click', '.btn-copy', e => {
const text = code.textContent;
copy(text)
const tooltip = getDescribedElement(e.delegateTarget)
if (tooltip) {
tooltip.querySelector('.tooltip-inner').innerHTML = title
}
})
EventHandler.on(el, 'click', '.btn-plus', e => {
e.preventDefault()
e.stopPropagation();
let preHeight = getHeight(preElement)
const codeHeight = getHeight(code)
if (preHeight < codeHeight) {
preHeight = Math.min(codeHeight, preHeight + 100)
}
preElement.style.maxHeight = `${preHeight}px`
})
EventHandler.on(el, 'click', '.btn-minus', e => {
e.preventDefault()
e.stopPropagation();
let preHeight = getHeight(preElement)
if (preHeight > 260) {
preHeight = Math.max(260, preHeight - 100)
}
preElement.style.maxHeight = `${preHeight}px`
})
}
}
export async function highlight(id) {
const el = document.getElementById(id);
if (el) {
const invoke = () => {
hljs.highlightElement(el.querySelector('code'))
el.querySelector('.loading').classList.add('d-none')
el.classList.remove('loaded')
}
const check = () => new Promise((resolve, reject) => {
const handler = setInterval(() => {
const done = window.hljs !== void 0;
if (done) {
clearInterval(handler)
resolve()
}
}, 20)
})
await check();
invoke();
}
}
export function dispose(id) {
const el = document.getElementById(id);
if (el === null) {
return
}
EventHandler.off(el, 'click', '.btn-copy')
EventHandler.off(el, 'click', '.btn-plus')
EventHandler.off(el, 'click', '.btn-minus')
}

View File

@ -0,0 +1,17 @@
@inject IOptionsMonitor<WebsiteOptions> WebsiteOption
@inject IStringLocalizer<QQGroup> Localzier
<div>
@Localzier["Group"]BootstrapAdmin & Blazor
<a class="mx-1" target="_blank" href="@WebsiteOption.CurrentValue.QQGroup1Link">
<span class="text-success fa-brands fa-qq">
<span class="ms-1"><b>795206915</b></span>
</span>
</a>
<a class="mx-1" target="_blank" href="@WebsiteOption.CurrentValue.QQGroup2Link">
<span class="text-success fa-brands fa-qq">
<span class="ms-1"><b>675147445</b></span>
</span>
</a>
@Localzier["Welcome"]
</div>

View File

@ -0,0 +1,8 @@
<ul class="ul-demo mb-3">
<li>更改数值后,点击 <code>确认</code> 返回主页面并 <b>更新</b> 数值</li>
<li>点击其余按钮时关闭弹窗 <b>不更新</b> 数值</li>
</ul>
<div style="width: 220px;">
<BootstrapInputNumber ShowButton="true" Step="1" Min="1" Max="10" @bind-Value="@Value"></BootstrapInputNumber>
</div>

View File

@ -0,0 +1,37 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class ResultDialogDemo : ComponentBase, IResultDialog
{
/// <summary>
///
/// </summary>
[Parameter]
public int Value { get; set; } = 1;
/// <summary>
///
/// </summary>
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
/// <summary>
///
/// </summary>
public async Task OnClose(DialogResult result)
{
if (result == DialogResult.Yes)
{
if (ValueChanged.HasDelegate)
{
await ValueChanged.InvokeAsync(Value);
}
}
}
}

View File

@ -0,0 +1,7 @@
<Table TItem="Foo" ClickToSelect="true" IsMultipleSelect="true" @bind-SelectedRows="@SelectedRows" OnQueryAsync="@OnQueryAsync">
<TableColumns>
<TableColumn @bind-Field="@context.Id" />
<TableColumn @bind-Field="@context.Name" />
<TableColumn @bind-Field="@context.Email" />
</TableColumns>
</Table>

View File

@ -0,0 +1,138 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using System.ComponentModel;
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class ResultDialogDemo2 : ComponentBase, IResultDialog
{
private List<Foo> SelectedRows { get; set; } = new List<Foo>();
[NotNull]
private List<Foo>? Items { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public IEnumerable<string>? Emails { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public EventCallback<IEnumerable<string>> EmailsChanged { get; set; }
[CascadingParameter(Name = "BodyContext")]
private object? BodyContext { get; set; }
[Inject]
[NotNull]
private MessageService? MessageService { get; set; }
private Task<QueryData<Foo>> OnQueryAsync(QueryPageOptions option)
{
// 模拟查询数据
var context = BodyContext as FooContext;
Items = GenerateItems(context?.Count ?? 10);
var data = new QueryData<Foo>()
{
TotalCount = Items.Count,
Items = Items
};
// 处理选中行
Emails = context?.Emails?.Split(";") ?? Array.Empty<string>();
SelectedRows.AddRange(Items.Where(i => Emails.Any(mail => mail == i.Email)));
return Task.FromResult(data);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public async Task<bool> OnClosing(DialogResult result)
{
var ret = true;
if (result == DialogResult.Yes && !SelectedRows.Any())
{
await MessageService.Show(new MessageOption()
{
Content = "请至少选择一位用户!"
});
ret = false;
}
return ret;
}
/// <summary>
///
/// </summary>
public async Task OnClose(DialogResult result)
{
if (result == DialogResult.Yes)
{
if (EmailsChanged.HasDelegate)
{
Emails = SelectedRows.Where(r => !string.IsNullOrEmpty(r.Email)).Select(r => r.Email!).ToList();
await EmailsChanged.InvokeAsync(Emails);
}
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private static List<Foo> GenerateItems(int startId) => new(Enumerable.Range(startId, 10).Select(i => new Foo()
{
Id = i,
Name = $"张三 {i:d4}",
Email = $"zhangsan{i:d4}@163.com"
}));
/// <summary>
///
/// </summary>
public class FooContext
{
/// <summary>
///
/// </summary>
public int Count { get; set; }
/// <summary>
///
/// </summary>
public string? Emails { get; set; }
}
/// <summary>
///
/// </summary>
private class Foo
{
/// <summary>
///
/// </summary>
[DisplayName("员工ID")]
public int? Id { get; set; }
/// <summary>
///
/// </summary>
[DisplayName("员工姓名")]
public string? Name { get; set; }
/// <summary>
///
/// </summary>
[DisplayName("员工邮箱")]
public string? Email { get; set; }
}
}

View File

@ -0,0 +1,36 @@
<h3>窗口加载后回调示例</h3>
<p>在实战应用中,有些脚本比如画图类,必须窗体显示后,由具体高度或者宽度后才能正确工作,此时需要先显示此窗体,然后再调用其方法,本示例中下面数字是通过窗体加载并且显示后,由回调方法触发后,延时 2秒 后显示当前时间</p>
<p>@_text</p>
@code {
[Parameter]
public Action<Func<Task>>? ShownTodo { get; set; }
private string? _text;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
if (firstRender)
{
// 首次加载时回调 ShowTodo 方法
if (ShownTodo != null)
{
ShownTodo(DoJob);
}
}
}
private async Task DoJob()
{
_text = "回调成功,开始延时 2 秒";
await InvokeAsync(StateHasChanged);
await Task.Delay(2000);
_text = $"当前时间: {DateTime.Now}";
await InvokeAsync(StateHasChanged);
}
}

View File

@ -0,0 +1,18 @@
@if (IsNew)
{
<Badge Color="Color.Danger" IsPill="true">
NEW
</Badge>
}
@if (IsUpdate)
{
<Badge Color="Color.Success" IsPill="true">
Upd
</Badge>
}
@if (Count > 0)
{
<Badge Color="Color.Info" IsPill="true">
@Count
</Badge>
}

View File

@ -0,0 +1,29 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public sealed partial class State
{
/// <summary>
/// 获得/设置 是否为新组件 默认为 false
/// </summary>
[Parameter]
public bool IsNew { get; set; }
/// <summary>
/// 获得/设置 是否为更新功能 默认为 false
/// </summary>
[Parameter]
public bool IsUpdate { get; set; }
/// <summary>
/// 获得/设置 组件数量
/// </summary>
[Parameter]
public int Count { get; set; }
}

View File

@ -0,0 +1 @@
<div>Why do I have this issue?</div>

View File

@ -0,0 +1,18 @@
@inherits WebSiteModuleComponentBase
@attribute [JSModuleAutoLoader("Components/ThemeChooser.razor.js")]
<div id="@Id" class="theme">
<PulseButton class="btn-fade btn-theme" Color="Color.None" ImageUrl="./images/m.svg" TooltipText="@Title" TooltipPlacement="Placement.Left" />
<div class="theme-list">
<div class="theme-header">
<div class="flex-fill">@HeaderText</div>
<button class="btn-close btn-close-white" type="button" aria-label="Close"></button>
</div>
@foreach (var item in Themes)
{
<div class="@GetThemeItemClass(item)" @onclick="@(e => OnClickTheme(item))">
@item.Text
</div>
}
</div>
</div>

View File

@ -0,0 +1,74 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using Microsoft.Extensions.Options;
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
///
/// </summary>
public partial class ThemeChooser
{
[NotNull]
private IEnumerable<SelectedItem>? Themes { get; set; }
[NotNull]
private string? Title { get; set; }
[NotNull]
private string? HeaderText { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<ThemeChooser>? Localizer { get; set; }
[Inject]
[NotNull]
private IOptionsMonitor<BootstrapBlazorOptions>? BootstrapOptions { get; set; }
[Inject]
[NotNull]
private IOptionsMonitor<WebsiteOptions>? SiteOptions { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
Title ??= Localizer[nameof(Title)];
HeaderText ??= Localizer[nameof(HeaderText)];
Themes = BootstrapOptions.CurrentValue.Themes.Select(kv => new SelectedItem(kv.Value, kv.Key));
SiteOptions.CurrentValue.CurrentTheme = Themes.FirstOrDefault(i => i.Text == "Motronic")?.Value ?? "";
}
private async Task OnClickTheme(SelectedItem item)
{
SiteOptions.CurrentValue.CurrentTheme = item.Value;
await InvokeVoidAsync("addScript", LinksCache[item.Value]);
}
private string? GetThemeItemClass(SelectedItem item) => CssBuilder.Default("theme-item")
.AddClass("active", SiteOptions.CurrentValue.CurrentTheme == item.Value)
.Build();
private Dictionary<string, ICollection<string>> LinksCache { get; } = new(new KeyValuePair<string, ICollection<string>>[]
{
new("bootstrap.blazor.bundle.min.css", new List<string>()),
new("motronic.min.css", new string[]
{
"./_content/BootstrapBlazor/css/motronic.min.css",
"./css/motronic.css"
}),
new("ant", new List<string>()),
new("layui", new List<string>()),
new("devui", new string[]
{
"./css/devui.css"
})
});
}

View File

@ -0,0 +1,63 @@
.theme-list {
position: fixed;
z-index: 10;
bottom: 12rem;
right: 1rem;
background: #FFFFFF;
box-shadow: 0 5px 24px 0 rgba(0, 0, 0, 0.2);
border-radius: var(--bs-border-radius);
width: 260px;
box-shadow: 0 0 12px #211b50;
height: 0;
overflow: hidden;
transition: height .3s ease-in-out;
}
.theme-list.is-open {
height: 306px;
}
.theme-header {
padding: 0.75rem 1rem;
background-color: #8759ff;
color: #fff;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
display: flex;
margin-bottom: 1rem;
}
.btn-close {
transition: opacity .3s linear;
}
.btn-close:hover {
opacity: 1;
}
.theme-item {
cursor: pointer;
border-radius: 100px;
padding: 6px 15px;
background-color: #f6f6f6;
color: #005980;
transition: background-color .3s linear;
margin: 0 1rem 1rem 1rem;
}
.theme-item:hover,
.theme-item.active {
background-color: #ab8aff;
}
::deep .btn-theme {
--bs-btn-bg: #034995;
--bs-btn-hover-bg: #034995;
--bs-btn-active-bg: #034995;
right: 1rem;
bottom: 9rem;
}
::deep .btn-theme img {
width: 20px;
}

View File

@ -0,0 +1,49 @@
import { insertAfter } from "../../_content/BootstrapBlazor/modules/utility.js?v=$version"
import Data from "../../_content/BootstrapBlazor/modules/data.js?v=$version"
import EventHandler from "../../_content/BootstrapBlazor/modules/event-handler.js?v=$version"
export function init(id) {
const el = document.getElementById(id)
if (el === null) {
return
}
const themeList = el.querySelector('.theme-list')
const chooser = { el, themeList }
Data.set(id, chooser);
EventHandler.on(el, 'click', () => {
themeList.classList.toggle('is-open')
})
}
export function addScript(args) {
const links = document.querySelectorAll('link')
if (links) {
const link = [].slice.call(links).filter(function (item) {
const href = item.getAttribute('href')
return href.indexOf('/css/site.css') > -1
});
const original = link[0]
while (original.nextElementSibling && original.nextElementSibling.nodeName === 'LINK') {
original.nextElementSibling.remove()
}
args.forEach(function (c) {
const link = document.createElement('link')
link.setAttribute('rel', 'stylesheet')
link.setAttribute('href', c)
insertAfter(original, link)
});
}
}
export function dispose(id) {
const chooser = Data.get(id)
Data.remove(id)
if (chooser) {
EventHandler.off(chooser.el, 'click')
}
}

View File

@ -0,0 +1,28 @@
@inherits BootstrapComponentBase
@inject IStringLocalizer<Tips> Localizer
<Alert AdditionalAttributes="@AdditionalAttributes" ShowBar="true" ShowBorder="true" Color="@Color">
<h4><i class="alert-icon @Icon"></i>@Localizer["Title"]</h4>
@ChildContent
</Alert>
@code {
/// <summary>
/// 获得/设置 图标
/// </summary>
[Parameter]
public string Icon { get; set; } = "fa-regular fa-lightbulb";
/// <summary>
/// 获得/设置 颜色 默认为 Info
/// </summary>
[Parameter]
public Color Color { get; set; } = Color.Info;
/// <summary>
/// 获得/设置 子组件内容
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
}

View File

@ -0,0 +1,28 @@
@inherits WebSiteModuleComponentBase
@inject IStringLocalizer<UpdateIntro> Localizer
@attribute [JSModuleAutoLoader("Components/UpdateIntro.razor.js")]
<div class="blazor-intro" id="@Id" data-bb-button=".blazor-intro-button" data-bb-version="@PackageVersionService.Version">
<h5><b>Bootstrap Blazor</b> @Localizer["H1"] <span class="version">@PackageVersionService.Version</span></h5>
<div class="d-flex">
<div class="blazor-intro-body">
<p>@Localizer["B1"] <b>Bootstrap</b> <b>Blazor</b> @Localizer["B2"] <a href="@WebsiteOption.CurrentValue.AdminUrl" target="_blank"><b>@Localizer["B3"]</b></a> @Localizer["B4"]。<b>Bootstrap Blazor</b> @((MarkupString)Localizer["B5"].Value)</p>
<p>
@Localizer["P1"] <span class="version">@PackageVersionService.Version</span> @Localizer["P2"] <a target="_blank" href="@UpdateLogUrl"><b>[@Localizer["P3"]]</b></a> @Localizer["P4"] <b>Star</b>
<a class="px-2" href="https://github.com/ArgoZhang/BootstrapBlazor" target="_blank">
<img src="./images/git.svg" alt="github" />
</a>
<a href="@WebsiteOption.CurrentValue.BootstrapBlazorLink" target="_blank">
<img src="./images/gitee.svg" alt="gitee" />
</a>
</p>
</div>
<div class="blazor-intro-barcode">
<img src="./images/QQGroup@2x.png" alt="QQGroup" />
<div>QQ 795206915</div>
</div>
</div>
<div class="blazor-intro-button">
<svg viewBox="0 0 24 24"><path d="M19.707 5.707l-1.414-1.414L12 10.586 5.707 4.293 4.293 5.707 10.586 12l-6.293 6.293 1.414 1.414L12 13.414l6.293 6.293 1.414-1.414L13.414 12l6.293-6.293z"></path></svg>
</div>
</div>

View File

@ -0,0 +1,35 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using Microsoft.Extensions.Options;
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// 更新日志介绍组件
/// </summary>
public partial class UpdateIntro
{
[Inject]
[NotNull]
private IOptionsMonitor<WebsiteOptions>? WebsiteOption { get; set; }
[Inject]
[NotNull]
private PackageVersionService? PackageVersionService { get; set; }
private string UpdateLogUrl => $"{WebsiteOption.CurrentValue.BootstrapBlazorLink}/wikis/%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97?sort_id=4062034";
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override async Task InvokeInitAsync()
{
#if DEBUG
await InvokeVoidAsync("init", "");
#else
await InvokeVoidAsync("init", Id, PackageVersionService.Version);
#endif
}
}

View File

@ -0,0 +1,68 @@
.blazor-intro {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 1900;
color: #ffffff;
background: linear-gradient(54.4deg,#771f89 -28.5%,#834cef 30.36%,#636cea 99.19%);
padding: 1rem;
height: 224px;
transform: translateY(100%);
transition: transform .3s ease-in-out;
}
.blazor-intro.show {
transform: translateY(0);
}
.blazor-intro .blazor-intro-body {
flex: 1 1 auto;
}
.blazor-intro .blazor-intro-body a {
color: #fff;
cursor: pointer;
text-decoration: underline;
}
.blazor-intro .blazor-intro-body img {
width: 44px;
}
.blazor-intro .blazor-intro-barcode {
text-align: center;
margin: 1rem 1rem 0 2rem;
}
.blazor-intro .blazor-intro-barcode img {
width: 110px;
}
.blazor-intro-button {
position: absolute;
top: 12px;
right: 12px;
color: #fff;
background-color: #4b4df6;
cursor: pointer;
border-radius: 50%;
padding: 6px;
transition: background-color .3s linear;
}
.blazor-intro-button:hover {
background-color: #3c3de2;
}
.blazor-intro-button svg {
width: 24px;
height: 24px;
fill: currentcolor;
}
@media print {
.blazor-intro {
display: none;
}
}

View File

@ -0,0 +1,56 @@
import Data from "../../_content/BootstrapBlazor/modules/data.js?v=$version"
import EventHandler from "../../_content/BootstrapBlazor/modules/event-handler.js?v=$version"
export function init(id, version) {
const el = document.getElementById(id)
if (el === null) {
return
}
const update = {
el,
key: `bb_intro_popup:${version}`,
}
Data.set(id, update)
check(update.key, update.el);
EventHandler.on(el, 'click', '.blazor-intro-button', () => {
close(update.key, el)
})
}
export function dispose(id) {
Data.remove(id)
const data = Data.get(id)
if (data) {
EventHandler.off(data.el, 'click', '.blazor-intro-button');
}
}
const check = (key, el) => {
const width = window.innerWidth
if (width >= 768) {
const isShown = localStorage.getItem(key)
if (!isShown) {
slideToggle(el)
// clean
for (let index = localStorage.length; index > 0; index--) {
const k = localStorage.key(index - 1);
if (k.indexOf('bb_intro_popup:') > -1) {
localStorage.removeItem(k);
}
}
}
}
}
const slideToggle = el => {
el.classList.toggle('show')
}
const close = (key, el) => {
localStorage.setItem(key, 'false')
slideToggle(el)
}

View File

@ -0,0 +1,42 @@
@inject IOptionsMonitor<WebsiteOptions> Options
@inject IStringLocalizer<Video> Localizer
<p><b>@Localizer["H1"]</b></p>
@if (VideoUrl.Any())
{
foreach (var url in VideoUrl)
{
<div class="mb-3">
<a class="fa-solid fa-video" href="@url" target="_blank"><span class="ms-2">@Localizer["L1"]</span></a>
</div>
}
}
else
{
<div class="mb-3">@Localizer["L2"]</div>
}
@code {
[NotNull]
private List<string> VideoUrl { get; } = new List<string>();
[Parameter]
public string? Name { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
if (!string.IsNullOrEmpty(Name) && Options.CurrentValue.Videos.TryGetValue(Name, out var url))
{
if (!string.IsNullOrEmpty(url))
{
VideoUrl.AddRange(url.Split(';').Select(a => $"{Options.CurrentValue.VideoUrl}{a}"));
}
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
namespace BootstrapBlazor.Server.Components.Components;
/// <summary>
/// WebSiteModuleComponentBase 组件
/// </summary>
public abstract class WebSiteModuleComponentBase : BootstrapModuleComponentBase
{
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnLoadJSModule()
{
base.OnLoadJSModule();
ModulePath = $"./Components/{ModulePath}";
}
}

View File

@ -0,0 +1,75 @@
<DropdownWidget class="text-end flex-fill px-3">
<DropdownWidgetItem Icon="fa-regular fa-envelope" BadgeNumber="4">
<HeaderTemplate>
<span>您有 4 个未读消息</span>
</HeaderTemplate>
<BodyTemplate>
@for (var index = 0; index < 4; index++)
{
<a class="dropdown-item d-flex align-items-center" href="#" @onclick:preventDefault>
<div style="width: 40px; height: 40px;">
<Avatar Url="./images/Argo-C.png" IsCircle="true" Size="Size.Small" />
</div>
<div class="ms-2">
<div class="d-flex position-relative">
<h4>Argo Zhang</h4>
<small><i class="fa-regular fa-clock"></i> @(4 + index) mins</small>
</div>
<div class="text-truncate">Why not buy a new awesome theme?</div>
</div>
</a>
}
</BodyTemplate>
<FooterTemplate>
<a href="#" @onclick:preventDefault>查看所有消息</a>
</FooterTemplate>
</DropdownWidgetItem>
<DropdownWidgetItem Icon="fa-regular fa-bell" BadgeNumber="10" HeaderColor="Color.Success" BadgeColor="Color.Warning">
<HeaderTemplate>
<span>您有 10 个未读通知</span>
</HeaderTemplate>
<BodyTemplate>
@for (var index = 0; index < 10; index++)
{
<a class="dropdown-item d-flex align-items-center" href="#" @onclick:preventDefault>
<i class="fa-solid fa-users text-primary"></i>
<div class="ms-2">5 new members joined</div>
</a>
}
</BodyTemplate>
<FooterTemplate>
<a href="#" @onclick:preventDefault>查看所有通知</a>
</FooterTemplate>
</DropdownWidgetItem>
<DropdownWidgetItem Icon="fa-solid fa-flag" BadgeNumber="9" HeaderColor="Color.Danger" BadgeColor="Color.Danger">
<HeaderTemplate>
<span>您有 3 个任务</span>
</HeaderTemplate>
<BodyTemplate>
<a href="#" class="dropdown-item" @onclick:preventDefault>
<h3 class="position-relative">
Design some buttons
<small class="pull-right">20%</small>
</h3>
<BootstrapBlazor.Components.Progress IsAnimated="true" IsStriped="true" Value="20" Color="Color.Primary"></BootstrapBlazor.Components.Progress>
</a>
<a href="#" class="dropdown-item" @onclick:preventDefault>
<h3 class="position-relative">
Create a nice theme
<small class="pull-right">40%</small>
</h3>
<BootstrapBlazor.Components.Progress Value="40" Color="Color.Success"></BootstrapBlazor.Components.Progress>
</a>
<a href="#" class="dropdown-item" @onclick:preventDefault>
<h3 class="position-relative">
Some task I need to do
<small class="pull-right">60%</small>
</h3>
<BootstrapBlazor.Components.Progress Value="60" Color="Color.Danger"></BootstrapBlazor.Components.Progress>
</a>
</BodyTemplate>
<FooterTemplate>
<a href="#" @onclick:preventDefault>查看所有任务</a>
</FooterTemplate>
</DropdownWidgetItem>
</DropdownWidget>

View File

@ -0,0 +1,9 @@
<Button Text="@Text" OnClick="@(e => OnClick(Text))" />
@code {
[Parameter]
public string Text { get; set; } = "Test";
[Parameter]
public Func<string, Task> OnClick { get; set; } = _ => Task.CompletedTask;
}

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