diff --git a/src/BootstrapBlazor.Server/Components/Components/Header.razor b/src/BootstrapBlazor.Server/Components/Components/Header.razor index baa74c34958..abf04151a31 100644 --- a/src/BootstrapBlazor.Server/Components/Components/Header.razor +++ b/src/BootstrapBlazor.Server/Components/Components/Header.razor @@ -20,12 +20,12 @@ - @if (CultureInfo.CurrentUICulture.Name == "zh-CN") + @* @if (CultureInfo.CurrentUICulture.Name == "zh-CN") { - } + } *@
diff --git a/src/BootstrapBlazor.Server/Components/Layout/TutorialsNavMenu.razor.cs b/src/BootstrapBlazor.Server/Components/Layout/TutorialsNavMenu.razor.cs index c92f1019ab0..3235e718788 100644 --- a/src/BootstrapBlazor.Server/Components/Layout/TutorialsNavMenu.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Layout/TutorialsNavMenu.razor.cs @@ -110,6 +110,11 @@ protected override async Task OnInitializedAsync() { Text = Localizer["OnlineSheet"], Url = "tutorials/online-sheet", + }, + new() + { + Text = Localizer["MemorialMode"], + Url = "tutorials/memorial", } ]); } diff --git a/src/BootstrapBlazor.Server/Components/Samples/Tutorials/Memorial.razor b/src/BootstrapBlazor.Server/Components/Samples/Tutorials/Memorial.razor new file mode 100644 index 00000000000..1bcbf7b2dbd --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/Tutorials/Memorial.razor @@ -0,0 +1,13 @@ +@page "/tutorials/memorial" + + + +

1. 加载 Utlity 工具

+
var module = await JSRuntime.LoadUtility();
+ +

2. 设置哀悼模式

+
await module.InvokeVoidAsync("SetMemorial", true);
+ +

3. 全站默认设置追悼模式方法

+

更新 App.razor 文档内容如下

+
<html lang="en" data-bs-theme='dark' data-bb-theme="memorial">
diff --git a/src/BootstrapBlazor.Server/Components/Samples/Tutorials/Memorial.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Tutorials/Memorial.razor.cs new file mode 100644 index 00000000000..1481aca7faa --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/Tutorials/Memorial.razor.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +using Microsoft.JSInterop; + +namespace BootstrapBlazor.Server.Components.Samples.Tutorials; + +/// +/// 追悼模式 +/// +public partial class Memorial +{ + [Inject, NotNull] + private IJSRuntime? JSRuntime { get; set; } + + private bool _isMemorial = false; + + private async Task OnToggle() + { + var module = await JSRuntime.LoadUtility(); + + _isMemorial = !_isMemorial; + await module.SetMemorialModeAsync(_isMemorial); + } +} diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 625906ea3c9..fea96186570 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -22,7 +22,8 @@ "TranslateSummary": "Translate", "DrawingSummary": "Drawing", "AdminSummary": "Admin", - "OnlineSheet": "UniverSheet" + "OnlineSheet": "UniverSheet", + "MemorialMode": "Memorial" }, "BootstrapBlazor.Server.Components.Components.Pre": { "LoadingText": "Loading ...", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index a812af54b4a..2959a357d90 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -22,7 +22,8 @@ "TranslateSummary": "翻译工具 Translate", "DrawingSummary": "画图 Drawing", "AdminSummary": "中台 Admin", - "OnlineSheet": "在线表格 UniverSheet" + "OnlineSheet": "在线表格 UniverSheet", + "MemorialMode": "追悼模式" }, "BootstrapBlazor.Server.Components.Components.Pre": { "LoadingText": "正在加载 ...", diff --git a/src/BootstrapBlazor/Extensions/JSModuleExtensions.cs b/src/BootstrapBlazor/Extensions/JSModuleExtensions.cs index 03276489e54..97f70b823b5 100644 --- a/src/BootstrapBlazor/Extensions/JSModuleExtensions.cs +++ b/src/BootstrapBlazor/Extensions/JSModuleExtensions.cs @@ -11,20 +11,20 @@ namespace BootstrapBlazor.Components; public static class JSModuleExtensions { /// - /// 导入 utility js 模块 + /// Load utility js module /// - /// - /// - /// A ]]> 模块加载器 + /// The instance + /// The version of the module + /// A ]]> module loader public static Task LoadUtility(this IJSRuntime jsRuntime, string? version = null) => LoadModuleByName(jsRuntime, "utility", version); /// - /// 通过名称导入内置脚本模块 + /// Load built-in script module by name /// - /// - /// - /// - /// A ]]> 模块加载器 + /// The instance + /// The name of the module + /// The version of the module + /// A ]]> module loader public static Task LoadModuleByName(this IJSRuntime jsRuntime, string moduleName, string? version = null) { var fileName = $"./_content/BootstrapBlazor/modules/{moduleName}.js"; @@ -32,12 +32,12 @@ public static Task LoadModuleByName(this IJSRuntime jsRuntime, string } /// - /// IJSRuntime 扩展方法 动态加载脚本 + /// IJSRuntime extension method to dynamically load scripts /// - /// - /// - /// - /// A ]]> 模块加载器 + /// The instance + /// The file name of the script + /// The version of the script + /// A ]]> module loader public static async Task LoadModule(this IJSRuntime jsRuntime, string fileName, string? version = null) { if (!string.IsNullOrEmpty(version)) @@ -64,10 +64,10 @@ public static async Task LoadModule(this IJSRuntime jsRuntime, string } /// - /// 获得指定类型的加载 Module 名称 + /// Get the module name of the specified type /// - /// - /// + /// The type + /// The module name public static string GetTypeModuleName(this Type type) { var name = type.Name; @@ -80,47 +80,47 @@ public static string GetTypeModuleName(this Type type) } /// - /// 在新标签页打开指定网址 + /// Open the specified URL in a new tab /// - /// 实例 - /// 打开网页地址 - /// 默认 _blank - /// 默认 null + /// instance + /// The URL to open + /// The target window, default is _blank + /// The features of the new window, default is null /// A that represents the asynchronous invocation operation. public static ValueTask OpenUrl(this JSModule module, string url, string? target = "_blank", string? features = null) => module.InvokeVoidAsync("openUrl", url, target, features); /// - /// 动态运行js代码 + /// Dynamically run js code /// - /// 实例 - /// + /// instance + /// The script to run /// A that represents the asynchronous invocation operation. public static async ValueTask Eval(this JSModule module, string script) => await module.InvokeVoidAsync("runEval", script); /// - /// 通过 Eval 动态运行 JavaScript 代码 + /// Dynamically run JavaScript code via Eval /// - /// 实例 - /// + /// instance + /// The script to run /// A that represents the asynchronous invocation operation. public static ValueTask Eval(this JSModule module, string script) => module.InvokeAsync("runEval", script); /// - /// 通过 Function 动态运行 JavaScript 代码 + /// Dynamically run JavaScript code via Function /// - /// 实例 - /// - /// + /// instance + /// The script to run + /// The arguments to pass to the script /// A that represents the asynchronous invocation operation. public static ValueTask Function(this JSModule module, string script, params object?[]? args) => module.InvokeVoidAsync("runFunction", script, args); /// - /// 动态运行js代码 + /// Dynamically run js code /// - /// - /// 实例 - /// - /// + /// The return type + /// instance + /// The script to run + /// The arguments to pass to the script /// A that represents the asynchronous invocation operation. public static async ValueTask Function(this JSModule module, string script, params object?[]? args) { @@ -133,14 +133,14 @@ public static string GetTypeModuleName(this Type type) } /// - /// 获取当前终端是否为移动设备 + /// Check if the current terminal is a mobile device /// - /// 实例 + /// instance /// A that represents the asynchronous invocation operation. public static ValueTask IsMobile(this JSModule module) => module.InvokeAsync("isMobile"); /// - /// 获取一个页面上不重复的元素ID + /// Get a unique element ID on a page /// /// An instance of /// A prefix of type @@ -148,26 +148,34 @@ public static string GetTypeModuleName(this Type type) public static ValueTask GenerateId(this JSModule module, string? prefix = null) => module.InvokeAsync("getUID", prefix); /// - /// 获取一个页面内指定元素 Html 字符串 + /// Get the HTML string of a specified element on a page /// /// An instance of - /// - /// + /// The ID of the element + /// The selector of the element /// Returns a formatted element ID public static ValueTask GetHtml(this JSModule module, string? id = null, string? selector = null) => module.InvokeAsync("getHtml", new { id, selector }); /// - /// 设置主题方法 + /// Set the theme method /// /// An instance of - /// theme name + /// The name of the theme /// public static ValueTask SetThemeAsync(this JSModule module, string themeName) => module.InvokeVoidAsync("setTheme", themeName, true); /// - /// 设置主题方法 + /// Get the theme method /// /// An instance of /// public static ValueTask GetThemeAsync(this JSModule module) => module.InvokeAsync("getTheme"); + + /// + /// Set memorial mode + /// + /// An instance of + /// Whether it is memorial mode + /// + public static ValueTask SetMemorialModeAsync(this JSModule module, bool isMemorial) => module.InvokeVoidAsync("setMemorialMode", isMemorial); } diff --git a/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs b/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs index 139868f135e..5e736586aa4 100644 --- a/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs +++ b/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs @@ -9,126 +9,131 @@ namespace BootstrapBlazor.Components; /// -/// 组件全局配置类 +/// Global configuration class for components /// public class BootstrapBlazorOptions : IOptions { /// - /// 获得/设置 Toast 组件 Delay 默认值 默认为 0 + /// Gets or sets the default delay for the Toast component, default is 0 /// public int ToastDelay { get; set; } /// - /// 获得/设置 Message 组件 Delay 默认值 默认为 0 + /// Gets or sets the default delay for the Message component, default is 0 /// public int MessageDelay { get; set; } /// - /// 获得/设置 Swal 组件 Delay 默认值 默认为 0 + /// Gets or sets the default delay for the Swal component, default is 0 /// public int SwalDelay { get; set; } /// - /// 获得/设置 回落默认语言文化 默认为 en 英文 + /// Gets or sets the fallback default language culture, default is "en" (English) /// public string FallbackCulture { get; set; } = "en"; /// - /// 获得/设置 Toast 组件全局弹窗默认位置 默认为 null 当设置值后覆盖整站设置 + /// Gets or sets the default position for the Toast component globally, default is null. When set, it overrides the site-wide setting. /// public Placement? ToastPlacement { get; set; } /// - /// 获得/设置 组件内置本地化语言列表 默认为 null + /// Gets or sets the list of built-in localization languages for components, default is null /// public List? SupportedCultures { get; set; } /// - /// 获得/设置 是否开启全局异常捕获功能 默认为 true + /// Gets or sets whether to enable global exception capture functionality, default is true /// public bool EnableErrorLogger { get; set; } = true; /// - /// 获得/设置 是否回落到 Fallback 文化 默认为 true + /// Gets or sets whether to fall back to the fallback culture, default is true /// public bool EnableFallbackCulture { get; set; } = true; /// - /// 获得/设置 是否忽略丢失文化日志信息 默认 null 未设置 + /// Gets or sets whether to ignore missing culture log information, default is null (not set) /// - /// 使用 默认值 + /// Uses the default value of public bool? IgnoreLocalizerMissing { get; set; } /// - /// 获得/设置 是否禁用从服务中获取本地化资源 默认 false 未禁用 + /// Gets or sets whether to disable fetching localization resources from the service, default is false (not disabled) /// public bool? DisableGetLocalizerFromService { get; set; } /// - /// 获得/设置 是否禁用获取 类型本地化资源 默认 false 未禁用 + /// Gets or sets whether to disable fetching localization resources of type , default is false (not disabled) /// public bool? DisableGetLocalizerFromResourceManager { get; set; } /// - /// 获得/设置 默认文化信息 + /// Gets or sets the default culture information /// - /// 开启多文化时此参数无效 + /// This parameter is invalid when multi-culture is enabled public string? DefaultCultureInfo { get; set; } /// - /// 获得/设置 是否禁用表单内回车自动提交功能 默认 null 未设置 + /// Gets or sets whether to disable the automatic form submission feature by pressing Enter, default is null (not set) /// public bool? DisableAutoSubmitFormByEnter { get; set; } /// - /// 获得/设置 JavaScript 模块脚本版本号 默认为 null + /// Gets or sets the JavaScript module script version number, default is null /// public string? JSModuleVersion { get; set; } /// - /// 获得/设置 表格设置实例 + /// Gets or sets the table settings instance /// public TableSettings TableSettings { get; set; } = new(); /// - /// 获得/设置 配置实例 + /// Gets or sets the configuration instance /// public StepSettings StepSettings { get; set; } = new(); /// - /// 获得/设置 配置 默认不为空 + /// Gets or sets the configuration, default is not null /// public ConnectionHubOptions ConnectionHubOptions { get; set; } = new(); /// - /// 获得/设置 配置 默认不为空 + /// Gets or sets the configuration, default is not null /// public WebClientOptions WebClientOptions { get; set; } = new(); /// - /// 获得/设置 配置 默认不为空 + /// Gets or sets the configuration, default is not null /// public IpLocatorOptions IpLocatorOptions { get; set; } = new(); /// - /// 获得/设置 配置 默认不为空 + /// Gets or sets the configuration, default is not null /// public ScrollOptions ScrollOptions { get; set; } = new(); /// - /// 获得/设置 配置 默认不为空 + /// Gets or sets the configuration, default is not null /// public ContextMenuOptions ContextMenuOptions { get; set; } = new(); /// - /// 获得/设置 CacheManagerOptions 配置 默认不为空 + /// Gets or sets the CacheManagerOptions configuration, default is not null /// public CacheManagerOptions CacheManagerOptions { get; set; } = new(); + /// + /// Get or sets website use memorial mode. default is false + /// + public bool IsMemorialMode { get; set; } + BootstrapBlazorOptions IOptions.Value => this; /// - /// 获得支持多语言集合 + /// Gets the collection of supported languages /// /// public IList GetSupportedCultures() => SupportedCultures?.Select(name => new CultureInfo(name)).ToList() diff --git a/src/BootstrapBlazor/wwwroot/modules/utility.js b/src/BootstrapBlazor/wwwroot/modules/utility.js index 3f5472e38e7..a8cf06a0271 100644 --- a/src/BootstrapBlazor/wwwroot/modules/utility.js +++ b/src/BootstrapBlazor/wwwroot/modules/utility.js @@ -849,6 +849,26 @@ export function calcCenterPosition(el) { } } +export function setMemorialMode(memorial) { + const el = document.documentElement; + if (memorial) { + const theme = el.getAttribute('data-bs-theme'); + if (theme) { + el.setAttribute('data-bs-original-theme', theme); + } + el.setAttribute('data-bs-theme', 'dark'); + el.setAttribute('data-bb-theme', 'memorial'); + } + else { + const theme = el.getAttribute('data-bs-original-theme'); + el.removeAttribute('data-bs-theme'); + el.removeAttribute('data-bb-theme'); + if (theme) { + el.setAttribute('data-bs-theme', theme); + } + } +} + export { autoAdd, autoRemove, diff --git a/src/BootstrapBlazor/wwwroot/scss/meilisearch.scss b/src/BootstrapBlazor/wwwroot/scss/meilisearch.scss index 6bbf3337385..f8c937f451f 100644 --- a/src/BootstrapBlazor/wwwroot/scss/meilisearch.scss +++ b/src/BootstrapBlazor/wwwroot/scss/meilisearch.scss @@ -1,8 +1,8 @@ .bb-g-search { --bb-global-search-input-margin-left: .5rem; - --bb-global-search-border-color: rgba(255,255,255,.5); - --bb-global-search-color: rgba(255,255,255,.5); - --bb-global-search-border-hover-color: rgba(255,255,255); + --bb-global-search-border-color: rgba(255, 255, 255, .5); + --bb-global-search-color: rgba(255, 255, 255, .5); + --bb-global-search-border-hover-color: rgba(255, 255, 255); --bb-global-search-padding: 0.25rem 0.75rem; --bb-global-search-width: 168px; display: flex; diff --git a/src/BootstrapBlazor/wwwroot/scss/root.scss b/src/BootstrapBlazor/wwwroot/scss/root.scss index 9b94bab5629..a71d9276647 100644 --- a/src/BootstrapBlazor/wwwroot/scss/root.scss +++ b/src/BootstrapBlazor/wwwroot/scss/root.scss @@ -58,13 +58,13 @@ a, a:hover, a:focus { } .input-group { - @include direction(right,last); - @include direction(left,first) + @include direction(right, last); + @include direction(left, first) } .btn-group { - @include direction(right,last); - @include direction(left,first) + @include direction(right, last); + @include direction(left, first) } .popover.popover-table-column-toolbox { @@ -120,42 +120,50 @@ a, a:hover, a:focus { } } +[data-bb-theme='memorial'] { + &:root { + --bs-body-bg: #000; + --bs-body-color: #b5b5c3; + filter: grayscale(100%); + } +} + body:before { content: "extraExtraSmall"; display: none; } -@media (min-width:375px) { +@media (min-width: 375px) { body:before { content: "extraSmall"; } } -@media (min-width:576px) { +@media (min-width: 576px) { body:before { content: "small"; } } -@media (min-width:768px) { +@media (min-width: 768px) { body:before { content: "medium"; } } -@media (min-width:992px) { +@media (min-width: 992px) { body:before { content: "large"; } } -@media (min-width:1200px) { +@media (min-width: 1200px) { body:before { content: "extraLarge"; } } -@media (min-width:1400px) { +@media (min-width: 1400px) { body:before { content: "extraExtraLarge"; } diff --git a/test/UnitTest/Utils/JSModuleTest.cs b/test/UnitTest/Utils/JSModuleTest.cs index c1588d93de1..936bf43e5f5 100644 --- a/test/UnitTest/Utils/JSModuleTest.cs +++ b/test/UnitTest/Utils/JSModuleTest.cs @@ -102,6 +102,14 @@ public async Task JSModule_TaskCanceledException() await module.InvokeAsync("test"); } + [Fact] + public async Task JSModule_SetMemorial() + { + var js = new MockTaskCanceledObjectReference(); + var module = new JSModule(js); + await module.SetMemorialModeAsync(true); + } + private class MockErrorJSObjectReference : MockJSObjectReference { protected override ValueTask DisposeAsyncCore(bool disposing)