diff --git a/BlazorTemplater.Library/BlazorTemplater.Library.csproj b/BlazorTemplater.Library/BlazorTemplater.Library.csproj index 224e4c1..7199dfe 100644 --- a/BlazorTemplater.Library/BlazorTemplater.Library.csproj +++ b/BlazorTemplater.Library/BlazorTemplater.Library.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -9,6 +9,14 @@ BlazorTemplater + + + + + + + + diff --git a/BlazorTemplater.Library/ComponentWithCss.razor b/BlazorTemplater.Library/ComponentWithCss.razor new file mode 100644 index 0000000..745d1eb --- /dev/null +++ b/BlazorTemplater.Library/ComponentWithCss.razor @@ -0,0 +1,3 @@ +

+ This component has owned CSS +

\ No newline at end of file diff --git a/BlazorTemplater.Library/ComponentWithCss.razor.css b/BlazorTemplater.Library/ComponentWithCss.razor.css new file mode 100644 index 0000000..bf4caca --- /dev/null +++ b/BlazorTemplater.Library/ComponentWithCss.razor.css @@ -0,0 +1,12 @@ +.nested { + font-size: large; + color: darkblue; + width: 100%; + padding: 1rem; + text-align: center; +} + +.nested strong{ + color: darkred; + font-style: italic; +} \ No newline at end of file diff --git a/BlazorTemplater.Library/LayoutComponentWithCss.razor b/BlazorTemplater.Library/LayoutComponentWithCss.razor new file mode 100644 index 0000000..910e60b --- /dev/null +++ b/BlazorTemplater.Library/LayoutComponentWithCss.razor @@ -0,0 +1,17 @@ +@inherits LayoutComponentBase + + + + LayoutFile + + +
+
+

This is a layout component that should be rendered components!

+
+ @Body +
+
+
+ + \ No newline at end of file diff --git a/BlazorTemplater.Library/LayoutComponentWithCss.razor.css b/BlazorTemplater.Library/LayoutComponentWithCss.razor.css new file mode 100644 index 0000000..450d033 --- /dev/null +++ b/BlazorTemplater.Library/LayoutComponentWithCss.razor.css @@ -0,0 +1,7 @@ +p { + font-size: large; + color: darkgreen; + width: 100%; + padding: 1rem; + text-align: center; +} \ No newline at end of file diff --git a/BlazorTemplater.Library/MultipleComponentsWithCss.razor b/BlazorTemplater.Library/MultipleComponentsWithCss.razor new file mode 100644 index 0000000..529d3ab --- /dev/null +++ b/BlazorTemplater.Library/MultipleComponentsWithCss.razor @@ -0,0 +1,5 @@ +@layout LayoutComponentWithCss +

+ Jan 1st is @(new DateTime(2021,1,1).ToString("yyyy-MM-dd")) +

+ \ No newline at end of file diff --git a/BlazorTemplater.Library/MultipleComponentsWithCss.razor.css b/BlazorTemplater.Library/MultipleComponentsWithCss.razor.css new file mode 100644 index 0000000..d4b8713 --- /dev/null +++ b/BlazorTemplater.Library/MultipleComponentsWithCss.razor.css @@ -0,0 +1,8 @@ +p +{ + font-size: large; + color: darkblue; + width: 100%; + padding: 1rem; + text-align: center; +} \ No newline at end of file diff --git a/BlazorTemplater.Library/SimpleWithCss.razor b/BlazorTemplater.Library/SimpleWithCss.razor new file mode 100644 index 0000000..24d6a59 --- /dev/null +++ b/BlazorTemplater.Library/SimpleWithCss.razor @@ -0,0 +1,3 @@ +

+ Jan 1st is @(new DateTime(2021,1,1).ToString("yyyy-MM-dd")) +

\ No newline at end of file diff --git a/BlazorTemplater.Library/SimpleWithCss.razor.css b/BlazorTemplater.Library/SimpleWithCss.razor.css new file mode 100644 index 0000000..d4b8713 --- /dev/null +++ b/BlazorTemplater.Library/SimpleWithCss.razor.css @@ -0,0 +1,8 @@ +p +{ + font-size: large; + color: darkblue; + width: 100%; + padding: 1rem; + text-align: center; +} \ No newline at end of file diff --git a/BlazorTemplater.Library/TemplatedSimpleWithCss.razor b/BlazorTemplater.Library/TemplatedSimpleWithCss.razor new file mode 100644 index 0000000..adfbbe6 --- /dev/null +++ b/BlazorTemplater.Library/TemplatedSimpleWithCss.razor @@ -0,0 +1,4 @@ +@layout LayoutComponentWithCss +

+ Jan 1st is @(new DateTime(2021,1,1).ToString("yyyy-MM-dd")) +

\ No newline at end of file diff --git a/BlazorTemplater.Library/TemplatedSimpleWithCss.razor.css b/BlazorTemplater.Library/TemplatedSimpleWithCss.razor.css new file mode 100644 index 0000000..ff414a9 --- /dev/null +++ b/BlazorTemplater.Library/TemplatedSimpleWithCss.razor.css @@ -0,0 +1,7 @@ +p.child { + font-size: medium; + color: darkblue; + width: 100%; + padding: 1rem; + text-align: center; +} \ No newline at end of file diff --git a/BlazorTemplater.Tests/BlazorTemplater.Tests.csproj b/BlazorTemplater.Tests/BlazorTemplater.Tests.csproj index 078eb8e..7b0660b 100644 --- a/BlazorTemplater.Tests/BlazorTemplater.Tests.csproj +++ b/BlazorTemplater.Tests/BlazorTemplater.Tests.csproj @@ -1,15 +1,15 @@ - netcoreapp3.1 + net8.0 false - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/BlazorTemplater.Tests/Templater_Tests.cs b/BlazorTemplater.Tests/Templater_Tests.cs index 9ae284a..db34c70 100644 --- a/BlazorTemplater.Tests/Templater_Tests.cs +++ b/BlazorTemplater.Tests/Templater_Tests.cs @@ -427,5 +427,111 @@ public void GetLayoutFromAttribute_TestWhenNotPresent() } #endregion + + #region Css Isolated + /// + /// Test a SimpleWithCss component with no parameters + /// + [TestMethod] + public void RenderComponent_SimpleWithCss_Test() + { + const string expected = @"

+ Jan 1st is 2021-01-01 +

"; + + var templater = new Templater(); + var actual = templater.RenderComponent(); + + Console.WriteLine(actual); + Assert.AreEqual(expected, actual); + } + + /// + /// Test a SimpleWithCss component with no parameters + /// + [TestMethod] + public void RenderComponent_TemplatedSimpleWithCss_Test() + { + const string expected = @" +

This is a layout component that should be rendered components!

+
+

+ Jan 1st is 2021-01-01 +

"; + + var templater = new Templater(); + var actual = templater.RenderComponent(); + + Console.WriteLine(actual); + StringAssert.Contains(actual, expected, "Content not found"); + } + + /// + /// Test a SimpleWithCss component with no parameters + /// + [TestMethod] + public void RenderComponent_MultipleComponentsWithCss_Test() + { + const string expected = @"

This is a layout component that should be rendered components!

+
+

+ Jan 1st is 2021-01-01 +

+

+ This component has owned CSS +

"; + + var templater = new Templater(); + var actual = templater + .AddScoppedCss() + .RenderComponent(); + + Console.WriteLine(actual); + StringAssert.Contains(actual, expected, "Content not found"); + } + #endregion Simple render } } \ No newline at end of file diff --git a/BlazorTemplater.sln b/BlazorTemplater.sln index 1579ad5..0246b3a 100644 --- a/BlazorTemplater.sln +++ b/BlazorTemplater.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31624.102 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34928.147 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution", "solution", "{C0365D75-5C0A-4E88-BC0F-FF595B731B6B}" ProjectSection(SolutionItems) = preProject @@ -18,6 +18,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{B0E3EF40-CE74-4196-993F-D65E256F52FB}" ProjectSection(SolutionItems) = preProject Docs\AddRazorSupport.md = Docs\AddRazorSupport.md + Docs\CssIsolated.md = Docs\CssIsolated.md Docs\Layouts.md = Docs\Layouts.md Docs\Templater.md = Docs\Templater.md Docs\Usage.md = Docs\Usage.md diff --git a/BlazorTemplater/BlazorTemplater.csproj b/BlazorTemplater/BlazorTemplater.csproj index f85bc72..4bf33da 100644 --- a/BlazorTemplater/BlazorTemplater.csproj +++ b/BlazorTemplater/BlazorTemplater.csproj @@ -1,24 +1,26 @@  - - netstandard2.0;net5.0 - 3.0 - conficient, PhotoAtomic - conficient - A HTML templating engine using Razor Components (.razor) files instead of .cshtml - https://github.com/conficient/BlazorTemplater - https://github.com/conficient/BlazorTemplater - Blazor RazorComponents HTML Email Templating - 1.5.1 - Added test for comment rendering - Latest - + + netstandard2.0;net5.0;net8.0 + 3.0 + conficient, PhotoAtomic + conficient + A HTML templating engine using Razor Components (.razor) files instead of .cshtml + https://github.com/conficient/BlazorTemplater + https://github.com/conficient/BlazorTemplater + Blazor RazorComponents HTML Email Templating + 1.6.2 + Added css isolated when rendering + Latest + - - - - - - + + + + + + + + diff --git a/BlazorTemplater/Templater.cs b/BlazorTemplater/Templater.cs index ce33000..5b92d9a 100644 --- a/BlazorTemplater/Templater.cs +++ b/BlazorTemplater/Templater.cs @@ -5,7 +5,10 @@ using Microsoft.Extensions.Logging.Abstractions; using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection; +using System.Text; namespace BlazorTemplater { @@ -51,6 +54,11 @@ public Templater() /// private readonly Lazy _serviceProvider; + /// + /// Store the Scoped CSS. Css Isolated. + /// + private StringBuilder _style = null; + /// /// Services provided by Dependency Injection /// @@ -99,6 +107,48 @@ public void AddServiceProvider(IServiceProvider serviceProvider) _serviceCollection.Add(serviceProvider); } + /// + /// Add additional isolated CSS from a nested components + /// + /// + public Templater AddScoppedCss() + { + Type type = typeof(TComponent); + AddScoppedCss(type); + return this; + } + + private void AddScoppedCss(Type componentType) + { + if (componentType != null) + { + string cssFile = $@"{componentType.FullName}.razor.css"; + StringBuilder css = new StringBuilder(); + try + { + Assembly assembly = componentType.Assembly; + using Stream stream = assembly.GetManifestResourceStream(cssFile); + using StreamReader sr = new StreamReader(stream!); + string content = sr.ReadToEndAsync().GetAwaiter().GetResult(); + if (!string.IsNullOrEmpty(content)) + { + css.Append(content); + } + } + catch (Exception ex) + { + Console.WriteLine($"Component = {componentType.Name} => Exception: {ex.Message}"); + css.Clear(); + } + finally + { + if (_style == null) + _style = new(); + _style.Append(css); + } + } + } + /// /// Render a component to HTML /// @@ -127,17 +177,28 @@ public string RenderComponent(Type componentType, IDictionary pa { ValidateComponentType(componentType); var layout = GetLayout(componentType); + AddScoppedCss(layout); + AddScoppedCss(componentType); // create a RenderFragment from the component - var childContent = (RenderFragment)(builder => + RenderFragment childContent = builder => { - builder.OpenComponent(0, componentType); - + int componentId = 0; + if (_style != null && _style.Length > 0) + { + builder.AddMarkupContent(componentId, $@""); + componentId++; + } + builder.OpenComponent(componentId, componentType); // add parameters if any if (parameters != null && parameters.Any()) - builder.AddMultipleAttributes(1, parameters); + { + componentId++; + builder.AddMultipleAttributes(componentId, parameters); + } + // add scoped css if any builder.CloseComponent(); - }); + }; // render a LayoutView and use the TComponent as the child content var layoutView = new RenderedComponent(Renderer); diff --git a/Docs/CssIsolated.md b/Docs/CssIsolated.md new file mode 100644 index 0000000..0b4b236 --- /dev/null +++ b/Docs/CssIsolated.md @@ -0,0 +1,48 @@ +# Issolating Css + +CSS Isolated, or CSS scope, it's and adventage to create components with with own CSS. + +Templater with add automatically the CSS scoped from the Layout and from the component is pretend to eb rendered. If you have child components must be add also the compoent type while adding the component. + +CSS Isolated you can add like. + +``` +LayoutComponentWithCss.razor +LayoutComponentWithCss.razor.css +ComponentWithCss.razor +ComponentWithCss.razor.css +``` + +## Considerations + +* CSS must be add to the assembly like a Embedded Resource +* Because component internal ID is not in the Embedded Resource recomendation is have a container wrapper to identify the component css when all is merged + +```xaml + + + + +``` + + +### Usage + +#### Normal Component with or without layout +When the component is rendered issolated css is automatically added between ```html ``` tags before render de component. + +So you can still using as a normal: +```c# +string html = new ComponentRenderer() + .Render(); +``` + +#### Component With Child component also with scoped CSS +If the component has also child or childs components with CSS isolated, then must to add this component type before rendering for the css can be added: +```c# +string html = new ComponentRenderer() + .AddScoppedCss() + .AddScoppedCss() + .Render(); +``` +