feat(Dialog): add ShowResize parameter on ModalDialog (#1349)

* doc: 更新资源文件

* chore: 更新配置

* refactor: 更新注释

* refactor: 调整代码格式

* refactor: 格式化代码

* feat: 增加 resizer 图标

* refactor: 增加 resizer 样式

* refactor: 重构 Modal 脚本

* refactor: 增加 Modal resizer 样式

* feat: 增加 ShowResize 参数

* feat: 增加 ShowResize 逻辑

* refactor: 更新样式

* feat: 增加调整窗口大小逻辑

* refactor: 重构脚本

* refactor: 精简代码

* refactor: HMI 示例更改为单击弹窗

* doc: 格式化文档

* refactor: 更新渲染方式

* doc: 格式化文档

* test: 增加 ShowResize 单元测试

* doc: 格式化文档
This commit is contained in:
Argo Zhang
2023-06-05 09:04:24 +08:00
committed by GitHub
parent 8be62e646b
commit 8a49c0472f
18 changed files with 193 additions and 158 deletions

View File

@@ -7,42 +7,41 @@ using Longbow.GitHubAuth;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
namespace BootstrapBlazor.Server.Controllers
namespace BootstrapBlazor.Server.Controllers;
/// <summary>
/// Account controller.
/// </summary>
public class AccountController : Controller
{
/// <summary>
/// Account controller.
/// Gitee 认证
/// </summary>
public class AccountController : Controller
/// <returns></returns>
[HttpGet]
public IActionResult Gitee()
{
/// <summary>
/// Gitee 认证
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult Gitee()
{
return Challenge(GiteeDefaults.AuthenticationScheme);
}
return Challenge(GiteeDefaults.AuthenticationScheme);
}
/// <summary>
/// GitHub 认证
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult GitHub()
{
return Challenge(GitHubDefaults.AuthenticationScheme);
}
/// <summary>
/// GitHub 认证
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult GitHub()
{
return Challenge(GitHubDefaults.AuthenticationScheme);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return LocalRedirect("~/ai-chat");
}
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return LocalRedirect("~/ai-chat");
}
}

View File

@@ -43,7 +43,7 @@
</div>
<component type="typeof(ReconnectorOutlet)" param-AutoReconnect="true" param-ReconnectInterval="5000" render-mode="Server" />
<component type="typeof(UpdateIntro)" render-mode="Static" />
<component type="typeof(UpdateIntro)" render-mode="Server" />
<script src="_content/BootstrapBlazor.SummerNote/js/jquery-3.5.1.min.js" asp-append-version="true"></script>
<environment include="Development">

View File

@@ -10,5 +10,20 @@
"WebsiteOptions": {
"ServerUrl": "http://localhost:5053",
"WasmUrl": "http://localhost:5055"
},
"BootstrapBlazorOptions": {
"ToastDelay": 4000,
"MessageDelay": 4000,
"SwalDelay": 4000,
"EnableErrorLogger": false,
"FallbackCulture": "en",
"SupportedCultures": [
"zh-CN",
"en-US"
],
"TableSettings": {
"CheckboxColumnWidth": 36
},
"IgnoreLocalizerMissing": true
}
}

View File

@@ -20,12 +20,10 @@ public partial class UpdateIntro
[NotNull]
private PackageVersionService? PackageVersionService { get; set; }
[NotNull]
private string? UpdateLogUrl => $"{WebsiteOption.CurrentValue.BootstrapBlazorLink}/wikis/%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97?sort_id=4062034";
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>
/// <returns></returns>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, PackageVersionService.Version);
}

View File

@@ -1,7 +1,7 @@
@inherits BootstrapComponentBase
@inherits WebSiteModuleComponentBase
@inject FanControllerDataService DataService
@inject SwalService SwalService
@implements IAsyncDisposable
@attribute [JSModuleAutoLoader("Demos/Topologies/TopologiesNormal.razor.js", JSObjectReference = true, AutoInvokeDispose = false)]
<Button OnClickWithoutRender="OnResize" Text="Resize" />
@@ -10,16 +10,6 @@
</div>
@code {
[NotNull]
private IJSObjectReference? Module { get; set; }
[Inject]
[NotNull]
private IVersionService? JSVersionService { get; set; }
[NotNull]
private DotNetObjectReference<TopologiesNormal>? Interop { get; set; }
/// <summary>
/// 获得/设置 EChart DOM 元素实例
/// </summary>
@@ -35,6 +25,8 @@
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
var assembly = typeof(TopologiesNormal).Assembly;
var strName = assembly.GetName().Name + ".topology.json";
var stream = assembly.GetManifestResourceStream(strName);
@@ -48,18 +40,7 @@
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="firstRender"></param>
/// <returns></returns>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
// import JavaScript
Module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"./_content/BootstrapBlazor.Shared/Demos/Topologies/TopologiesNormal.razor.js?v={JSVersionService.GetVersion()}");
Interop = DotNetObjectReference.Create(this);
await Module.InvokeVoidAsync("init", Element, Interop, nameof(ToggleFan));
}
}
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Interop, nameof(ToggleFan));
private Task OnResize()
{
@@ -92,7 +73,7 @@
private async Task OnBeforePushData()
{
await Module.InvokeVoidAsync("execute");
await InvokeVoidAsync("execute");
// 推送数据
var data = DataService.GetDatas();
@@ -101,31 +82,4 @@
// 数据订阅
DataService.OnDataChange = async datas => await TopologyElement.PushData(datas);
}
/// <summary>
/// Dispose
/// </summary>
/// <param name="disposing"></param>
protected virtual async ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
Interop?.Dispose();
if (Module != null)
{
await Module.InvokeVoidAsync("dispose", Element);
await Module.DisposeAsync();
}
}
}
/// <summary>
/// Dispose
/// </summary>
public async ValueTask DisposeAsync()
{
await DisposeAsync(true);
GC.SuppressFinalize(this);
}
}

View File

@@ -1,19 +1,11 @@
import Data from '../../../BootstrapBlazor/modules/data.js'
export function init(el, invoker, callback) {
const meta = {}
Data.set(el, meta)
window.BlazorDemoTopology = {}
window.BlazorDemoTopology.handler = tagName => {
invoker.invokeMethodAsync(callback, tagName)
export function init(invoke, callback) {
window.BlazorDemoTopology = {
handler: tagName => {
invoke.invokeMethodAsync(callback, tagName)
}
}
}
export function execute() {
window.topology.setOptions({ hoverColor: '', hoverCursor: '', activeColor: '' });
}
export function dispose(el) {
Data.remove(el)
}

View File

@@ -3134,7 +3134,7 @@
"MultiSelectVeryLongTextTitle": "Options for extra-long text",
"MultiSelectVeryLongTextIntro": "The candidate text is particularly long",
"MultiSelectButtonTitle": "Full and reverse buttons",
"BMultiSelectButtonIntro": "By setting <code>ShowToolbar</code> value settings component displays the toolbox, by setting <code>ShowDefaultButtons</code> value settings component displays the default buttons, by setting <code>ShowSearch</code> value settings component displays the search box",
"MultiSelectButtonIntro": "By setting <code>ShowToolbar</code> value settings component displays the toolbox, by setting <code>ShowDefaultButtons</code> value settings component displays the default buttons, by setting <code>ShowSearch</code> value settings component displays the search box",
"MultiSelectMaxMinTitle": "Set the maximum and minimum number of options",
"MultiSelectMaxMinIntro": "Set the limit on the number of options available to components by setting the <code>max</code> <code>Min</code> values",
"MultiSelectExpandButtonTitle": "Extend the toolbar button",

View File

@@ -3145,7 +3145,7 @@
"MultiSelectVeryLongTextTitle": "选项超长文字",
"MultiSelectVeryLongTextIntro": "候选项文字特别长",
"MultiSelectButtonTitle": "全选与反选按钮",
"BMultiSelectButtonIntro": "通过参数 <code>ShowToolbar</code> 控制是否显示工具栏,通过参数 <code>ShowDefaultButtons</code> 控制是否显示内置的三个按钮,通过参数 <code>ShowSearch</code> 控制是否显示搜索栏",
"MultiSelectButtonIntro": "通过参数 <code>ShowToolbar</code> 控制是否显示工具栏,通过参数 <code>ShowDefaultButtons</code> 控制是否显示内置的三个按钮,通过参数 <code>ShowSearch</code> 控制是否显示搜索栏",
"MultiSelectMaxMinTitle": "设置选项最大数与最小数",
"MultiSelectMaxMinIntro": "通过设置 <code>Max</code> <code>Min</code> 值设置组件可选项数量限制",
"MultiSelectExpandButtonTitle": "扩展工具栏按钮",

View File

@@ -182,7 +182,7 @@
},
"events": [
{
"name": "dblclick",
"name": "click",
"action": 5,
"value": "BlazorDemoTopology.handler(pen.tags[0]);",
"fn": null

View File

@@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging;
namespace BootstrapBlazor.Components;
/// <summary>
///
/// ErrorLogger 全局异常组件
/// </summary>
public class ErrorLogger
#if NET6_0_OR_GREATER
@@ -20,16 +20,10 @@ public class ErrorLogger
: ComponentBase, IErrorLogger
#endif
{
/// <summary>
///
/// </summary>
[Inject]
[NotNull]
private ILogger<ErrorLogger>? Logger { get; set; }
/// <summary>
///
/// </summary>
[Inject]
[NotNull]
private IConfiguration? Configuration { get; set; }
@@ -79,15 +73,12 @@ public class ErrorLogger
public RenderFragment<Exception>? ErrorContent { get; set; }
#endif
/// <summary>
///
/// </summary>
protected Exception? Exception { get; set; }
private Exception? Exception { get; set; }
private bool ShowErrorDetails { get; set; }
/// <summary>
/// OnInitialized 方法
/// <inheritdoc/>
/// </summary>
protected override void OnInitialized()
{
@@ -101,10 +92,13 @@ public class ErrorLogger
{
ErrorContent ??= RenderException();
}
#if NET6_0_OR_GREATER
MaximumErrorCount = 1;
#endif
}
/// <summary>
/// OnParametersSet 方法
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{

View File

@@ -15,10 +15,7 @@ namespace BootstrapBlazor.Components;
/// </summary>
public partial class Layout : IHandlerException
{
/// <summary>
///
/// </summary>
protected bool IsSmallScreen { get; set; }
private bool IsSmallScreen { get; set; }
/// <summary>
/// 获得/设置 侧边栏状态
@@ -286,16 +283,16 @@ public partial class Layout : IHandlerException
[Parameter]
public object? Resource { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<Layout>? Localizer { get; set; }
/// <summary>
/// 获得 登录授权信息
/// </summary>
[CascadingParameter]
private Task<AuthenticationState>? AuthenticationStateTask { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<Layout>? Localizer { get; set; }
[Inject]
private IAuthorizationPolicyProvider? AuthorizationPolicyProvider { get; set; }

View File

@@ -10,6 +10,10 @@
cursor: move;
}
.modal-content {
height: 100%;
}
.modal-footer {
padding: 0.5rem 1rem;
}
@@ -22,6 +26,16 @@
margin-right: .5rem;
}
.modal-resizer {
position: absolute;
bottom: 2px;
right: 2px;
cursor: nwse-resize;
pointer-events: auto;
width: 1.3rem;
height: auto;
}
.bb-printview {
background-color: #fff;
padding: 1rem;

View File

@@ -5,7 +5,7 @@ import EventHandler from "../../modules/event-handler.js?v=$version"
export function init(id, invoke, shownCallback, closeCallback) {
const el = document.getElementById(id)
const modal = {
element: el,
el,
invoke,
shownCallback,
closeCallback,
@@ -22,6 +22,48 @@ export function init(id, invoke, shownCallback, closeCallback) {
}
Data.set(id, modal)
const resizer = el.querySelector('.modal-resizer')
if(resizer) {
const dialog = el.querySelector('.modal-dialog')
drag(resizer,
e => {
dialog.originX = e.clientX || e.touches[0].clientX;
dialog.originY = e.clientY || e.touches[0].clientY;
const rect = dialog.getBoundingClientRect()
dialog.dialogWidth = rect.width
dialog.dialogHeight = rect.height
dialog.style.maxWidth = 'auto'
dialog.style.width = `${dialog.dialogWidth}px`
dialog.style.height = `${dialog.dialogHeight}px`
dialog.classList.add('is-resize')
},
e => {
if (dialog.classList.contains('is-resize')) {
const eventX = e.clientX || e.changedTouches[0].clientX;
const eventY = e.clientY || e.changedTouches[0].clientY;
let newValX = dialog.dialogWidth + Math.ceil(eventX - dialog.originX);
let newValY = dialog.dialogHeight + Math.ceil(eventY - dialog.originY);
if (newValX > window.innerWidth) {
newValX = window.innerWidth
}
if (newValY > window.innerHeight) {
newValY = window.innerHeight
}
dialog.style.maxWidth = `${newValX}px`
dialog.style.width = `${newValX}px`
dialog.style.height = `${newValY}px`
}
},
() => {
dialog.classList.remove('is-drag')
})
}
EventHandler.on(el, 'shown.bs.modal', () => {
invoke.invokeMethodAsync(shownCallback)
})
@@ -39,16 +81,16 @@ export function init(id, invoke, shownCallback, closeCallback) {
EventHandler.on(window, 'popstate', modal.pop)
modal.show = () => {
const dialogs = modal.element.querySelectorAll('.modal-dialog')
const dialogs = el.querySelectorAll('.modal-dialog')
if (dialogs.length === 1) {
let backdrop = modal.element.getAttribute('data-bs-backdrop') !== 'static'
let backdrop = el.getAttribute('data-bs-backdrop') !== 'static'
if (!backdrop) {
backdrop = 'static'
}
if (!modal.modal) {
modal.modal = bootstrap.Modal.getOrCreateInstance(modal.element, { focus: false })
modal.modal = bootstrap.Modal.getOrCreateInstance(el, { focus: false })
}
modal.modal._config.keyboard = modal.element.getAttribute('data-bs-keyboard') === 'true'
modal.modal._config.keyboard = el.getAttribute('data-bs-keyboard') === 'true'
modal.modal._config.backdrop = backdrop
modal.modal.show()
} else {
@@ -74,7 +116,7 @@ export function init(id, invoke, shownCallback, closeCallback) {
modal.header = modal.dialog.querySelector('.modal-header')
drag(modal.header,
e => {
if (e.srcElement.closest('.modal-header-buttons')) {
if (e.target.closest('.modal-header-buttons')) {
return true
}
modal.originX = e.clientX || e.touches[0].clientX;
@@ -109,14 +151,14 @@ export function init(id, invoke, shownCallback, closeCallback) {
}
}
},
e => {
() => {
modal.dialog.classList.remove('is-drag')
})
}
}
modal.hide = () => {
if (modal.element.children.length === 1) {
if (el.children.length === 1) {
modal.modal.hide()
} else {
modal.invoke.invokeMethodAsync(modal.closeCallback)
@@ -137,7 +179,7 @@ export function init(id, invoke, shownCallback, closeCallback) {
modal.handlerEscape = e => {
if (e.key === 'Escape') {
const keyboard = modal.element.getAttribute('data-bs-keyboard')
const keyboard = el.getAttribute('data-bs-keyboard')
if (keyboard === 'true') {
modal.hide()
}
@@ -145,9 +187,9 @@ export function init(id, invoke, shownCallback, closeCallback) {
}
EventHandler.on(document, 'keyup', modal.handlerEscape)
EventHandler.on(modal.element, 'click', e => {
EventHandler.on(el, 'click', e => {
if (e.target.closest('.modal-dialog') === null) {
const backdrop = modal.element.getAttribute('data-bs-backdrop')
const backdrop = el.getAttribute('data-bs-backdrop')
if (backdrop !== 'static') {
modal.hide()
}
@@ -180,20 +222,22 @@ export function dispose(id) {
const modal = Data.get(id)
Data.remove(id)
if (modal.draggable) {
modal.disposeDrag()
}
if(modal) {
if (modal.draggable) {
modal.disposeDrag()
}
EventHandler.off(modal.element, 'shown.bs.modal')
EventHandler.off(modal.element, 'hide.bs.modal')
EventHandler.off(modal.element, 'click')
EventHandler.off(modal.el, 'shown.bs.modal')
EventHandler.off(modal.el, 'hide.bs.modal')
EventHandler.off(modal.el, 'click')
if (modal.hook_keyboard_backdrop) {
EventHandler.off(document, 'keyup', modal.handlerEscape)
}
if (modal.hook_keyboard_backdrop) {
EventHandler.off(document, 'keyup', modal.handlerEscape)
}
EventHandler.off(window, 'popstate', modal.pop)
if (modal.modal) {
modal.modal.dispose()
EventHandler.off(window, 'popstate', modal.pop)
if (modal.modal) {
modal.modal.dispose()
}
}
}

View File

@@ -63,4 +63,8 @@
</CascadingValue>
</CascadingValue>
</div>
@if (ShowResize)
{
<svg class="modal-resizer" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M319.20128 974.56128L348.16 1003.52l655.36-655.36-28.95872-28.95872-655.36 655.36zM675.84 1003.52l327.68-327.68-28.95872-28.95872-327.68 327.68L675.84 1003.52z" fill="#8a8a8a" p-id="6971"></path></svg>
}
</div>

View File

@@ -7,7 +7,7 @@ using Microsoft.Extensions.Localization;
namespace BootstrapBlazor.Components;
/// <summary>
///
/// ModalDialog 组件
/// </summary>
public partial class ModalDialog : IHandlerException, IDisposable
{
@@ -44,6 +44,12 @@ public partial class ModalDialog : IHandlerException, IDisposable
[Parameter]
public string? Class { get; set; }
/// <summary>
/// 获得/设置 是否可以 Resize 弹窗 默认 false
/// </summary>
[Parameter]
public bool ShowResize { get; set; }
/// <summary>
/// 获得/设置 弹窗大小
/// </summary>
@@ -123,7 +129,7 @@ public partial class ModalDialog : IHandlerException, IDisposable
public bool ShowPrintButtonInHeader { get; set; }
/// <summary>
/// 获得/设置 Header 中打印按钮显示文字 默认为资源文件中 打印
/// 获得/设置 Header 中打印按钮显示文字 默认为资源文件中 打印
/// </summary>
[Parameter]
public string? PrintButtonText { get; set; }

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
import Data from "./data.js?v=$version"
import EventHandler from "./event-handler.js?v=$version"
export function init(id, invoker, interval, callback) {
var m = { invoker, interval, callback, mousePosition: {}, count: 1000 }
export function init(id, invoke, interval, callback) {
const m = { invoke, interval, callback, mousePosition: {}, count: 1000 }
Data.set(id, m)
m.fnMouseHandler = e => {
@@ -26,7 +26,7 @@ export function init(id, invoker, interval, callback) {
clearInterval(m.lockHandler)
m.lockHandler = null
m.invoker.invokeMethodAsync(m.callback)
invoke.invokeMethodAsync(callback)
}
}, 1000)
}
@@ -35,10 +35,12 @@ export function dispose(id) {
const m = Data.get(id)
Data.remove(id)
EventHandler.off(document, 'mousemove', m.fnMouseHandler)
EventHandler.off(document, 'keydown', m.fnKeyHandler)
if(m) {
EventHandler.off(document, 'mousemove', m.fnMouseHandler)
EventHandler.off(document, 'keydown', m.fnKeyHandler)
if (m.lockHandler) {
clearInterval(m.lockHandler)
if (m.lockHandler) {
clearInterval(m.lockHandler)
}
}
}

View File

@@ -52,6 +52,22 @@ public class ModalDialogTest : BootstrapBlazorTestBase
Assert.Contains("test_class", cut.Markup);
}
[Fact]
public void ShowResize_Ok()
{
var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb =>
{
pb.AddChildContent<Modal>(pb =>
{
pb.AddChildContent<ModalDialog>(pb =>
{
pb.Add(d => d.ShowResize, true);
});
});
});
Assert.Contains("modal-resizer", cut.Markup);
}
[Fact]
public void ShowMaximizeButton_Ok()
{