Skip to content

Commit 94305a5

Browse files
authored
[FluentCombobox] Add EnableClickToClose (#3186)
* Add EnableClickToClose option to FluentCombobox for dropdown toggle control * [FluentCombobox] Add EnableClickToClose for dropdown list
1 parent 0b88b47 commit 94305a5

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5941,6 +5941,11 @@
59415941
Gets or sets the open attribute.
59425942
</summary>
59435943
</member>
5944+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentCombobox`1.EnableClickToClose">
5945+
<summary>
5946+
Gets or sets the option to allow closing the FluentCombobox list by clicking the dropdown button. Default is false.
5947+
</summary>
5948+
</member>
59445949
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentCombobox`1.Position">
59455950
<summary>
59465951
Gets or sets the placement for the listbox when the combobox is open.

src/Core/Components/List/FluentCombobox.razor.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace Microsoft.FluentUI.AspNetCore.Components;
1111

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

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

40+
/// <summary>
41+
/// Gets or sets the option to allow closing the FluentCombobox list by clicking the dropdown button. Default is false.
42+
/// </summary>
43+
[Parameter]
44+
public bool? EnableClickToClose { get; set; } = false;
45+
4046
/// <summary>
4147
/// Gets or sets the placement for the listbox when the combobox is open.
4248
/// See <seealso cref="AspNetCore.Components.SelectPosition"/>
@@ -64,6 +70,11 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
6470
{
6571
Module ??= await JSRuntime.InvokeAsync<IJSObjectReference>("import", JAVASCRIPT_FILE.FormatCollocatedUrl(LibraryConfiguration));
6672
await Module.InvokeVoidAsync("setControlAttribute", Id, "autocomplete", "off");
73+
74+
if (EnableClickToClose ?? true)
75+
{
76+
await Module.InvokeVoidAsync("attachIndicatorClickHandler", Id);
77+
}
6778
}
6879
}
6980
}
@@ -162,4 +173,14 @@ protected override async Task ChangeHandlerAsync(ChangeEventArgs e)
162173
return null;
163174
}
164175
}
176+
177+
public new async ValueTask DisposeAsync()
178+
{
179+
if (Module is not null && !string.IsNullOrEmpty(Id))
180+
{
181+
await Module.InvokeVoidAsync("detachIndicatorClickHandler", Id);
182+
await Module.DisposeAsync();
183+
}
184+
await base.DisposeAsync();
185+
}
165186
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,54 @@
1+
const handlers = new Map();
2+
13
export function setControlAttribute(id, attrName, value) {
24
const fieldElement = document.querySelector("#" + id)?.shadowRoot?.querySelector(".selected-value");
35

46
if (!!fieldElement) {
57
fieldElement?.setAttribute(attrName, value);
68
}
79
}
10+
11+
export function attachIndicatorClickHandler(id) {
12+
const combobox = document.querySelector("#" + id);
13+
if (!combobox) return;
14+
15+
const indicator = combobox.shadowRoot?.querySelector('[part="indicator"]');
16+
17+
if (!indicator) return;
18+
19+
const clickHandler = (event) => {
20+
if (combobox.hasAttribute('open')) {
21+
event.preventDefault();
22+
event.stopImmediatePropagation();
23+
24+
const escEvent = new KeyboardEvent('keydown', {
25+
key: 'Escape',
26+
code: 'Escape',
27+
keyCode: 27,
28+
bubbles: true,
29+
cancelable: true
30+
});
31+
32+
combobox.dispatchEvent(escEvent);
33+
}
34+
};
35+
36+
if (indicator) {
37+
indicator.addEventListener('click', clickHandler);
38+
}
39+
40+
handlers.set(id, { indicator, clickHandler });
41+
}
42+
43+
export function detachHandlers(id) {
44+
const handler = handlers.get(id);
45+
if (handler) {
46+
const { indicator, clickHandler } = handler;
47+
48+
if (indicator) {
49+
indicator.removeEventListener('click', clickHandler);
50+
}
51+
52+
handlers.delete(id);
53+
}
54+
}

0 commit comments

Comments
 (0)