Skip to content

Commit f074f90

Browse files
authored
Add Drag and Drop components (#412)
* Add Drag and Drop components * Fix PR comment
1 parent 7365c14 commit f074f90

File tree

14 files changed

+602
-0
lines changed

14 files changed

+602
-0
lines changed

examples/FluentUI.Demo.Shared/Microsoft.Fast.Components.FluentUI.xml

Lines changed: 141 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@page "/Drag"
2+
@using FluentUI.Demo.Shared.Pages.Drag.Examples;
3+
4+
<h1>Drag and Drop</h1>
5+
6+
7+
<p>A web component implementation of a <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API" target="_blank" rel="noopener noreferrer">HTML Drag and Drop API</a>.</p>
8+
9+
<p>
10+
The user may select draggable elements with a mouse, drag those elements to a droppable element,
11+
and drop them by releasing the mouse button. A translucent representation of the draggable elements
12+
follows the pointer during the drag operation.
13+
</p>
14+
15+
<h2>Examples</h2>
16+
17+
<DemoSection Title="Usage examples" Component="@typeof(DragDropBasic)"></DemoSection>
18+
19+
<h2>Documentation</h2>
20+
21+
<ApiDocumentation Component="typeof(FluentDragContainer<>)" InstanceType="typeof(object)" GenericLabel="TItem" />
22+
<ApiDocumentation Component="typeof(FluentDropZone<>)" InstanceType="typeof(object)" GenericLabel="TItem" />
23+
<ApiDocumentation Component="typeof(FluentDragEventArgs<>)" InstanceType="typeof(object)" GenericLabel="TItem" />
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<FluentDragContainer TItem="string"
2+
OnDragEnter="@(e => DemoLogger.WriteLine($"{e.Source.Id} is entered in {e.Target.Id}"))"
3+
OnDragLeave="@(e => DemoLogger.WriteLine($"{e.Source.Id} has left {e.Target.Id}"))"
4+
OnDropEnd="@(e => DemoLogger.WriteLine($"{e.Source.Id} dropped in {e.Target.Id}"))">
5+
<FluentStack>
6+
<FluentDropZone Id="Item1" Draggable="true" Droppable="true">
7+
<div style="width: 50px; height: 50px; background-color: pink;">
8+
Item 1
9+
</div>
10+
</FluentDropZone>
11+
<FluentDropZone Id="Item2" Draggable="true" Droppable="true">
12+
<div style="width: 50px; height: 50px; background-color: lightgreen;">
13+
Item 2
14+
</div>
15+
</FluentDropZone>
16+
</FluentStack>
17+
</FluentDragContainer>

examples/FluentUI.Demo.Shared/Shared/DemoNavMenuList.razor

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@
111111
<li>
112112
<FluentAnchor Href="Divider" Appearance=Appearance.Neutral>Divider</FluentAnchor>
113113
</li>
114+
<li>
115+
<FluentAnchor Href="Drag" Appearance=Appearance.Neutral>Drag</FluentAnchor>
116+
</li>
114117
<li>
115118
<FluentAnchor Href="Emoji" Appearance=Appearance.Neutral>Emoji</FluentAnchor>
116119
</li>

examples/FluentUI.Demo.Shared/Shared/DemoNavMenuTree.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
<FluentNavMenuLink Href="/DataGrid" Icon="@FluentIcons.Grid" Text="Data grid" />
5454
<FluentNavMenuLink Href="/Dialog" Icon="@FluentIcons.AppGeneric" Text="Dialog" />
5555
<FluentNavMenuLink Href="/Divider" Icon="@FluentIcons.DividerShort" Text="Divider" />
56+
<FluentNavMenuLink Href="/Drag" Icon="@FluentIcons.Drag" Text="Drag and Drop" />
5657
<FluentNavMenuLink Href="/Emoji" Icon="@FluentIcons.EmojiSmileSlight" Text="Emoji" />
5758
<FluentNavMenuLink Href="/Flipper" Icon="@FluentIcons.NavigationPlay" Text="Flipper" />
5859
<FluentNavMenuLink Href="/Highlighter" Icon="@FluentIcons.Highlight" Text="Highlighter" />
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<FluentDragContainer TItem="string"
2+
OnDragEnter="@(e => DemoLogger.WriteLine($"{e.Source.Id} is entered in {e.Target.Id}"))"
3+
OnDragLeave="@(e => DemoLogger.WriteLine($"{e.Source.Id} has left {e.Target.Id}"))"
4+
OnDropEnd="@(e => DemoLogger.WriteLine($"{e.Source.Id} dropped in {e.Target.Id}"))">
5+
<FluentStack>
6+
<FluentDropZone Id="Item1" Draggable="true" Droppable="true">
7+
<div style="width: 50px; height: 50px; background-color: pink;">
8+
Item 1
9+
</div>
10+
</FluentDropZone>
11+
<FluentDropZone Id="Item2" Draggable="true" Droppable="true">
12+
<div style="width: 50px; height: 50px; background-color: lightgreen;">
13+
Item 2
14+
</div>
15+
</FluentDropZone>
16+
</FluentStack>
17+
</FluentDragContainer>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
<div>
3+
<div draggable="true" blazor:ondragstart="1" blazor:ondragover="2" blazor:ondragenter="3" blazor:ondragleave="4" blazor:ondrop="5" blazor:ondrop:preventdefault="" b-9aejvkglh2="">Item 1</div>
4+
<div blazor:ondragstart="6" blazor:ondragover="7" blazor:ondragover:preventdefault="" blazor:ondragenter="8" blazor:ondragenter:preventdefault="" blazor:ondragleave="9" blazor:ondragleave:preventdefault="" blazor:ondrop="10" blazor:ondrop:preventdefault="" b-9aejvkglh2="">Item 2</div>
5+
</div>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using Bunit;
2+
using Xunit;
3+
4+
namespace Microsoft.Fast.Components.FluentUI.Tests.Drag
5+
{
6+
public class FluentDragTests : TestBase
7+
{
8+
[Fact]
9+
public void FluentDrag_SimpleTest()
10+
{
11+
// Arrange
12+
using var ctx = new TestContext();
13+
14+
// Act
15+
var cut = ctx.RenderComponent<FluentDragContainer<int>>(parameters =>
16+
{
17+
parameters.Add(p => p.OnDragStart, (e) => { });
18+
parameters.Add(p => p.OnDragEnter, (e) => { });
19+
parameters.Add(p => p.OnDragOver, (e) => { });
20+
parameters.Add(p => p.OnDragLeave, (e) => { });
21+
parameters.Add(p => p.OnDropEnd, (e) => { });
22+
23+
parameters.AddChildContent<FluentDropZone<int>>(zone =>
24+
{
25+
zone.Add(p => p.Draggable, true);
26+
zone.Add(p => p.Item, 1);
27+
zone.AddChildContent("Item 1");
28+
29+
zone.Add(p => p.OnDragStart, (e) => { });
30+
zone.Add(p => p.OnDragEnter, (e) => { });
31+
zone.Add(p => p.OnDragOver, (e) => { });
32+
zone.Add(p => p.OnDragLeave, (e) => { });
33+
zone.Add(p => p.OnDropEnd, (e) => { });
34+
});
35+
36+
parameters.AddChildContent<FluentDropZone<int>>(zone =>
37+
{
38+
zone.Add(p => p.Droppable, true);
39+
zone.Add(p => p.Item, 2);
40+
zone.AddChildContent("Item 2");
41+
42+
zone.Add(p => p.OnDragStart, (e) => { });
43+
zone.Add(p => p.OnDragEnter, (e) => { });
44+
zone.Add(p => p.OnDragOver, (e) => { });
45+
zone.Add(p => p.OnDragLeave, (e) => { });
46+
zone.Add(p => p.OnDropEnd, (e) => { });
47+
});
48+
});
49+
50+
// Assert
51+
cut.Verify();
52+
}
53+
}
54+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@namespace Microsoft.Fast.Components.FluentUI
2+
@inherits FluentComponentBase
3+
4+
@typeparam TItem
5+
@attribute [CascadingTypeParameter(nameof(TItem))]
6+
<div id=@Id
7+
class=@ClassValue
8+
style=@StyleValue>
9+
<CascadingValue Value="this">
10+
@ChildContent
11+
</CascadingValue>
12+
</div>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// --------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// --------------------------------------------------------------
4+
5+
using Microsoft.AspNetCore.Components;
6+
using Microsoft.Fast.Components.FluentUI.Utilities;
7+
8+
namespace Microsoft.Fast.Components.FluentUI;
9+
10+
/// <summary />
11+
public partial class FluentDragContainer<TItem> : FluentComponentBase
12+
{
13+
/// <summary />
14+
protected virtual string? ClassValue => new CssBuilder(Class).Build();
15+
16+
/// <summary />
17+
protected virtual string? StyleValue => new StyleBuilder().AddStyle(Style).Build();
18+
19+
/// <summary>
20+
/// Gets or sets the content to be rendered inside the component.
21+
/// </summary>
22+
[Parameter]
23+
public RenderFragment? ChildContent { get; set; }
24+
25+
/// <summary>
26+
/// This event is fired when the user starts dragging an element.
27+
/// </summary>
28+
[Parameter]
29+
public Action<FluentDragEventArgs<TItem>>? OnDragStart { get; set; }
30+
31+
/// <summary>
32+
/// This event is fired when a dragged element enters a valid drop target.
33+
/// </summary>
34+
[Parameter]
35+
public Action<FluentDragEventArgs<TItem>>? OnDragEnter { get; set; }
36+
37+
/// <summary>
38+
/// This event is fired when an element is being dragged over a valid drop target.
39+
/// </summary>
40+
[Parameter]
41+
public Action<FluentDragEventArgs<TItem>>? OnDragOver { get; set; }
42+
43+
/// <summary>
44+
/// This event is fired when a dragged element leaves a valid drop target.
45+
/// </summary>
46+
[Parameter]
47+
public Action<FluentDragEventArgs<TItem>>? OnDragLeave { get; set; }
48+
49+
/// <summary>
50+
/// This event is fired when an element is dropped on a valid drop target.
51+
/// </summary>
52+
[Parameter]
53+
public Action<FluentDragEventArgs<TItem>>? OnDropEnd { get; set; }
54+
55+
/// <summary>
56+
/// property to keep the zone currently dragged.
57+
/// </summary>
58+
internal FluentDropZone<TItem>? StartedZone { get; private set; }
59+
60+
/// <summary />
61+
internal void SetStartedZone(FluentDropZone<TItem>? value)
62+
{
63+
StartedZone = value;
64+
StateHasChanged();
65+
}
66+
}

0 commit comments

Comments
 (0)