diff --git a/src/Masa.Blazor/Components/ExpansionPanels/MExpansionPanel.razor b/src/Masa.Blazor/Components/ExpansionPanels/MExpansionPanel.razor index 10a9c57f1d..159cdfed03 100644 --- a/src/Masa.Blazor/Components/ExpansionPanels/MExpansionPanel.razor +++ b/src/Masa.Blazor/Components/ExpansionPanels/MExpansionPanel.razor @@ -1,5 +1,5 @@ @namespace Masa.Blazor -@inherits MGroupItem +@inherits Masa.Blazor.Components.ItemGroup.MGroupableBase
+namespace Masa.Blazor; + +public partial class MExpansionPanel : MGroupableBase { public MExpansionPanel() : base(GroupType.ExpansionPanels, true) { @@ -15,7 +17,10 @@ public MExpansionPanel() : base(GroupType.ExpansionPanels, true) [Parameter] [MasaApiParameter(ReleasedOn = "v1.6.0")] public string? Text { get; set; } - + + [Parameter] + public RenderFragment? ChildContent { get; set; } + public bool NextActive => ItemGroup != null && ItemGroup.NextActiveKeys.Contains(Value); public bool IsDisabled => ItemGroup != null && (ItemGroup.Disabled || Disabled); @@ -32,13 +37,6 @@ public async Task Toggle() private static Block _block = new("m-expansion-panel"); private ModifierBuilder _modifierBuilder = _block.CreateModifierBuilder(); - protected override void OnParametersSet() - { - base.OnParametersSet(); - - ActiveClass ??= "m-item--active"; - } - protected override IEnumerable BuildComponentClass() { yield return _modifierBuilder diff --git a/src/Masa.Blazor/Components/ItemGroup/MGroupItem.cs b/src/Masa.Blazor/Components/ItemGroup/MGroupItem.cs index 88f705614c..64bce0eb17 100644 --- a/src/Masa.Blazor/Components/ItemGroup/MGroupItem.cs +++ b/src/Masa.Blazor/Components/ItemGroup/MGroupItem.cs @@ -1,5 +1,4 @@ using Masa.Blazor.Components.ItemGroup; -using Masa.Blazor.Mixins; namespace Masa.Blazor; diff --git a/src/Masa.Blazor/Components/ItemGroup/MGroupable.cs b/src/Masa.Blazor/Components/ItemGroup/MGroupable.cs index a965f40d83..fd708ff9f3 100644 --- a/src/Masa.Blazor/Components/ItemGroup/MGroupable.cs +++ b/src/Masa.Blazor/Components/ItemGroup/MGroupable.cs @@ -1,201 +1,25 @@ namespace Masa.Blazor.Components.ItemGroup; -public abstract class MGroupable : MasaComponentBase, IGroupable +/// +/// The base class for groupable components. +/// The active state can be controlled by its parent group component or IsActive property. +/// +/// +public abstract class MGroupable : MGroupableBase, IGroupable where TGroup : MItemGroupBase { - [CascadingParameter] - public TGroup? ItemGroup { get; set; } - - [Parameter] - public string? ActiveClass { get; set; } - - [Parameter] - public virtual bool Disabled { get; set; } - - // TODO: use Value instead of IsActive - [Parameter] - public bool IsActive - { - get => _isActive ?? false; - set => _isActive = value; - } - - [Parameter] - public StringNumber? Value - { - get => _value; - set - { - if (value == null) return; - - _value = value; - } - } - - /// - /// whether to enable bootable. - /// - private readonly bool _bootable; - - /// - /// the of the groupable component. - /// - private readonly GroupType _groupType; - - private bool? _isActive; - private StringNumber? _value; - private bool _firstRenderAfterBooting; - - /// - /// Initializes a base component with the . - /// - /// the of the groupable component. - protected MGroupable(GroupType groupType) - { - _groupType = groupType; - } - - /// - /// Initializes a base component with the - /// and specifies whether to bootable. - /// - /// the of the groupable component. - /// determines whether bootable is enabled or not. - protected MGroupable(GroupType groupType, bool bootable) : this(groupType) - { - _bootable = bootable; - } - - protected string? ComputedActiveClass => ActiveClass ?? ItemGroup?.ActiveClass; - - /// - /// Determines whether the component has a routable ancestor component. - /// - protected virtual bool HasRoutableAncestor => ItemGroup is IAncestorRoutable { Routable: true }; - - /// - /// The routable ancestor. - /// - protected IAncestorRoutable? RoutableAncestor => HasRoutableAncestor ? (IAncestorRoutable)ItemGroup! : null; - - protected bool Matched => ItemGroup != null && (ItemGroup.GroupType == _groupType); - - protected bool ValueMatched => Matched && ItemGroup!.InternalValues.Contains(Value); - - public bool InternalIsActive { get; private set; } - - /// - /// Determines whether the component has been booted. - /// - protected bool IsBooted { get; private set; } - - protected virtual bool IsEager { get; } = false; - - protected virtual bool HasTransition { get; } = false; - - protected override async Task OnInitializedAsync() + protected MGroupable(GroupType groupType) : base(groupType) { - if (!Matched) return; - - if (this is IGroupable item) - { - await ItemGroup!.Register(item); - } - - await base.OnInitializedAsync(); } - protected override async Task OnParametersSetAsync() + protected MGroupable(GroupType groupType, bool bootable) : base(groupType, bootable) { - if (_isActive.HasValue) // if setting by [Parameter]IsActive, Matched is not required. - { - await SetInternalIsActive(_isActive.Value); - } - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - await base.OnAfterRenderAsync(firstRender); - - if (_firstRenderAfterBooting) - { - await Task.Delay(16); - _firstRenderAfterBooting = false; - - InternalIsActive = true; - StateHasChanged(); - } - } - - public async Task RefreshState() - { - if (!Matched || HasRoutableAncestor) return; - - await SetInternalIsActive(ValueMatched); } - protected async Task ToggleAsync() - { - if (!Matched) return; - - await ItemGroup!.ToggleAsync(Value); - } - - protected async Task SetInternalIsActive(bool val, bool force = false) - { - if (IsEager) - { - if (InternalIsActive != val || force) - { - InternalIsActive = val; - StateHasChanged(); - } - } - else - { - if (!IsBooted) - { - if (val) - { - IsBooted = true; - - if (HasTransition) - { - _firstRenderAfterBooting = true; - - await Task.Delay(16); - } - else - { - InternalIsActive = true; - } - - StateHasChanged(); - } - } - else if (InternalIsActive != val || force) - { - if (_firstRenderAfterBooting) - { - // waiting for one frame(16ms) to make sure the element has been rendered, - // and then set the InternalIsActive to be true to invoke transition. - await Task.Delay(16); - _firstRenderAfterBooting = false; - } - - InternalIsActive = val; - StateHasChanged(); - } - } - } - - protected override async ValueTask DisposeAsyncCore() + [Parameter] + public bool IsActive { - if (Matched && this is IGroupable item) - { - ItemGroup!.Unregister(item); - } - - await base.DisposeAsyncCore(); + get => UserActive ?? false; + set => UserActive = value; } } \ No newline at end of file diff --git a/src/Masa.Blazor/Components/ItemGroup/MGroupableBase.cs b/src/Masa.Blazor/Components/ItemGroup/MGroupableBase.cs new file mode 100644 index 0000000000..017d66902b --- /dev/null +++ b/src/Masa.Blazor/Components/ItemGroup/MGroupableBase.cs @@ -0,0 +1,192 @@ +namespace Masa.Blazor.Components.ItemGroup; + +/// +/// A base class for groupable components. +/// The active state is must be controlled by its parent group component. +/// +/// +public abstract class MGroupableBase : MasaComponentBase, IGroupable + where TGroup : MItemGroupBase +{ + [CascadingParameter] + public TGroup? ItemGroup { get; set; } + + [Parameter] + public string? ActiveClass { get; set; } + + [Parameter] + public virtual bool Disabled { get; set; } + + [Parameter] + public StringNumber? Value + { + get => _value; + set + { + if (value == null) return; + + _value = value; + } + } + + /// + /// the of the groupable component. + /// + private readonly GroupType _groupType; + + protected bool? UserActive; + private StringNumber? _value; + private bool _firstRenderAfterBooting; + + /// + /// Initializes a base component with the . + /// + /// the of the groupable component. + protected MGroupableBase(GroupType groupType) + { + _groupType = groupType; + } + + /// + /// Initializes a base component with the + /// and specifies whether to bootable. + /// + /// the of the groupable component. + /// determines whether bootable is enabled or not. + protected MGroupableBase(GroupType groupType, bool bootable) : this(groupType) + { + } + + protected string? ComputedActiveClass => ActiveClass ?? ItemGroup?.ActiveClass; + + /// + /// Determines whether the component has a routable ancestor component. + /// + protected virtual bool HasRoutableAncestor => ItemGroup is IAncestorRoutable { Routable: true }; + + /// + /// The routable ancestor. + /// + protected IAncestorRoutable? RoutableAncestor => HasRoutableAncestor ? (IAncestorRoutable)ItemGroup! : null; + + protected bool Matched => ItemGroup != null && (ItemGroup.GroupType == _groupType); + + protected bool ValueMatched => Matched && ItemGroup!.InternalValues.Contains(Value); + + public bool InternalIsActive { get; private set; } + + /// + /// Determines whether the component has been booted. + /// + protected bool IsBooted { get; private set; } + + protected virtual bool IsEager { get; } = false; + + protected virtual bool HasTransition { get; } = false; + + protected override async Task OnInitializedAsync() + { + if (!Matched) return; + + if (this is IGroupable item) + { + await ItemGroup!.Register(item); + } + + await base.OnInitializedAsync(); + } + + protected override async Task OnParametersSetAsync() + { + if (UserActive.HasValue) // if setting by [Parameter]IsActive, Matched is not required. + { + await SetInternalIsActive(UserActive.Value); + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (_firstRenderAfterBooting) + { + await Task.Delay(16); + _firstRenderAfterBooting = false; + + InternalIsActive = true; + StateHasChanged(); + } + } + + public async Task RefreshState() + { + if (!Matched || HasRoutableAncestor) return; + + await SetInternalIsActive(ValueMatched); + } + + protected async Task ToggleAsync() + { + if (!Matched) return; + + await ItemGroup!.ToggleAsync(Value); + } + + protected async Task SetInternalIsActive(bool val, bool force = false) + { + if (IsEager) + { + if (InternalIsActive != val || force) + { + InternalIsActive = val; + StateHasChanged(); + } + } + else + { + if (!IsBooted) + { + if (val) + { + IsBooted = true; + + if (HasTransition) + { + _firstRenderAfterBooting = true; + + await Task.Delay(16); + } + else + { + InternalIsActive = true; + } + + StateHasChanged(); + } + } + else if (InternalIsActive != val || force) + { + if (_firstRenderAfterBooting) + { + // waiting for one frame(16ms) to make sure the element has been rendered, + // and then set the InternalIsActive to be true to invoke transition. + await Task.Delay(16); + _firstRenderAfterBooting = false; + } + + InternalIsActive = val; + StateHasChanged(); + } + } + } + + protected override async ValueTask DisposeAsyncCore() + { + if (Matched && this is IGroupable item) + { + ItemGroup!.Unregister(item); + } + + await base.DisposeAsyncCore(); + } +} \ No newline at end of file diff --git a/src/Masa.Blazor/Components/ItemGroup/MItemGroupBase.cs b/src/Masa.Blazor/Components/ItemGroup/MItemGroupBase.cs index f1e6840f1f..9cf66e1541 100644 --- a/src/Masa.Blazor/Components/ItemGroup/MItemGroupBase.cs +++ b/src/Masa.Blazor/Components/ItemGroup/MItemGroupBase.cs @@ -10,7 +10,8 @@ protected MItemGroupBase(GroupType groupType) } [Parameter] - public string? ActiveClass { get; set; } + [MasaApiParameter("m-item--active")] + public string? ActiveClass { get; set; } = "m-item--active"; [Parameter] public RenderFragment? ChildContent { get; set; }