mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-05 13:39:39 +08:00
!2393 feat(#I4SNXQ): add DragDrop component
* Merge branch 'main' into dev-drop * refactor: 更新拖拽组件菜单 * refactor: 格式化文档 * feat: 增加 IDispose 接口调用 * refactor: 格式代码 * 修复警告,添加文档 * 修复最后一个Item不执行OnDragEnd的问题 * Merge branch 'main' into dev-drop * !2366 创建DragDrop组件 * 同步版本 * 解决一些bug * 第一个版本 * 增加Card相关文档
This commit is contained in:
parent
2ac51a7520
commit
13ee6a2207
@ -2543,6 +2543,7 @@
|
||||
"Client": "WebClientService",
|
||||
"LayoutComponents": "LAYOUTS",
|
||||
"Divider": "Divider",
|
||||
"Dragdrop": "DragDrop",
|
||||
"Layout": "Layout",
|
||||
"Footer": "Footer",
|
||||
"Row": "Row",
|
||||
|
@ -2548,6 +2548,7 @@
|
||||
"LayoutComponents": "布局组件",
|
||||
"Client": "客户信息服务 Client",
|
||||
"Divider": "分割线 Divider",
|
||||
"Dragdrop": "拖拽组件 DragDrop",
|
||||
"Layout": "布局组件 Layout",
|
||||
"FullScreen": "全屏组件 FullScreen",
|
||||
"Footer": "页脚组件 Footer",
|
||||
|
86
src/BootstrapBlazor.Shared/Samples/DragDrops.razor
Normal file
86
src/BootstrapBlazor.Shared/Samples/DragDrops.razor
Normal file
@ -0,0 +1,86 @@
|
||||
@page "/dragdrops"
|
||||
|
||||
<h3>DragDrop 拖拽</h3>
|
||||
|
||||
<h4>用于拖拽使用</h4>
|
||||
|
||||
<DemoBlock Title="基本用法" Introduction="简单拖拽" Name="Normal">
|
||||
<Row ItemsPerRow="ItemsPerRow.Two">
|
||||
<Dropzone TItem="string" Items="@StrList1">
|
||||
<Card IsShadow="true">
|
||||
<CardBody>
|
||||
@context
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Dropzone>
|
||||
<Dropzone TItem="string" Items="@StrList2">
|
||||
<Card IsShadow="true">
|
||||
<CardBody>
|
||||
@context
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Dropzone>
|
||||
</Row>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="复制到新容器" Introduction="使用<code>CopyItem</code>复制一份新的到新位置" Name="Normal">
|
||||
<Row ItemsPerRow="ItemsPerRow.Two">
|
||||
<Dropzone TItem="string" Items="@StrList1">
|
||||
<Card IsShadow="true">
|
||||
<CardBody>
|
||||
@context
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Dropzone>
|
||||
<Dropzone TItem="string" Items="@StrList2" CopyItem="s => new string(s)">
|
||||
<Card IsShadow="true">
|
||||
<CardBody>
|
||||
@context
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Dropzone>
|
||||
</Row>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="限制拖入的内容" Introduction="使用<code>Accepts</code>只允许左侧拖入10并且使用<code>AllowsDrag</code>限制2不能被拖动,使用<code>MaxItems</code>限制右侧最多拥有6个节点" Name="Normal">
|
||||
<Row ItemsPerRow="ItemsPerRow.Two">
|
||||
<Dropzone TItem="string" Items="@StrList1" Accepts="@((s, s1) => s == "10")" AllowsDrag="@(s => s != "2")">
|
||||
<Card IsShadow="true">
|
||||
<CardBody>
|
||||
@context
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Dropzone>
|
||||
<Dropzone TItem="string" Items="@StrList2" MaxItems="6">
|
||||
<Card IsShadow="true">
|
||||
<CardBody>
|
||||
@context
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Dropzone>
|
||||
</Row>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="各种事件" Introduction="当拖入数量超限时<code>OnItemDropRejectedByMaxItemLimit</code>,当拖拽被禁止时<code>OnItemDropRejected</code>,返回底下的Item<code>OnReplacedItemDrop</code>,返回放下的Item<code>OnItemDrop</code>" Name="Normal">
|
||||
<Row ItemsPerRow="ItemsPerRow.Two">
|
||||
<Dropzone TItem="string" Items="@StrList1" Accepts="@((s, s1) => s == "10")" OnItemDropRejected="@OnItemDropRejected" OnItemDrop="@OnItemDrop" OnReplacedItemDrop="@OnReplacedItemDrop">
|
||||
<Card IsShadow="true">
|
||||
<CardBody>
|
||||
@context
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Dropzone>
|
||||
<Dropzone TItem="string" Items="@StrList2" MaxItems="6" OnItemDropRejectedByMaxItemLimit="@OnItemDropRejectedByMaxItemLimit" OnItemDrop="@OnItemDrop" OnReplacedItemDrop="@OnReplacedItemDrop">
|
||||
<Card IsShadow="true">
|
||||
<CardBody>
|
||||
@context
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Dropzone>
|
||||
</Row>
|
||||
<BlockLogger @ref="Trace" class="mt-3" />
|
||||
</DemoBlock>
|
||||
|
||||
<AttributeTable Items="@GetAttributes()" />
|
||||
|
||||
<MethodTable Items="GetMethods()"></MethodTable>
|
153
src/BootstrapBlazor.Shared/Samples/DragDrops.razor.cs
Normal file
153
src/BootstrapBlazor.Shared/Samples/DragDrops.razor.cs
Normal file
@ -0,0 +1,153 @@
|
||||
// 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.Components;
|
||||
using BootstrapBlazor.Shared.Common;
|
||||
using BootstrapBlazor.Shared.Components;
|
||||
|
||||
namespace BootstrapBlazor.Shared.Samples;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public partial class DragDrops
|
||||
{
|
||||
[NotNull]
|
||||
private List<string>? StrList1 { get; set; }
|
||||
|
||||
[NotNull]
|
||||
private List<string>? StrList2 { get; set; }
|
||||
|
||||
[NotNull]
|
||||
private BlockLogger? Trace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
StrList1 = new List<string>()
|
||||
{
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5"
|
||||
};
|
||||
StrList2 = new List<string>()
|
||||
{
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private void OnReplacedItemDrop(string item)
|
||||
{
|
||||
Trace?.Log($"新元素放在{item}下");
|
||||
}
|
||||
|
||||
private void OnItemDrop(string item)
|
||||
{
|
||||
Trace?.Log($"{item}被放下");
|
||||
}
|
||||
|
||||
private void OnItemDropRejected(string item)
|
||||
{
|
||||
Trace?.Log($"{item}被拒绝");
|
||||
}
|
||||
|
||||
private void OnItemDropRejectedByMaxItemLimit(string item)
|
||||
{
|
||||
Trace?.Log($"{item}由于超过最大数量限制被禁止");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<AttributeItem> GetAttributes() => new AttributeItem[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Name = "MaxItems",
|
||||
Description = "最大数量,null为不限制",
|
||||
Type = "int?",
|
||||
ValueList = " — ",
|
||||
DefaultValue = "null"
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = "ChildContent",
|
||||
Description = "内容组件",
|
||||
Type = "RenderFragment<TItem>?",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
};
|
||||
|
||||
private static IEnumerable<MethodItem> GetMethods() => new MethodItem[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Name = nameof(Dropzone<MethodItem>.Accepts),
|
||||
Description = "是否运行拖放",
|
||||
Parameters = "Func<TItem?, TItem?, bool>",
|
||||
ReturnValue = "bool "
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = nameof(Dropzone<MethodItem>.AllowsDrag),
|
||||
Description = "节点是否允许被拖拽",
|
||||
Parameters = "TItem",
|
||||
ReturnValue = "bool"
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = nameof(Dropzone<MethodItem>.CopyItem),
|
||||
Description = "复制一个新的 Item 到目标位置",
|
||||
Parameters = "TItem, TItem",
|
||||
ReturnValue = "TItem"
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = nameof(Dropzone<MethodItem>.ItemWrapperClass),
|
||||
Description = "针对 Item 添加特殊的 css class",
|
||||
Parameters = "TItem",
|
||||
ReturnValue = "string"
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = nameof(Dropzone<MethodItem>.OnItemDrop),
|
||||
Description = "Item 释放时的事件",
|
||||
Parameters = " — ",
|
||||
ReturnValue = " — "
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = nameof(Dropzone<MethodItem>.OnItemDropRejected),
|
||||
Description = "Item 释放被拒绝时的事件",
|
||||
Parameters = " — ",
|
||||
ReturnValue = " — "
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = nameof(Dropzone<MethodItem>.OnReplacedItemDrop),
|
||||
Description = "当 Item 在另一个 Item 上,不是空白处被释放时的事件",
|
||||
Parameters = " — ",
|
||||
ReturnValue = " — "
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = nameof(Dropzone<MethodItem>.OnItemDropRejectedByMaxItemLimit),
|
||||
Description = "Item 因为 Dropzone 内最大数量超限被拒绝时的事件",
|
||||
Parameters = " — ",
|
||||
ReturnValue = " — "
|
||||
}
|
||||
};
|
||||
}
|
@ -523,7 +523,7 @@ public sealed partial class NavMenu
|
||||
{
|
||||
Text = Localizer["Transition"],
|
||||
Url = "transitions"
|
||||
},
|
||||
}
|
||||
};
|
||||
AddBadge(item);
|
||||
}
|
||||
@ -885,6 +885,12 @@ public sealed partial class NavMenu
|
||||
{
|
||||
item.Items = new List<DemoMenuItem>
|
||||
{
|
||||
new()
|
||||
{
|
||||
IsNew = true,
|
||||
Text = Localizer["Dragdrop"],
|
||||
Url = "dragdrops"
|
||||
},
|
||||
new()
|
||||
{
|
||||
Text = Localizer["Divider"],
|
||||
|
@ -14,6 +14,7 @@
|
||||
"datetimepickers": "DateTimePickers",
|
||||
"datetimeranges": "DateTimeRanges",
|
||||
"doughnut": "Charts/Doughnut",
|
||||
"dragdrops": "DragDrops",
|
||||
"dropdownlists": "DropdownLists",
|
||||
"dropdownwidgets": "DropdownWidgets",
|
||||
"fullscreens": "FullScreens",
|
||||
|
92
src/BootstrapBlazor/Components/DragDrap/DragDrop.css
Normal file
92
src/BootstrapBlazor/Components/DragDrap/DragDrop.css
Normal file
@ -0,0 +1,92 @@
|
||||
/*add this to avoid flickering*/
|
||||
.bb-dd-inprogess > * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*dropzone style style*/
|
||||
.bb-dd-dropzone {
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
/*drag drop styles*/
|
||||
|
||||
.bb-dd-spacing {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.bb-dd-spacing-dragged-over {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.bb-dd-dragged-over {
|
||||
background-color: lightgray;
|
||||
opacity: 0.6;
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
.bb-dd-dragged-over > div {
|
||||
background-color: lightgray;
|
||||
opacity: 0.6;
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
.bb-dd-dragged-over-denied {
|
||||
background-color: red;
|
||||
opacity: 0.6;
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
.bb-dd-in-transit {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.bb-dd-in-transit > div {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.blink_me {
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
/*for flex demo*/
|
||||
|
||||
.bb-flex .bb-dd-spacing {
|
||||
width: 20px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.bb-flex .bb-dd-dragged-over {
|
||||
background-color: lightgray;
|
||||
opacity: 0.6;
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
.bb-flex .bb-dd-dragged-over > div {
|
||||
background-color: lightgray;
|
||||
opacity: 0.9;
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
.bb-flex .bb-dd-in-transit {
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
.bb-flex .bb-dd-in-transit > div {
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
.bb-dd-noselect {
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Old versions of Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
supported by Chrome, Edge, Opera and Firefox */
|
||||
}
|
61
src/BootstrapBlazor/Components/DragDrap/DragDropService.cs
Normal file
61
src/BootstrapBlazor/Components/DragDrap/DragDropService.cs
Normal 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/
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽服务
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class DragDropService<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 活动的Item
|
||||
/// </summary>
|
||||
public T? ActiveItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 悬停的项目
|
||||
/// </summary>
|
||||
public T? DragTargetItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 被拖拽的Items
|
||||
/// </summary>
|
||||
public IList<T?>? Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 活动的Id
|
||||
/// </summary>
|
||||
public int? ActiveSpacerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 之前的位置
|
||||
/// </summary>
|
||||
public int? OldIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知刷新
|
||||
/// </summary>
|
||||
public EventHandler? StateHasChanged { get; set; }
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (OldIndex is >= 0 && Items != null && ActiveItem != null)
|
||||
{
|
||||
Items.Insert(OldIndex.Value, ActiveItem);
|
||||
}
|
||||
Commit();
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
{
|
||||
ActiveItem = default;
|
||||
ActiveSpacerId = null;
|
||||
Items = null;
|
||||
DragTargetItem = default;
|
||||
|
||||
StateHasChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
41
src/BootstrapBlazor/Components/DragDrap/Dropzone.razor
Normal file
41
src/BootstrapBlazor/Components/DragDrap/Dropzone.razor
Normal file
@ -0,0 +1,41 @@
|
||||
@typeparam TItem
|
||||
@inherits BootstrapComponentBase
|
||||
|
||||
@namespace BootstrapBlazor.Components
|
||||
|
||||
<div @attributes="@AdditionalAttributes" class="@ClassName"
|
||||
@ondragover:preventDefault @ondragover="() => {}"
|
||||
@ondragenter:preventDefault @ondragenter="() => {}"
|
||||
@ondrop:preventDefault @ondrop="@OnDrop"
|
||||
ondragstart="event.dataTransfer.setData('text', event.target.id);"
|
||||
@ondrop:stopPropagation
|
||||
@ondragenter:stopPropagation
|
||||
@ondragend:stopPropagation
|
||||
@ondragover:stopPropagation
|
||||
@ondragleave:stopPropagation
|
||||
@ondragstart:stopPropagation>
|
||||
|
||||
<div @ondrop="() => OnDropItemOnSpacing(0)" @ondrop:stopPropagation @ondragenter="() => DragDropService.ActiveSpacerId = 0"
|
||||
@ondragleave="() => DragDropService.ActiveSpacerId = null" class="@GetClassesForSpacing(0)"></div>
|
||||
|
||||
@foreach (var item in Items)
|
||||
{
|
||||
<div draggable="@IsItemDragable(item)"
|
||||
@ondragstart="() => OnDragStart(item)"
|
||||
@ondragend="@OnDragEnd"
|
||||
@ondragenter="() => OnDragEnter(item)"
|
||||
@ondragleave="@OnDragLeave"
|
||||
class="@GetItemClass(item) @ItemClass"
|
||||
style="@(item == null ? "display:none" : "")">
|
||||
@if (item != null)
|
||||
{
|
||||
@ChildContent?.Invoke(item)
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (MaxItems is null or > 1)
|
||||
{
|
||||
<div @ondrop="()=>OnDropItemOnSpacing(Items.IndexOf(item)+1)" @ondrop:stopPropagation @ondragenter="()=>DragDropService.ActiveSpacerId = Items.IndexOf(item)+1" @ondragleave="()=>DragDropService.ActiveSpacerId = null" class="@ItemClass @GetClassesForSpacing(Items.IndexOf(item)+1)"></div>
|
||||
}
|
||||
}
|
||||
</div>
|
420
src/BootstrapBlazor/Components/DragDrap/Dropzone.razor.cs
Normal file
420
src/BootstrapBlazor/Components/DragDrap/Dropzone.razor.cs
Normal file
@ -0,0 +1,420 @@
|
||||
// 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.AspNetCore.Components;
|
||||
using System.Text;
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽容器
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
public partial class Dropzone<TItem> : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取/设置 拖拽列表
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[NotNull]
|
||||
public IList<TItem?>? Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取/设置 最大数量,Null为不限制
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int? MaxItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子组件
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment<TItem>? ChildContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每个Item的特殊class
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<TItem, string>? ItemWrapperClass { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 复制内容
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<TItem, TItem>? CopyItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许拖拽释放
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<TItem?, TItem?, bool>? Accepts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当拖拽因为数量超限被禁止时调用
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<TItem> OnItemDropRejectedByMaxItemLimit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当拖拽被禁止时调用
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<TItem> OnItemDropRejected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回被替换的Item
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<TItem> OnReplacedItemDrop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回放下的Item
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<TItem> OnItemDrop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前节点是否允许被拖拽
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<TItem, bool>? AllowsDrag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// service
|
||||
/// </summary>
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private DragDropService<TItem>? DragDropService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得 拖拽容器样式集合
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string? ClassName => CssBuilder.Default("bb-dd-dropzone").Build();
|
||||
|
||||
|
||||
private string? ItemClass =>
|
||||
CssBuilder.Default("").AddClass("bb-dd-inprogess", DragDropService.ActiveItem != null).Build();
|
||||
|
||||
private string GetItemClass(TItem? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("bb-dd-draggable");
|
||||
if (ItemWrapperClass != null)
|
||||
{
|
||||
builder.Append($" {ItemWrapperClass(item)}");
|
||||
}
|
||||
|
||||
var activeItem = DragDropService.ActiveItem;
|
||||
if (activeItem == null)
|
||||
{
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
if (item.Equals(activeItem))
|
||||
{
|
||||
builder.Append(" no-pointer-events");
|
||||
}
|
||||
|
||||
if (!item.Equals(activeItem) && item.Equals(DragDropService.DragTargetItem))
|
||||
{
|
||||
builder.Append(IsItemAccepted(DragDropService.DragTargetItem)
|
||||
? " bb-dd-dragged-over"
|
||||
: " bb-dd-dragged-over-denied");
|
||||
}
|
||||
|
||||
if (AllowsDrag != null && !AllowsDrag(item))
|
||||
{
|
||||
builder.Append(" bb-dd-noselect");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private string GetClassesForSpacing(int spacerId)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("bb-dd-spacing");
|
||||
if (DragDropService.ActiveItem == null)
|
||||
{
|
||||
return builder.ToString();
|
||||
}
|
||||
//if active space id and item is from another dropzone -> always create insert space
|
||||
if (DragDropService.ActiveSpacerId == spacerId && Items.IndexOf(DragDropService.ActiveItem) == -1)
|
||||
{
|
||||
builder.Append(" bb-dd-spacing-dragged-over");
|
||||
} // else -> check if active space id and that it is an item that needs space
|
||||
else if (DragDropService.ActiveSpacerId == spacerId && (spacerId != Items.IndexOf(DragDropService.ActiveItem)) && (spacerId != Items.IndexOf(DragDropService.ActiveItem) + 1))
|
||||
{
|
||||
builder.Append(" bb-dd-spacing-dragged-over");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private string IsItemDragable(TItem? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return "false";
|
||||
}
|
||||
if (AllowsDrag == null)
|
||||
{
|
||||
return "true";
|
||||
}
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
return "false";
|
||||
}
|
||||
|
||||
return AllowsDrag(item).ToString();
|
||||
}
|
||||
|
||||
private bool IsDropAllowed()
|
||||
{
|
||||
if (!IsValidItem())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var activeItem = DragDropService.ActiveItem;
|
||||
|
||||
if (IsMaxItemLimitReached())
|
||||
{
|
||||
OnItemDropRejectedByMaxItemLimit.InvokeAsync(activeItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsItemAccepted(activeItem))
|
||||
{
|
||||
OnItemDropRejected.InvokeAsync(activeItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsItemAccepted(TItem? dragTargetItem)
|
||||
{
|
||||
if (Accepts == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Accepts(DragDropService.ActiveItem, dragTargetItem);
|
||||
}
|
||||
|
||||
private bool IsMaxItemLimitReached()
|
||||
{
|
||||
var activeItem = DragDropService.ActiveItem;
|
||||
if (activeItem == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return (!Items.Contains(activeItem) && MaxItems.HasValue && MaxItems == Items.Count);
|
||||
}
|
||||
|
||||
private bool IsValidItem()
|
||||
{
|
||||
return DragDropService.ActiveItem != null;
|
||||
}
|
||||
|
||||
private void OnDropItemOnSpacing(int newIndex)
|
||||
{
|
||||
if (!IsDropAllowed())
|
||||
{
|
||||
DragDropService.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
var activeItem = DragDropService.ActiveItem;
|
||||
if (activeItem == null)
|
||||
{
|
||||
DragDropService.Reset();
|
||||
return;
|
||||
}
|
||||
// 当从其他地方拖过来并且当前以达到最大限制,放弃
|
||||
if (IsMaxItemLimitReached())
|
||||
{
|
||||
DragDropService.Reset();
|
||||
return;
|
||||
}
|
||||
bool sameDropZone = Equals(DragDropService.Items, Items);
|
||||
|
||||
if (CopyItem == null || sameDropZone)
|
||||
{
|
||||
Items.Insert(newIndex, activeItem);
|
||||
DragDropService.Commit();
|
||||
}
|
||||
else
|
||||
{
|
||||
// for the same zone - do not call CopyItem
|
||||
Items.Insert(newIndex, CopyItem(activeItem));
|
||||
DragDropService.Reset();
|
||||
}
|
||||
|
||||
//Operation is finished
|
||||
OnItemDrop.InvokeAsync(activeItem);
|
||||
}
|
||||
|
||||
private void OnDragStart(TItem? item)
|
||||
{
|
||||
DragDropService.OldIndex = Items.IndexOf(item);
|
||||
DragDropService.ActiveItem = item;
|
||||
DragDropService.Items = Items;
|
||||
Items.Remove(item);
|
||||
if (DragDropService.OldIndex >= Items.Count)
|
||||
{
|
||||
Items.Add(default);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDragEnd()
|
||||
{
|
||||
if (DragDropService.Items != null && DragDropService.OldIndex != null && DragDropService.ActiveItem != null)
|
||||
{
|
||||
DragDropService.Items.Insert(DragDropService.OldIndex.Value, DragDropService.ActiveItem);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
Items.Remove(default);
|
||||
}
|
||||
|
||||
private void OnDragEnter(TItem? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var activeItem = DragDropService.ActiveItem;
|
||||
if (activeItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (item.Equals(activeItem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValidItem())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsMaxItemLimitReached())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsItemAccepted(item))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DragDropService.DragTargetItem = item;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void OnDragLeave()
|
||||
{
|
||||
DragDropService.DragTargetItem = default;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void OnDrop()
|
||||
{
|
||||
if (!IsDropAllowed())
|
||||
{
|
||||
DragDropService.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
var activeItem = DragDropService.ActiveItem;
|
||||
if (activeItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果没有释放在Item上,则添加到最后
|
||||
if (DragDropService.DragTargetItem == null)
|
||||
{
|
||||
// 当从其他位置拖拽过来的时候
|
||||
if (!Equals(DragDropService.Items, Items) && CopyItem != null)
|
||||
{
|
||||
Items.Insert(Items.Count, CopyItem(activeItem));
|
||||
DragDropService.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
Items.Insert(Items.Count, activeItem);
|
||||
DragDropService.Commit();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnReplacedItemDrop.InvokeAsync(DragDropService.DragTargetItem);
|
||||
if (!Equals(DragDropService.Items, Items) && CopyItem != null)
|
||||
{
|
||||
Swap(DragDropService.DragTargetItem, CopyItem(activeItem));
|
||||
DragDropService.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
Swap(DragDropService.DragTargetItem, activeItem);
|
||||
DragDropService.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
OnItemDrop.InvokeAsync(activeItem);
|
||||
}
|
||||
|
||||
private void Swap(TItem draggedOverItem, TItem activeItem)
|
||||
{
|
||||
var indexDraggedOverItem = Items.IndexOf(draggedOverItem);
|
||||
//insert into new zone
|
||||
Items.Insert(indexDraggedOverItem + 1, activeItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnInitialized 方法
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
DragDropService.StateHasChanged += ForceRender;
|
||||
}
|
||||
|
||||
private void ForceRender(object? sender, EventArgs e)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose 方法
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
DragDropService.StateHasChanged -= ForceRender;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose 方法
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
@ -48,6 +48,7 @@ public static class BootstrapBlazorServiceCollectionExtensions
|
||||
services.TryAddScoped<DownloadService>();
|
||||
services.TryAddScoped<WebClientService>();
|
||||
services.TryAddScoped<AjaxService>();
|
||||
services.TryAddScoped(typeof(DragDropService<>));
|
||||
|
||||
services.TryAddSingleton<IConfigureOptions<BootstrapBlazorOptions>, ConfigureOptions<BootstrapBlazorOptions>>();
|
||||
services.ConfigureBootstrapBlazorOption(configureOptions);
|
||||
@ -58,7 +59,7 @@ public static class BootstrapBlazorServiceCollectionExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="locatorAction"></param>
|
||||
@ -70,7 +71,7 @@ public static class BootstrapBlazorServiceCollectionExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="options"></param>
|
||||
@ -93,7 +94,7 @@ public static class BootstrapBlazorServiceCollectionExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="localizationAction"></param>
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user