From f531a518b99e3dcd3e5aba21f9e3e6a2d49b83b8 Mon Sep 17 00:00:00 2001
From: MattMo <matt@montoyatech.com>
Date: Tue, 25 Jul 2023 23:21:54 -0700
Subject: [PATCH] Added new converter template that can be used to convert a
 value into something that can be rendered.

---
 ignite-template.js | 125 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 124 insertions(+), 1 deletion(-)

diff --git a/ignite-template.js b/ignite-template.js
index 2139600..e547a7f 100644
--- a/ignite-template.js
+++ b/ignite-template.js
@@ -385,7 +385,7 @@ class IgniteTemplate {
 
     /**
      * Sets the inner html of the element to be constructed by this template.
-     * @param {String|IgniteProperty} value InnerHTML to set for element. If a property is passed the html will auto update.
+     * @param {String|IgniteProperty|IgniteProperty[]} value InnerHTML to set for element. If a property is passed the html will auto update.
      * @param {Function} converter Optional function that can be used to convert the value if needed.
      * @returns This ignite template so funciton calls can be chained.
      */
@@ -2528,6 +2528,128 @@ class list extends IgniteTemplate {
     }
 }
 
+/**
+ * A special ignite template that converts a value to HTML or a Template and supports
+ * dynamic value changes and dynamic converter changes.
+ */
+class converter extends IgniteTemplate {
+    constructor(value, converter) {
+        super();
+
+        if (converter instanceof IgniteProperty) {
+            this._callbacks.push(converter.attachOnChange((oldValue, newValue) => this.onConverterChanged(newValue)));
+        
+            this.converter = converter.value;
+        } else if (converter instanceof Function) {
+            this.converter = converter;
+        }
+
+        if (value instanceof IgniteProperty) {
+            this._callbacks.push(value.attachOnChange((oldValue, newValue) => this.onValueChanged(newValue)));
+            this._callbacks.push(value.attachOnPush((list, items) => this.onValueChanged(list)));
+            this._callbacks.push(value.attachOnUnshift((list, items) => this.onValueChanged(list)));
+            this._callbacks.push(value.attachOnPop(list => this.onValueChanged(list)));
+            this._callbacks.push(value.attachOnShift(list => this.onValueChanged(list)));
+            this._callbacks.push(value.attachOnSplice((list, start, deleteCount, items) => this.onValueChanged(list)));
+
+            this.value = value.value;
+        } else if (value) {
+            this.value = value;
+        }
+
+        var child = this.value;
+
+        if (this.converter) {
+            child = this.converter(this.value);
+        }
+        
+        if (child instanceof IgniteTemplate) {
+            this.child = child;
+        } else if (child) {
+            this.child = new html(child);
+        } else {
+            this.child = null;
+        }
+
+        this.tagName = "converter";
+    }
+
+    construct(parent, sibling) {
+        //Don't construct if we have no parent, no sibling and no element.
+        if (!parent && !sibling && !this.element) {
+            return;
+        }
+
+        if (!this.element) {
+            this.element = window.document.createTextNode(""); //Use a textnode as our placeholder
+
+            if (sibling) {
+                sibling.parentElement.insertBefore(this.element, sibling);
+            } else {
+                parent.appendChild(this.element);
+            }
+        } else {
+            parent = this.element.parentElement;
+        }
+
+        if (this.child) {
+            this.child.construct(parent, this.element);
+        }
+    }
+
+    onValueChanged(newValue) {
+        this.value = newValue;
+
+        if (this.child) {
+            this.child.deconstruct();
+        }
+
+        var child = newValue;
+
+        if (this.converter) {
+            child = this.converter(child);
+        } 
+        
+        if (child instanceof IgniteTemplate) {
+            this.child = child;
+
+            this.child.construct(null, this.element);
+        } else if (child) {
+            this.child = new html(child);
+
+            this.child.construct(null, this.element);
+        } else {
+            this.child = null;
+        }
+    }
+
+    onConverterChanged(newConverter) {
+        this.converter = newConverter;
+
+        if (this.child) {
+            this.child.deconstruct();
+        }
+
+        var child = this.value;
+
+        if (this.converter) {
+            child = this.converter(child);
+        }
+
+        if (child instanceof IgniteTemplate) {
+            this.child = child;
+
+            this.child.construct(null, this.element);
+        } else if (child) {
+            this.child = new html(child);
+
+            this.child.construct(null, this.element);
+        } else {
+            this.child = null;
+        }
+    }
+}
+
 /**
  * A slot template that mimicks the functionality of a slot element in Web Components. This can
  * be used to place children of a IgniteElement anywhere in the DOM. Slots don't actually construct an element,
@@ -3417,6 +3539,7 @@ export {
     text,
     html,
     list,
+    converter,
     a,
     input,
     textarea,