Browse Source

Stateless dynamic component (#618)

* add stateless dynamic component

* use stateless dynamic component in examples

* merge dynamic component interfaces

---------

Co-authored-by: Marcin Ziąbek <[email protected]>
Bennet Bo Fenner 2 years ago
parent
commit
4deecd8235

+ 1 - 3
Source/QuestPDF.Examples/ContentDirectionExamples.cs

@@ -381,10 +381,8 @@ namespace QuestPDF.Examples
             }
         }
         
-        class SimpleDynamic : IDynamicComponent<int>
+        class SimpleDynamic : IDynamicComponent
         {
-            public int State { get; set; }
-
             public DynamicComponentComposeResult Compose(DynamicContext context)
             {
                 var content = context.CreateElement(container =>

+ 1 - 3
Source/QuestPDF.Examples/DynamicPageNumberLeftRight.cs

@@ -9,10 +9,8 @@ using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Examples
 {
-    public class FooterWithAlternatingAlignment : IDynamicComponent<int>
+    public class FooterWithAlternatingAlignment : IDynamicComponent
     {
-        public int State { get; set; }
-        
         public DynamicComponentComposeResult Compose(DynamicContext context)
         {
             var content = context.CreateElement(element =>

+ 1 - 3
Source/QuestPDF.Examples/DynamicProgressHeader.cs

@@ -9,10 +9,8 @@ using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Examples
 {
-    public class ProgressHeader : IDynamicComponent<int>
+    public class ProgressHeader : IDynamicComponent
     {
-        public int State { get; set; }
-        
         public DynamicComponentComposeResult Compose(DynamicContext context)
         {
             var content = context.CreateElement(container =>

+ 19 - 1
Source/QuestPDF/Fluent/DynamicComponentExtensions.cs

@@ -1,10 +1,28 @@
-using QuestPDF.Elements;
+using QuestPDF.Elements;
 using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Fluent
 {
     public static class DynamicComponentExtensions
     {
+        /// <summary>
+        /// Represents a dynamically generated section of the document.
+        /// Components are page-aware, understand their positioning, can dynamically construct other content elements, and assess their dimensions, enabling complex layout creations.
+        /// <a href="https://www.questpdf.com/concepts/dynamic-components.html">Learn more</a>
+        /// </summary>
+        /// <example>
+        /// <para>
+        /// Consider an invoice that presents all purchased items in a table format.
+        /// Instead of just showing the final total price under the table, the requirement is to display the cumulative prices on each separate page.
+        /// </para>
+        /// <para>Using the dynamic component, you can manually assemble the table, count how many items are visible on each page, calculate the price sum for items visible on each page, and then render the result under each sub-table.</para>
+        /// </example>
+        public static void Dynamic(this IContainer element, IDynamicComponent dynamicElement)
+        {
+            var componentProxy = DynamicComponentProxy.CreateFrom(dynamicElement);
+            element.Element(new DynamicHost(componentProxy));
+        }
+
         /// <summary>
         /// Represents a section of the document dynamically created based on its inner state.
         /// Components are page-aware, understand their positioning, can dynamically construct other content elements, and assess their dimensions, enabling complex layout creations.

+ 32 - 12
Source/QuestPDF/Infrastructure/IDynamicComponent.cs

@@ -1,4 +1,4 @@
-using System;
+using System;
 using QuestPDF.Elements;
 
 namespace QuestPDF.Infrastructure
@@ -18,6 +18,16 @@ namespace QuestPDF.Infrastructure
                 Compose = component.Compose
             };
         }
+        
+        internal static DynamicComponentProxy CreateFrom(IDynamicComponent component)
+        {
+            return new DynamicComponentProxy
+            {
+                GetState = () => null,
+                SetState = _ => { },
+                Compose = component.Compose
+            };
+        }
     }
 
     /// <summary>
@@ -37,6 +47,26 @@ namespace QuestPDF.Infrastructure
         public bool HasMoreContent { get; set; }
     }
     
+    /// <summary>
+    /// Represents a dynamically generated section of the document.
+    /// Components are page-aware, understand their positioning, can dynamically construct other content elements, and assess their dimensions, enabling complex layout creations.
+    /// </summary>
+    /// <remarks>
+    /// Though dynamic components offer great flexibility, be cautious of potential performance impacts.
+    /// </remarks>
+    public interface IDynamicComponent
+    {
+        /// <summary>
+        /// Method invoked by the library to plan and create new content for each page. 
+        /// </summary>
+        /// <remarks>
+        /// Remember, the QuestPDF library can invoke the Compose method more than once for each page and might adjust the state internally.
+        /// </remarks>
+        /// <param name="context">Context offering additional information (like current page number, entire document size) and the capability to produce dynamic content elements.</param>
+        /// <returns>Representation of content that should be placed on the current page.</returns>
+        DynamicComponentComposeResult Compose(DynamicContext context);
+    }
+    
     /// <summary>
     /// Represents a section of the document dynamically created based on its inner state.
     /// Components are page-aware, understand their positioning, can dynamically construct other content elements, and assess their dimensions, enabling complex layout creations.
@@ -45,7 +75,7 @@ namespace QuestPDF.Infrastructure
     /// Though dynamic components offer great flexibility, be cautious of potential performance impacts.
     /// </remarks>
     /// <typeparam name="TState">Structure type representing the internal state of the component.</typeparam>
-    public interface IDynamicComponent<TState> where TState : struct
+    public interface IDynamicComponent<TState> : IDynamicComponent where TState : struct
     {
         /// <summary>
         /// Represents the component's current state.
@@ -59,15 +89,5 @@ namespace QuestPDF.Infrastructure
         /// <para>Remember, the QuestPDF library can invoke the Compose method more than once for each page and might adjust the state internally.</para>
         /// </remarks>
         TState State { get; set; }
-        
-        /// <summary>
-        /// Method invoked by the library to plan and create new content for each page. 
-        /// </summary>
-        /// <remarks>
-        /// Remember, the QuestPDF library can invoke the Compose method more than once for each page and might adjust the state internally.
-        /// </remarks>
-        /// <param name="context">Context offering additional information (like current page number, entire document size) and the capability to produce dynamic content elements.</param>
-        /// <returns>Representation of content that should be placed on the current page.</returns>
-        DynamicComponentComposeResult Compose(DynamicContext context);
     }
 }