|
|
|
@ -1,5 +1,4 @@
|
|
|
|
|
using Connected.Annotations;
|
|
|
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
|
using Microsoft.AspNetCore.Components.Rendering;
|
|
|
|
|
using Microsoft.AspNetCore.Components.Web;
|
|
|
|
|
|
|
|
|
@ -11,72 +10,97 @@ namespace Connected.Components;
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class Element : UIComponent
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Child content
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Parameter]
|
|
|
|
|
[Category(CategoryTypes.Element.Misc)]
|
|
|
|
|
public RenderFragment ChildContent { get; set; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Child content
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Parameter]
|
|
|
|
|
public RenderFragment? ChildContent { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The HTML element that will be rendered in the root by the component
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Parameter]
|
|
|
|
|
[Category(CategoryTypes.Element.Misc)]
|
|
|
|
|
public string HtmlTag { get; set; } = "span";
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The ElementReference to bind to.
|
|
|
|
|
/// Use like @bind-Ref="myRef"
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Parameter]
|
|
|
|
|
[Category(CategoryTypes.Element.Misc)]
|
|
|
|
|
public ElementReference? Ref { get; set; }
|
|
|
|
|
#region EventCallbacks
|
|
|
|
|
[Parameter]
|
|
|
|
|
public EventCallback<ElementReference> RefChanged { get; set; }
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
[Parameter] public EventCallback<ElementReference> RefChanged { get; set; }
|
|
|
|
|
#region Content placeholders
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Ref to bind to.
|
|
|
|
|
/// Usage: @bind-Ref="myRef"
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Parameter]
|
|
|
|
|
public ElementReference? Ref { get; set; }
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Calling StateHasChanged to refresh the component's state
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void Refresh() => StateHasChanged();
|
|
|
|
|
#region Styling properties
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// A space separated list of class names, added on top of the default class list.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Parameter]
|
|
|
|
|
public string? ClassList { get; set; }
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
|
|
|
|
{
|
|
|
|
|
base.BuildRenderTree(builder);
|
|
|
|
|
//Open
|
|
|
|
|
builder.OpenElement(0, HtmlTag);
|
|
|
|
|
#region Behavior properties
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The HTML element that will be rendered in the root by the component
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Parameter]
|
|
|
|
|
public string HtmlTag { get; set; } = "span";
|
|
|
|
|
|
|
|
|
|
//splatted attributes
|
|
|
|
|
foreach (var attribute in CustomAttributes)
|
|
|
|
|
{
|
|
|
|
|
// checking if the value is null, we can get rid of null event handlers
|
|
|
|
|
// for example `@onmouseenter=@(IsOpen ? HandleEnter : null)`
|
|
|
|
|
// this is a powerful feature that in normal HTML elements doesn't work, because
|
|
|
|
|
// Blazor adds always the attribute value and creates an EventCallback
|
|
|
|
|
if (attribute.Value != null)
|
|
|
|
|
builder.AddAttribute(1, attribute.Key, attribute.Value);
|
|
|
|
|
}
|
|
|
|
|
//Class
|
|
|
|
|
builder.AddAttribute(2, "class", AdditionalClassList);
|
|
|
|
|
[Parameter]
|
|
|
|
|
public bool PreventOnClickPropagation { get; set; }
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
// StopPropagation
|
|
|
|
|
// the order matters. This has to be before content is added
|
|
|
|
|
if (HtmlTag == "button")
|
|
|
|
|
builder.AddEventStopPropagationAttribute(5, "onclick", true);
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Schedules a component's state refresh
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void Refresh() => StateHasChanged();
|
|
|
|
|
|
|
|
|
|
//Reference capture
|
|
|
|
|
if (Ref != null)
|
|
|
|
|
{
|
|
|
|
|
builder.AddElementReferenceCapture(6, async capturedRef =>
|
|
|
|
|
{
|
|
|
|
|
Ref = capturedRef;
|
|
|
|
|
await RefChanged.InvokeAsync(Ref.Value);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
|
|
|
|
{
|
|
|
|
|
base.BuildRenderTree(builder);
|
|
|
|
|
|
|
|
|
|
//Content
|
|
|
|
|
builder.AddContent(10, ChildContent);
|
|
|
|
|
builder.OpenElement(0, HtmlTag);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Handle splatted attributes
|
|
|
|
|
*/
|
|
|
|
|
foreach (var attribute in CustomAttributes)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Checking if the value is null, we can get rid of null event handlers,
|
|
|
|
|
* for example `@onmouseenter=@(IsOpen ? HandleEnter : null)`.
|
|
|
|
|
* This is a powerful feature that does not work in regular html elements
|
|
|
|
|
* because Blazor always adds the attribute value and creates an EventCallback.
|
|
|
|
|
* */
|
|
|
|
|
if (attribute.Value is not null)
|
|
|
|
|
builder.AddAttribute(1, attribute.Key, attribute.Value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Close
|
|
|
|
|
builder.CloseElement();
|
|
|
|
|
}
|
|
|
|
|
builder.AddAttribute(2, "class", ClassList);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Stop onclock propagation for buttons. The order matters.
|
|
|
|
|
* This has to be before content is added
|
|
|
|
|
*/
|
|
|
|
|
if (PreventOnClickPropagation)
|
|
|
|
|
builder.AddEventStopPropagationAttribute(5, "onclick", true);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Add reference capture propagation.
|
|
|
|
|
*/
|
|
|
|
|
if (Ref is not null)
|
|
|
|
|
{
|
|
|
|
|
builder.AddElementReferenceCapture(6, async capturedRef =>
|
|
|
|
|
{
|
|
|
|
|
Ref = capturedRef;
|
|
|
|
|
await RefChanged.InvokeAsync(Ref.Value);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Render child content
|
|
|
|
|
*/
|
|
|
|
|
builder.AddContent(10, ChildContent);
|
|
|
|
|
|
|
|
|
|
builder.CloseElement();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|