diff --git a/.gitignore b/.gitignore index f400df3..b15f811 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ obj/ /packages/ riderModule.iml /_ReSharper.Caches/ -.idea/ \ No newline at end of file +.idea/ +.vs/ \ No newline at end of file diff --git a/BemIt.Tests/BlockTests.cs b/BemIt.Tests/BlockTests.cs index b6ba818..8c5bbfc 100644 --- a/BemIt.Tests/BlockTests.cs +++ b/BemIt.Tests/BlockTests.cs @@ -16,6 +16,18 @@ public void Extend() Assert.Equal("m-list-item-title", _block.Extend("title").Build()); } + [Fact] + public void Extend_element() + { + Assert.Equal("m-list-item-title__intro", _block.Extend("title").Element("intro").Build()); + } + + [Fact] + public void Extend_element_modifier() + { + Assert.Equal("m-list-item-title__intro m-list-item-title__intro--hoverable", _block.Extend("title").Element("intro").Modifier("hoverable").Build()); + } + [Fact] public void Block_modifier_empty_ctor() { diff --git a/BemIt.sln b/BemIt.sln index 1d0adf1..088b8cb 100644 --- a/BemIt.sln +++ b/BemIt.sln @@ -1,8 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BemIt", "BemIt\BemIt.csproj", "{DD0FDF41-698B-464E-829E-13F0AD00809E}" +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34310.174 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BemIt", "BemIt\BemIt.csproj", "{DD0FDF41-698B-464E-829E-13F0AD00809E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BemIt.Tests", "BemIt.Tests\BemIt.Tests.csproj", "{F686E4F1-1AA0-4473-B7FB-F3654B7377E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BemIt.Tests", "BemIt.Tests\BemIt.Tests.csproj", "{F686E4F1-1AA0-4473-B7FB-F3654B7377E1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,4 +22,10 @@ Global {F686E4F1-1AA0-4473-B7FB-F3654B7377E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {F686E4F1-1AA0-4473-B7FB-F3654B7377E1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0D42B177-36B3-4337-856C-0C54B0B452E0} + EndGlobalSection EndGlobal diff --git a/BemIt.sln.DotSettings.user b/BemIt.sln.DotSettings.user index 97909df..e45f201 100644 --- a/BemIt.sln.DotSettings.user +++ b/BemIt.sln.DotSettings.user @@ -1,38 +1,42 @@  <SessionState ContinuousTestingMode="0" Name="Block" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <TestAncestor> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.UnitTest1.Block</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.UnitTest1.Block</TestId> </TestAncestor> </SessionState> <SessionState ContinuousTestingMode="0" Name="All tests from &lt;BemIt.Tests&gt; #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Project Location="F:\code\BemIt\BemIt.Tests" Presentation="&lt;BemIt.Tests&gt;" /> </SessionState> - <SessionState ContinuousTestingMode="0" IsActive="True" Name="Block_modifier_with_true_condition #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" Name="Block_modifier_with_true_condition #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Or> <TestAncestor> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.BlockTests.Block_modifier_with_true_condition</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.ElementTests.Element_modifier</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.BlockTests.ChildBlock</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.ElementTests</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.ModifierTests.Modifier_with_true_condition</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.ModifierTests.Modifier_with_two_mods</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.ModifierTests.Modifier_three_withs</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.ModifierTests.Modifier_with_enum_with_one_mod</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.ModifierTests.Modifier_with_enum_with_multi</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.ModifierTests.Modifier_with_args_with_enum</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.BlockTests.Block_modifier_with_true_condition</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.ElementTests.Element_modifier</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.BlockTests.ChildBlock</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.ElementTests</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.ModifierTests.Modifier_with_true_condition</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.ModifierTests.Modifier_with_two_mods</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.ModifierTests.Modifier_three_withs</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.ModifierTests.Modifier_with_enum_with_one_mod</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.ModifierTests.Modifier_with_enum_with_multi</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.ModifierTests.Modifier_with_args_with_enum</TestId> </TestAncestor> <ProjectFile>F686E4F1-1AA0-4473-B7FB-F3654B7377E1/f:BlockTests.cs</ProjectFile> </Or> </SessionState> <SessionState ContinuousTestingMode="0" Name="Block_modifier_with_true_condition" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <TestAncestor> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.BlockTests.Block_modifier_with_true_condition</TestId> - <TestId>xUnit::F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::BemIt.Tests.BlockTests.Block_modifier</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.BlockTests.Block_modifier_with_true_condition</TestId> + <TestId>F686E4F1-1AA0-4473-B7FB-F3654B7377E1::net6.0::xUnit::BemIt.Tests.BlockTests.Block_modifier</TestId> </TestAncestor> </SessionState> <SessionState ContinuousTestingMode="0" Name="All tests from &lt;BemIt.Tests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <Project Location="F:\code\BemIt\BemIt.Tests" Presentation="&lt;BemIt.Tests&gt;" /> </SessionState> - <SessionState ContinuousTestingMode="0" Name="All tests from UnitTest1.cs" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <ProjectFile>F686E4F1-1AA0-4473-B7FB-F3654B7377E1/f:UnitTest1.cs</ProjectFile> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from UnitTest1.cs" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Or> + <ProjectFile>F686E4F1-1AA0-4473-B7FB-F3654B7377E1/f:BlockTests.cs</ProjectFile> + <ProjectFile>F686E4F1-1AA0-4473-B7FB-F3654B7377E1/f:ElementTests.cs</ProjectFile> + <ProjectFile>F686E4F1-1AA0-4473-B7FB-F3654B7377E1/f:ModifierTests.cs</ProjectFile> + </Or> </SessionState> \ No newline at end of file diff --git a/BemIt/Abstract/IBem.cs b/BemIt/Abstract/IBem.cs index 85a4c4e..623ef11 100644 --- a/BemIt/Abstract/IBem.cs +++ b/BemIt/Abstract/IBem.cs @@ -1,7 +1,14 @@ -namespace BemIt; +using System.Collections.Generic; + +namespace BemIt; public interface IBem { + /// + /// The name of the block or element + /// + string Name { get; } + /// /// Adds original class names /// @@ -9,9 +16,23 @@ public interface IBem /// IBem AddClass(params string?[] classNames); + /// + /// Adds original class name if condition is true + /// + /// a single or a list of class names + /// + /// + IBem AddClass(string className, bool condition); + + /// + /// Generates a list of css classes + /// + /// + IEnumerable GenerateCssClasses(); + /// /// Builds the css class /// /// string Build(); -} +} \ No newline at end of file diff --git a/BemIt/Abstract/IBlockOrElement.cs b/BemIt/Abstract/IBlockOrElement.cs index b6ee007..7a3e87a 100644 --- a/BemIt/Abstract/IBlockOrElement.cs +++ b/BemIt/Abstract/IBlockOrElement.cs @@ -1,6 +1,6 @@ namespace BemIt; -public interface IBlockOrElement: IBem +public interface IBlockOrElement { string Name { get; } -} +} \ No newline at end of file diff --git a/BemIt/BemBase.cs b/BemIt/BemBase.cs index 603ebcd..8f66a29 100644 --- a/BemIt/BemBase.cs +++ b/BemIt/BemBase.cs @@ -1,9 +1,17 @@ using System.Collections.Generic; +using System.Linq; namespace BemIt; -public class BemBase : IBem +public abstract class BemBase : IBem { + public string Name { get; } + + protected BemBase(string blockOrElement) + { + Name = blockOrElement; + } + protected List ClassNames { get; } = new(); // inherits @@ -13,9 +21,37 @@ public IBem AddClass(params string?[] classNames) return this; } + // inherits + public IBem AddClass(string? className, bool condition) + { + if (condition) + { + ClassNames.Add(className); + } + + return this; + } + + // inherits + public virtual IEnumerable GenerateCssClasses() + { + yield return Name; + + foreach (var className in ClassNames.Where(className => !string.IsNullOrWhiteSpace(className))) + { + yield return className!; + } + } + // inherits public virtual string Build() { - return string.Join(" ", ClassNames).Trim(); + return string.Join(" ", GenerateCssClasses()); + } + + // inherits + public override string ToString() + { + return Build(); } -} +} \ No newline at end of file diff --git a/BemIt/BemIt.csproj b/BemIt/BemIt.csproj index 201b0ca..dd947cc 100644 --- a/BemIt/BemIt.csproj +++ b/BemIt/BemIt.csproj @@ -7,7 +7,7 @@ BemIt https://github.com/capdiem/BemIt capdiem - 1.1.2 + 1.2.0 https://github.com/capdiem/BemIt blazor;css;bem;block;element;modifier; True diff --git a/BemIt/Block.cs b/BemIt/Block.cs index bc052af..2dbae6e 100644 --- a/BemIt/Block.cs +++ b/BemIt/Block.cs @@ -13,16 +13,10 @@ public class Block : BemBase, IBlockOrElement /// Creates a block /// /// The name of the block - public Block(string name) + public Block(string name) : base(name) { - Name = name; } - /// - /// The name of the block - /// - public string Name { get; } - /// /// Creates a block extends from current block /// @@ -51,19 +45,4 @@ public Element Element(string element) { return new Element(Name, element); } - - /// - /// Builds the css class from a block - /// - /// - public override string Build() - { - return (Name + " " + string.Join(" ", ClassNames)).Trim(); - } - - // inherit - public override string ToString() - { - return Build(); - } -} +} \ No newline at end of file diff --git a/BemIt/Element.cs b/BemIt/Element.cs index a093042..b67edf1 100644 --- a/BemIt/Element.cs +++ b/BemIt/Element.cs @@ -10,31 +10,7 @@ public class Element : BemBase, IBlockOrElement /// /// From which block /// The name of the element, appended to the block name with double underscore - public Element(string block, string element) + public Element(string block, string element) : base($"{block}__{element}") { - Name = $"{block}__{element}"; } - - /// - /// The name of the element - /// - /// - /// {block}__{element} - /// - public string Name { get; } - - /// - /// Builds the css class from a element - /// - /// - public override string Build() - { - return (Name + " " + string.Join(" ", ClassNames)).Trim(); - } - - // inherit - public override string ToString() - { - return Build(); - } -} +} \ No newline at end of file diff --git a/BemIt/Extensions/BlockOrElementExtensions.cs b/BemIt/Extensions/BlockOrElementExtensions.cs index 2b3d5c2..43a74e6 100644 --- a/BemIt/Extensions/BlockOrElementExtensions.cs +++ b/BemIt/Extensions/BlockOrElementExtensions.cs @@ -10,7 +10,7 @@ public static Modifier Modifier(this IBlockOrElement blockOrElement) { return new Modifier(blockOrElement.Name); } - + public static Modifier Modifier(this IBlockOrElement blockOrElement, string modifier) { return new Modifier(blockOrElement.Name, modifier); @@ -33,7 +33,8 @@ public static Modifier Modifier(this IBlockOrElement blockOrElement, Enum value, return new Modifier(blockOrElement.Name, modifier); } - public static Modifier Modifier(this IBlockOrElement blockOrElement, bool modifier, [CallerArgumentExpression("modifier")] string name = "") + public static Modifier Modifier(this IBlockOrElement blockOrElement, bool modifier, + [CallerArgumentExpression("modifier")] string name = "") { return new Modifier(blockOrElement.Name, name, modifier); } @@ -66,4 +67,4 @@ public static Modifier Modifier(this IBlockOrElement blockOrElement, bool modifi { name3, modifier3 }, }); } -} +} \ No newline at end of file diff --git a/BemIt/Extensions/StringExtensions.cs b/BemIt/Extensions/StringExtensions.cs index 2bd8f0a..eac3bfa 100644 --- a/BemIt/Extensions/StringExtensions.cs +++ b/BemIt/Extensions/StringExtensions.cs @@ -12,11 +12,12 @@ public static string ToKebab(this string name) #else var split = Regex.Split(name, "(? s.Trim('-')); #endif - return string.Join("-", split).ToLowerInvariant(); + return string.Join("-", split).ToLowerInvariant() + .TrimStart('_'); // for private fields like _myField } #if NET7_0_OR_GREATER [GeneratedRegex("(? public class Modifier : BemBase { - private readonly string _blockOrElement; private readonly IDictionary _modifiers = new Dictionary(); /// /// Creates a modifier instance with a block or element /// /// - public Modifier(string blockOrElement) + public Modifier(string blockOrElement) : base(blockOrElement) { - _blockOrElement = blockOrElement; } /// @@ -27,10 +25,8 @@ public Modifier(string blockOrElement) /// /// /// - public Modifier(string blockOrElement, string m) + public Modifier(string blockOrElement, string m) : this(blockOrElement) { - _blockOrElement = blockOrElement; - _modifiers.Add(m, true); } @@ -40,10 +36,8 @@ public Modifier(string blockOrElement, string m) /// /// /// - public Modifier(string blockOrElement, string m, bool condition) + public Modifier(string blockOrElement, string m, bool condition) : this(blockOrElement) { - _blockOrElement = blockOrElement; - _modifiers.Add(m, condition); } @@ -52,10 +46,8 @@ public Modifier(string blockOrElement, string m, bool condition) /// /// /// - public Modifier(string blockOrElement, IDictionary modifiers) + public Modifier(string blockOrElement, IDictionary modifiers) : this(blockOrElement) { - _blockOrElement = blockOrElement; - _modifiers = modifiers; } @@ -85,12 +77,14 @@ public Modifier Add(string modifier) /// /// Adds a modifier of type enum /// - /// + /// The value of enum + /// Applies the modifier if /// - public Modifier Add(Enum value) + public Modifier Add(Enum value, bool apply = true) { - var modifier = FormatEnum(value); + if (!apply) return this; + var modifier = FormatEnum(value); return Add(modifier); } @@ -99,11 +93,13 @@ public Modifier Add(Enum value) /// /// /// + /// Applies the modifier if /// - public Modifier Add(Enum value, string name) + public Modifier Add(Enum value, string name, bool apply = true) { - var modifier = FormatEnum(value, name); + if (!apply) return this; + var modifier = FormatEnum(value, name); return Add(modifier); } @@ -180,6 +176,69 @@ public Modifier Add(bool modifier1, bool modifier2, bool modifier3, }); } + /// + /// Adds a modifier based on the specified parameters. + /// Only one of the parameters that's value is true will be added. + /// + /// + /// + /// + /// + /// + public Modifier AddOneOf(bool modifier1, bool modifier2, + [CallerArgumentExpression("modifier1")] + string name1 = "", + [CallerArgumentExpression("modifier2")] + string name2 = "") + { + if (modifier1) + { + Add(name1); + } + else if (modifier2) + { + Add(name2); + } + + return this; + } + + /// + /// Adds a modifier based on the specified parameters. + /// Only one of the parameters that's value is true will be added. + /// + /// + /// + /// + /// + /// + /// + /// + public Modifier AddOneOf(bool modifier1, bool modifier2, bool modifier3, + [CallerArgumentExpression("modifier1")] + string name1 = "", + [CallerArgumentExpression("modifier2")] + string name2 = "", + [CallerArgumentExpression("modifier2")] + string name3 = "" + ) + { + if (modifier1) + { + Add(name1); + } + else if (modifier2) + { + Add(name2); + } + else if (modifier3) + { + Add(name3); + } + + return this; + } + /// /// Formats an enum to a BEM modifier /// @@ -194,16 +253,35 @@ public static string FormatEnum(Enum @enum, string? inputName = null) return $"{name}-{value}"; } + // inherits + public override IEnumerable GenerateCssClasses() + { + yield return Name; + + foreach (var item in _modifiers) + { + var className = Combine(Name, item.Key.ToKebab(), item.Value); + if (string.IsNullOrWhiteSpace(className)) + { + continue; + } + + yield return className; + } + + foreach (var className in ClassNames.Where(c => !string.IsNullOrWhiteSpace(c))) + { + yield return className!; + } + } + /// /// Builds the css class from a modifier /// /// public override string Build() { - var bemCss = _modifiers.Aggregate(_blockOrElement, - (current, modifier) => current + Combine(_blockOrElement, modifier.Key.ToKebab(), modifier.Value)); - - return (bemCss + " " + string.Join(" ", ClassNames)).Trim(); + return string.Join(" ", GenerateCssClasses()).Trim(); } // inherit @@ -214,6 +292,6 @@ public override string ToString() private static string Combine(string be, string modifier, bool condition) { - return condition ? $" {be}--{modifier}" : string.Empty; + return condition ? $"{be}--{modifier}" : string.Empty; } } \ No newline at end of file