!3526 feat(#I60WG3): redesign RibbonTab use dynamic load javascript module

* chore: 更新 js 脚本
* refactor: 增加 JSObjectReference 参数
* style: 更新样式变量化
* chore: 增加字体大小样式
* refactor: 更改 HeaderClick 为 MenuClick
* chore: 更新样式
* doc: 更新示例
* feat: 增加 ChildContent 参数
* chore: 增加 IsBorder 参数
* chore: 更新样式
* chore: 格式化代码
* chore: 增加 card-shadow 变量
* chore: 更新 Tab 样式
* chore: 增加 ribbon-tab 脚本
* doc: 更新示例
This commit is contained in:
Argo 2022-11-13 14:16:51 +00:00
parent 51f83e7f95
commit acf3a5de01
20 changed files with 209 additions and 262 deletions

View File

@ -4022,7 +4022,9 @@
"Items11": "Task 2",
"Items12": "Task 3",
"ItemsGroupName1": "Group one",
"ItemsGroupName2": "Group two"
"ItemsGroupName2": "Group two",
"HeaderClickTitle": "Header click callback",
"HeaderClickIntro": "Handler the event of click header via <code>OnHeaderClickAsync</code>"
},
"BootstrapBlazor.Shared.Samples.Logouts": {
"H1": "Logout component",

View File

@ -4021,8 +4021,9 @@
"Items11": "常规操作",
"Items12": "常规操作",
"ItemsGroupName1": "操作组一",
"ItemsGroupName2": "操作组三"
"ItemsGroupName2": "操作组三",
"HeaderClickTitle": "Header 点击回调",
"HeaderClickIntro": "通过设置 <code>OnHeaderClickAsync</code> 回调方法处理点击 <code>Header</code> 部分逻辑"
},
"BootstrapBlazor.Shared.Samples.Logouts": {
"H1": "Logout 登出组件",

View File

@ -29,12 +29,11 @@
</RibbonTab>
</DemoBlock>
<DemoBlock Title="@Localizer["HeaderClick"]" Introduction="@Localizer["HeaderClickIntro"]" Name="HeaderClick">
<RibbonTab Items="@Items" OnHeaderClickAsync="@OnHeaderClickAsync" />
<div class="ribbon-bodys">
<DemoBlock Title="@Localizer["HeaderClickTitle"]" Introduction="@Localizer["HeaderClickIntro"]" Name="HeaderClick">
<RibbonTab Items="@Items" OnHeaderClickAsync="@OnHeaderClickAsync">
<div class="@FileClassString">@Localizer["ItemsText1"]</div>
<div class="@EditClassString">@Localizer["ItemsText2"]</div>
</div>
</RibbonTab>
</DemoBlock>
<AttributeTable Items="@GetAttributes()" />

View File

@ -46,15 +46,17 @@ public partial class RibbonTabs
}
}
};
ActiveTabText = Localizer["ItemsText1"];
}
private string? ActiveTabText { get; set; }
private string? FileClassString => CssBuilder.Default("ribbon-body collapse")
private string? FileClassString => CssBuilder.Default("collapse")
.AddClass("show", ActiveTabText == Localizer["ItemsText1"])
.Build();
private string? EditClassString => CssBuilder.Default("ribbon-body collapse")
private string? EditClassString => CssBuilder.Default("collapse")
.AddClass("show", ActiveTabText == Localizer["ItemsText2"])
.Build();

View File

@ -1,8 +1,5 @@
.ribbon-demo {
height: 196px;
border: 1px solid #ddd;
border-radius: var(--bs-border-radius);
overflow: hidden;
}
.ribbon-demo-body {

View File

@ -63,6 +63,7 @@
--bs-btn-bg: transparent;
border: none;
padding: 0;
background-color: var(--bs-btn-bg);
}
.link-button:disabled img {

View File

@ -1,8 +1,8 @@
.card {
--bs-card-border-radius: var(--bs-border-radius);
--bs-card-inner-border-radius: calc(var(--bs-border-radius) - 1px);
--bb-card-shadow: 0 0 8px 0 #e8edfa99, 0 2px 4px 0 #e8edfa80;
--bb-card-hover-shadow: 0 1px 7px 0 #0000000d, 0 2px 8px 0 #00000012, 0 3px 9px 0 #0000000f, 0 5px 10px 0 #00000008;
--bb-card-shadow: var(--bb-shadow);
--bb-card-hover-shadow: var(--bb-hover-shadow);
}
.card .card-collapse {

View File

@ -1,4 +1,13 @@
:root {
.ribbon-tab {
--bb-ribbon-tabs-header-bg: #f5f7fa;
--bb-ribbon-tabs-item-color: #343a40;
--bb-ribbon-tabs-item-hover-color: #409eff;
--bb-ribbon-button-hover-bg: #d0e7ff;
--bb-ribbon-button-hover-border-color: #8bb5e0;
--bb-ribbon-button-active-bg: #acd4fd;
--bb-ribbon-button-active-border-color: #8bb5e0;
--bb-ribbon-button-border-width: 1px;
--bb-ribbon-button-border-color: transparent;
--bb-ribbon-button-radius: 3px;
--bb-ribbon-button-padding: 0.25rem;
--bb-ribbon-button-fontsize: 0.75rem;
@ -6,60 +15,69 @@
--bb-ribbon-content-height: 82.5px;
--bb-ribbon-command-padding: .5rem;
--bb-ribbon-group-fontsize: 11px;
--bb-ribbon-group-color: #adb5bd;
--bb-ribbon-group-margin-top: .25rem;
--bb-ribbon-item-height: 30px;
--bb-ribbon-item-radius: 6px;
--bb-ribbon-item-radius: var(--bs-border-radius);
--bb-ribbon-item-padding: .5rem;
}
.ribbon-tab {
--bb-ribbon-item-margin-top: 10px;
--bb-ribbon-item-border-color: var(--bs-border-color);
--bb-ribbon-body-padding: 1rem;
position: relative;
z-index: 10;
}
.ribbon-tab .tabs.tabs-top.tabs-border-card .tabs-header {
border-top-left-radius: 0;
border-top-right-radius: 0;
.ribbon-tab .ribbon-header {
position: relative;
}
.ribbon-tab .tabs-border-card {
border-radius: unset;
border: none;
}
.ribbon-tab .tabs-border-card.tabs-top .tabs-header .tabs-item {
padding: 0 .5rem;
margin-top: calc(var(--bb-tab-item-height) - var(--bb-ribbon-item-height));
height: var(--bb-ribbon-item-height);
border-top-left-radius: var(--bb-ribbon-item-radius);
border-top-right-radius: var(--bb-ribbon-item-radius);
transition: background-color .3s ease-in-out, color .3s ease-in-out, border-right-color .3s ease-in-out, border-left-color .3s ease-in-out;
.ribbon-tab .ribbon-header .tabs-border-card.tabs-top .tabs-header {
border-top-left-radius: 0;
border-top-right-radius: 0;
background-color: var(--bb-ribbon-tabs-header-bg);
}
.ribbon-tab .tabs-border-card.tabs-top .tabs-header .tabs-item.active {
border-top-color: #dcdfe6;
.ribbon-tab .ribbon-header .tabs-border-card {
border-radius: unset;
border: none;
box-shadow: none;
}
.ribbon-tab .ribbon-header .tabs-border-card .tabs-header .tabs-item {
padding: 0 var(--bb-ribbon-item-padding);
margin-top: var(--bb-ribbon-item-margin-top);
height: var(--bb-ribbon-item-height);
border-top-left-radius: var(--bb-ribbon-item-radius);
border-top-right-radius: var(--bb-ribbon-item-radius);
transition: background-color .3s ease-in-out, color .3s ease-in-out, border-right-color .3s ease-in-out, border-left-color .3s ease-in-out;
}
.ribbon-tab .tabs-top.tabs-border-card > .tabs-header .tabs-item:nth-child(2) {
padding-left: var(--bb-ribbon-item-padding);
}
.ribbon-tab .ribbon-header .tabs-border-card .tabs-header .tabs-item.active {
border-left-color: var(--bb-ribbon-item-border-color);
border-right-color: var(--bb-ribbon-item-border-color);
border-top-color: var(--bb-ribbon-item-border-color);
}
.ribbon-tab .tabs-top.tabs-border-card > .tabs-header .tabs-item:last-child {
padding-right: var(--bb-ribbon-item-padding);
}
.ribbon-tab .ribbon-header .tabs-border-card .tabs-header .tabs-item:nth-child(2) {
padding-left: var(--bb-ribbon-item-padding);
}
.ribbon-tab .tabs-nav-wrap {
background-color: var(--meta-blue);
}
.ribbon-tab .ribbon-header .tabs-border-card .tabs-header .tabs-item:last-child {
padding-right: var(--bb-ribbon-item-padding);
}
.ribbon-tab .ribbon-header .tabs-border-card .tabs-header .tabs-item:not(:hover):not(.active) {
color: var(--bb-ribbon-tabs-item-color);
}
.ribbon-tab .ribbon-header .tabs-border-card .tabs-header .tabs-item:hover {
color: var(--bb-ribbon-tabs-item-hover-color);
}
.ribbon-tab .tabs-nav-scroll {
padding: 0 var(--bb-ribbon-item-padding);
}
.ribbon-tab .tabs-header .tabs-item:not(.active):hover {
background-color: #e9ecef;
color: #333 !important;
}
.ribbon-tab .tabs .tabs-body {
padding: 0;
}
@ -98,15 +116,17 @@
}
.ribbon-tab .link-button {
font-size: var(--bb-ribbon-button-fontsize);
padding: var(--bb-ribbon-button-padding);
border-radius: var(--bb-ribbon-button-radius);
border: var(--bb-ribbon-button-border-width) solid var(--bb-ribbon-button-border-color);
min-width: var(--bb-ribbon-button-min-width);
transition: background-color .3s ease-in-out, border-color .3s ease-in-out, box-shadow .3s ease-in-out;
}
.ribbon-tab .link-button.active {
background-color: #acd4fd;
border-color: #8bb5e0;
background-color: var(--bb-ribbon-button-active-bg);
border-color: var(--bb-ribbon-button-active-border-color);
}
.ribbon-tab .link-button:not(:last-child) {
@ -114,14 +134,10 @@
}
.ribbon-tab .link-button:not([disabled]):hover {
background-color: #d0e7ff;
border-color: #8bb5e0;
background-color: var(--bb-ribbon-button-hover-bg);
border-color: var(--bb-ribbon-button-hover-border-color);
}
.ribbon-tab .link-button:not([disabled]):hover > * {
color: var(--meta-blue);
}
.ribbon-tab .link-button > i {
font-size: 1rem;
margin-bottom: .25rem;
@ -132,12 +148,11 @@
}
.ribbon-tab .link-group-name {
margin-top: .25rem;
color: #adb5bd;
margin-top: var(--bb-ribbon-group-margin-top);
color: var(--bb-ribbon-group-color);
font-size: var(--bb-ribbon-group-fontsize)
}
.ribbon-tab .divider-vertical {
margin: 0 4px;
}
@ -145,7 +160,7 @@
.ribbon-tab .ribbon-buttons {
position: absolute;
right: 1rem;
top: calc(var(--bb-tab-item-height) - var(--bb-ribbon-item-height));
top: var(--bb-ribbon-item-margin-top);
height: var(--bb-ribbon-item-height);
}
@ -173,3 +188,8 @@
.ribbon-tab .ribbon-button:hover {
background-color: var(--bb-tab-item-hover-color);
}
.ribbon-tab .ribbon-body {
border-top: var(--bs-border-width) solid var(--bs-border-color);
padding: var(--bb-ribbon-body-padding);
}

View File

@ -1,32 +0,0 @@
(function ($) {
$.extend({
bb_ribbon: function (id, obj, method) {
if (window.bb_ribbons === undefined) {
window.bb_ribbons = {};
}
window.bb_ribbons[id] = { obj, method };
},
});
$(function () {
$(document)
.on('click', function (e) {
var $ele = $(e.target);
var $ribbon = $('.ribbon-tab');
if ($ribbon.hasClass('is-expand')) {
var parent = $ele.closest('.ribbon-tab').length === 0;
if (parent) {
$ribbon.toArray().forEach(function (item) {
var id = item.id;
if (id) {
var ribbon = window.bb_ribbons[id];
if (ribbon) {
ribbon.obj.invokeMethodAsync(ribbon.method);
}
}
});
}
}
});
});
})(jQuery);

View File

@ -1,53 +1,61 @@
@namespace BootstrapBlazor.Components
@inherits IdComponentBase
@inherits BootstrapModuleComponentBase
<div @attributes="@AdditionalAttributes" id="@Id" class="@HeaderClassString">
<Tab IsBorderCard="true" IsOnlyRenderActiveTab="true" OnClickTab="OnClickTab">
@foreach (var item in GetItems())
{
<TabItem Text="@item.Text">
<div class="tab-commands">
@foreach (var groups in item.Items.OfType<RibbonTabItem>().GroupBy(s => s.GroupName))
{
<div class="link-group">
<div class="d-flex">
@foreach (var group in groups)
{
if (group.Component != null || group.Template != null)
<div class="ribbon-header">
<Tab IsBorderCard="true" IsOnlyRenderActiveTab="true" OnClickTab="OnClickTab">
@foreach (var item in GetItems())
{
<TabItem Text="@item.Text">
<div class="tab-commands">
@foreach (var groups in item.Items.OfType<RibbonTabItem>().GroupBy(s => s.GroupName))
{
<div class="link-group">
<div class="d-flex">
@foreach (var group in groups)
{
@RenderTemplate(group)
if (group.Component != null || group.Template != null)
{
@RenderTemplate(group)
}
else
{
<LinkButton Icon="@group.Icon" ImageUrl="@group.ImageUrl" IsDisabled="@group.IsDisabled"
Url="@group.Url" Target="@group.Target"
Text="@group.Text" IsVertical="true" Color="Color.Secondary"
class="@GetClassString(group)" StopPropagation="true" OnClick="() => OnClick(group)" />
}
}
else
{
<LinkButton Icon="@group.Icon" ImageUrl="@group.ImageUrl" IsDisabled="@group.IsDisabled"
Url="@group.Url" Target="@group.Target"
Text="@group.Text" IsVertical="true" Color="Color.Secondary"
class="@GetClassString(group)" StopPropagation="true" OnClick="() => OnClick(group)" />
}
}
</div>
<div class="link-group-name">
@groups.Key
</div>
</div>
<div class="link-group-name">
@groups.Key
</div>
</div>
<Divider IsVertical="true" />
}
<Divider IsVertical="true" />
}
</div>
</TabItem>
}
</Tab>
<div class="ribbon-buttons">
@if (RightButtonsTemplate != null)
{
<div class="ribbon-customer-buttons">
@RightButtonsTemplate
</div>
</TabItem>
}
</Tab>
<div class="ribbon-buttons">
@if (RightButtonsTemplate != null)
{
<div class="ribbon-customer-buttons">
@RightButtonsTemplate
</div>
}
@if (ShowFloatButton)
{
<div class="ribbon-arrow">
<i class="@ArrowIconClassString" @onclick="OnToggleFloat"></i>
</div>
}
}
@if (ShowFloatButton)
{
<div class="ribbon-arrow">
<i class="@ArrowIconClassString" @onclick="OnToggleFloat"></i>
</div>
}
</div>
</div>
@if (ChildContent != null)
{
<div class="ribbon-body">
@ChildContent
</div>
}
</div>

View File

@ -7,8 +7,15 @@ namespace BootstrapBlazor.Components;
/// <summary>
/// RibbonTab 组件
/// </summary>
public partial class RibbonTab : IDisposable
[JSModuleAutoLoader("ribbon-tab", JSObjectReference = true)]
public partial class RibbonTab
{
/// <summary>
/// 获得/设置 是否仅渲染当前 Tab 默认 true
/// </summary>
[Parameter]
public bool IsOnlyRenderActiveTab { get; set; } = true;
/// <summary>
/// 获得/设置 是否显示悬浮小箭头 默认 false 不显示
/// </summary>
@ -63,10 +70,10 @@ public partial class RibbonTab : IDisposable
public Func<RibbonTabItem, Task>? OnItemClickAsync { get; set; }
/// <summary>
/// 获得/设置 点击标签 Header 回调方法
/// 获得/设置 点击标签 Menu 回调方法
/// </summary>
[Parameter]
public Func<string?, string?, Task>? OnHeaderClickAsync { get; set; }
public Func<string?, string?, Task>? OnMenuClickAsync { get; set; }
/// <summary>
/// 获得/设置 右侧按钮模板
@ -74,13 +81,24 @@ public partial class RibbonTab : IDisposable
[Parameter]
public RenderFragment? RightButtonsTemplate { get; set; }
private bool IsExpand { get; set; }
/// <summary>
/// 获得/设置 内容模板
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
private JSInterop<RibbonTab>? Interop { get; set; }
/// <summary>
/// 获得/设置 是否为带边框卡片样式 默认 true
/// </summary>
[Parameter]
public bool IsBorder { get; set; } = true;
private bool IsExpand { get; set; }
private string? HeaderClassString => CssBuilder.Default("ribbon-tab")
.AddClass("is-float", IsFloat)
.AddClass("is-expand", IsFloat && IsExpand)
.AddClass("border", IsBorder)
.AddClassFromAttributes(AdditionalAttributes)
.Build();
@ -89,18 +107,10 @@ public partial class RibbonTab : IDisposable
.Build();
/// <summary>
/// OnAfterRenderAsync 方法
/// <inheritdoc/>
/// </summary>
/// <param name="firstRender"></param>
/// <returns></returns>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
Interop = new(JSRuntime);
await Interop.InvokeVoidAsync(this, Id, "bb_ribbon", nameof(SetExpand));
}
}
protected override Task ModuleInitAsync() => InvokeInitAsync(Id, nameof(SetExpand));
/// <summary>
/// SetExpand 方法
@ -122,9 +132,9 @@ public partial class RibbonTab : IDisposable
private async Task OnClickTab(TabItem item)
{
if (OnHeaderClickAsync != null)
if (OnMenuClickAsync != null)
{
await OnHeaderClickAsync(item.Text, item.Url);
await OnMenuClickAsync(item.Text, item.Url);
}
if (OnItemClickAsync != null)
{
@ -154,29 +164,4 @@ public partial class RibbonTab : IDisposable
}
private static RenderFragment? RenderTemplate(RibbonTabItem item) => item.Component?.Render() ?? item.Template;
/// <summary>
/// Dispose 方法
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (Interop != null)
{
Interop.Dispose();
Interop = null;
}
}
}
/// <summary>
/// Dispose 方法
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

View File

@ -1,9 +1,7 @@
:root {
--bb-tab-item-height: 40px;
--bb-tab-item-hover-color: #e9ecef;
}
.tabs {
.tabs {
--bb-tabs-item-height: 40px;
--bb-tabs-item-hover-color: #e9ecef;
--bb-tabs-border-card-top-item-margin-top: -1px;
display: flex;
}
@ -112,7 +110,7 @@
.tabs .tabs-nav-next, .tabs .tabs-nav-prev {
position: absolute;
cursor: pointer;
height: var(--bb-tab-item-height);
height: var(--bb-tabs-item-height);
font-size: 16px;
color: #909399;
width: 30px;
@ -270,7 +268,7 @@
.tabs.tabs-card.tabs-top .tabs-body {
border-top-width: 0;
border-radius: 0 0 4px 4px;
border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
}
.tabs.tabs-card.tabs-bottom .tabs-body {
@ -309,7 +307,7 @@
.tabs-item {
padding: 0 20px;
height: var(--bb-tab-item-height);
height: var(--bb-tabs-item-height);
display: flex;
list-style: none;
font-weight: 500;
@ -451,7 +449,7 @@
}
.tabs-border-card > .tabs-header .tabs-item:not(.active):hover {
background-color: var(--bb-tab-item-hover-color);
background-color: var(--bb-tabs-item-hover-color);
}
.tabs-border-card.tabs-top .tabs-header {
@ -463,7 +461,7 @@
}
.tabs-border-card.tabs-top .tabs-header .tabs-item {
margin-top: -1px;
margin-top: var(--tabs-border-card-top-item-margin-top);
}
.tabs-border-card.tabs-top .tabs-header .tabs-item:first-child {

View File

@ -63,7 +63,7 @@ public partial class Tab : IHandlerException, IDisposable
public bool IsCard { get; set; }
/// <summary>
/// 获得/设置 是否为带边框卡片样式
/// 获得/设置 是否为带边框卡片样式 默认 false
/// </summary>
[Parameter]
public bool IsBorderCard { get; set; }

View File

@ -2100,39 +2100,6 @@
});
})(jQuery);
(function ($) {
$.extend({
bb_ribbon: function (id, obj, method) {
if (window.bb_ribbons === undefined) {
window.bb_ribbons = {};
}
window.bb_ribbons[id] = { obj, method };
},
});
$(function () {
$(document)
.on('click', function (e) {
var $ele = $(e.target);
var $ribbon = $('.ribbon-tab');
if ($ribbon.hasClass('is-expand')) {
var parent = $ele.closest('.ribbon-tab').length === 0;
if (parent) {
$ribbon.toArray().forEach(function (item) {
var id = item.id;
if (id) {
var ribbon = window.bb_ribbons[id];
if (ribbon) {
ribbon.obj.invokeMethodAsync(ribbon.method);
}
}
});
}
}
});
});
})(jQuery);
(function ($) {
$.extend({
bb_row: function (el) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -20087,39 +20087,6 @@ return jQuery;
});
})(jQuery);
(function ($) {
$.extend({
bb_ribbon: function (id, obj, method) {
if (window.bb_ribbons === undefined) {
window.bb_ribbons = {};
}
window.bb_ribbons[id] = { obj, method };
},
});
$(function () {
$(document)
.on('click', function (e) {
var $ele = $(e.target);
var $ribbon = $('.ribbon-tab');
if ($ribbon.hasClass('is-expand')) {
var parent = $ele.closest('.ribbon-tab').length === 0;
if (parent) {
$ribbon.toArray().forEach(function (item) {
var id = item.id;
if (id) {
var ribbon = window.bb_ribbons[id];
if (ribbon) {
ribbon.obj.invokeMethodAsync(ribbon.method);
}
}
});
}
}
});
});
})(jQuery);
(function ($) {
$.extend({
bb_row: function (el) {

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,30 @@
import BlazorComponent from "./base/blazor-component.js"
import EventHandler from "./base/event-handler.js"
export class RibbonTab extends BlazorComponent {
_init() {
this._invoker = this._config.arguments[0]
this._invokerMethod = this._config.arguments[1]
this._setListeners()
}
_setListeners() {
this._handlerClick = e => {
const isFloat = this._element.classList.contains('is-float')
if (isFloat) {
const expanded = this._element.classList.contains('is-expand')
if (expanded) {
const ribbonTab = e.target.closest('.ribbon-tab')
if (ribbonTab !== this._element) {
this._invoker.invokeMethodAsync(this._invokerMethod)
}
}
}
}
EventHandler.on(document, 'click', this._handlerClick)
}
_dispose() {
EventHandler.off(document, 'click', this._handlerClick)
}
}

View File

@ -11,6 +11,8 @@
--bb-height: 35px;
--bb-disabled-opactiy: .65;
--bb-dropdown-max-height: 274px;
--bb-shadow: 0 0 8px 0 #e8edfa99, 0 2px 4px 0 #e8edfa80;
--bb-hover-shadow: 0 1px 7px 0 #0000000d, 0 2px 8px 0 #00000012, 0 3px 9px 0 #0000000f, 0 5px 10px 0 #00000008;
}
body,