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
112 changes: 108 additions & 4 deletions examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,73 @@
Gets or sets the status message to be read by screen readers.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentAccessibilityStatus.DebugDisplay">
<summary>
In Debug mode, you can set this to true to display the status message on the page (on right, in yellow).
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentAccessibilityStatus.Content">
<summary />
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentAccessibilityStatus.AriaHidden">
<summary />
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentAccessibilityStatus.Style">
<summary />
</member>
<member name="T:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode">
<summary>
Extends the OnKeyDown blazor event to provide a more fluent way to evaluate the key code.
The anchor must refer to the ID of an element (or sub-element) accepting the focus.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.JSRuntime">
<summary />
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.Module">
<summary />
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.Anchor">
<summary>
Required. Gets or sets the control identifier associated with the KeyCode engine.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.OnKeyDown">
<summary>
Event triggered when a KeyDown event is raised.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.IgnoreModifier">
<summary>
Ignore modifier keys (Shift, Alt, Ctrl, Meta) when evaluating the key code.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.Only">
<summary>
Gets or sets the list of <see cref="T:Microsoft.FluentUI.AspNetCore.Components.KeyCode"/> to accept, and only this list, when evaluating the key code.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.Ignore">
<summary>
Gets or sets the list of <see cref="T:Microsoft.FluentUI.AspNetCore.Components.KeyCode"/> to ignore when evaluating the key code.
</summary>
</member>
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.OnAfterRenderAsync(System.Boolean)">
<summary />
</member>
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentKeyCode.OnKeyDownRaised(System.Int32,System.String,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Int32)">
<summary>
Internal method.
</summary>
<param name="keyCode"></param>
<param name="value"></param>
<param name="ctrlKey"></param>
<param name="shiftKey"></param>
<param name="altKey"></param>
<param name="metaKey"></param>
<param name="location"></param>
<returns></returns>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentAccordion.ExpandMode">
<summary>
Controls the expand mode of the Accordion, either allowing
Expand Down Expand Up @@ -3981,9 +4048,6 @@
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.Module">
<summary />
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.AccessibilityStatusMessage">
<summary />
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.Placeholder">
<summary>
Gets or sets the placeholder value of the element, generally used to provide a hint to the user.
Expand Down Expand Up @@ -4088,7 +4152,7 @@
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.InputHandlerAsync(Microsoft.AspNetCore.Components.ChangeEventArgs)">
<summary />
</member>
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.KeyDownHandlerAsync(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs)">
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.KeyDownHandlerAsync(Microsoft.FluentUI.AspNetCore.Components.FluentKeyCodeEventArgs)">
<summary />
</member>
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.OnAfterRenderAsync(System.Boolean)">
Expand Down Expand Up @@ -11432,6 +11496,46 @@
Justify content space-evenly.
</summary>
</member>
<member name="T:Microsoft.FluentUI.AspNetCore.Components.KeyLocation">
<summary>
the location of the key on the keyboard or other input device
</summary>
</member>
<member name="F:Microsoft.FluentUI.AspNetCore.Components.KeyLocation.Unknown">
<summary>
Not defined
</summary>
</member>
<member name="F:Microsoft.FluentUI.AspNetCore.Components.KeyLocation.Standard">
<summary>
The key has only one version, or can't be distinguished between the left and right versions of the key, and was not pressed on the numeric keypad or a key that is considered to be part of the keypad.
</summary>
</member>
<member name="F:Microsoft.FluentUI.AspNetCore.Components.KeyLocation.Left">
<summary>
The key was the left-hand version of the key; for example, the left-hand Control key was pressed on a standard 101 key US keyboard. This value is only used for keys that have more than one possible location on the keyboard.
</summary>
</member>
<member name="F:Microsoft.FluentUI.AspNetCore.Components.KeyLocation.Right">
<summary>
The key was the right-hand version of the key; for example, the right-hand Control key is pressed on a standard 101 key US keyboard. This value is only used for keys that have more than one possible location on the keyboard.
</summary>
</member>
<member name="F:Microsoft.FluentUI.AspNetCore.Components.KeyLocation.NumPad">
<summary>
The key was on the numeric keypad, or has a virtual key code that corresponds to the numeric keypad.
</summary>
</member>
<member name="F:Microsoft.FluentUI.AspNetCore.Components.KeyLocation.Mobile">
<summary>
The key was on a mobile device; this can be on either a physical keypad or a virtual keyboard.
</summary>
</member>
<member name="F:Microsoft.FluentUI.AspNetCore.Components.KeyLocation.Joystick">
<summary>
The key was a button on a game controller or a joystick on a mobile device.
</summary>
</member>
<member name="T:Microsoft.FluentUI.AspNetCore.Components.LocalizationDirection">
<summary>
The (reading) direction of objects in the UI.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<FluentKeyCode Anchor="MyCard" OnKeyDown="@KeyDownHandler" />

<FluentStack>
<FluentCard Id="MyCard" tabindex="0" Width="300px" Height="120px">
Click here and press any key to get the event keycode info.
</FluentCard>

<ul>
<li><span>Value:</span> <code>@LastKeyCode?.Value</code></li>
<li><span>Key:</span> <code>@LastKeyCode?.Key.ToString()</code></li>
<li><span>Code:</span> <code>@LastKeyCode?.KeyCode</code></li>
<li><span>Meta:</span>
@if (LastKeyCode?.ShiftKey == true)
{
<FluentIcon Value="@(new Icons.Filled.Size20.KeyboardShift())" />
}
else
{
<FluentIcon Value="@(new Icons.Regular.Size20.KeyboardShift())" Color="Color.Neutral" />
}
@if (LastKeyCode?.CtrlKey == true)
{
<FluentIcon Value="@(new Icons.Filled.Size20.ControlButton())" />
}
else
{
<FluentIcon Value="@(new Icons.Regular.Size20.ControlButton())" Color="Color.Neutral" />
}
@if (LastKeyCode?.AltKey == true)
{
<FluentIcon Value="@(new Icons.Filled.Size20.KeyCommand())" />
}
else
{
<FluentIcon Value="@(new Icons.Regular.Size20.KeyCommand())" Color="Color.Neutral" />
}
@if (LastKeyCode?.MetaKey == true)
{
<FluentIcon Value="@(new Icons.Filled.Size20.ArrowBounce())" />
}
else
{
<FluentIcon Value="@(new Icons.Regular.Size20.ArrowBounce())" Color="Color.Neutral" />
}
</li>
<li><span>Location:</span> <code>@LastKeyCode?.Location.ToString()</code></li>
</ul>
</FluentStack>

<style>
li > span {
float: left;
width: 70px;
font-weight: bold;
}
</style>

@code
{
FluentKeyCodeEventArgs? LastKeyCode;

void KeyDownHandler(FluentKeyCodeEventArgs e)
{
LastKeyCode = e;
}
}
27 changes: 27 additions & 0 deletions examples/Demo/Shared/Pages/KeyCode/KeyCodePage.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@page "/KeyCode"

@using FluentUI.Demo.Shared.Pages.KeyCode.Examples

<h1>KeyCode</h1>

<p>
In some circumstances, Blazor does not retrieve all the <b>KeyDown</b> information received from JavaScript.
<b>FluentKeyCode</b> retrieves this data, in a similar way to the <a href="https://www.npmjs.com/package/keycode" target="_blank">JavaScript KeyCode library</a>
and to <a href="https://www.toptal.com/developers/keycode">this demo sample</a>.
</p>

<p>The <b>FluentKeyCode</b> component extends the functionality of <b>OnKeyDown</b> by adding the <b>KeyCode</b> property when the <b>OnKeyDown</b> event is raised.</p>

<h2 id="example">Examples</h2>

<DemoSection Title="Default" Component="@typeof(KeyCodeExample)">
<Description>

</Description>
</DemoSection>

<h2 id="documentation">Documentation</h2>

<ApiDocumentation Component="typeof(FluentKeyCode)" />

<ApiDocumentation Component="typeof(FluentKeyCodeEventArgs)" />
1 change: 1 addition & 0 deletions examples/Demo/Shared/Shared/DemoNavMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
<FluentNavLink Href="/Lab/MarkdownSection" Icon="@(new Icons.Regular.Size20.ArrowSortDown())">MarkdownSection</FluentNavLink>
<FluentNavLink Href="/Lab/TableOfContents" Icon="@(new Icons.Regular.Size20.DocumentTextLink())">TableOfContents</FluentNavLink>
<FluentNavLink Href="/MultiSplitter" Icon="@(new Icons.Regular.Size20.SplitHorizontal())">Multi Splitter</FluentNavLink>
<FluentNavLink Href="/KeyCode" Icon="@(new Icons.Regular.Size20.Keyboard())">KeyCode</FluentNavLink>
</ChildContent>
</FluentNavGroup>
</FluentNavMenu>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@namespace Microsoft.FluentUI.AspNetCore.Components
<div tabIndex="-1" role="status" style="position: absolute; left: -9999px; width: 1px; height: 1px;" aria-hidden="@(string.IsNullOrEmpty(Message))">@Message</div>
<div tabIndex="-1" role="status" style="@Style" aria-hidden="@AriaHidden">@Content</div>
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,22 @@ public partial class FluentAccessibilityStatus
/// </summary>
[Parameter]
public string? Message { get; set; }
}

/// <summary>
/// In Debug mode, you can set this to true to display the status message on the page (on right, in yellow).
/// </summary>
[Parameter]
public bool DebugDisplay { get; set; } = false;

/// <summary />
private string Content => string.IsNullOrEmpty(Message) ? string.Empty : Message;

/// <summary />
private bool AriaHidden => string.IsNullOrEmpty(Message);

/// <summary />
private string Style => DebugDisplay
? "position: absolute; right: 0px; background: yellow; color: black; min-width: 40px; min-height: 15px;"
: "position: absolute; left: -9999px; width: 1px; height: 1px;";

}
1 change: 1 addition & 0 deletions src/Core/Components/Accessibility/FluentKeyCode.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@namespace Microsoft.FluentUI.AspNetCore.Components
96 changes: 96 additions & 0 deletions src/Core/Components/Accessibility/FluentKeyCode.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// Extends the OnKeyDown blazor event to provide a more fluent way to evaluate the key code.
/// The anchor must refer to the ID of an element (or sub-element) accepting the focus.
/// </summary>
public partial class FluentKeyCode
{
private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/Accessibility/FluentKeyCode.razor.js";
private DotNetObjectReference<FluentKeyCode>? _dotNetHelper = null;
private readonly KeyCode[] _Modifiers = new[] { KeyCode.Shift, KeyCode.Alt, KeyCode.Ctrl, KeyCode.Meta };

/// <summary />
[Inject]
private IJSRuntime JSRuntime { get; set; } = default!;

/// <summary />
private IJSObjectReference? Module { get; set; }

/// <summary>
/// Required. Gets or sets the control identifier associated with the KeyCode engine.
/// </summary>
[Parameter]
[EditorRequired]
public string Anchor { get; set; } = string.Empty;

/// <summary>
/// Event triggered when a KeyDown event is raised.
/// </summary>
[Parameter]
public EventCallback<FluentKeyCodeEventArgs> OnKeyDown { get; set; }

/// <summary>
/// Ignore modifier keys (Shift, Alt, Ctrl, Meta) when evaluating the key code.
/// </summary>
[Parameter]
public bool IgnoreModifier { get; set; } = true;

/// <summary>
/// Gets or sets the list of <see cref="KeyCode"/> to accept, and only this list, when evaluating the key code.
/// </summary>
[Parameter]
public KeyCode[] Only { get; set; } = Array.Empty<KeyCode>();

/// <summary>
/// Gets or sets the list of <see cref="KeyCode"/> to ignore when evaluating the key code.
/// </summary>
[Parameter]
public KeyCode[] Ignore { get; set; } = Array.Empty<KeyCode>();

/// <summary />
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
Module ??= await JSRuntime.InvokeAsync<IJSObjectReference>("import", JAVASCRIPT_FILE);
_dotNetHelper = DotNetObjectReference.Create(this);

await Module.InvokeVoidAsync("RegisterKeyCode", Anchor, Only, IgnoreModifier ? Ignore.Union(_Modifiers) : Ignore, _dotNetHelper);
}
}

/// <summary>
/// Internal method.
/// </summary>
/// <param name="keyCode"></param>
/// <param name="value"></param>
/// <param name="ctrlKey"></param>
/// <param name="shiftKey"></param>
/// <param name="altKey"></param>
/// <param name="metaKey"></param>
/// <param name="location"></param>
/// <returns></returns>
[JSInvokable]
public async Task OnKeyDownRaised(int keyCode, string value, bool ctrlKey, bool shiftKey, bool altKey, bool metaKey, int location)
{
if (OnKeyDown.HasDelegate)
{
await OnKeyDown.InvokeAsync(new FluentKeyCodeEventArgs
{
Location = Enum.IsDefined(typeof(KeyLocation), location) ? (KeyLocation)location : KeyLocation.Unknown,
Key = Enum.IsDefined(typeof(KeyCode), keyCode) ? (KeyCode)keyCode : KeyCode.Unknown,
KeyCode = keyCode,
Value = value,
CtrlKey = ctrlKey,
ShiftKey = shiftKey,
AltKey = altKey,
MetaKey = metaKey
});
}
}
}

22 changes: 22 additions & 0 deletions src/Core/Components/Accessibility/FluentKeyCode.razor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function RegisterKeyCode(id, onlyCodes, excludeCodes, dotNetHelper) {
const element = document.getElementById(id);
if (!!element) {
element.addEventListener('keydown', function (e) {
const keyCode = e.which || e.keyCode || e.charCode;

if (!!dotNetHelper && !!dotNetHelper.invokeMethodAsync) {

// Exclude
if (excludeCodes.length > 0 && excludeCodes.includes(keyCode)) {
return;
}

// All or Include only
if (onlyCodes.length == 0 || (onlyCodes.length > 0 && onlyCodes.includes(keyCode))) {
dotNetHelper.invokeMethodAsync("OnKeyDownRaised", keyCode, e.key, e.ctrlKey, e.shiftKey, e.altKey, e.metaKey, e.location);
return;
}
}
})
}
}
Loading