dbMango/Rms.Risk.Mango.Pivot.UI/Controls/MultiSelectCheckboxDropdown.razor
Alexander Shabarshov 2a7a24c9e7 Initial contribution
2025-11-03 14:43:26 +00:00

278 lines
9.1 KiB
Plaintext

@*
* dbMango
*
* Copyright 2025 Deutsche Bank AG
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*@
<div class="dropdown @Class">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@if (ShowSelectionText)
{
var s = string.Join(", ", _selectedValues.OrderBy(x => x).Select(x => string.IsNullOrWhiteSpace(x) ? DenoteEmptyString : x));
if ( s == string.Empty )
{
s = "(None)";
}
else if ( s.Length > 30 )
{
s = s[..20] + "...";
}
<span>@s</span>
}
else
{
@Name
}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
@if (EnableDropdownMenu)
{
<div class="dropdown-item">
<span>
<button class="btn" @onclick="ToggleFilterable" title="toggle type-in filter">
<span class="m-auto ui-icon-font ui-icon-sm icon-search-sm"></span>
</button></span>
<span><button class="btn" @onclick="@CancelClicked" id="cancel" name="cancel" title="discard changes">Cancel</button></span>
<span><button class="btn" @onclick="@ApplyClicked" id="apply" name="apply" title="apply changes">Apply</button></span>
</div>
}
@if (Filterable)
{
<div class="dropdown-item">
<input class="form-control" @bind="FilterString" @bind:event="oninput"/>
</div>
}
<div class="dropdown-item">
@if (_allSelected)
{
<input type="checkbox" @onchange="_ => AllClicked()" checked id="all" name="all" value="all">
}
else
{
<input type="checkbox" @onchange="_ => AllClicked()" id="all" name="all" value="all">
}
<label for="all">(All)</label>
</div>
@foreach (var item in _filteredDropdown)
{
<div class="dropdown-item">
@if (_selectedValues.Contains(item))
{
<input type="checkbox" @onchange="eventArgs => { ItemCheckboxClicked(item, eventArgs.Value); }" checked id=@item name=@item value=@item>
}
else
{
<input type="checkbox" @onchange="eventArgs => { ItemCheckboxClicked(item, eventArgs.Value); }" id=@item name=@item value=@item>
}
<label for=@item>@item</label>
</div>
}
</div>
</div>
@code
{
[Parameter] public string Name { get; set; } = "Select";
[Parameter] public string DenoteEmptyString { get; set; } = "(Blank)";
[Parameter] public string Class { get; set; } = "";
[Parameter] public bool ShowSelectionText { get; set; }
[Parameter]
public List<string> DropdownValues
{
get;
set
{
value ??= [];
if (field.SequenceEqual(value))
return;
field = value;
SetValues(BlankedDropdownValues);
UpdateAllSelected();
InvokeAsync(StateHasChanged);
}
} = [];
[Parameter] public EventCallback<List<string>> SelectionChanged { get; set; }
[Parameter] public bool EnableDropdownRefresh { get; set; }
[Parameter] public bool EnableDropdownMenu { get; set; } = true;
[Parameter] public bool Filterable { get; set; }
[Parameter] public bool ResetNow { get; set; }
[Parameter] public bool SelectAllByDefault { get; set; } = true;
[Parameter]
public string FilterString
{
get;
set
{
field = value;
_filterList = field.Split(",").Select(v => v.Trim()).Where(v => !string.IsNullOrWhiteSpace(v)).ToList();
RefreshFilter();
}
} = string.Empty;
private bool _allSelected = true;
private List<string> _dropdownValuesList = [];
private HashSet<string> _dropdownValuesSet = [];
private HashSet<string> _selectedValues = [];
private HashSet<string> _selectedValuesBackup = [];
private List<string> _filterList = [];
private List<string> _filteredDropdown = [];
IEnumerable<string> BlankedDropdownValues
=> DropdownValues?.Select(x => string.IsNullOrWhiteSpace(x) ? DenoteEmptyString : x) ?? Array.Empty<string>();
[Parameter] public HashSet<string> SelectedValues
{
get => _selectedValues;
set
{
if (value == null || ReferenceEquals(_selectedValues, value) || _selectedValues.SequenceEqual(value))
return;
_selectedValues = value;
SelectedValuesChanged.InvokeAsync(_selectedValues);
}
}
[Parameter] public EventCallback<HashSet<string>> SelectedValuesChanged { get; set; }
private void RefreshFilter()
{
_filteredDropdown.Clear();
if (_filterList.Count == 0)
_filteredDropdown.AddRange(BlankedDropdownValues);
else
_filteredDropdown.AddRange(
BlankedDropdownValues.Where(x =>
_filterList.Any(v => x.Contains(v, StringComparison.OrdinalIgnoreCase))));
}
private void SetValues(IEnumerable<string> values)
{
_dropdownValuesList.Clear();
_dropdownValuesList.AddRange(values);
_dropdownValuesSet.Clear();
_dropdownValuesSet.UnionWith(_dropdownValuesList);
}
private void ResetMenu(IEnumerable<string> values)
{
SetValues(values);
_selectedValues .Clear();
_selectedValues .UnionWith(_dropdownValuesSet);
_allSelected = true;
SelectedValuesChanged.InvokeAsync(_selectedValues);
}
protected override void OnInitialized()
{
if (SelectAllByDefault)
ResetMenu(BlankedDropdownValues);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (!firstRender)
return;
if (!SelectAllByDefault)
{
SetValues(BlankedDropdownValues);
if ( UpdateAllSelected() )
await InvokeAsync(StateHasChanged);
}
}
private bool UpdateAllSelected()
{
var sel = _selectedValues.SetEquals(_dropdownValuesSet);
if (_allSelected != sel)
{
_allSelected = sel;
return true;
}
return false;
}
protected override void OnParametersSet()
{
if (ResetNow)
{
ResetMenu(BlankedDropdownValues);
FilterString = string.Empty;
}
else if (EnableDropdownRefresh)
{
var newDropdownValues = new HashSet<string>(BlankedDropdownValues);
if (!newDropdownValues.SetEquals(_dropdownValuesSet))
ResetMenu(BlankedDropdownValues);
}
RefreshFilter();
}
public async Task AllClicked()
{
_allSelected = !_allSelected;
_selectedValues.Clear();
if (_allSelected)
_selectedValues.UnionWith(_dropdownValuesSet);
await SelectedValuesChanged.InvokeAsync(_selectedValues);
await InvokeAsync(StateHasChanged);
}
public void ItemCheckboxClicked(string item, object? checkedValue)
{
if (checkedValue is true)
_selectedValues.Add(item);
else
_selectedValues.Remove(item);
_allSelected = _selectedValues.SetEquals(_dropdownValuesSet);
SelectedValuesChanged.InvokeAsync(_selectedValues);
}
public void CancelClicked()
{
_selectedValues.Clear();
_selectedValues.UnionWith(_selectedValuesBackup);
_allSelected = _selectedValues.SetEquals(_dropdownValuesSet);
SelectedValuesChanged.InvokeAsync(_selectedValues);
}
public Task ApplyClicked()
{
_selectedValuesBackup.Clear();
_selectedValuesBackup.UnionWith(_selectedValues);
return SelectionChanged.InvokeAsync(_selectedValues.Select(x => x == DenoteEmptyString ? "" : x)
.ToList());
}
private void ToggleFilterable()
{
Filterable = !Filterable;
FilterString = string.Empty;
}
}