Files
SharepointToolbox-Web/Components/Shared/SitePicker.razor
T

126 lines
4.8 KiB
Plaintext

@inject ISiteDiscoveryService SiteDiscovery
@inject TranslationSource T
<div class="site-picker">
<div class="flex-row" style="gap:8px;align-items:flex-end">
<div class="form-group" style="flex:1">
<label class="form-label">@(Single ? T["sitepicker.label.site"] : T["sitepicker.label.sites"])</label>
<input class="form-input" @bind="_filter" @bind:event="oninput" placeholder="@T["sitepicker.ph.filter"]" />
</div>
<button class="btn btn-secondary" @onclick="LoadSites" disabled="@_loading">
@(_loading ? T["sitepicker.status.loadingShort"] : (_all.Count > 0 ? T["sitepicker.btn.reload"] : T["sitepicker.btn.load"]))
</button>
</div>
@if (!string.IsNullOrEmpty(_error))
{
<div class="alert alert-error mt-8">@_error</div>
}
@if (_all.Count > 0)
{
<div class="flex-row mt-8" style="gap:12px;align-items:center">
@if (!Single)
{
<button class="btn btn-link btn-sm" @onclick="SelectAllFiltered">@string.Format(T["sitepicker.btn.selectAllCount"], Filtered.Count())</button>
}
<button class="btn btn-link btn-sm" @onclick="ClearSelection">@T["sitepicker.btn.clear"]</button>
<span class="spacer"></span>
<span class="count-badge">@string.Format(T["sitepicker.status.selectedCount"], SelectedSites.Count)</span>
</div>
<div class="site-picker-list" style="max-height:240px;overflow:auto;border:1px solid var(--border);border-radius:4px;padding:4px;margin-top:6px">
@foreach (var s in Filtered)
{
<label style="display:flex;align-items:center;gap:8px;padding:3px 6px;cursor:pointer">
@if (Single)
{
<input type="radio" name="@_radioName" checked="@IsSelected(s)" @onchange="e => SelectSingle(s)" />
}
else
{
<input type="checkbox" checked="@IsSelected(s)" @onchange="e => Toggle(s, (bool)(e.Value ?? false))" />
}
<span>@s.Title</span>
<span class="text-muted" style="font-size:11px">@s.Url</span>
</label>
}
@if (!Filtered.Any())
{
<div class="text-muted" style="padding:6px">@T["sitepicker.empty.noMatch"]</div>
}
</div>
}
else if (!_loading)
{
<div class="text-muted mt-8" style="font-size:12px">@(Single ? T["sitepicker.hint.loadSingle"] : T["sitepicker.hint.loadMulti"])</div>
}
</div>
@code {
[Parameter] public TenantProfile Profile { get; set; } = default!;
[Parameter] public List<SiteInfo> SelectedSites { get; set; } = new();
[Parameter] public EventCallback<List<SiteInfo>> SelectedSitesChanged { get; set; }
[Parameter] public bool Disabled { get; set; }
[Parameter] public bool Single { get; set; }
private readonly string _radioName = "sp-" + Guid.NewGuid().ToString("N");
private List<SiteInfo> _all = new();
private string _filter = string.Empty;
private bool _loading;
private string _error = string.Empty;
private IEnumerable<SiteInfo> Filtered =>
string.IsNullOrWhiteSpace(_filter)
? _all
: _all.Where(s =>
s.Title.Contains(_filter, StringComparison.OrdinalIgnoreCase) ||
s.Url.Contains(_filter, StringComparison.OrdinalIgnoreCase));
private bool IsSelected(SiteInfo s) =>
SelectedSites.Any(x => string.Equals(x.Url, s.Url, StringComparison.OrdinalIgnoreCase));
private async Task LoadSites()
{
_loading = true; _error = string.Empty;
try
{
_all = (await SiteDiscovery.SearchSitesAsync(Profile)).ToList();
if (_all.Count == 0) _error = T["sitepicker.err.noSites"];
}
catch (Exception ex) { _error = ex.Message; }
finally { _loading = false; }
}
private async Task Toggle(SiteInfo s, bool on)
{
if (on)
{
if (!IsSelected(s)) SelectedSites.Add(s);
}
else
{
SelectedSites.RemoveAll(x => string.Equals(x.Url, s.Url, StringComparison.OrdinalIgnoreCase));
}
await SelectedSitesChanged.InvokeAsync(SelectedSites);
}
private async Task SelectSingle(SiteInfo s)
{
SelectedSites = new List<SiteInfo> { s };
await SelectedSitesChanged.InvokeAsync(SelectedSites);
}
private async Task SelectAllFiltered()
{
foreach (var s in Filtered)
if (!IsSelected(s)) SelectedSites.Add(s);
await SelectedSitesChanged.InvokeAsync(SelectedSites);
}
private async Task ClearSelection()
{
SelectedSites.Clear();
await SelectedSitesChanged.InvokeAsync(SelectedSites);
}
}