Skip to content

Commit

Permalink
[FluentCombobox] Add EnableClickToClose (#3186)
Browse files Browse the repository at this point in the history
* Add EnableClickToClose option to FluentCombobox for dropdown toggle control

* [FluentCombobox] Add EnableClickToClose for dropdown list
  • Loading branch information
hml-coder authored Jan 16, 2025
1 parent 0b88b47 commit 94305a5
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -5941,6 +5941,11 @@
Gets or sets the open attribute.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentCombobox`1.EnableClickToClose">
<summary>
Gets or sets the option to allow closing the FluentCombobox list by clicking the dropdown button. Default is false.
</summary>
</member>
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentCombobox`1.Position">
<summary>
Gets or sets the placement for the listbox when the combobox is open.
Expand Down
23 changes: 22 additions & 1 deletion src/Core/Components/List/FluentCombobox.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace Microsoft.FluentUI.AspNetCore.Components;

[CascadingTypeParameter(nameof(TOption))]
public partial class FluentCombobox<TOption> : ListComponentBase<TOption> where TOption : notnull
public partial class FluentCombobox<TOption> : ListComponentBase<TOption>, IAsyncDisposable where TOption : notnull
{
private const string JAVASCRIPT_FILE = "./_content/Microsoft.FluentUI.AspNetCore.Components/Components/List/FluentCombobox.razor.js";

Expand All @@ -37,6 +37,12 @@ public partial class FluentCombobox<TOption> : ListComponentBase<TOption> where
[Parameter]
public bool? Open { get; set; }

/// <summary>
/// Gets or sets the option to allow closing the FluentCombobox list by clicking the dropdown button. Default is false.
/// </summary>
[Parameter]
public bool? EnableClickToClose { get; set; } = false;

/// <summary>
/// Gets or sets the placement for the listbox when the combobox is open.
/// See <seealso cref="AspNetCore.Components.SelectPosition"/>
Expand Down Expand Up @@ -64,6 +70,11 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
{
Module ??= await JSRuntime.InvokeAsync<IJSObjectReference>("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration));
await Module.InvokeVoidAsync("setControlAttribute", Id, "autocomplete", "off");

if (EnableClickToClose ?? true)
{
await Module.InvokeVoidAsync("attachIndicatorClickHandler", Id);
}
}
}
}
Expand Down Expand Up @@ -162,4 +173,14 @@ protected override async Task ChangeHandlerAsync(ChangeEventArgs e)
return null;
}
}

public new async ValueTask DisposeAsync()
{
if (Module is not null && !string.IsNullOrEmpty(Id))
{
await Module.InvokeVoidAsync("detachIndicatorClickHandler", Id);
await Module.DisposeAsync();
}
await base.DisposeAsync();
}
}
47 changes: 47 additions & 0 deletions src/Core/Components/List/FluentCombobox.razor.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,54 @@
const handlers = new Map();

export function setControlAttribute(id, attrName, value) {
const fieldElement = document.querySelector("#" + id)?.shadowRoot?.querySelector(".selected-value");

if (!!fieldElement) {
fieldElement?.setAttribute(attrName, value);
}
}

export function attachIndicatorClickHandler(id) {
const combobox = document.querySelector("#" + id);
if (!combobox) return;

const indicator = combobox.shadowRoot?.querySelector('[part="indicator"]');

if (!indicator) return;

const clickHandler = (event) => {
if (combobox.hasAttribute('open')) {
event.preventDefault();
event.stopImmediatePropagation();

const escEvent = new KeyboardEvent('keydown', {
key: 'Escape',
code: 'Escape',
keyCode: 27,
bubbles: true,
cancelable: true
});

combobox.dispatchEvent(escEvent);
}
};

if (indicator) {
indicator.addEventListener('click', clickHandler);
}

handlers.set(id, { indicator, clickHandler });
}

export function detachHandlers(id) {
const handler = handlers.get(id);
if (handler) {
const { indicator, clickHandler } = handler;

if (indicator) {
indicator.removeEventListener('click', clickHandler);
}

handlers.delete(id);
}
}

0 comments on commit 94305a5

Please sign in to comment.