Skip to content
41 changes: 40 additions & 1 deletion DisCatSharp/Entities/Components/DiscordLabelComponent.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Collections.Generic;

using DisCatSharp.Enums;
using DisCatSharp.Net.Serialization;

Expand Down Expand Up @@ -75,6 +77,36 @@ public DiscordLabelComponent WithFileUploadComponent(DiscordFileUploadComponent
return this;
}

/// <summary>
/// Sets the radio group component for the label.
/// </summary>
/// <param name="component">The radio group component to attach to the label.</param>
public DiscordLabelComponent WithRadioGroupComponent(DiscordRadioGroupComponent component)
{
this.Component = component;
return this;
}

/// <summary>
/// Sets the checkbox group component for the label.
/// </summary>
/// <param name="component">The checkbox group component to attach to the label.</param>
public DiscordLabelComponent WithCheckboxGroupComponent(DiscordCheckboxGroupComponent component)
{
this.Component = component;
return this;
}

/// <summary>
/// Sets the checkbox component for the label.
/// </summary>
/// <param name="component">The checkbox component to attach to the label.</param>
public DiscordLabelComponent WithCheckboxComponent(DiscordCheckboxComponent component)
{
this.Component = component;
return this;
}

/// <summary>
/// The label.
/// </summary>
Expand All @@ -94,12 +126,19 @@ public DiscordLabelComponent WithFileUploadComponent(DiscordFileUploadComponent
public ILabelComponent Component { get; internal set; }

/// <summary>
/// Helper to determine whether a <see cref="DiscordTextInputComponent"/> or <see cref="DiscordBaseSelectComponent"/> is attached to the label.
/// Helper to determine the type of component attached to the label (e.g., <see cref="DiscordTextInputComponent"/>, <see cref="DiscordBaseSelectComponent"/>, <see cref="DiscordRadioGroupComponent"/>, <see cref="DiscordCheckboxGroupComponent"/>, <see cref="DiscordCheckboxComponent"/>, or <see cref="DiscordFileUploadComponent"/>).
/// </summary>
[JsonIgnore]
public ComponentType SubComponentType
=> (this.Component as DiscordComponent).Type;

/// <inheritdoc />
public override IEnumerable<DiscordComponent> GetChildren()
{
if (this.Component is DiscordComponent component)
yield return component;
}

/// <summary>
/// Assigns a unique id to this component.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;

using DisCatSharp.Enums;

using Newtonsoft.Json;

namespace DisCatSharp.Entities;

/// <summary>
/// Represents a single checkbox component. Modal-only.
/// </summary>
public sealed class DiscordCheckboxComponent : DiscordComponent, ILabelComponent
{
/// <summary>
/// Creates a new empty checkbox component.
/// </summary>
internal DiscordCheckboxComponent()
{
this.Type = ComponentType.Checkbox;
}

/// <summary>
/// Creates a new checkbox component with the provided options.
/// </summary>
/// <param name="customId">The custom id for this component.</param>
/// <param name="isDefault">Whether the checkbox is checked by default.</param>
public DiscordCheckboxComponent(string? customId = null, bool? isDefault = null)
: this()
{
this.CustomId = customId ?? Guid.NewGuid().ToString();
this.Default = isDefault;
}

/// <summary>
/// The custom id for this component.
/// </summary>
[JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
public override string? CustomId { get; internal set; } = Guid.NewGuid().ToString();

/// <summary>
/// Whether the checkbox is checked by default.
/// </summary>
[JsonProperty("default", NullValueHandling = NullValueHandling.Ignore)]
public bool? Default { get; internal set; }

/// <summary>
/// The submitted value. Present on modal submit interactions.
/// </summary>
[JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
public bool? Value { get; internal set; }

/// <summary>
/// Assigns a unique id to this component.
/// </summary>
/// <param name="id">The id to assign.</param>
public DiscordCheckboxComponent WithId(int id)
{
this.Id = id;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;

using DisCatSharp.Enums;

using Newtonsoft.Json;

namespace DisCatSharp.Entities;

/// <summary>
/// Represents a checkbox group component. Modal-only.
/// </summary>
public sealed class DiscordCheckboxGroupComponent : DiscordComponent, ILabelComponent
{
/// <summary>
/// Creates a new empty checkbox group component.
/// </summary>
internal DiscordCheckboxGroupComponent()
{
this.Type = ComponentType.CheckboxGroup;
}

/// <summary>
/// Creates a new checkbox group component with the provided options.
/// </summary>
/// <param name="options">The selectable options. Must contain between 1 and 10 entries.</param>
/// <param name="customId">The custom id for this component.</param>
/// <param name="minValues">The minimum number of selections. Defaults to 1.</param>
/// <param name="maxValues">The maximum number of selections. Defaults to the number of options.</param>
/// <param name="required">Whether a selection is required.</param>
public DiscordCheckboxGroupComponent(IEnumerable<DiscordCheckboxGroupComponentOption> options, string? customId = null, int? minValues = null, int? maxValues = null, bool? required = null)
: this()
{
ArgumentNullException.ThrowIfNull(options);
var optionList = options.ToList();
if (optionList.Count is < 1 or > 10)
throw new ArgumentException("Checkbox groups must include between 1 and 10 options.");

var minimum = minValues ?? 1;
var maximum = maxValues ?? optionList.Count;

if (minimum is < 0 or > 10)
throw new ArgumentException("Minimum values must be between 0 and 10.", nameof(minValues));
if (maximum is < 1 or > 10)
throw new ArgumentException("Maximum values must be between 1 and 10.", nameof(maxValues));
if (minimum > maximum)
throw new ArgumentException("Minimum values cannot exceed maximum values.");
if (maximum > optionList.Count)
throw new ArgumentException("Maximum values cannot exceed the number of options.");

this.CustomId = customId ?? Guid.NewGuid().ToString();
this.Options = optionList;
this.MinimumValues = minimum;
this.MaximumValues = maximum;
this.Required = required;
}

/// <summary>
/// The custom id for this component.
/// </summary>
[JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
public override string? CustomId { get; internal set; } = Guid.NewGuid().ToString();

/// <summary>
/// The available options.
/// </summary>
[JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
public IReadOnlyList<DiscordCheckboxGroupComponentOption> Options { get; internal set; } = Array.Empty<DiscordCheckboxGroupComponentOption>();

/// <summary>
/// The minimum number of selections.
/// </summary>
[JsonProperty("min_values", NullValueHandling = NullValueHandling.Ignore)]
public int? MinimumValues { get; internal set; } = 0;

/// <summary>
/// The maximum number of selections.
/// </summary>
[JsonProperty("max_values", NullValueHandling = NullValueHandling.Ignore)]
public int? MaximumValues { get; internal set; }

/// <summary>
/// Whether the component requires a selection.
/// </summary>
[JsonProperty("required", NullValueHandling = NullValueHandling.Ignore)]
public bool? Required { get; internal set; }

/// <summary>
/// The submitted values. Present on modal submit interactions.
/// </summary>
[JsonProperty("values", NullValueHandling = NullValueHandling.Ignore)]
public string[]? Values { get; internal set; }

/// <summary>
/// Assigns a unique id to this component.
/// </summary>
/// <param name="id">The id to assign.</param>
public DiscordCheckboxGroupComponent WithId(int id)
{
this.Id = id;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;

using Newtonsoft.Json;

namespace DisCatSharp.Entities;

/// <summary>
/// Represents an option within a checkbox group component.
/// </summary>
public sealed class DiscordCheckboxGroupComponentOption : ObservableApiObject
{
/// <summary>
/// Creates a new checkbox group option.
/// </summary>
/// <param name="label">The display label. Max 100 characters.</param>
/// <param name="value">The option value. Max 100 characters.</param>
/// <param name="description">An optional description. Max 100 characters.</param>
/// <param name="isDefault">Whether this option should be selected by default.</param>
public DiscordCheckboxGroupComponentOption(string label, string value, string? description = null, bool isDefault = false)
{
if (string.IsNullOrWhiteSpace(label))
throw new ArgumentException("Label must be provided.", nameof(label));
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("Value must be provided.", nameof(value));
if (label.Length > 100)
throw new ArgumentException("Option label cannot exceed 100 characters.", nameof(label));
if (value.Length > 100)
throw new ArgumentException("Option value cannot exceed 100 characters.", nameof(value));
if (description is { Length: > 100 })
throw new ArgumentException("Option description cannot exceed 100 characters.", nameof(description));

this.Label = label;
this.Value = value;
this.Description = description;
this.Default = isDefault;
}

/// <summary>
/// The display label.
/// </summary>
[JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
public string Label { get; internal set; }

/// <summary>
/// The underlying value returned on submit.
/// </summary>
[JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
public string Value { get; internal set; }

/// <summary>
/// Optional helper text.
/// </summary>
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
public string? Description { get; internal set; }

/// <summary>
/// Whether this option is pre-selected.
/// </summary>
[JsonProperty("default", NullValueHandling = NullValueHandling.Ignore)]
public bool Default { get; internal set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;

using DisCatSharp.Enums;

using Newtonsoft.Json;

namespace DisCatSharp.Entities;

/// <summary>
/// Represents a radio group component. Modal-only.
/// </summary>
public sealed class DiscordRadioGroupComponent : DiscordComponent, ILabelComponent
{
/// <summary>
/// Creates a new empty radio group component.
/// </summary>
internal DiscordRadioGroupComponent()
{
this.Type = ComponentType.RadioGroup;
}

/// <summary>
/// Creates a new radio group component with the provided options.
/// </summary>
/// <param name="options">The selectable options. Must contain between 2 and 10 entries.</param>
/// <param name="customId">The custom id for this component.</param>
/// <param name="required">Whether a selection is required.</param>
public DiscordRadioGroupComponent(IEnumerable<DiscordRadioGroupComponentOption> options, string? customId = null, bool? required = null)
: this()
{
ArgumentNullException.ThrowIfNull(options);
var optionList = options.ToList();
if (optionList.Count is < 2 or > 10)
throw new ArgumentException("Radio groups must include between 2 and 10 options.");
if (optionList.Count(x => x.Default) > 1)
throw new ArgumentException("Only one radio option can be marked as default.");

this.CustomId = customId ?? Guid.NewGuid().ToString();
this.Options = optionList;
this.Required = required;
}

/// <summary>
/// The custom id for this component.
/// </summary>
[JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
public override string? CustomId { get; internal set; } = Guid.NewGuid().ToString();

/// <summary>
/// The available options.
/// </summary>
[JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
public IReadOnlyList<DiscordRadioGroupComponentOption> Options { get; internal set; } = Array.Empty<DiscordRadioGroupComponentOption>();

/// <summary>
/// Whether the component requires a selection.
/// </summary>
[JsonProperty("required", NullValueHandling = NullValueHandling.Ignore)]
public bool? Required { get; internal set; }

/// <summary>
/// The submitted value. Present on modal submit interactions.
/// </summary>
[JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
public string? SelectedValue { get; internal set; }

/// <summary>
/// Assigns a unique id to this component.
/// </summary>
/// <param name="id">The id to assign.</param>
public DiscordRadioGroupComponent WithId(int id)
{
this.Id = id;
return this;
}
}
Loading
Loading