Adding a ButtonGroup component to group button children together

This commit is contained in:
krut_ni 2026-03-10 16:12:00 +01:00
parent 2ccb72c77d
commit dbdcdef83c
No known key found for this signature in database
GPG Key ID: A5C0151B4DDB172C
8 changed files with 186 additions and 1 deletions

View File

@ -103,7 +103,42 @@
Class='@MergeClass(button.Class, "mb-3")'
Style="@this.GetOptionalStyle(button.Style)">
@button.Text
</MudButton></div>
</MudButton></div>
}
break;
case AssistantComponentType.BUTTON_GROUP:
if (component is AssistantButtonGroup assistantButtonGroup)
{
var buttonGroup = assistantButtonGroup;
<MudButtonGroup Variant="@buttonGroup.GetVariant()"
Color="@AssistantComponentPropHelper.GetColor(buttonGroup.Color, Color.Default)"
Size="@AssistantComponentPropHelper.GetComponentSize(buttonGroup.Size, Size.Medium)"
OverrideStyles="@buttonGroup.OverrideStyles"
Vertical="@buttonGroup.Vertical"
DropShadow="@buttonGroup.DropShadow"
Class='@MergeClass(buttonGroup.Class, "mb-3")'
Style="@this.GetOptionalStyle(buttonGroup.Style)">
@foreach (var child in buttonGroup.Children)
{
if (child is AssistantButton childButton)
{
<MudButton Variant="@childButton.GetButtonVariant()"
Color="@AssistantComponentPropHelper.GetColor(childButton.Color, Color.Default)"
OnClick="@(() => this.ExecuteButtonActionAsync(childButton))"
Size="@AssistantComponentPropHelper.GetComponentSize(childButton.Size, Size.Medium)"
FullWidth="@childButton.IsFullWidth"
StartIcon="@AssistantComponentPropHelper.GetIconSvg(childButton.StartIcon)"
EndIcon="@AssistantComponentPropHelper.GetIconSvg(childButton.EndIcon)"
IconColor="@AssistantComponentPropHelper.GetColor(childButton.IconColor, Color.Inherit)"
IconSize="@AssistantComponentPropHelper.GetComponentSize(childButton.IconSize, Size.Medium)"
Disabled="@this.IsButtonActionRunning(childButton.Name)"
Class="@childButton.Class"
Style="@this.GetOptionalStyle(childButton.Style)">
@childButton.Text
</MudButton>
}
}
</MudButtonGroup>
}
break;
case AssistantComponentType.PROVIDER_SELECTION:

View File

@ -12,6 +12,7 @@ Supported types (matching the Blazor UI components):
- `TEXT_AREA`: user input field based on `MudTextField`; requires `Name`, `Label`, and may include `HelperText`, `HelperTextOnFocus`, `Adornment`, `AdornmentIcon`, `AdornmentText`, `AdornmentColor`, `Counter`, `MaxLength`, `IsImmediate`, `UserPrompt`, `PrefillText`, `IsSingleLine`, `ReadOnly`, `Class`, `Style`.
- `DROPDOWN`: selects between variants; `Props` must include `Name`, `Label`, `Default`, `Items`, and optionally `ValueType` plus `UserPrompt`.
- `BUTTON`: invokes a Lua callback; `Props` must include `Name`, `Text`, `Action`, and may include `Variant`, `Color`, `IsFullWidth`, `Size`, `StartIcon`, `EndIcon`, `IconColor`, `IconSize`, `Class`, `Style`.
- `BUTTON_GROUP`: groups multiple `BUTTON` children in a `MudButtonGroup`; `Children` must contain only `BUTTON` components and `Props` may include `Variant`, `Color`, `Size`, `OverrideStyles`, `Vertical`, `DropShadow`, `Class`, `Style`.
- `SWITCH`: boolean option; requires `Name`, `Label`, `Value`, and may include `Disabled`, `UserPrompt`, `LabelOn`, `LabelOff`, `LabelPlacement`, `Icon`, `IconColor`, `CheckedColor`, `UncheckedColor`, `Class`, `Style`.
- `COLOR_PICKER`: color input based on `MudColorPicker`; requires `Name`, `Label`, and may include `Placeholder`, `ShowAlpha`, `ShowToolbar`, `ShowModeSwitch`, `PickerVariant`, `UserPrompt`, `Class`, `Style`.
- `PROVIDER_SELECTION` / `PROFILE_SELECTION`: hooks into the shared provider/profile selectors.
@ -221,6 +222,64 @@ Example:
}
```
### `BUTTON_GROUP` reference
- Use `Type = "BUTTON_GROUP"` to render multiple `BUTTON` children as a single MudBlazor button group.
- Required structure:
- `Children`: array of `BUTTON` component tables. Other child component types are ignored.
- Optional props:
- `Variant`: one of the MudBlazor `Variant` enum names such as `Filled`, `Outlined`, `Text`; omitted values fall back to `Filled`.
- `Color`: one of the MudBlazor `Color` enum names such as `Default`, `Primary`, `Secondary`, `Info`; omitted values fall back to `Default`.
- `Size`: one of the MudBlazor `Size` enum names such as `Small`, `Medium`, `Large`; omitted values fall back to `Medium`.
- `OverrideStyles`: defaults to `false`; enables MudBlazor button-group style overrides.
- `Vertical`: defaults to `false`; when `true`, buttons are rendered vertically instead of horizontally.
- `DropShadow`: defaults to `true`; controls the group shadow.
- `Class`, `Style`: forwarded to the rendered `MudButtonGroup` for layout/styling.
- Child buttons use the existing `BUTTON` props and behavior, including Lua `Action(input)`.
Example:
```lua
{
["Type"] = "BUTTON_GROUP",
["Props"] = {
["Variant"] = "Filled",
["Color"] = "Primary",
["Size"] = "Medium",
["OverrideStyles"] = false,
["Vertical"] = false,
["DropShadow"] = true
},
["Children"] = {
{
["Type"] = "BUTTON",
["Props"] = {
["Name"] = "buildEmailOutput",
["Text"] = "Build output",
["Action"] = function(input)
return {
fields = {
outputBuffer = input.fields.emailContent or ""
}
}
end,
["StartIcon"] = "Icons.Material.Filled.Build"
}
},
{
["Type"] = "BUTTON",
["Props"] = {
["Name"] = "logColor",
["Text"] = "Log color",
["Action"] = function(input)
LogError("ColorPicker value: " .. tostring(input.fields.colorPicker or ""))
return nil
end,
["EndIcon"] = "Icons.Material.Filled.BugReport"
}
}
}
}
```
### `SWITCH` reference
- Use `Type = "SWITCH"` to render a boolean toggle.
- Required props:

View File

@ -152,6 +152,22 @@ ASSISTANT = {
["Style"] = "<optional css styles>",
}
},
{
["Type"] = "BUTTON_GROUP",
["Props"] = {
["Variant"] = "<Filled|Outlined|Text>", -- display variation of the group. Defaults to Filled
["Color"] = "<Dark|Error|Info|Inherit|Primary|Secondary|Success|Surface|Tertiary|Transparent|Warning>", -- color of the group. Defaults to Default
["Size"] = "<Small|Medium|Large>", -- size of the group. Defaults to Medium
["OverrideStyles"] = false, -- allows MudBlazor group style overrides. Defaults to false
["Vertical"] = false, -- renders buttons vertically instead of horizontally. Defaults to false
["DropShadow"] = true, -- applies a group shadow. Defaults to true
["Class"] = "<optional MudBlazor or css classes>",
["Style"] = "<optional css styles>",
},
["Children"] = {
-- BUTTON_ELEMENTS
}
},
{
["Type"] = "PROVIDER_SELECTION", -- required
["Props"] = {

View File

@ -19,6 +19,8 @@ public class AssistantComponentFactory
return new AssistantTextArea { Props = props, Children = children };
case AssistantComponentType.BUTTON:
return new AssistantButton { Props = props, Children = children};
case AssistantComponentType.BUTTON_GROUP:
return new AssistantButtonGroup { Props = props, Children = children };
case AssistantComponentType.DROPDOWN:
return new AssistantDropdown { Props = props, Children = children };
case AssistantComponentType.PROVIDER_SELECTION:

View File

@ -0,0 +1,58 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public sealed class AssistantButtonGroup : AssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.BUTTON_GROUP;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Variant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Variant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Variant), value);
}
public string Color
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Color));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Color), value);
}
public string Size
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Size));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Size), value);
}
public bool OverrideStyles
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.OverrideStyles), false);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.OverrideStyles), value);
}
public bool Vertical
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.Vertical), false);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.Vertical), value);
}
public bool DropShadow
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.DropShadow), true);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.DropShadow), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
public Variant GetVariant() => Enum.TryParse<Variant>(this.Variant, out var variant) ? variant : MudBlazor.Variant.Filled;
}

View File

@ -5,6 +5,7 @@ public enum AssistantComponentType
FORM,
TEXT_AREA,
BUTTON,
BUTTON_GROUP,
DROPDOWN,
PROVIDER_SELECTION,
PROFILE_SELECTION,

View File

@ -24,6 +24,10 @@ public static class ComponentPropSpecs
"StartIcon", "EndIcon", "IconColor", "IconSize", "Class", "Style"
]
),
[AssistantComponentType.BUTTON_GROUP] = new(
required: [],
optional: ["Variant", "Color", "Size", "OverrideStyles", "Vertical", "DropShadow", "Class", "Style"]
),
[AssistantComponentType.DROPDOWN] = new(
required: ["Name", "Label", "Default", "Items"],
optional: ["UserPrompt", "Class", "Style"]

View File

@ -288,6 +288,16 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
textArea.IsImmediate = true;
}
if (component is AssistantButtonGroup buttonGroup)
{
var invalidChildren = buttonGroup.Children.Where(child => child.Type != AssistantComponentType.BUTTON).ToList();
if (invalidChildren.Count > 0)
{
LOGGER.LogWarning("Assistant plugin '{PluginName}' BUTTON_GROUP contains non-BUTTON children. Only BUTTON children are supported and invalid children are ignored.", this.Name);
buttonGroup.Children = buttonGroup.Children.Where(child => child.Type == AssistantComponentType.BUTTON).ToList();
}
}
return true;
}