Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,14 @@ public readonly partial struct ElementReference
private readonly object _dummy;
private readonly int _dummyPrimitive;
public ElementReference(string id) { throw null; }
public ElementReference(string id, Microsoft.AspNetCore.Components.ElementReferenceContext? context) { throw null; }
public Microsoft.AspNetCore.Components.ElementReferenceContext? Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string Id { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
public abstract partial class ElementReferenceContext
{
protected ElementReferenceContext() { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct EventCallback
{
Expand Down Expand Up @@ -468,6 +474,7 @@ public abstract partial class Renderer : System.IDisposable
{
public Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
public abstract Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get; }
protected internal Microsoft.AspNetCore.Components.ElementReferenceContext? ElementReferenceContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] protected set { } }
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }
public virtual System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo fieldInfo, System.EventArgs eventArgs) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,14 @@ public readonly partial struct ElementReference
private readonly object _dummy;
private readonly int _dummyPrimitive;
public ElementReference(string id) { throw null; }
public ElementReference(string id, Microsoft.AspNetCore.Components.ElementReferenceContext? context) { throw null; }
public Microsoft.AspNetCore.Components.ElementReferenceContext? Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string Id { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
public abstract partial class ElementReferenceContext
{
protected ElementReferenceContext() { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct EventCallback
{
Expand Down Expand Up @@ -467,6 +473,7 @@ public abstract partial class Renderer : System.IDisposable
{
public Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
public abstract Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get; }
protected internal Microsoft.AspNetCore.Components.ElementReferenceContext? ElementReferenceContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] protected set { } }
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }
public virtual System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo fieldInfo, System.EventArgs eventArgs) { throw null; }
Expand Down
25 changes: 22 additions & 3 deletions src/Components/Components/src/ElementReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,32 @@ public readonly struct ElementReference
/// </remarks>
public string Id { get; }

public ElementReference(string id)
/// <summary>
/// Gets the <see cref="ElementReferenceContext"/> instance.
/// </summary>
public ElementReferenceContext? Context { get; }

/// <summary>
/// Instantiates a new <see cref="ElementReference" />.
/// </summary>
/// <param name="id">A unique identifier for this <see cref="ElementReference"/>.</param>
/// <param name="context">The nullable <see cref="ElementReferenceContext"/> instance.</param>
public ElementReference(string id, ElementReferenceContext? context)
{
Id = id;
Context = context;
}

/// <summary>
/// Instantiates a new <see cref="ElementReference"/>.
/// </summary>
/// <param name="id">A unique identifier for this <see cref="ElementReference"/>.</param>
public ElementReference(string id) : this(id, null)
{
}

internal static ElementReference CreateWithUniqueId()
=> new ElementReference(CreateUniqueId());
internal static ElementReference CreateWithUniqueId(ElementReferenceContext? context)
=> new ElementReference(CreateUniqueId(), context);

private static string CreateUniqueId()
{
Expand Down
12 changes: 12 additions & 0 deletions src/Components/Components/src/ElementReferenceContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNetCore.Components
{
/// <summary>
/// Context for an <see cref="ElementReference"/>.
/// </summary>
public abstract class ElementReferenceContext
Comment thread
MackinnonBuck marked this conversation as resolved.
{
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ private static void InitializeNewAttributeFrame(ref DiffContext diffContext, ref

private static void InitializeNewElementReferenceCaptureFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
{
var newElementReference = ElementReference.CreateWithUniqueId();
var newElementReference = ElementReference.CreateWithUniqueId(diffContext.Renderer.ElementReferenceContext);
newFrame = newFrame.WithElementReferenceCaptureId(newElementReference.Id);
newFrame.ElementReferenceCaptureAction(newElementReference);
}
Expand Down
6 changes: 6 additions & 0 deletions src/Components/Components/src/RenderTree/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
/// </summary>
public abstract Dispatcher Dispatcher { get; }

/// <summary>
/// Gets or sets the <see cref="Components.ElementReferenceContext"/> associated with this <see cref="Renderer"/>,
/// if it exists.
/// </summary>
protected internal ElementReferenceContext? ElementReferenceContext { get; protected set; }

/// <summary>
/// Constructs a new component of the specified type.
/// </summary>
Expand Down
3 changes: 2 additions & 1 deletion src/Components/Server/src/Circuits/CircuitFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public CircuitHost CreateCircuitHost(
_loggerFactory,
_options,
client,
_loggerFactory.CreateLogger<RemoteRenderer>());
_loggerFactory.CreateLogger<RemoteRenderer>(),
jsRuntime.ElementReferenceContext);

var circuitHandlers = scope.ServiceProvider.GetServices<CircuitHandler>()
.OrderBy(h => h.Order)
Expand Down
5 changes: 4 additions & 1 deletion src/Components/Server/src/Circuits/RemoteJSRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ internal class RemoteJSRuntime : JSRuntime
private readonly ILogger<RemoteJSRuntime> _logger;
private CircuitClientProxy _clientProxy;

public ElementReferenceContext ElementReferenceContext { get; }

public RemoteJSRuntime(IOptions<CircuitOptions> options, ILogger<RemoteJSRuntime> logger)
{
_options = options.Value;
_logger = logger;
DefaultAsyncTimeout = _options.JSInteropDefaultCallTimeout;
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter());
ElementReferenceContext = new WebElementReferenceContext(this);
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
}

internal void Initialize(CircuitClientProxy clientProxy)
Expand Down
5 changes: 4 additions & 1 deletion src/Components/Server/src/Circuits/RemoteRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ public RemoteRenderer(
ILoggerFactory loggerFactory,
CircuitOptions options,
CircuitClientProxy client,
ILogger logger)
ILogger logger,
ElementReferenceContext? elementReferenceContext)
: base(serviceProvider, loggerFactory)
{
_client = client;
_options = options;
_logger = logger;

ElementReferenceContext = elementReferenceContext;
}

public override Dispatcher Dispatcher { get; } = Dispatcher.CreateDefault();
Expand Down
8 changes: 7 additions & 1 deletion src/Components/Server/test/Circuits/CircuitHostTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,13 @@ private static TestRemoteRenderer GetRemoteRenderer()
private class TestRemoteRenderer : RemoteRenderer
{
public TestRemoteRenderer(IServiceProvider serviceProvider, IClientProxy client)
: base(serviceProvider, NullLoggerFactory.Instance, new CircuitOptions(), new CircuitClientProxy(client, "connection"), NullLogger.Instance)
: base(
serviceProvider,
NullLoggerFactory.Instance,
new CircuitOptions(),
new CircuitClientProxy(client, "connection"),
NullLogger.Instance,
null)
{
}

Expand Down
2 changes: 1 addition & 1 deletion src/Components/Server/test/Circuits/RemoteRendererTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ private TestRemoteRenderer GetRemoteRenderer(IServiceProvider serviceProvider, C
private class TestRemoteRenderer : RemoteRenderer
{
public TestRemoteRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, CircuitOptions options, CircuitClientProxy client, ILogger logger)
: base(serviceProvider, loggerFactory, options, client, logger)
: base(serviceProvider, loggerFactory, options, client, logger, null)
{
}

Expand Down
3 changes: 2 additions & 1 deletion src/Components/Server/test/Circuits/TestCircuitHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public static CircuitHost Create(
NullLoggerFactory.Instance,
new CircuitOptions(),
clientProxy,
NullLogger.Instance);
NullLogger.Instance,
null);
}

handlers = handlers ?? Array.Empty<CircuitHandler>();
Expand Down
17 changes: 13 additions & 4 deletions src/Components/Server/test/ElementReferenceJsonConverterTest.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO;
using System.Text;
using System.Text.Json;
using Moq;
using Xunit;

namespace Microsoft.AspNetCore.Components
{
public class ElementReferenceJsonConverterTest
{
private readonly ElementReferenceJsonConverter Converter = new ElementReferenceJsonConverter();
private readonly ElementReferenceContext ElementReferenceContext;
private readonly ElementReferenceJsonConverter Converter;

public ElementReferenceJsonConverterTest()
{
ElementReferenceContext = Mock.Of<ElementReferenceContext>();
Converter = new ElementReferenceJsonConverter(ElementReferenceContext);
}

[Fact]
public void Serializing_Works()
{
// Arrange
var elementReference = ElementReference.CreateWithUniqueId();
var elementReference = ElementReference.CreateWithUniqueId(ElementReferenceContext);
var expected = $"{{\"__internalId\":\"{elementReference.Id}\"}}";
var memoryStream = new MemoryStream();
var writer = new Utf8JsonWriter(memoryStream);
Expand All @@ -34,7 +43,7 @@ public void Serializing_Works()
public void Deserializing_Works()
{
// Arrange
var id = ElementReference.CreateWithUniqueId().Id;
var id = ElementReference.CreateWithUniqueId(ElementReferenceContext).Id;
var json = $"{{\"__internalId\":\"{id}\"}}";
var bytes = Encoding.UTF8.GetBytes(json);
var reader = new Utf8JsonReader(bytes);
Expand All @@ -51,7 +60,7 @@ public void Deserializing_Works()
public void Deserializing_WithFormatting_Works()
{
// Arrange
var id = ElementReference.CreateWithUniqueId().Id;
var id = ElementReference.CreateWithUniqueId(ElementReferenceContext).Id;
var json =
@$"{{
""__internalId"": ""{id}""
Expand Down
9 changes: 8 additions & 1 deletion src/Components/Shared/src/ElementReferenceJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ internal sealed class ElementReferenceJsonConverter : JsonConverter<ElementRefer
{
private static readonly JsonEncodedText IdProperty = JsonEncodedText.Encode("__internalId");

private readonly ElementReferenceContext _elementReferenceContext;

public ElementReferenceJsonConverter(ElementReferenceContext elementReferenceContext)
{
_elementReferenceContext = elementReferenceContext;
}

public override ElementReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string id = null;
Expand Down Expand Up @@ -39,7 +46,7 @@ public override ElementReference Read(ref Utf8JsonReader reader, Type typeToConv
throw new JsonException("__internalId is required.");
}

return new ElementReference(id);
return new ElementReference(id, _elementReferenceContext);
}

public override void Write(Utf8JsonWriter writer, ElementReference value, JsonSerializerOptions options)
Expand Down
8 changes: 4 additions & 4 deletions src/Components/Web.JS/dist/Release/blazor.server.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.webassembly.js

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions src/Components/Web.JS/src/DomWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import '@microsoft/dotnet-js-interop';

export const domFunctions = {
focus,
};

function focus(element: HTMLElement): void {
if (element instanceof HTMLElement) {
element.focus();
} else {
throw new Error('Unable to focus an invalid element.');
}
}
2 changes: 2 additions & 0 deletions src/Components/Web.JS/src/GlobalExports.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { navigateTo, internalFunctions as navigationManagerInternalFunctions } from './Services/NavigationManager';
import { attachRootComponentToElement } from './Rendering/Renderer';
import { domFunctions } from './DomWrapper';

// Make the following APIs available in global scope for invocation from JS
window['Blazor'] = {
Expand All @@ -8,5 +9,6 @@ window['Blazor'] = {
_internal: {
attachRootComponentToElement,
navigationManager: navigationManagerInternalFunctions,
domWrapper: domFunctions,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ public BindInputElementAttribute(string? type, string? suffix, string? valueAttr
public string? Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string? ValueAttribute { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
public static partial class ElementReferenceExtensions
{
public static System.Threading.Tasks.ValueTask FocusAsync(this Microsoft.AspNetCore.Components.ElementReference elementReference) { throw null; }
}
public partial class WebElementReferenceContext : Microsoft.AspNetCore.Components.ElementReferenceContext
{
public WebElementReferenceContext(Microsoft.JSInterop.IJSRuntime jsRuntime) { }
}
}
namespace Microsoft.AspNetCore.Components.Forms
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ public BindInputElementAttribute(string? type, string? suffix, string? valueAttr
public string? Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string? ValueAttribute { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
public static partial class ElementReferenceExtensions
{
public static System.Threading.Tasks.ValueTask FocusAsync(this Microsoft.AspNetCore.Components.ElementReference elementReference) { throw null; }
}
public partial class WebElementReferenceContext : Microsoft.AspNetCore.Components.ElementReferenceContext
{
public WebElementReferenceContext(Microsoft.JSInterop.IJSRuntime jsRuntime) { }
}
}
namespace Microsoft.AspNetCore.Components.Forms
{
Expand Down
12 changes: 12 additions & 0 deletions src/Components/Web/src/DomWrapperInterop.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNetCore.Components
{
internal static class DomWrapperInterop
{
private const string Prefix = "Blazor._internal.domWrapper.";

public const string Focus = Prefix + "focus";
}
}
39 changes: 39 additions & 0 deletions src/Components/Web/src/ElementReferenceExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace Microsoft.AspNetCore.Components
{
public static class ElementReferenceExtensions
{
/// <summary>
/// Gives focus to an element given its <see cref="ElementReference"/>.
/// </summary>
/// <param name="elementReference">A reference to the element to focus.</param>
/// <returns>The <see cref="ValueTask"/> representing the asynchronous focus operation.</returns>
public static ValueTask FocusAsync(this ElementReference elementReference)
{
var jsRuntime = elementReference.GetJSRuntime();

if (jsRuntime == null)
{
throw new InvalidOperationException("No JavaScript runtime found.");
}

return jsRuntime.InvokeVoidAsync(DomWrapperInterop.Focus, elementReference);
}

internal static IJSRuntime GetJSRuntime(this ElementReference elementReference)
{
if (!(elementReference.Context is WebElementReferenceContext context))
{
throw new InvalidOperationException("ElementReference has not been configured correctly.");
}

return context.JSRuntime;
}
}
}
Loading