From 4ec045d1d59013daa256019743c3fd7a613236ad Mon Sep 17 00:00:00 2001
From: Joshua Sedler <55201-Grevil@users.noreply.drupalcode.org>
Date: Fri, 25 Apr 2025 10:46:59 +0000
Subject: [PATCH] Issue #3521021: Add parent calls, adjust templates and fix
 formatter settings

---
 .../VidstackPlayerFormatterTrait.php          | 70 +++++++++++--------
 .../VidstackPlayerRemoteVideoFormatter.php    | 50 ++++++++-----
 .../VidstackPlayerVideoFormatter.php          | 64 +++++++----------
 .../vidstack-player-remote-video.html.twig    |  9 ++-
 templates/vidstack-player-video.html.twig     |  9 ++-
 5 files changed, 114 insertions(+), 88 deletions(-)

diff --git a/src/Plugin/Field/FieldFormatter/VidstackPlayerFormatterTrait.php b/src/Plugin/Field/FieldFormatter/VidstackPlayerFormatterTrait.php
index 2c1dee0..8f2b449 100644
--- a/src/Plugin/Field/FieldFormatter/VidstackPlayerFormatterTrait.php
+++ b/src/Plugin/Field/FieldFormatter/VidstackPlayerFormatterTrait.php
@@ -6,9 +6,12 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
 
 /**
  * Shared trait for Vidstack Player video formatters.
+ *
+ * NOTE, this trait does not portray ALL shared settings. Notably:
+ * 'controls', 'autoplay' and 'loop' are shared, but not part of this trait.
+ * This is because they get partially inherited by some formatters parent class.
  */
 trait VidstackPlayerFormatterTrait {
-
   use StringTranslationTrait;
 
   /**
@@ -20,60 +23,67 @@ trait VidstackPlayerFormatterTrait {
    * @return array
    *   The shared settings to reduce duplicate code.
    */
-  public function getSharedSettings(array $form) {
-    $form['autoplay'] = [
-      '#title' => $this->t('Autoplay'),
-      '#type' => 'checkbox',
-      '#description' => $this->t('Play the video immediately upon page load. This is generally advised against on UX grounds. It is also disabled by default in some browsers.'),
-      '#default_value' => $this->getSetting('autoplay'),
-    ];
-    $form['loop'] = [
-      '#title' => $this->t('Loop'),
-      '#type' => 'checkbox',
-      '#description' => $this->t('Loop the video.'),
-      '#default_value' => $this->getSetting('loop'),
-    ];
+  protected function getSharedSettings(array $form) {
+    // @see Trait class docblock.
     $form['muted'] = [
       '#title' => $this->t('Muted'),
       '#type' => 'checkbox',
       '#description' => $this->t('Start the video muted.'),
       '#default_value' => $this->getSetting('muted'),
     ];
-    $form['controls'] = [
-      '#title' => $this->t('Default controls'),
-      '#type' => 'checkbox',
-      '#description' => $this->t('Show the default controls of the video (e.g. from the browser itself or a provider like YouTube).'),
-      '#default_value' => $this->getSetting('controls'),
-    ];
     $form['load'] = [
       '#title' => $this->t('Load strategy'),
       '#type' => 'select',
-      '#options' => [
-        'eager' => $this->t('Load immediately'),
-        'idle' => $this->t('Load after the page is loaded'),
-        'visible' => $this->t('Load after becoming visible'),
-        'play' => $this->t('Load after hitting play'),
-      ],
+      '#options' => $this->getLoadStrategyOptions(),
       '#description' => $this->t('The load strategy that will be used to determine when the video content should start loading.'),
       '#default_value' => $this->getSetting('load'),
     ];
     return $form;
   }
 
+  /**
+   * Returns the load strategy form options.
+   *
+   * @return array
+   *   The load strategy options.
+   */
+  protected function getLoadStrategyOptions() {
+    return [
+      'eager' => $this->t('Load immediately'),
+      'idle' => $this->t('Load after the page is loaded'),
+      'visible' => $this->t('Load after becoming visible'),
+      'play' => $this->t('Load after hitting play'),
+    ];
+  }
+
   /**
    * Returns the default settings which are shared between formatters.
    *
    * @return array
    *   The shared default settings to reduce duplicate code.
    */
-  public static function getSharedDefaultSettings() {
+  protected static function getSharedDefaultSettings() {
     return [
-      'autoplay' => FALSE,
-      'loop' => FALSE,
       'muted' => FALSE,
-      'controls' => FALSE,
       'load' => 'visible',
     ];
   }
 
+  /**
+   * Returns the settings summary shared between formatters.
+   *
+   * @return array
+   *   The shared settings summary.
+   */
+  protected function getSharedSettingsSummary() {
+    $summary = [];
+    $summary[] = $this->t('Muted: %muted', [
+      '%muted' => $this->getSetting('muted') ? $this->t('yes') : $this->t('no'),
+    ]);
+    $summary[] = $this->t('Load strategy: %load', [
+      '%load' => $this->getLoadStrategyOptions()[$this->getSetting('load')],
+    ]);
+    return $summary;
+  }
+
 }
diff --git a/src/Plugin/Field/FieldFormatter/VidstackPlayerRemoteVideoFormatter.php b/src/Plugin/Field/FieldFormatter/VidstackPlayerRemoteVideoFormatter.php
index 49a7a8a..48173e7 100644
--- a/src/Plugin/Field/FieldFormatter/VidstackPlayerRemoteVideoFormatter.php
+++ b/src/Plugin/Field/FieldFormatter/VidstackPlayerRemoteVideoFormatter.php
@@ -25,7 +25,6 @@ use Drupal\media\Plugin\media\Source\OEmbedInterface;
  * )
  */
 class VidstackPlayerRemoteVideoFormatter extends FormatterBase {
-
   use VidstackPlayerFormatterTrait;
 
   /**
@@ -80,20 +79,17 @@ class VidstackPlayerRemoteVideoFormatter extends FormatterBase {
    * {@inheritdoc}
    */
   public function settingsSummary() {
-    $loadStrategies = [
-      'eager' => $this->t('Load immediately'),
-      'idle' => $this->t('Load after page-load'),
-      'visible' => $this->t('Load after visible'),
-      'play' => $this->t('Load after play'),
-    ];
-    $summary = [];
-    $summary[] = $this->t('Autoplay: <strong>@autoplay</strong><br>Loop: <strong>@loop</strong><br>Muted: <strong>@muted</strong><br>Default controls: <strong>@controls</strong><br>Load strategy: <strong>@load</strong>', [
-      '@autoplay' => $this->getSetting('autoplay') ? $this->t('Yes') : $this->t('No'),
-      '@loop' => $this->getSetting('loop') ? $this->t('Yes') : $this->t('No'),
-      '@muted' => $this->getSetting('muted') ? $this->t('Yes') : $this->t('No'),
-      '@controls' => $this->getSetting('controls') ? $this->t('Yes') : $this->t('No'),
-      '@load' => $loadStrategies[$this->getSetting('load')],
+    $summary = parent::settingsSummary();
+    $summary[] = $this->t('Playback controls: %controls', [
+      '%controls' => $this->getSetting('controls') ? $this->t('visible') : $this->t('hidden'),
+    ]);
+    $summary[] = $this->t('Autoplay: %autoplay', [
+      '%autoplay' => $this->getSetting('autoplay') ? $this->t('yes') : $this->t('no'),
+    ]);
+    $summary[] = $this->t('Loop: %loop', [
+      '%loop' => $this->getSetting('loop') ? $this->t('yes') : $this->t('no'),
     ]);
+    $summary += $this->getSharedSettingsSummary();
     return $summary;
   }
 
@@ -101,14 +97,36 @@ class VidstackPlayerRemoteVideoFormatter extends FormatterBase {
    * {@inheritdoc}
    */
   public static function defaultSettings() {
-    return self::getSharedDefaultSettings();
+    $settings = parent::defaultSettings();
+    $settings['controls'] = FALSE;
+    $settings['autoplay'] = FALSE;
+    $settings['loop'] = FALSE;
+    $settings += self::getSharedDefaultSettings();
+    return $settings;
   }
 
   /**
    * {@inheritdoc}
    */
   public function settingsForm(array $form, FormStateInterface $form_state) {
-    return $this->getSharedSettings($form);
+    $form = parent::settingsForm($form, $form_state);
+    $form['controls'] = [
+      '#title' => $this->t('Show playback controls'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->getSetting('controls'),
+    ];
+    $form['autoplay'] = [
+      '#title' => $this->t('Autoplay'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->getSetting('autoplay'),
+    ];
+    $form['loop'] = [
+      '#title' => $this->t('Loop'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->getSetting('loop'),
+    ];
+    $form += $this->getSharedSettings($form);
+    return $form;
   }
 
   /**
diff --git a/src/Plugin/Field/FieldFormatter/VidstackPlayerVideoFormatter.php b/src/Plugin/Field/FieldFormatter/VidstackPlayerVideoFormatter.php
index 1bdd04a..5608ac8 100644
--- a/src/Plugin/Field/FieldFormatter/VidstackPlayerVideoFormatter.php
+++ b/src/Plugin/Field/FieldFormatter/VidstackPlayerVideoFormatter.php
@@ -24,7 +24,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  * )
  */
 class VidstackPlayerVideoFormatter extends FileMediaFormatterBase implements FileMediaFormatterInterface {
-
   use VidstackPlayerFormatterTrait;
 
   /**
@@ -86,38 +85,21 @@ class VidstackPlayerVideoFormatter extends FileMediaFormatterBase implements Fil
    * {@inheritdoc}
    */
   public function settingsSummary() {
-    $loadStrategies = [
-      'eager' => $this->t('Load immediately'),
-      'idle' => $this->t('Load after page-load'),
-      'visible' => $this->t('Load after visible'),
-      'play' => $this->t('Load after play'),
-    ];
-    $summary = [];
-    $posterValue = '';
-    switch ($this->getSetting('poster')) {
-      case 'none':
-        $posterValue = $this->t('None (Show first frame)');
-        break;
-
-      case 'custom':
-        $posterValue = $this->t('Custom URL');
-        break;
-
-      default:
-        $posterValue = $this->t('Field "@name" (@style)', [
-          '@name' => $this->getSetting('poster'),
-          '@style' => $this->getSetting('posterStyle') === '' ? $this->t('None (original image)') : image_style_options(TRUE)[$this->getSetting('posterStyle')],
-        ]);
-        break;
-    }
-    $summary[] = $this->t('Autoplay: <strong>@autoplay</strong><br>Loop: <strong>@loop</strong><br>Muted: <strong>@muted</strong><br>Default controls: <strong>@controls</strong><br>Load strategy: <strong>@load</strong><br>Poster image: <strong>@poster</strong><br>Poster load strategy: <strong>@posterLoad</strong>', [
-      '@autoplay' => $this->getSetting('autoplay') ? $this->t('Yes') : $this->t('No'),
-      '@loop' => $this->getSetting('loop') ? $this->t('Yes') : $this->t('No'),
-      '@muted' => $this->getSetting('muted') ? $this->t('Yes') : $this->t('No'),
-      '@controls' => $this->getSetting('controls') ? $this->t('Yes') : $this->t('No'),
-      '@load' => $loadStrategies[$this->getSetting('load')],
-      '@poster' => $posterValue,
-      '@posterLoad' => $loadStrategies[$this->getSetting('posterLoad')],
+    $summary = parent::settingsSummary();
+    $summary += $this->getSharedSettingsSummary();
+    $posterValue = match($this->getSetting('poster')) {
+      'none' => $this->t('None (Show first frame)'),
+      'custom' => $this->t('Custom URL'),
+      default => $this->t('Field "%name" (%style)', [
+        '%name' => $this->getSetting('poster'),
+        '%style' => $this->getSetting('posterStyle') === '' ? $this->t('None (original image)') : image_style_options(TRUE)[$this->getSetting('posterStyle')],
+      ]),
+    };
+    $summary[] = $this->t('Poster image: %poster', [
+      '%poster' => $posterValue,
+    ]);
+    $summary[] = $this->t('Poster load strategy: %load', [
+      '%load' => $this->getLoadStrategyOptions()[$this->getSetting('posterLoad')],
     ]);
     return $summary;
   }
@@ -126,19 +108,21 @@ class VidstackPlayerVideoFormatter extends FileMediaFormatterBase implements Fil
    * {@inheritdoc}
    */
   public static function defaultSettings() {
-    return self::getSharedDefaultSettings() + [
-      'poster' => 'none',
-      'posterUrl' => '',
-      'posterLoad' => 'visible',
-      'posterStyle' => '',
-    ];
+    $settings = parent::defaultSettings();
+    $settings += self::getSharedDefaultSettings();
+    $settings['poster'] = 'none';
+    $settings['posterUrl'] = '';
+    $settings['posterLoad'] = 'visible';
+    $settings['posterStyle'] = '';
+    return $settings;
   }
 
   /**
    * {@inheritdoc}
    */
   public function settingsForm(array $form, FormStateInterface $form_state) {
-    $form = $this->getSharedSettings($form);
+    $form = parent::settingsForm($form, $form_state);
+    $form += $this->getSharedSettings($form);
 
     $form['poster'] = [
       '#title' => $this->t('Poster image'),
diff --git a/templates/vidstack-player-remote-video.html.twig b/templates/vidstack-player-remote-video.html.twig
index f621e33..3b66ea3 100644
--- a/templates/vidstack-player-remote-video.html.twig
+++ b/templates/vidstack-player-remote-video.html.twig
@@ -1 +1,8 @@
-<iframe {{ attributes }} allowfullscreen></iframe>
+{#
+  NOTE: We are using "progressive enhancement" here, as documented:
+  "The target can be any element but if it’s a <audio>, <video>, or
+  <iframe> element it will be replaced and enhanced (i.e., progressive
+  enhancement)."
+  @see https://vidstack.io/docs/player/getting-started/installation/javascript/?provider=youtube&styling=default-layout&install=cdn
+#}
+<iframe {{ attributes }}></iframe>
diff --git a/templates/vidstack-player-video.html.twig b/templates/vidstack-player-video.html.twig
index cecb4c0..d656593 100644
--- a/templates/vidstack-player-video.html.twig
+++ b/templates/vidstack-player-video.html.twig
@@ -1,4 +1,11 @@
-<video {{ attributes }} playsinline>
+{#
+  NOTE: We are using "progressive enhancement" here, as documented:
+  "The target can be any element but if it’s a <audio>, <video>, or
+  <iframe> element it will be replaced and enhanced (i.e., progressive
+  enhancement)."
+  @see https://vidstack.io/docs/player/getting-started/installation/javascript/?provider=video&styling=default-layout&install=cdn
+#}
+<video {{ attributes }}>
   {% for file in files %}
     <source {{ file.source_attributes }} />
   {% endfor %}
-- 
GitLab