From 61e940a03f6241865749f077f4fd3867a94ec9f0 Mon Sep 17 00:00:00 2001 From: desmondinho Date: Sat, 22 Feb 2025 14:10:01 +0200 Subject: [PATCH] build: merge LumexUI.Utilities with the main project --- LumexUI.sln | 7 - .../LumexUI.Docs.Client.csproj | 1 - docs/LumexUI.Docs.Client/_Imports.razor | 1 + .../Components/Popover/LumexPopover.razor.cs | 1 + .../Providers/LumexThemeProvider.razor.cs | 1 + .../Components/Textbox/LumexTextbox.razor.cs | 2 +- src/LumexUI/Extensions/EnumExtensions.cs | 25 +++ src/LumexUI/LumexUI.csproj | 14 -- src/LumexUI/Utilities/ElementClass.cs | 135 +++++++++++++ src/LumexUI/Utilities/ElementStyle.cs | 181 ++++++++++++++++++ src/LumexUI/Utilities/Identifier.cs | 30 +++ src/LumexUI/Utilities/Utils.cs | 2 + .../Components/Button/ButtonTests.cs | 2 +- tests/LumexUI.Tests/LumexUI.Tests.csproj | 1 - 14 files changed, 378 insertions(+), 25 deletions(-) create mode 100644 src/LumexUI/Extensions/EnumExtensions.cs create mode 100644 src/LumexUI/Utilities/ElementClass.cs create mode 100644 src/LumexUI/Utilities/ElementStyle.cs create mode 100644 src/LumexUI/Utilities/Identifier.cs diff --git a/LumexUI.sln b/LumexUI.sln index 50310894..dd54c833 100644 --- a/LumexUI.sln +++ b/LumexUI.sln @@ -12,8 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LumexUI.Utilities", "src\LumexUI.Utilities\LumexUI.Utilities.csproj", "{92A1C629-AB3E-4264-B03D-407E09162902}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LumexUI.Docs.Generator", "docs\LumexUI.Docs.Generator\LumexUI.Docs.Generator.csproj", "{157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DBC44B8-5B92-4504-8B8F-91A1672251E6}" @@ -42,10 +40,6 @@ Global {F5DCF265-756B-4B25-AECA-15B9FF2DCB66}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5DCF265-756B-4B25-AECA-15B9FF2DCB66}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5DCF265-756B-4B25-AECA-15B9FF2DCB66}.Release|Any CPU.Build.0 = Release|Any CPU - {92A1C629-AB3E-4264-B03D-407E09162902}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92A1C629-AB3E-4264-B03D-407E09162902}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92A1C629-AB3E-4264-B03D-407E09162902}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92A1C629-AB3E-4264-B03D-407E09162902}.Release|Any CPU.Build.0 = Release|Any CPU {157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C}.Debug|Any CPU.Build.0 = Debug|Any CPU {157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -76,7 +70,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {F5DCF265-756B-4B25-AECA-15B9FF2DCB66} = {5DBC44B8-5B92-4504-8B8F-91A1672251E6} - {92A1C629-AB3E-4264-B03D-407E09162902} = {5DBC44B8-5B92-4504-8B8F-91A1672251E6} {157B8BDD-2EEF-4A69-AEA5-CAA3650CF73C} = {3F4DE3FA-2636-440C-ACCC-137BAD95BAC4} {06E849E9-A8EA-410B-83BC-0366524C4953} = {898610F1-A6C1-422C-84C9-C1E386957F31} {2A341376-2409-422C-A7A9-1AE6E86F91D9} = {3F4DE3FA-2636-440C-ACCC-137BAD95BAC4} diff --git a/docs/LumexUI.Docs.Client/LumexUI.Docs.Client.csproj b/docs/LumexUI.Docs.Client/LumexUI.Docs.Client.csproj index ee6ffd02..ab825da6 100644 --- a/docs/LumexUI.Docs.Client/LumexUI.Docs.Client.csproj +++ b/docs/LumexUI.Docs.Client/LumexUI.Docs.Client.csproj @@ -15,7 +15,6 @@ - diff --git a/docs/LumexUI.Docs.Client/_Imports.razor b/docs/LumexUI.Docs.Client/_Imports.razor index 8d1a9dc5..332f3aee 100644 --- a/docs/LumexUI.Docs.Client/_Imports.razor +++ b/docs/LumexUI.Docs.Client/_Imports.razor @@ -16,6 +16,7 @@ @using LumexUI.Docs.Client.Theme @using LumexUI.Common +@using LumexUI.Extensions @using LumexUI.Theme @using LumexUI.Utilities diff --git a/src/LumexUI/Components/Popover/LumexPopover.razor.cs b/src/LumexUI/Components/Popover/LumexPopover.razor.cs index 1fcddb9b..a9dbd14c 100644 --- a/src/LumexUI/Components/Popover/LumexPopover.razor.cs +++ b/src/LumexUI/Components/Popover/LumexPopover.razor.cs @@ -3,6 +3,7 @@ // See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE using LumexUI.Common; +using LumexUI.Extensions; using LumexUI.Services; using LumexUI.Styles; using LumexUI.Utilities; diff --git a/src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs b/src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs index ed95aa84..8114f76f 100644 --- a/src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs +++ b/src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Text; +using LumexUI.Extensions; using LumexUI.Theme; using LumexUI.Utilities; diff --git a/src/LumexUI/Components/Textbox/LumexTextbox.razor.cs b/src/LumexUI/Components/Textbox/LumexTextbox.razor.cs index 182eff5c..4a384bab 100644 --- a/src/LumexUI/Components/Textbox/LumexTextbox.razor.cs +++ b/src/LumexUI/Components/Textbox/LumexTextbox.razor.cs @@ -3,7 +3,7 @@ // See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE using LumexUI.Common; -using LumexUI.Utilities; +using LumexUI.Extensions; using Microsoft.AspNetCore.Components; diff --git a/src/LumexUI/Extensions/EnumExtensions.cs b/src/LumexUI/Extensions/EnumExtensions.cs new file mode 100644 index 00000000..c4654e3f --- /dev/null +++ b/src/LumexUI/Extensions/EnumExtensions.cs @@ -0,0 +1,25 @@ +// Copyright (c) LumexUI 2024 +// LumexUI licenses this file to you under the MIT license +// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE + +using System.ComponentModel; + +namespace LumexUI.Extensions; + +public static class EnumExtensions +{ + public static string ToDescription( this Enum value ) + { + var attributes = (DescriptionAttribute[])value + .GetType() + .GetField( value.ToString() )! + .GetCustomAttributes( typeof( DescriptionAttribute ), inherit: false ); + + return attributes is { Length: > 0 } ? attributes[0].Description : value.ToLowerInvariant(); + } + + public static string ToLowerInvariant( this Enum value ) + { + return value.ToString().ToLowerInvariant(); + } +} diff --git a/src/LumexUI/LumexUI.csproj b/src/LumexUI/LumexUI.csproj index 1d6fe484..36ec8c16 100644 --- a/src/LumexUI/LumexUI.csproj +++ b/src/LumexUI/LumexUI.csproj @@ -27,12 +27,6 @@ true - - - $(TargetsForTfmSpecificContentInPackage);AddUtilitiesToOutput - - - @@ -59,17 +53,9 @@ - - - - - - - - diff --git a/src/LumexUI/Utilities/ElementClass.cs b/src/LumexUI/Utilities/ElementClass.cs new file mode 100644 index 00000000..8da410d2 --- /dev/null +++ b/src/LumexUI/Utilities/ElementClass.cs @@ -0,0 +1,135 @@ +// Copyright (c) LumexUI 2024 +// LumexUI licenses this file to you under the MIT license +// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE + +namespace LumexUI.Utilities; + +/// +/// Represents a CSS class for the rendered element. +/// +public record struct ElementClass +{ + private string? _stringBuffer; + + /// + /// Initializes a new instance of the with the specified value. + /// + /// The value to initialize the instance with. + public ElementClass( string? value ) + { + _stringBuffer = value; + } + + /// + /// Creates a new instance of the . + /// + /// An instance. + public static ElementClass Empty() => new(); + + /// + /// Creates a new instance of the with the specified value. + /// + /// The value to be set for the new instance. + /// An instance. + public static ElementClass Default( string? value ) => new( value ); + + /// + /// Adds a CSS class to the current instance. + /// + /// The CSS class to add. + /// An instance. + public ElementClass Add( string? value ) + { + if( !string.IsNullOrEmpty( value ) ) + { + _stringBuffer += " " + value; + } + + return this; + } + + /// + /// Conditionally adds a CSS class to the current instance. + /// + /// The CSS class to add. + /// A boolean value that determines whether the CSS class should be added. + /// An instance. + public ElementClass Add( string? value, bool when ) => when ? Add( value ) : this; + + /// + /// Conditionally adds a CSS class to the current instance. + /// + /// The CSS class to add. + /// A function that returns a boolean value determining whether the CSS class should be added. + /// An instance. + public ElementClass Add( string? value, Func when ) => Add( value, when() ); + + /// + /// Conditionally adds a CSS class to the current instance. + /// + /// A function that returns the CSS class to add. + /// A boolean value that determines whether the CSS class should be added. + /// An instance. + public ElementClass Add( Func value, bool when ) => when ? Add( value() ) : this; + + /// + /// Conditionally adds a CSS class to the current instance. + /// + /// A function that returns the CSS class to add. + /// A function that returns a boolean value determining whether the CSS class should be added. + /// An instance. + public ElementClass Add( Func value, Func when ) => Add( value, when() ); + + /// + /// Adds a CSS class from another instance to the current instance. + /// + /// The instance whose CSS classes will be added. + /// An instance. + public ElementClass Add( ElementClass elementClass ) => Add( elementClass.ToString() ); + + /// + /// Conditionally adds a CSS class from another instance to the current instance. + /// + /// The instance whose CSS classes will be added. + /// A boolean value that determines whether the CSS class should be added. + /// An instance. + public ElementClass Add( ElementClass elementClass, bool when ) => when ? Add( elementClass.ToString() ) : this; + + /// + /// Conditionally adds a CSS class from another instance to the current instance. + /// + /// The instance whose CSS classes will be added. + /// A function that returns a boolean value determining whether the CSS class should be added. + /// An instance. + public ElementClass Add( ElementClass elementClass, Func when ) => Add( elementClass, when() ); + + /// + /// Conditionally adds a CSS class to the current instance when it exists in the specified additional attributes. + /// + /// The additional attributes. + /// An instance. + public ElementClass Add( IReadOnlyDictionary? additionalAttributes ) + { + if( additionalAttributes is null ) + { + return this; + } + + if( additionalAttributes.TryGetValue( "class", out var value ) ) + { + if( value is not null ) + { + return Add( value.ToString() ); + } + } + + return this; + } + + /// + /// Returns a string representation of the current instance. + /// + /// A trimmed representation of the CSS classes if the internal buffer is not empty; otherwise, an empty string. + public readonly override string ToString() + => !string.IsNullOrEmpty( _stringBuffer ) ? _stringBuffer.Trim() : string.Empty; +} \ No newline at end of file diff --git a/src/LumexUI/Utilities/ElementStyle.cs b/src/LumexUI/Utilities/ElementStyle.cs new file mode 100644 index 00000000..3f5fccef --- /dev/null +++ b/src/LumexUI/Utilities/ElementStyle.cs @@ -0,0 +1,181 @@ +// Copyright (c) LumexUI 2024 +// LumexUI licenses this file to you under the MIT license +// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE + +namespace LumexUI.Utilities; + +/// +/// Represents an in-line style for the rendered element. +/// +public record struct ElementStyle +{ + private string? _stringBuffer; + + /// + /// Initializes a new instance of the with a specified style. + /// + /// The CSS property name. + /// The value of the CSS property. + /// + /// Thrown if is null, empty, or consists exclusively of white-space characters. + /// + public ElementStyle( string property, string? value ) + { + if( string.IsNullOrWhiteSpace( property ) ) + { + throw new ArgumentException( "CSS property value cannot be null, empty or consist exlusively of white-space characters.", nameof( property ) ); + } + + _stringBuffer = $"{property}:{value};"; + } + + /// + /// Creates a new instance of the . + /// + /// An instance. + public static ElementStyle Empty() => new(); + + /// + /// Creates a new instance of the with a specified style. + /// + /// The CSS property name. + /// The value of the CSS property. + /// An instance. + /// + /// Thrown if is null, empty, or consists exclusively of white-space characters. + /// + public static ElementStyle Default( string property, string? value ) => new( property, value ); + + /// + /// Adds a style to the current instance if the value is not null or whitespace. + /// + /// The value of the CSS property. + /// An instance. + public ElementStyle Add( string? value ) => !string.IsNullOrWhiteSpace( value ) ? AddRaw( $"{value};" ) : this; + + /// + /// Adds a style to the current instance. + /// + /// The CSS property name. + /// The value of the CSS property. + /// An instance. + /// + /// Thrown if is null, empty, or consists exclusively of white-space characters. + /// + public ElementStyle Add( string property, string? value ) + { + if( string.IsNullOrWhiteSpace( property ) ) + { + throw new ArgumentException( "CSS property value cannot be null, empty or consist exlusively of white-space characters.", nameof( property ) ); + } + + return AddRaw( $"{property}:{value};" ); + } + + /// + /// Conditionally adds a style to the current instance. + /// + /// The CSS property name. + /// The value of the CSS property. + /// A boolean value that determines whether the style should be added. + /// An instance. + /// + /// Thrown if is null, empty, or consists exclusively of white-space characters. + /// + public ElementStyle Add( string property, string? value, bool when ) => when ? Add( property, value ) : this; + + /// + /// Conditionally adds a style to the current instance. + /// + /// The CSS property name. + /// A function that returns the value of the CSS property. + /// A boolean value that determines whether the style should be added. + /// An instance. + /// + /// Thrown if is null, empty, or consists exclusively of white-space characters. + /// + public ElementStyle Add( string property, Func value, bool when ) => when ? Add( property, value() ) : this; + + /// + /// Conditionally adds a style to the current instance. + /// + /// The CSS property name. + /// The value of the CSS property. + /// A function that returns a boolean value determining whether the style should be added. + /// An instance. + /// + /// Thrown if is null, empty, or consists exclusively of white-space characters. + /// + public ElementStyle Add( string property, string? value, Func when ) => Add( property, value, when() ); + + /// + /// Conditionally adds a style to the current instance. + /// + /// The CSS property name. + /// A function that returns the value of the CSS property. + /// A function that returns a boolean value determining whether the style should be added. + /// An instance. + /// + /// Thrown if is null, empty, or consists exclusively of white-space characters. + /// + public ElementStyle Add( string property, Func value, Func when ) => Add( property, value(), when() ); + + /// + /// Adds the styles from another instance to the current instance. + /// + /// The instance whose styles will be added. + /// An instance. + public ElementStyle Add( ElementStyle elementStyle ) => AddRaw( elementStyle.ToString() ); + + /// + /// Conditionally adds the styles from another instance to the current instance. + /// + /// The instance whose styles will be added. + /// A boolean value that determines whether the styles should be added. + /// An instance. + public ElementStyle Add( ElementStyle elementStyle, bool when ) => when ? AddRaw( elementStyle.ToString() ) : this; + + /// + /// Conditionally adds the styles from another instance to the current instance. + /// + /// The instance whose styles will be added. + /// A function that returns a boolean value determining whether the styles should be added. + /// An instance. + public ElementStyle Add( ElementStyle elementStyle, Func when ) => Add( elementStyle, when() ); + + /// + /// Conditionally adds a CSS style to the current instance when it exists in the specified additional attributes. + /// + /// The additional attributes. + /// An instance. + public ElementStyle Add( IReadOnlyDictionary? additionalAttributes ) + { + if( additionalAttributes is null ) + { + return this; + } + + if( additionalAttributes.TryGetValue( "style", out var value ) ) + { + if( value is not null ) + { + return AddRaw( value.ToString() ); + } + } + + return this; + } + + /// + /// Returns a string representation of the current instance. + /// + /// A trimmed representation of the styles if the internal buffer is not empty; otherwise, a . + public readonly override string? ToString() + => !string.IsNullOrEmpty( _stringBuffer ) ? _stringBuffer.Trim() : null; + + private ElementStyle AddRaw( string? value ) + { + _stringBuffer += value; + return this; + } +} \ No newline at end of file diff --git a/src/LumexUI/Utilities/Identifier.cs b/src/LumexUI/Utilities/Identifier.cs new file mode 100644 index 00000000..55e82dcd --- /dev/null +++ b/src/LumexUI/Utilities/Identifier.cs @@ -0,0 +1,30 @@ +// Copyright (c) LumexUI 2024 +// LumexUI licenses this file to you under the MIT license +// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE + +namespace LumexUI.Utilities; + +public static class Identifier +{ + private static readonly Random _rnd = new(); + + /// + /// Generates a new small Id. For example, 'f127d9edf14385adb'. + /// + /// HTML id must start with a letter. + /// A that represents the generated ID. + public static string New( int length = 8 ) + { + if( length > 16 ) + { + throw new ArgumentOutOfRangeException( nameof( length ), "length must be less than 16" ); + } + + if( length <= 8 ) + { + return $"f{_rnd.Next():x}"; + } + + return $"f{_rnd.Next():x}{_rnd.Next():x}"[..length]; + } +} diff --git a/src/LumexUI/Utilities/Utils.cs b/src/LumexUI/Utilities/Utils.cs index b848f188..ba55b96e 100644 --- a/src/LumexUI/Utilities/Utils.cs +++ b/src/LumexUI/Utilities/Utils.cs @@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis; +using LumexUI.Extensions; + namespace LumexUI.Utilities; [ExcludeFromCodeCoverage] diff --git a/tests/LumexUI.Tests/Components/Button/ButtonTests.cs b/tests/LumexUI.Tests/Components/Button/ButtonTests.cs index 36cb76de..6234de77 100644 --- a/tests/LumexUI.Tests/Components/Button/ButtonTests.cs +++ b/tests/LumexUI.Tests/Components/Button/ButtonTests.cs @@ -3,7 +3,7 @@ // See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE using LumexUI.Common; -using LumexUI.Utilities; +using LumexUI.Extensions; using LumexUI.Variants; using Microsoft.Extensions.DependencyInjection; diff --git a/tests/LumexUI.Tests/LumexUI.Tests.csproj b/tests/LumexUI.Tests/LumexUI.Tests.csproj index 3df53085..c1cb42c4 100644 --- a/tests/LumexUI.Tests/LumexUI.Tests.csproj +++ b/tests/LumexUI.Tests/LumexUI.Tests.csproj @@ -25,7 +25,6 @@ -