Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/BootstrapBlazor/Components/Affix/Affix.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public partial class Affix
.Build();

private string? StyleString => CssBuilder.Default("position: sticky;")
.AddClass($"z-index: {ZIndex};", ZIndex.HasValue)
.AddClass($"{Position.ToDescriptionString()}: {Offset}px;")
.AddStyle("z-index", $"{ZIndex}", ZIndex.HasValue)
.AddStyle($"{Position.ToDescriptionString()}", $"{Offset}px")
.AddStyleFromAttributes(AdditionalAttributes)
.Build();
}
6 changes: 3 additions & 3 deletions src/BootstrapBlazor/Components/Captcha/Captcha.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ public partial class Captcha
/// 获得 组件宽度
/// </summary>
private string? StyleString => CssBuilder.Default()
.AddClass($"width: {Width + 42}px;", Width > 0)
.AddStyle("width", $"{Width + 42}px", Width > 0)
.Build();

/// <summary>
/// 获得 加载图片失败样式
/// </summary>
private string? FailedStyle => CssBuilder.Default()
.AddClass($"width: {Width}px;", Width > 0)
.AddClass($"height: {Height}px;", Height > 0)
.AddStyle("width", $"{Width}px", Width > 0)
.AddStyle("height", $"{Height}px", Height > 0)
.Build();

/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions src/BootstrapBlazor/Components/Drawer/Drawer.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ public partial class Drawer
.Build();

private string? StyleString => CssBuilder.Default()
.AddClass($"--bb-drawer-position: {Position};", !string.IsNullOrEmpty(Position))
.AddStyle("--bb-drawer-position", $"{Position}", !string.IsNullOrEmpty(Position))
.AddStyleFromAttributes(AdditionalAttributes)
.Build();

/// <summary>
/// 获得 抽屉 Style 字符串
/// </summary>
private string? DrawerStyleString => CssBuilder.Default()
.AddClass($"--bb-drawer-width: {Width};", !string.IsNullOrEmpty(Width) && Placement != Placement.Top && Placement != Placement.Bottom)
.AddClass($"--bb-drawer-height: {Height};", !string.IsNullOrEmpty(Height) && (Placement == Placement.Top || Placement == Placement.Bottom))
.AddStyle("--bb-drawer-width", $"{Width}", !string.IsNullOrEmpty(Width) && Placement != Placement.Top && Placement != Placement.Bottom)
.AddStyle("--bb-drawer-height", $"{Height}", !string.IsNullOrEmpty(Height) && (Placement == Placement.Top || Placement == Placement.Bottom))
.Build();

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public partial class EditorForm<TModel> : IShowLabel
.Build();

private string? FormStyleString => CssBuilder.Default()
.AddClass($"--bb-row-label-width: {LabelWidth}px;", LabelWidth.HasValue)
.AddStyle("--bb-row-label-width", $"{LabelWidth}px", LabelWidth.HasValue)
.Build();

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public partial class BootstrapLabel
.Build();

private string? StyleString => CssBuilder.Default()
.AddClass($"--bb-row-label-width: {LabelWidth}px;", LabelWidth.HasValue)
.AddStyle($"--bb-row-label-width", $"{LabelWidth}px", LabelWidth.HasValue)
.Build();

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public partial class ValidateForm
private string? ShowAllInvalidResultString => ShowAllInvalidResult ? "true" : null;

private string? StyleString => CssBuilder.Default()
.AddClass($"--bb-row-label-width: {LabelWidth}px;", LabelWidth.HasValue)
.AddStyle("--bb-row-label-width", $"{LabelWidth}px", LabelWidth.HasValue)
.Build();

/// <summary>
Expand Down
66 changes: 65 additions & 1 deletion src/BootstrapBlazor/Utils/CssBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,71 @@ public CssBuilder AddClassFromAttributes(IDictionary<string, object>? additional
}

/// <summary>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider simplifying the AddStyle overloads by using a parameter object to reduce the number of methods and centralize the logic.

The multiple AddStyle overloads can be simplified into a more maintainable pattern using a parameter object approach. This reduces complexity while maintaining all functionality:

public record StyleOptions
{
    public string Key { get; init; } = "";
    public string? Value { get; init; }
    public Func<string?>? ValueFactory { get; init; }
    public bool? When { get; init; }
    public Func<bool>? WhenFactory { get; init; }
}

public CssBuilder AddStyle(StyleOptions options)
{
    if (ShouldAddStyle(options))
    {
        var value = GetStyleValue(options);
        if (!string.IsNullOrEmpty(value))
            stringBuffer.Add($"{options.Key}: {value};");
    }
    return this;
}

// Convenience overload for the most common case
public CssBuilder AddStyle(string key, string? value, bool when = true) => 
    AddStyle(new StyleOptions { Key = key, Value = value, When = when });

private static bool ShouldAddStyle(StyleOptions options) =>
    (options.WhenFactory?.Invoke() ?? options.When ?? true);

private static string? GetStyleValue(StyleOptions options) =>
    options.ValueFactory?.Invoke() ?? options.Value;

This approach:

  • Reduces the number of overloads from 8 to 2
  • Makes the API more extensible for future additions
  • Maintains all current functionality
  • Improves maintainability with centralized logic
  • Keeps the simple cases simple while supporting complex scenarios

/// Adds a conditional Style when it exists in a dictionary to the builder with space separator.
/// Adds a raw string to the builder that will be concatenated with the next style or value added to the builder.
/// </summary>
/// <param name="key">style property name</param>
/// <param name="value">CSS style to conditionally add.</param>
/// <returns>CssBuilder</returns>
public CssBuilder AddStyle(string key, string? value)
{
if (!string.IsNullOrEmpty(value)) stringBuffer.Add($"{key}: {value};");
return this;
}

/// <summary>
/// Adds a conditional css Style to the builder with space separator.
/// </summary>
/// <param name="key">style property name</param>
/// <param name="value">CSS style to conditionally add.</param>
/// <param name="when">Condition in which the CSS style is added.</param>
/// <returns>CssBuilder</returns>
public CssBuilder AddStyle(string key, string? value, bool when = true) => when ? AddStyle(key, value) : this;

/// <summary>
/// Adds a conditional css Style to the builder with space separator.
/// </summary>
/// <param name="key">style property name</param>
/// <param name="value">CSS style to conditionally add.</param>
/// <param name="when">Condition in which the CSS Style is added.</param>
/// <returns>CssBuilder</returns>
public CssBuilder AddStyle(string key, string? value, Func<bool> when) => AddStyle(key, value, when());

/// <summary>
/// Adds a conditional css Style to the builder with space separator.
/// </summary>
/// <param name="key">style property name</param>
/// <param name="value">Function that returns a css Style to conditionally add.</param>
/// <param name="when">Condition in which the CSS Style is added.</param>
/// <returns>CssBuilder</returns>
public CssBuilder AddStyle(string key, Func<string?> value, bool when = true) => when ? AddStyle(key, value()) : this;

/// <summary>
/// Adds a conditional css Style to the builder with space separator.
/// </summary>
/// <param name="key">style property name</param>
/// <param name="value">Function that returns a css Style to conditionally add.</param>
/// <param name="when">Condition in which the CSS Style is added.</param>
/// <returns>CssBuilder</returns>
public CssBuilder AddStyle(string key, Func<string?> value, Func<bool> when) => AddStyle(key, value, when());

/// <summary>
/// Adds a conditional nested CssBuilder to the builder with space separator.
/// </summary>
/// <param name="builder">CSS Style to conditionally add.</param>
/// <param name="when">Condition in which the CSS Style is added.</param>
/// <returns>CssBuilder</returns>
public CssBuilder AddStyle(CssBuilder builder, bool when = true) => when ? AddClass(builder.Build()) : this;

/// <summary>
/// Adds a conditional CSS Class to the builder with space separator.
/// </summary>
/// <param name="builder">CSS Class to conditionally add.</param>
/// <param name="when">Condition in which the CSS Class is added.</param>
/// <returns>CssBuilder</returns>
public CssBuilder AddStyle(CssBuilder builder, Func<bool> when) => AddClass(builder, when());

/// <summary>
/// Adds a conditional css Style when it exists in a dictionary to the builder with space separator.
/// Null safe operation.
/// </summary>
/// <param name="additionalAttributes">Additional Attribute splat parameters</param>
Expand Down
66 changes: 66 additions & 0 deletions test/UnitTest/Utils/CssBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,51 @@ public void AddClass_When()
Assert.Contains("cls_test", classString);
}

[Fact]
public void AddStyle_When()
{
var classString = CssBuilder.Default()
.AddStyle("width", () => "cls_test", () => false)
.Build();
Assert.DoesNotContain("widht: cls_test;", classString);

classString = CssBuilder.Default()
.AddStyle("width", () => "cls_test", false)
.Build();
Assert.DoesNotContain("widht: cls_test;", classString);

classString = CssBuilder.Default()
.AddStyle("width", "cls_test", false)
.Build();
Assert.DoesNotContain("widht: cls_test;", classString);

classString = CssBuilder.Default()
.AddStyle("width", () => "cls_test", () => true)
.Build();
Assert.Contains("width: cls_test;", classString);

classString = CssBuilder.Default()
.AddStyle("width", () => "cls_test", true)
.Build();
Assert.Contains("width: cls_test;", classString);

classString = CssBuilder.Default()
.AddStyle("width", "cls_test", true)
.Build();
Assert.Contains("width: cls_test;", classString);

classString = CssBuilder.Default()
.AddStyle("width", "cls_test", () => true)
.Build();
Assert.Contains("width: cls_test;", classString);

classString = CssBuilder.Default()
.AddStyle("width", "cls_test_width")
.AddStyle("height", "cls_test_height")
.Build();
Assert.Equal("width: cls_test_width; height: cls_test_height;", classString);
}

[Fact]
public void AddClass_Builder()
{
Expand All @@ -37,6 +82,27 @@ public void AddClass_Builder()
Assert.Contains("cls_test", classString);
}

[Fact]
public void AddStyle_Builder()
{
var builder = CssBuilder.Default("width: cls_test_width;");

var classString = CssBuilder.Default()
.AddStyle(builder, false)
.Build();
Assert.DoesNotContain("width: cls_test_width;", classString);

classString = CssBuilder.Default()
.AddStyle(builder, () => true)
.Build();
Assert.Contains("width: cls_test_width;", classString);

classString = CssBuilder.Default()
.AddStyle(builder, true)
.Build();
Assert.Contains("width: cls_test_width;", classString);
}

[Fact]
public void AddStyleFromAttributes_Ok()
{
Expand Down