From 590b3dff6d836a26b819611909c6072b0b2533ad Mon Sep 17 00:00:00 2001
From: Christian Wiedemann <christian.wiedemann@key-tec.de>
Date: Sat, 10 Feb 2024 20:06:00 +0100
Subject: [PATCH] Issue #3420628: Introduce settings array to source plugin

---
 .../FieldFormatter/ComponentAllFormatter.php  |   2 +-
 src/Element/ComponentFormBuilder.php          |   1 -
 src/Element/ComponentFormBuilderBase.php      |   2 +-
 src/Element/ComponentFormPropsBuilder.php     |   9 +-
 src/Element/ComponentFormSlotsBuilder.php     |   2 +-
 src/Element/DropdownAction.php                |   4 +-
 src/Form/ComponentFormBuilderTrait.php        |   2 +-
 .../UiPatterns/Source/AttributesWidget.php    |   6 +-
 .../UiPatterns/Source/ChecboxesWidget.php     |   6 +-
 .../UiPatterns/Source/CheckboxWidget.php      |   4 +-
 src/Plugin/UiPatterns/Source/ColorWidget.php  |   4 +-
 .../UiPatterns/Source/ListTextareaWidget.php  |   6 +-
 src/Plugin/UiPatterns/Source/MenuSource.php   |   4 +-
 src/Plugin/UiPatterns/Source/NumberWidget.php |   6 +-
 src/Plugin/UiPatterns/Source/PathSource.php   |   4 +-
 src/Plugin/UiPatterns/Source/RadiosWidget.php |   6 +-
 src/Plugin/UiPatterns/Source/SelectWidget.php |   6 +-
 .../UiPatterns/Source/TextfieldWidget.php     |   4 +-
 src/Plugin/UiPatterns/Source/TokenSource.php  |   6 +-
 src/Plugin/UiPatterns/Source/UrlWidget.php    |   4 +-
 .../UiPatterns/Source/WysiwygWidget.php       |   6 +-
 src/SourceInterface.php                       |  68 +++++++++++-
 src/SourcePluginBase.php                      | 100 +++++++++++-------
 src/SourcePluginManager.php                   |   7 +-
 .../src/Plugin/UiPatterns/Source/Foo.php      |   4 +-
 25 files changed, 184 insertions(+), 89 deletions(-)

diff --git a/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentAllFormatter.php b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentAllFormatter.php
index fefcc6540..924e56587 100644
--- a/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentAllFormatter.php
+++ b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentAllFormatter.php
@@ -51,7 +51,7 @@ class ComponentAllFormatter extends FormatterBase {
   /**
    * {@inheritdoc}
    */
-  public function settingsForm(array $form, FormStateInterface $form_state) {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     return $this->componentSettingsForm($form, $form_state);
   }
 
diff --git a/src/Element/ComponentFormBuilder.php b/src/Element/ComponentFormBuilder.php
index 71273a0fa..5f1047d7b 100644
--- a/src/Element/ComponentFormBuilder.php
+++ b/src/Element/ComponentFormBuilder.php
@@ -5,7 +5,6 @@ namespace Drupal\ui_patterns\Element;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Render\Element\FormElement;
 
 /**
  * Provides a Component form builder element.
diff --git a/src/Element/ComponentFormBuilderBase.php b/src/Element/ComponentFormBuilderBase.php
index b9ca7441b..9ce73c78d 100644
--- a/src/Element/ComponentFormBuilderBase.php
+++ b/src/Element/ComponentFormBuilderBase.php
@@ -110,7 +110,7 @@ abstract class ComponentFormBuilderBase extends FormElement {
    * Returns the parents belonging to the form builder.
    *
    * @param array $parents
-   *  The form_state '#parents' value.
+   *   The form_state '#parents' value.
    *
    * @return array
    *   The parents to locate the form builder.
diff --git a/src/Element/ComponentFormPropsBuilder.php b/src/Element/ComponentFormPropsBuilder.php
index ab2f79a41..54179d154 100644
--- a/src/Element/ComponentFormPropsBuilder.php
+++ b/src/Element/ComponentFormPropsBuilder.php
@@ -5,7 +5,6 @@ namespace Drupal\ui_patterns\Element;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
-use Drupal\Core\Render\Element\FormElement;
 use Drupal\ui_patterns\SourcePluginManager;
 
 /**
@@ -71,7 +70,8 @@ class ComponentFormPropsBuilder extends ComponentFormBuilderBase {
 
     foreach ($props as $prop_id => $prop) {
       $prop_type = $prop['ui_patterns']['type_definition'];
-      $source_ids = array_values(array_keys($source_plugin_manager->getDefinitionsForPropType($prop_type->getPluginId(), $contexts)));
+      $source_ids = array_keys($source_plugin_manager->getDefinitionsForPropType($prop_type->getPluginId(), $contexts));
+      $source_ids = array_combine($source_ids, $source_ids);
       if (count($source_ids) === 0) {
         continue;
       }
@@ -88,7 +88,7 @@ class ComponentFormPropsBuilder extends ComponentFormBuilderBase {
         }
       }
       if ($selected_source) {
-        $element[$prop_id]['value'] = $selected_source->buildConfigurationForm(
+        $element[$prop_id]['value'] = $selected_source->settingsForm(
           $element,
           $form_state
         );
@@ -100,7 +100,8 @@ class ComponentFormPropsBuilder extends ComponentFormBuilderBase {
         $element[$prop_id]['#prefix'] = '<div id="' . $wrapper_id . '" class="ui-patterns-wrapper-absolute">';
         $element[$prop_id]['#suffix'] = '</div>';
         $dropdown_actions = [];
-        foreach ($sources as $source_id => $source) {
+        foreach ($sources as $source) {
+          $source_id = $source->getPluginId();
           if ($source_id !== $selected_source->getPluginId()) {
             $button = [
               '#type' => 'submit',
diff --git a/src/Element/ComponentFormSlotsBuilder.php b/src/Element/ComponentFormSlotsBuilder.php
index e37ec979a..412b903cc 100644
--- a/src/Element/ComponentFormSlotsBuilder.php
+++ b/src/Element/ComponentFormSlotsBuilder.php
@@ -100,7 +100,7 @@ class ComponentFormSlotsBuilder extends ComponentFormBuilderBase {
             $source_configuration['source_id'],
             SourcePluginManager::buildPluginConfiguration($slot_id, $slot, $source_configuration)
           );
-          $element[$slot_id]['sources'][$delta]['value'] = $source->buildConfigurationForm(
+          $element[$slot_id]['sources'][$delta]['value'] = $source->settingsForm(
             [],
             $form_state
           );
diff --git a/src/Element/DropdownAction.php b/src/Element/DropdownAction.php
index 8aded9c26..1c74157c4 100644
--- a/src/Element/DropdownAction.php
+++ b/src/Element/DropdownAction.php
@@ -20,9 +20,9 @@ use Drupal\Core\Render\Element\RenderElement;
  *   'actions' => $actions,
  *   'dropdown_actions' => $dropdown_actions,
  * ];
- * $dropdown_actions['button'] = array(
+ * $dropdown_actions['button'] = [
  *   '#type' => 'submit',
- * );
+ * ];
  * @endcode
  *
  * @FormElement("ui_patterns_actions")
diff --git a/src/Form/ComponentFormBuilderTrait.php b/src/Form/ComponentFormBuilderTrait.php
index d31907b17..f9eb28fff 100644
--- a/src/Form/ComponentFormBuilderTrait.php
+++ b/src/Form/ComponentFormBuilderTrait.php
@@ -103,7 +103,7 @@ trait ComponentFormBuilderTrait {
   }
 
   /**
-   *
+   * Build component data.
    */
   public function buildComponentData() {
     return [];
diff --git a/src/Plugin/UiPatterns/Source/AttributesWidget.php b/src/Plugin/UiPatterns/Source/AttributesWidget.php
index 677082ae5..3b13f617a 100644
--- a/src/Plugin/UiPatterns/Source/AttributesWidget.php
+++ b/src/Plugin/UiPatterns/Source/AttributesWidget.php
@@ -40,12 +40,12 @@ class AttributesWidget extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     // In UI Patterns 2.x, attributes are associative arrays, not strings.
     // We use textfield (like UI Patterns Settings do), but we don't store it
     // as a string in config.
     // It would be better to use something else than a textfield one day.
-    $value = new Attribute($this->getConfigurationFormValue());
+    $value = new Attribute($this->getSetting('value'));
     $form = [
       '#type' => 'textfield',
       '#title' => $this->propDefinition['title'],
@@ -66,7 +66,7 @@ class AttributesWidget extends SourcePluginBase {
    */
   final public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
     parent::submitConfigurationForm($form, $form_state);
-    $value = $this->getConfigurationFormValue();
+    $value = $this->getSetting('value');
     $parse_html = '<div ' . $value . '></div>';
     $attributes = [];
     foreach (Html::load($parse_html)->getElementsByTagName('div') as $div) {
diff --git a/src/Plugin/UiPatterns/Source/ChecboxesWidget.php b/src/Plugin/UiPatterns/Source/ChecboxesWidget.php
index 9cb9bcfcc..071a442ad 100644
--- a/src/Plugin/UiPatterns/Source/ChecboxesWidget.php
+++ b/src/Plugin/UiPatterns/Source/ChecboxesWidget.php
@@ -25,13 +25,13 @@ class ChecboxesWidget extends SourcePluginBase {
    * {@inheritdoc}
    */
   public function getData(): mixed {
-    return array_filter($this->getConfigurationFormValue());
+    return array_filter($this->getSetting('value'));
   }
 
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     $options = [];
     foreach ($this->propDefinition['items']['enum'] as $item) {
       $options[$item] = $item;
@@ -40,7 +40,7 @@ class ChecboxesWidget extends SourcePluginBase {
     return [
       '#type' => 'checkboxes',
       '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue() ?? [],
+      '#default_value' => $this->getSetting('value') ?? [],
       "#options" => $options,
     ];
   }
diff --git a/src/Plugin/UiPatterns/Source/CheckboxWidget.php b/src/Plugin/UiPatterns/Source/CheckboxWidget.php
index d670190ba..c6873bb1c 100644
--- a/src/Plugin/UiPatterns/Source/CheckboxWidget.php
+++ b/src/Plugin/UiPatterns/Source/CheckboxWidget.php
@@ -31,11 +31,11 @@ class CheckboxWidget extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
       '#type' => 'checkbox',
       '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/ColorWidget.php b/src/Plugin/UiPatterns/Source/ColorWidget.php
index 57a61b244..b961b9dbd 100644
--- a/src/Plugin/UiPatterns/Source/ColorWidget.php
+++ b/src/Plugin/UiPatterns/Source/ColorWidget.php
@@ -24,11 +24,11 @@ class ColorWidget extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
       '#type' => 'color',
       '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
     ];
   }
 
diff --git a/src/Plugin/UiPatterns/Source/ListTextareaWidget.php b/src/Plugin/UiPatterns/Source/ListTextareaWidget.php
index 12c7f4294..2f0b75d8b 100644
--- a/src/Plugin/UiPatterns/Source/ListTextareaWidget.php
+++ b/src/Plugin/UiPatterns/Source/ListTextareaWidget.php
@@ -24,8 +24,8 @@ class ListTextareaWidget extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
-    $items = $this->getConfigurationFormValue();
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
+    $items = $this->getSetting('value');
     $long_text = implode("\r", $items);
     $form = [
       '#type' => 'textarea',
@@ -41,7 +41,7 @@ class ListTextareaWidget extends SourcePluginBase {
    */
   final public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
     parent::submitConfigurationForm($form, $form_state);
-    $long_text = $this->getConfigurationFormValue();
+    $long_text = $this->getSetting('value');
     $items = preg_split("/\r\n|\n|\r/", $long_text);
     $this->configuration['form_value'] = $items;
   }
diff --git a/src/Plugin/UiPatterns/Source/MenuSource.php b/src/Plugin/UiPatterns/Source/MenuSource.php
index c65202331..9398c501c 100644
--- a/src/Plugin/UiPatterns/Source/MenuSource.php
+++ b/src/Plugin/UiPatterns/Source/MenuSource.php
@@ -78,8 +78,8 @@ class MenuSource extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
-    $value = $this->getConfigurationFormValue() ?? [];
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
+    $value = $this->getSetting('value') ?? [];
     $form = [];
     $form["menu"] = [
       '#type' => 'select',
diff --git a/src/Plugin/UiPatterns/Source/NumberWidget.php b/src/Plugin/UiPatterns/Source/NumberWidget.php
index 6ac300035..10d2faca3 100644
--- a/src/Plugin/UiPatterns/Source/NumberWidget.php
+++ b/src/Plugin/UiPatterns/Source/NumberWidget.php
@@ -25,7 +25,7 @@ class NumberWidget extends SourcePluginBase {
    * {@inheritdoc}
    */
   public function getData(): mixed {
-    $value = $this->getConfigurationFormValue();
+    $value = $this->getSetting('value');
     // Add 0 to automatically cast to a float OR an integer.
     if (empty($value)) {
       return $value;
@@ -36,11 +36,11 @@ class NumberWidget extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     $form = [
       '#type' => 'number',
       '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
       '#step' => 0.01,
     ];
     if ($this->propDefinition["type"] === "integer") {
diff --git a/src/Plugin/UiPatterns/Source/PathSource.php b/src/Plugin/UiPatterns/Source/PathSource.php
index 9acb250f8..b07c0bf12 100644
--- a/src/Plugin/UiPatterns/Source/PathSource.php
+++ b/src/Plugin/UiPatterns/Source/PathSource.php
@@ -47,8 +47,8 @@ class PathSource extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
-    $value = $this->getConfigurationFormValue();
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
+    $value = $this->getSetting('value');
     $value = $this->getUrlFromRoute($value);
     return [
       '#type' => 'path',
diff --git a/src/Plugin/UiPatterns/Source/RadiosWidget.php b/src/Plugin/UiPatterns/Source/RadiosWidget.php
index d5a0617f6..270235a34 100644
--- a/src/Plugin/UiPatterns/Source/RadiosWidget.php
+++ b/src/Plugin/UiPatterns/Source/RadiosWidget.php
@@ -25,13 +25,13 @@ class RadiosWidget extends SourcePluginBase {
    * {@inheritdoc}
    */
   public function getData(): mixed {
-    return $this->getConfigurationFormValue();
+    return $this->getSetting('value');
   }
 
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     $options = [];
     foreach ($this->propDefinition['enum'] as $item) {
       $options[$item] = $item;
@@ -39,7 +39,7 @@ class RadiosWidget extends SourcePluginBase {
     return [
       '#type' => 'radios',
       '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
       "#options" => $options,
     ];
   }
diff --git a/src/Plugin/UiPatterns/Source/SelectWidget.php b/src/Plugin/UiPatterns/Source/SelectWidget.php
index 7239dca79..64320990b 100644
--- a/src/Plugin/UiPatterns/Source/SelectWidget.php
+++ b/src/Plugin/UiPatterns/Source/SelectWidget.php
@@ -25,13 +25,13 @@ class SelectWidget extends SourcePluginBase {
    * {@inheritdoc}
    */
   public function getData(): mixed {
-    return $this->getConfigurationFormValue();
+    return $this->getSetting('value');
   }
 
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     $options = [];
     foreach ($this->propDefinition['enum'] as $item) {
       $options[$item] = $item;
@@ -39,7 +39,7 @@ class SelectWidget extends SourcePluginBase {
     $form = [
       '#type' => 'select',
       '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
       "#options" => $options,
     ];
     // With Firefox, autocomplete may override #default_value.
diff --git a/src/Plugin/UiPatterns/Source/TextfieldWidget.php b/src/Plugin/UiPatterns/Source/TextfieldWidget.php
index 45f64e5ce..915be57e7 100644
--- a/src/Plugin/UiPatterns/Source/TextfieldWidget.php
+++ b/src/Plugin/UiPatterns/Source/TextfieldWidget.php
@@ -26,11 +26,11 @@ class TextfieldWidget extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     $form = [
       '#type' => 'textfield',
       '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
     ];
     $description = [];
     if (isset($this->propDefinition["pattern"])) {
diff --git a/src/Plugin/UiPatterns/Source/TokenSource.php b/src/Plugin/UiPatterns/Source/TokenSource.php
index 28c963110..ae96500dd 100644
--- a/src/Plugin/UiPatterns/Source/TokenSource.php
+++ b/src/Plugin/UiPatterns/Source/TokenSource.php
@@ -25,7 +25,7 @@ class TokenSource extends SourcePluginBase {
    * {@inheritdoc}
    */
   public function getData(): mixed {
-    $value = $this->getConfigurationFormValue();
+    $value = $this->getSetting('value');
     if (!is_scalar($value)) {
       return "";
     }
@@ -39,11 +39,11 @@ class TokenSource extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
       '#type' => 'textfield',
       '#title' => $this->t("@prop, with token", ["@prop" => $this->propDefinition['title']]),
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
       // Tokens always start with a [ and end with a ].
       '#pattern' => '^\[.+\]$',
     ];
diff --git a/src/Plugin/UiPatterns/Source/UrlWidget.php b/src/Plugin/UiPatterns/Source/UrlWidget.php
index ebce32237..8fb919957 100644
--- a/src/Plugin/UiPatterns/Source/UrlWidget.php
+++ b/src/Plugin/UiPatterns/Source/UrlWidget.php
@@ -24,11 +24,11 @@ class UrlWidget extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
       '#type' => 'url',
       '#title' => $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
       '#description' => $this->t("Enter an external URL"),
     ];
   }
diff --git a/src/Plugin/UiPatterns/Source/WysiwygWidget.php b/src/Plugin/UiPatterns/Source/WysiwygWidget.php
index 375814653..60ca15d4f 100644
--- a/src/Plugin/UiPatterns/Source/WysiwygWidget.php
+++ b/src/Plugin/UiPatterns/Source/WysiwygWidget.php
@@ -25,7 +25,7 @@ class WysiwygWidget extends SourcePluginBase {
    * {@inheritdoc}
    */
   public function getData(): mixed {
-    $value = $this->getConfigurationFormValue();
+    $value = $this->getSetting('value');
     return [
       "#type" => "processed_text",
       "#text" => $value["value"],
@@ -36,8 +36,8 @@ class WysiwygWidget extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
-    $value = $this->getConfigurationFormValue();
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
+    $value = $this->getSetting('value');
     $form = [
       '#type' => 'text_format',
       '#default_value' => $value["value"],
diff --git a/src/SourceInterface.php b/src/SourceInterface.php
index 766102e99..f22dbbc72 100644
--- a/src/SourceInterface.php
+++ b/src/SourceInterface.php
@@ -6,12 +6,62 @@ namespace Drupal\ui_patterns;
 
 use Drupal\Component\Plugin\ConfigurableInterface;
 use Drupal\Component\Plugin\PluginInspectionInterface;
-use Drupal\Core\Plugin\PluginFormInterface;
+use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Interface for source plugins.
  */
-interface SourceInterface extends PluginFormInterface, ConfigurableInterface, PluginInspectionInterface {
+interface SourceInterface extends ConfigurableInterface, PluginInspectionInterface {
+
+  /**
+   * Defines the default settings for this plugin.
+   *
+   * @return array
+   *   A list of default settings, keyed by the setting name.
+   */
+  public function defaultSettings();
+
+  /**
+   * Returns the array of settings, including defaults for missing settings.
+   *
+   * @return array
+   *   The array of settings.
+   */
+  public function getSettings();
+
+  /**
+   * Returns the value of a setting, or its default value if absent.
+   *
+   * @param string $key
+   *   The setting name.
+   *
+   * @return mixed
+   *   The setting value.
+   */
+  public function getSetting($key);
+
+  /**
+   * Sets the settings for the plugin.
+   *
+   * @param array $settings
+   *   The array of settings, keyed by setting names. Missing settings will be
+   *   assigned their default values.
+   *
+   * @return $this
+   */
+  public function setSettings(array $settings);
+
+  /**
+   * Sets the value of a setting for the plugin.
+   *
+   * @param string $key
+   *   The setting name.
+   * @param mixed $value
+   *   The setting value.
+   *
+   * @return $this
+   */
+  public function setSetting($key, $value);
 
   /**
    * Returns the translated plugin label.
@@ -23,6 +73,20 @@ interface SourceInterface extends PluginFormInterface, ConfigurableInterface, Pl
    */
   public function getData(): mixed;
 
+
+  /**
+   * Returns a form to configure settings for the source plugins.
+   *
+   * @param array $form
+   *   The form where the settings form is being included in.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   The form elements for the source settings.
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state): array;
+
   /**
    * Returns the associated prop id.
    */
diff --git a/src/SourcePluginBase.php b/src/SourcePluginBase.php
index 57b7a18bc..0cf20b616 100644
--- a/src/SourcePluginBase.php
+++ b/src/SourcePluginBase.php
@@ -6,8 +6,6 @@ namespace Drupal\ui_patterns;
 
 use Drupal\Component\Plugin\PluginBase;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Form\SubformState;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
 use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
@@ -39,6 +37,28 @@ abstract class SourcePluginBase extends PluginBase implements
    */
   protected string $propId;
 
+
+  /**
+   * The plugin settings.
+   *
+   * @var array
+   */
+  protected $settings = [];
+
+  /**
+   * Whether default settings have been merged into the current $settings.
+   *
+   * @var bool
+   */
+  protected $defaultSettingsMerged = FALSE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultSettings() {
+    return ['value' => $this->propDefinition['default'] ?? NULL];
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -70,51 +90,67 @@ abstract class SourcePluginBase extends PluginBase implements
     $this->setDefinedContextValues();
   }
 
+
   /**
    * {@inheritdoc}
    */
-  public function label(): string {
-    // Cast the label to a string since it is a TranslatableMarkup object.
-    return (string) $this->pluginDefinition['label'];
+  public function getSettings() {
+    // Merge defaults before returning the array.
+    if (!$this->defaultSettingsMerged) {
+      $this->mergeDefaults();
+    }
+    return $this->settings;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
-    return [];
+  public function getSetting($key) {
+    // Merge defaults if we have no value for the key.
+    if (!$this->defaultSettingsMerged && !array_key_exists($key, $this->settings)) {
+      $this->mergeDefaults();
+    }
+    return $this->settings[$key] ?? NULL;
+  }
+
+  /**
+   * Merges default settings values into $settings.
+   */
+  protected function mergeDefaults() {
+    $this->settings += $this->defaultSettings();
+    $this->defaultSettingsMerged = TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getData(): mixed {
-    // If one day, we want to inject default values by default, we can replace
-    // this by a call to getConfigurationFormValue().
-    $configuration = $this->getConfiguration();
-    return $configuration["form_value"] ?? NULL;
+  public function setSettings(array $settings) {
+    $this->settings = $settings;
+    $this->defaultSettingsMerged = FALSE;
+    return $this;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function validateConfigurationForm(
-    array &$form,
-    FormStateInterface $form_state
-  ) {}
+  public function setSetting($key, $value) {
+    $this->settings[$key] = $value;
+    return $this;
+  }
 
   /**
    * {@inheritdoc}
    */
-  public function submitConfigurationForm(
-    array &$form,
-    FormStateInterface $form_state
-  ) {
-    $complete_state = $form_state instanceof SubformState ? $form_state->getCompleteFormState() : $form_state;
-    $parents = $form['#parents'];
-    $parents[] = 'value';
-    $value = $complete_state->getValue($parents);
-    $this->configuration['form_value'] = $value;
+  public function label(): string {
+    // Cast the label to a string since it is a TranslatableMarkup object.
+    return (string) $this->pluginDefinition['label'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getData(): mixed {
+    return $this->getSetting('value');
   }
 
   /**
@@ -134,6 +170,9 @@ abstract class SourcePluginBase extends PluginBase implements
     if (isset($configuration['prop_id'])) {
       $this->propId = $configuration['prop_id'];
     }
+    if (isset($configuration['settings'])) {
+      $this->setSettings($configuration['settings']);
+    }
     $this->configuration = $configuration;
   }
 
@@ -187,17 +226,6 @@ abstract class SourcePluginBase extends PluginBase implements
     }
   }
 
-  /**
-   * Get configuration form value.
-   */
-  final protected function getConfigurationFormValue(): mixed {
-    if (!isset($this->configuration['configuration']['value'])) {
-      // @todo default value fomr prop definition doesn't work.
-      return $this->propDefinition['default'] ?? NULL;
-    }
-    return $this->configuration['configuration']['value'] ?? NULL;
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/src/SourcePluginManager.php b/src/SourcePluginManager.php
index d78c6e711..2717073be 100644
--- a/src/SourcePluginManager.php
+++ b/src/SourcePluginManager.php
@@ -68,11 +68,14 @@ class SourcePluginManager extends DefaultPluginManager implements ContextAwarePl
     return NULL;
   }
 
-  public static function buildPluginConfiguration(string $prop_id, array $prop_definition, array $configuration = []):array {
+  /**
+   * Builds default plugin configuration array.
+   */
+public static function buildPluginConfiguration(string $prop_id, array $prop_definition, array $settings = []):array {
     return [
       'prop_id' => $prop_id,
       'prop_definition' => $prop_definition,
-      'configuration' => $configuration,
+      'settings' => $settings,
     ];
   }
 
diff --git a/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php b/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php
index b6af203c0..3e02f6738 100644
--- a/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php
+++ b/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php
@@ -24,11 +24,11 @@ final class Foo extends SourcePluginBase {
   /**
    * {@inheritdoc}
    */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     return [
       '#type' => 'textfield',
       '#title' => 'FOO: ' . $this->propDefinition['title'],
-      '#default_value' => $this->getConfigurationFormValue(),
+      '#default_value' => $this->getSetting('value'),
     ];
   }
 
-- 
GitLab