From 7a9c9c28cf193d51d9e301545e55e3177fe57007 Mon Sep 17 00:00:00 2001
From: Michael Danielczok <michael@pcsg.de>
Date: Tue, 29 Oct 2024 15:51:24 +0100
Subject: [PATCH] fix: improve timeline control

---
 locale.xml                                    | 26 ++++---
 site.xml                                      | 18 ++---
 src/QUI/Timeline/Bricks/Timeline.html         |  2 +-
 src/QUI/Timeline/Bricks/Timeline.php          | 23 +++---
 .../Controls/Timeline.VerticalBothSides.css   | 73 +++++--------------
 .../Controls/Timeline.VerticalBothSides.html  |  7 +-
 src/QUI/Timeline/Controls/Timeline.php        | 53 ++++++++++----
 7 files changed, 91 insertions(+), 111 deletions(-)

diff --git a/locale.xml b/locale.xml
index b92a783..efe4e9c 100644
--- a/locale.xml
+++ b/locale.xml
@@ -39,6 +39,10 @@
             <de><![CDATA[Bild verhalten]]></de>
             <en><![CDATA[Bild mode]]></en>
         </locale>
+        <locale name="brick.timeline.setting.imageFillMode.desc">
+            <de><![CDATA[Die Einstellung bezieht sich nur auf die Bilder, nicht auf die Icons]]></de>
+            <en><![CDATA[The setting refers only to the images, not to the icons]]></en>
+        </locale>
         <locale name="brick.timeline.setting.imageFillMode.cover">
             <de><![CDATA[Ganze Fläche füllen. Das Bild kann u.U. gestreckt werden (cover)]]></de>
             <en><![CDATA[Fill entire area. The image can be stretched (cover)]]></en>
@@ -98,22 +102,22 @@
             <en><![CDATA[Show links to the pages?]]></en>
         </locale>
 
-        <!-- image fit -->
-        <locale name="timeline.imageFit">
-            <de><![CDATA[Bild]]></de>
-            <en><![CDATA[Image]]></en>
+        <!-- image fill mode -->
+        <locale name="timeline.setting.imageFillMode">
+            <de><![CDATA[Bild verhalten]]></de>
+            <en><![CDATA[Bild mode]]></en>
         </locale>
-        <locale name="timeline.imageFit.desc">
+        <locale name="timeline.setting.imageFillMode.desc">
             <de><![CDATA[Die Einstellung bezieht sich nur auf die Bilder, nicht auf die Icons]]></de>
             <en><![CDATA[The setting refers only to the images, not to the icons]]></en>
         </locale>
-        <locale name="timeline.imageFit.original">
-            <de><![CDATA[Original]]></de>
-            <en><![CDATA[Original]]></en>
+        <locale name="timeline.setting.imageFillMode.cover">
+            <de><![CDATA[Ganze Fläche füllen. Das Bild kann u.U. gestreckt werden (cover)]]></de>
+            <en><![CDATA[Fill entire area. The image can be stretched (cover)]]></en>
         </locale>
-        <locale name="timeline.imageFit.cover">
-            <de><![CDATA[Decken (Bild kann gestreckt werden)]]></de>
-            <en><![CDATA[Cover (image can be stretched)]]></en>
+        <locale name="timeline.setting.imageFillMode.contain">
+            <de><![CDATA[Ganzes Bild anzeigen (contain)]]></de>
+            <en><![CDATA[Show full image (contain)]]></en>
         </locale>
 
         <!-- counter text -->
diff --git a/site.xml b/site.xml
index b17d8db..480c246 100644
--- a/site.xml
+++ b/site.xml
@@ -12,8 +12,8 @@
             <!-- list attributes -->
             <attributes>
                 <attribute default="VerticalBothSides">quiqqer.timeline.display</attribute>
-                <attribute default="1">quiqqer.timeline.showLinks</attribute>
-                <attribute default="original">quiqqer.timeline.imageFit</attribute>
+                <attribute default="0">quiqqer.timeline.showLinks</attribute>
+                <attribute default="cover">quiqqer.timeline.imageFillMode</attribute>
             </attributes>
 
             <!-- list settings -->
@@ -44,23 +44,23 @@
                             </text>
                         </input>
 
-                        <select conf="quiqqer.timeline.imageFit">
+                        <select conf="quiqqer.timeline.imageFillMode">
                             <text>
                                 <locale group="quiqqer/timeline"
-                                        var="timeline.imageFit"/>
+                                        var="timeline.setting.imageFillMode"/>
                             </text>
                             <description>
                                 <locale group="quiqqer/timeline"
-                                        var="timeline.imageFit.desc"/>
+                                        var="timeline.setting.imageFillMode.desc"/>
                             </description>
 
-                            <option value="original">
+                            <option value="cover">
                                 <locale group="quiqqer/timeline"
-                                        var="timeline.imageFit.original"/>
+                                        var="timeline.setting.imageFillMode.cover"/>
                             </option>
-                            <option value="cover">
+                            <option value="contain">
                                 <locale group="quiqqer/timeline"
-                                        var="timeline.imageFit.cover"/>
+                                        var="timeline.setting.imageFillMode.contain"/>
                             </option>
                         </select>
 
diff --git a/src/QUI/Timeline/Bricks/Timeline.html b/src/QUI/Timeline/Bricks/Timeline.html
index aba7f76..9c451cf 100644
--- a/src/QUI/Timeline/Bricks/Timeline.html
+++ b/src/QUI/Timeline/Bricks/Timeline.html
@@ -11,5 +11,5 @@
 {/if}
 
 <div class="control-template">
-    {$timelineHtml}
+    {$TimelineControl->create()}
 </div>
diff --git a/src/QUI/Timeline/Bricks/Timeline.php b/src/QUI/Timeline/Bricks/Timeline.php
index 8f33e19..b9d053c 100644
--- a/src/QUI/Timeline/Bricks/Timeline.php
+++ b/src/QUI/Timeline/Bricks/Timeline.php
@@ -49,27 +49,26 @@ public function getBody(): string
 
         $limit = $this->getAttribute('limit');
 
-        if ($limit === '') {
+        if (!$limit || $limit < 1) {
             $limit = 10;
         }
 
-        if ($limit <= 0) {
-            $limit = 1;
-        }
+        $attributes = [
+            'parentInputList' => $this->getAttribute('site'),
+            'imageFillMode' => $this->getAttribute('imageFillMode'),
+            'order' => $this->getAttribute('order'),
+            'showLinks' => $this->getAttribute('showLinks'),
+            'display' => $this->getAttribute('template'),
+            'limit' => $limit,
+        ];
 
-        $Control->setAttribute('parentInputList', $this->getAttribute('site'));
-        $Control->setAttribute('imageFillMode', $this->getAttribute('imageFillMode'));
-        $Control->setAttribute('order', $this->getAttribute('order'));
-        $Control->setAttribute('showLinks', $this->getAttribute('showLinks'));
-        $Control->setAttribute('display', $this->getAttribute('template'));
-        $Control->setAttribute('limit', $limit);
-        $html = $Control->create();
+        $Control->setAttributes($attributes);
 
         $this->addCSSFiles($Control->getCSSFiles());
 
         $options = [
             'this' => $this,
-            'timelineHtml' => $html
+            'TimelineControl' => $Control
         ];
 
         $Engine->assign($options);
diff --git a/src/QUI/Timeline/Controls/Timeline.VerticalBothSides.css b/src/QUI/Timeline/Controls/Timeline.VerticalBothSides.css
index b086e39..36665ff 100644
--- a/src/QUI/Timeline/Controls/Timeline.VerticalBothSides.css
+++ b/src/QUI/Timeline/Controls/Timeline.VerticalBothSides.css
@@ -25,7 +25,7 @@
 .timeline-entry-icon-wrapper {
     color: #aaa;
     font-size: 53px;
-    border: 8px solid #aaa;
+    border: 8px solid var(--_qui-timeline-timeline__borderColor);
     text-align: center;
     height: 120px;
     width: 120px;
@@ -48,7 +48,7 @@
     border-right: none;
     border-bottom: 20px solid transparent;
     border-top: 20px solid transparent;
-    border-left: 30px solid #aaa;
+    border-left: 30px solid var(--_qui-timeline-timeline__borderColor);
 }
 
 /* image */
@@ -62,11 +62,10 @@
     justify-content: center;
 }
 
-.timeline-entry-icon-wrapper picture img.image-fit-cover {
-    object-fit: cover;
+.timeline-entry__image {
+    object-fit: var(--_qui-timeline-timeline-imageFillMode);
     width: 100%;
     height: 100%;
-
 }
 
 /* line - middle */
@@ -96,7 +95,7 @@
     height: 12px;
     background: none;
     border-radius: 16px;
-    border: 2px solid #999;
+    border: 2px solid var(--_qui-timeline-timeline__borderColor);
 }
 
 .timeline-entry-line-caption-number {
@@ -111,7 +110,6 @@
     position: relative;
 }
 
-
 /* text content */
 .timeline-entry-desc {
     padding-left: 40px;
@@ -133,10 +131,7 @@
     font-size: 16px;
 }
 
-
 /* nth child */
-/*************/
-
 .timeline-entry:nth-child(2n+2) .timeline-entry-desc {
     order: 1;
     padding-left: 0;
@@ -150,7 +145,7 @@
 .timeline-entry:nth-child(2n+2) .timeline-entry-icon-wrapper:after {
     right: auto;
     left: -30px;
-    border-right: 30px solid #aaa;
+    border-right: 30px solid var(--_qui-timeline-timeline__borderColor);
     border-left: none;
 }
 
@@ -165,62 +160,28 @@
     content: '';
 }
 
-
-/* FARBEN */
-/* 1 */
-.timeline-entry:nth-child(5n+1) .timeline-entry-icon-wrapper,
-.timeline-entry:nth-child(5n+1) .timeline-entry-line-caption:after {
-    border-color: lightcoral;
-}
-.timeline-entry:nth-child(5n+1) .timeline-entry-icon-wrapper:after {
-    border-left-color: lightcoral;
-    border-right-color: lightcoral;
+/* colors */
+.timeline-entry:nth-child(5n+1) {
+    --_qui-timeline-timeline__borderColor: var(--qui-timeline-timeline__borderColor, #f08080);
 }
 
-/* 2 */
-.timeline-entry:nth-child(5n+2) .timeline-entry-icon-wrapper,
-.timeline-entry:nth-child(5n+2) .timeline-entry-line-caption:after {
-    border-color: lightblue;
-}
-.timeline-entry:nth-child(5n+2) .timeline-entry-icon-wrapper:after {
-    border-left-color: lightblue;
-    border-right-color: lightblue;
+.timeline-entry:nth-child(5n+2) {
+    --_qui-timeline-timeline__borderColor: var(--qui-timeline-timeline__borderColor, #add8e6);
 }
 
-/* 3 */
-.timeline-entry:nth-child(5n+3) .timeline-entry-icon-wrapper,
-.timeline-entry:nth-child(5n+3) .timeline-entry-line-caption:after {
-    border-color: lightsalmon;
-}
-.timeline-entry:nth-child(5n+3) .timeline-entry-icon-wrapper:after {
-    border-left-color: lightsalmon;
-    border-right-color: lightsalmon;
+.timeline-entry:nth-child(5n+3) {
+    --_qui-timeline-timeline__borderColor: var(--qui-timeline-timeline__borderColor, #ffa07a);
 }
 
-/* 4 */
-.timeline-entry:nth-child(5n+4) .timeline-entry-icon-wrapper,
-.timeline-entry:nth-child(5n+4) .timeline-entry-line-caption:after {
-    border-color: lightseagreen;
-}
-.timeline-entry:nth-child(5n+4) .timeline-entry-icon-wrapper:after {
-    border-left-color: lightseagreen;
-    border-right-color: lightseagreen;
+.timeline-entry:nth-child(5n+4) {
+    --_qui-timeline-timeline__borderColor: var(--qui-timeline-timeline__borderColor, #1fb2aa);
 }
 
-/* 5 */
-.timeline-entry:nth-child(5n+5) .timeline-entry-icon-wrapper,
-.timeline-entry:nth-child(5n+5) .timeline-entry-line-caption:after {
-    border-color: yellowgreen;
-}
-.timeline-entry:nth-child(5n+5) .timeline-entry-icon-wrapper:after {
-    border-left-color: yellowgreen;
-    border-right-color: yellowgreen;
+.timeline-entry:nth-child(5n+5) {
+    --_qui-timeline-timeline__borderColor: var(--qui-timeline-timeline__borderColor, #9acd32);
 }
 
-/***************/
 /* media query */
-/***************/
-
 @media screen and (max-width: 767px) {
     .timeline-entry:nth-child(2n+2) .timeline-entry-desc {
         order: 3;
diff --git a/src/QUI/Timeline/Controls/Timeline.VerticalBothSides.html b/src/QUI/Timeline/Controls/Timeline.VerticalBothSides.html
index 11a45b2..115e5a1 100644
--- a/src/QUI/Timeline/Controls/Timeline.VerticalBothSides.html
+++ b/src/QUI/Timeline/Controls/Timeline.VerticalBothSides.html
@@ -26,18 +26,13 @@
                  itemtype="{$this->getAttribute('child-itemtype')}"
                  class="timeline-entry"
         >
-            {assign var=imageFitClass value=''}
-            {if $imageFit == 'cover'}
-                {assign var=imageFitClass value='image-fit-cover'}
-            {/if}
-
             <!-- Entry Icon -->
             <div class="timeline-entry-icon">
                 {if $Child->getAttribute('image_site')}
                 <div class="timeline-entry-icon-wrapper">
                     {image src=$Child->getAttribute('image_site') max-width="500" max-height="500" type="resize"
                     title="{$Child->getAttribute('title')}"
-                    class="$imageFitClass"}
+                    class="timeline-entry__image"}
                 </div>
                 {/if}
             </div>
diff --git a/src/QUI/Timeline/Controls/Timeline.php b/src/QUI/Timeline/Controls/Timeline.php
index 1a64be3..226d079 100644
--- a/src/QUI/Timeline/Controls/Timeline.php
+++ b/src/QUI/Timeline/Controls/Timeline.php
@@ -7,6 +7,7 @@
 namespace QUI\Timeline\Controls;
 
 use QUI;
+use QUI\Exception;
 use QUI\Projects\Site\Utils;
 
 /**
@@ -56,21 +57,13 @@ public function getBody(): string
         $Engine = QUI::getTemplateManager()->getEngine();
         $Site = $this->getSite();
         $Project = $this->getProject();
-        $limit = $this->getAttribute('limit');
 
         if (!$Site && !$this->getAttribute('parentInputList')) {
             return '';
         }
 
-        if (!$limit) {
-            $limit = 10;
-        }
-
-        $parents = $this->getAttribute('parentInputList');
-
-        if (!$parents) {
-            $parents = $Site->getId();
-        }
+        $limit = $this->getAttribute('limit') ?: 10;
+        $parents = $this->getAttribute('parentInputList') ?: $Site->getId();
 
         // only active sites
         $where['active'] = 1;
@@ -100,6 +93,10 @@ public function getBody(): string
             'imageFit' => $this->getAttribute('imageFillMode')
         ]);
 
+        if ($this->getAttribute('imageFillMode')) {
+            $this->setCustomVariable('imageFillMode', $this->getAttribute('imageFillMode'));
+        }
+
         // load custom template (if set)
         if (
             $this->getAttribute('displayTemplate')
@@ -115,19 +112,19 @@ public function getBody(): string
             return $Engine->fetch($this->getAttribute('displayTemplate'));
         }
 
+        // Load default template
+        $cssFile = dirname(__FILE__) . '/Timeline.' . $this->getAttribute('display') . '.css';
+        $templateFile = dirname(__FILE__) . '/Timeline.' . $this->getAttribute('display') . '.html';
 
-        // template
-        $css = dirname(__FILE__) . '/Timeline.' . $this->getAttribute('display') . '.css';
-        $template = dirname(__FILE__) . '/Timeline.' . $this->getAttribute('display') . '.html';
-
-        $this->addCSSFile($css);
+        $this->addCSSFile($cssFile);
 
-        return $Engine->fetch($template);
+        return $Engine->fetch($templateFile);
     }
 
 
     /**
      * @return null|QUI\Projects\Site
+     * @throws Exception
      */
     protected function getSite(): ?QUI\Interfaces\Projects\Site
     {
@@ -141,4 +138,28 @@ protected function getSite(): ?QUI\Interfaces\Projects\Site
 
         return $Site;
     }
+
+    /**
+     * Set custom css variable to the control as inline style
+     *   --_qui-timeline-timeline-$name: var(--qui-timeline-timeline-$name, $value);
+     *
+     * Example:
+     *   --_qui-timeline-timeline-imageFillMode: var(--qui-timeline-timeline-imageFillMode, 'cover');
+     *
+     * @param string $name
+     * @param string $value
+     *
+     * @return void
+     */
+    private function setCustomVariable(string $name, string $value): void
+    {
+        if (!$name || !$value) {
+            return;
+        }
+
+        $this->setStyle(
+            '--_qui-timeline-timeline-' . $name,
+            'var(--qui-timeline-timeline-' . $name . ', ' . $value . ')'
+        );
+    }
 }
-- 
GitLab