diff --git a/src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj b/src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj index 664f20b8..b8ef9ff5 100644 --- a/src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj +++ b/src/components/BootstrapBlazor.Region/BootstrapBlazor.Region.csproj @@ -1,7 +1,7 @@  - 9.0.0 + 9.0.1 diff --git a/src/components/BootstrapBlazor.Region/Components/SelectCity.razor b/src/components/BootstrapBlazor.Region/Components/SelectCity.razor new file mode 100644 index 00000000..31c3f8c8 --- /dev/null +++ b/src/components/BootstrapBlazor.Region/Components/SelectCity.razor @@ -0,0 +1,32 @@ +@namespace BootstrapBlazor.Components +@inherits SelectRegionBase +@attribute [JSModuleAutoLoader("./_content/BootstrapBlazor.Region/Components/SelectRegion.razor.js")] + +@if (IsShowLabel) +{ + +} +
+ + @if (!IsDisabled) + { + + } + +
diff --git a/src/components/BootstrapBlazor.Region/Components/SelectCity.razor.cs b/src/components/BootstrapBlazor.Region/Components/SelectCity.razor.cs new file mode 100644 index 00000000..c4287977 --- /dev/null +++ b/src/components/BootstrapBlazor.Region/Components/SelectCity.razor.cs @@ -0,0 +1,142 @@ +// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// SelectCity 组件 +/// +public partial class SelectCity +{ + /// + /// 获得/设置 是否可多选 默认 false 单选 + /// + [Parameter] + public bool IsMultiple { get; set; } + + private string? InputId => $"{Id}_input"; + + private string? ClassString => CssBuilder.Default("select bb-city") + .AddClass("disabled", IsDisabled) + .AddClassFromAttributes(AdditionalAttributes) + .Build(); + + private readonly HashSet _values = []; + + private string? GetActiveClass(string item) => _values.Contains(item) || CurrentValue == item ? "active" : null; + + private async Task OnClearValue() + { + if (IsMultiple) + { + _values.Clear(); + } + CurrentValue = ""; + + if (OnClearAsync != null) + { + await OnClearAsync(); + } + } + + private void OnSelectProvince(string province) + { + if (IsMultiple) + { + HashSet cities; + if (province == "直辖市") + { + cities = Municipalities; + } + else if (province == "特别行政区") + { + cities = SpecialAdministrativeRegions; + } + else + { + cities = GetCities(province); + } + foreach (var city in cities) + { + if (!_values.Remove(city)) + { + _values.Add(city); + } + } + CurrentValue = string.Join(",", _values); + } + } + + private void OnSelectCity(string item) + { + if (IsMultiple) + { + if (!_values.Remove(item)) + { + _values.Add(item); + } + CurrentValue = string.Join(",", _values); + } + else + { + CurrentValue = item; + } + } + + private static HashSet GetProvinces() + { + return + [ + "直辖市", + "河北省", + "山西省", + "辽宁省", + "吉林省", + "黑龙江省", + "江苏省", + "浙江省", + "安徽省", + "福建省", + "江西省", + "山东省", + "河南省", + "湖北省", + "湖南省", + "广东省", + "海南省", + "四川省", + "贵州省", + "云南省", + "陕西省", + "甘肃省", + "青海省", + "内蒙古自治区", + "广西壮族自治区", + "西藏自治区", + "宁夏回族自治区", + "新疆维吾尔自治区", + "台湾省", + "特别行政区" + ]; + } + + private static readonly HashSet Municipalities = ["北京市", "天津市", "上海市", "重庆市"]; + + private static readonly HashSet SpecialAdministrativeRegions = ["香港特别行政区", "澳门特别行政区"]; + + private HashSet GetCities(string provinceName) + { + if (provinceName == "直辖市") + { + return Municipalities; + } + + if (provinceName == "特别行政区") + { + return SpecialAdministrativeRegions; + } + + return RegionService.GetCities(provinceName); + } +} diff --git a/src/components/BootstrapBlazor.Region/Components/SelectCity.razor.css b/src/components/BootstrapBlazor.Region/Components/SelectCity.razor.css new file mode 100644 index 00000000..9b99033f --- /dev/null +++ b/src/components/BootstrapBlazor.Region/Components/SelectCity.razor.css @@ -0,0 +1,55 @@ +.bb-city { + position: relative; +} + + .bb-city:not(.disabled):hover .form-select-append { + display: none; + } + +.dropdown-menu { + --bs-dropdown-padding-y: .5rem; + --bs-dropdown-padding-x: .5rem; + max-width: 400px; + max-height: 400px; + overflow-y: auto; +} + + .dropdown-menu ul { + margin: 0; + padding: 0; + display: flex; + flex-wrap: wrap; + gap: 5px; + } + + .dropdown-menu li { + list-style: none; + transition: background-color .3s linear, color .3s linear; + cursor: pointer; + padding: 3px 12px; + border-radius: var(--bs-border-radius); + color: #777; + } + + .dropdown-menu li:hover { + background-color: #e9ecef; + color: #000; + } + + .dropdown-menu li.active { + background-color: #e3e3e3; + color: #000; + } + +.bb-region-city-item:not(:last-child) { + margin-bottom: 3px; + padding-bottom: 3px; + border-bottom: 1px solid var(--bs-border-color); +} + +.bb-region-city-title { + padding: 3px 12px; + color: #000; + font-weight: bold; + cursor: pointer; +} diff --git a/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor b/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor index e6759b13..fba41d09 100644 --- a/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor +++ b/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor @@ -1,5 +1,5 @@ @namespace BootstrapBlazor.Components -@inherits PopoverSelectBase +@inherits SelectRegionBase @attribute [JSModuleAutoLoader("./_content/BootstrapBlazor.Region/Components/SelectRegion.razor.js")] @if (IsShowLabel) diff --git a/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor.cs b/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor.cs index ee63e0e2..de778f56 100644 --- a/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor.cs +++ b/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor.cs @@ -9,75 +9,13 @@ namespace BootstrapBlazor.Components; /// public partial class SelectRegion { - /// - /// Gets or sets the placeholder text. - /// - [Parameter] - public string? PlaceHolder { get; set; } - - /// - /// Gets or sets the color. The default is (no color). - /// - [Parameter] - public Color Color { get; set; } - - /// - /// Gets or sets the dropdown icon. The default is "fa-solid fa-angle-up". - /// - [Parameter] - [NotNull] - public string? DropdownIcon { get; set; } - - /// - /// Gets or sets the callback method when the clear button is clicked. Default is null. - /// - [Parameter] - public Func? OnClearAsync { get; set; } - - /// - /// Gets or sets the right-side clear icon. Default is fa-solid fa-angle-up. - /// - [Parameter] - [NotNull] - public string? ClearIcon { get; set; } - - /// - /// Gets or sets the service instance. - /// - [Inject] - [NotNull] - private IIconTheme? IconTheme { get; set; } - - [Inject] - [NotNull] - private IRegionService? RegionService { get; set; } + private string? InputId => $"{Id}_input"; private string? ClassString => CssBuilder.Default("select bb-region") .AddClass("disabled", IsDisabled) .AddClassFromAttributes(AdditionalAttributes) .Build(); - private string? InputId => $"{Id}_input"; - - private string? InputClassString => CssBuilder.Default("form-select form-control") - .AddClass($"border-{Color.ToDescriptionString()}", Color != Color.None && !IsDisabled && !IsValid.HasValue) - .AddClass($"border-success", IsValid.HasValue && IsValid.Value) - .AddClass($"border-danger", IsValid.HasValue && !IsValid.Value) - .AddClass(CssClass).AddClass(ValidCss) - .Build(); - - private string? AppendClassString => CssBuilder.Default("form-select-append") - .AddClass($"text-{Color.ToDescriptionString()}", Color != Color.None && !IsDisabled && !IsValid.HasValue) - .AddClass($"text-success", IsValid.HasValue && IsValid.Value) - .AddClass($"text-danger", IsValid.HasValue && !IsValid.Value) - .Build(); - - private string? ClearClassString => CssBuilder.Default("clear-icon") - .AddClass($"text-{Color.ToDescriptionString()}", Color != Color.None) - .AddClass($"text-success", IsValid.HasValue && IsValid.Value) - .AddClass($"text-danger", IsValid.HasValue && !IsValid.Value) - .Build(); - private string? GetHeaderActiveClass(RegionViewMode type) => _currentViewMode == type ? "active" : null; private string? GetBodyActiveClass(RegionViewMode type) => CssBuilder.Default("bb-region-body-item") @@ -109,9 +47,6 @@ protected override void OnParametersSet() { base.OnParametersSet(); - DropdownIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectDropdownIcon); - ClearIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectClearIcon); - ResetValue(); } diff --git a/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor.css b/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor.css index bc73d85a..978fceb8 100644 --- a/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor.css +++ b/src/components/BootstrapBlazor.Region/Components/SelectRegion.razor.css @@ -6,19 +6,19 @@ display: none; } -.popover-region .dropdown-menu { +.dropdown-menu { --bs-dropdown-padding-y: 0; } -.popover-region ul { - margin: 0; - padding: 0; -} + .dropdown-menu ul { + margin: 0; + padding: 0; + } -.popover-region li { - list-style: none; - transition: background-color .3s linear, color .3s linear; -} + .dropdown-menu li { + list-style: none; + transition: background-color .3s linear, color .3s linear; + } .bb-region-header { width: 400px; diff --git a/src/components/BootstrapBlazor.Region/Components/SelectRegionBase.cs b/src/components/BootstrapBlazor.Region/Components/SelectRegionBase.cs new file mode 100644 index 00000000..3aa2f320 --- /dev/null +++ b/src/components/BootstrapBlazor.Region/Components/SelectRegionBase.cs @@ -0,0 +1,96 @@ +// Copyright (c) BootstrapBlazor & Argo Zhang (argo@live.ca). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// SelectRegion 组件基类 +/// +public abstract class SelectRegionBase : PopoverSelectBase +{ + /// + /// Gets or sets the placeholder text. + /// + [Parameter] + public string? PlaceHolder { get; set; } + + /// + /// Gets or sets the color. The default is (no color). + /// + [Parameter] + public Color Color { get; set; } + + /// + /// Gets or sets the dropdown icon. The default is "fa-solid fa-angle-up". + /// + [Parameter] + [NotNull] + public string? DropdownIcon { get; set; } + + /// + /// Gets or sets the callback method when the clear button is clicked. Default is null. + /// + [Parameter] + public Func? OnClearAsync { get; set; } + + /// + /// Gets or sets the right-side clear icon. Default is fa-solid fa-angle-up. + /// + [Parameter] + [NotNull] + public string? ClearIcon { get; set; } + + /// + /// Gets or sets the service instance. + /// + [Inject] + [NotNull] + protected IIconTheme? IconTheme { get; set; } + + /// + /// service instance + /// + [Inject] + [NotNull] + protected IRegionService? RegionService { get; set; } + + /// + /// 文本框样式 + /// + protected string? InputClassString => CssBuilder.Default("form-select form-control") + .AddClass($"border-{Color.ToDescriptionString()}", Color != Color.None && !IsDisabled && !IsValid.HasValue) + .AddClass($"border-success", IsValid.HasValue && IsValid.Value) + .AddClass($"border-danger", IsValid.HasValue && !IsValid.Value) + .AddClass(CssClass).AddClass(ValidCss) + .Build(); + + /// + /// 下拉框按钮样式 + /// + protected string? AppendClassString => CssBuilder.Default("form-select-append") + .AddClass($"text-{Color.ToDescriptionString()}", Color != Color.None && !IsDisabled && !IsValid.HasValue) + .AddClass($"text-success", IsValid.HasValue && IsValid.Value) + .AddClass($"text-danger", IsValid.HasValue && !IsValid.Value) + .Build(); + + /// + /// 清空按钮样式 + /// + protected string? ClearClassString => CssBuilder.Default("clear-icon") + .AddClass($"text-{Color.ToDescriptionString()}", Color != Color.None) + .AddClass($"text-success", IsValid.HasValue && IsValid.Value) + .AddClass($"text-danger", IsValid.HasValue && !IsValid.Value) + .Build(); + + /// + /// + /// + protected override void OnParametersSet() + { + base.OnParametersSet(); + + DropdownIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectDropdownIcon); + ClearIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectClearIcon); + } +}