Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
DesignTokensPanel
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 10
420
0.00% covered (danger)
0.00%
0 / 1
 create
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildForm
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
42
 validateForm
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 alterElement
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 onAttachToRoot
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onAttachToSlot
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onActive
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onDelete
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isApplicable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 filterValues
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\display_builder\Island;
6
7use Drupal\Core\Extension\ModuleHandlerInterface;
8use Drupal\Core\Form\FormStateInterface;
9use Drupal\Core\StringTranslation\TranslatableMarkup;
10use Drupal\display_builder\Attribute\Island;
11use Drupal\display_builder\InstanceInterface;
12use Drupal\display_builder\Island\IslandPluginBase;
13use Drupal\display_builder\Island\IslandType;
14use Drupal\display_builder\Island\IslandWithFormInterface;
15use Drupal\display_builder\Island\IslandWithFormTrait;
16use Drupal\display_builder\Island\RenderableAltererInterface;
17use Drupal\ui_skins\CssVariable\CssVariablePluginManagerInterface;
18use Drupal\ui_skins\UiSkinsUtility;
19use Symfony\Component\DependencyInjection\ContainerInterface;
20
21/**
22 * Skins island plugin implementation.
23 *
24 * @todo must move to UI Styles module.
25 */
26#[Island(
27  id: 'tokens',
28  label: new TranslatableMarkup('Design tokens (CSS variables)'),
29  description: new TranslatableMarkup('Override CSS variables for the active component or block.'),
30  type: IslandType::Contextual,
31  modules: ['ui_skins'],
32  icon: 'braces',
33)]
34class DesignTokensPanel extends IslandPluginBase implements IslandWithFormInterface, RenderableAltererInterface {
35
36  use IslandWithFormTrait;
37
38  /**
39   * The UI Skins CSS variables manager.
40   */
41  protected CssVariablePluginManagerInterface $cssVariablePluginManager;
42
43  /**
44   * The module handler.
45   */
46  protected ModuleHandlerInterface $moduleHandler;
47
48  /**
49   * {@inheritdoc}
50   */
51  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
52    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
53    $instance->cssVariablePluginManager = $container->get('plugin.manager.ui_skins.css_variable');
54    $instance->moduleHandler = $container->get('module_handler');
55
56    return $instance;
57  }
58
59  /**
60   * {@inheritdoc}
61   */
62  public function buildForm(array &$form, FormStateInterface $form_state): void {
63    $data = $form_state->getBuildInfo()['args'][0];
64    $instance = $data['instance'] ?? [];
65    $grouped_plugin_definitions = $this->cssVariablePluginManager->getGroupedDefinitions();
66
67    if (empty($grouped_plugin_definitions)) {
68      return;
69    }
70
71    foreach ($grouped_plugin_definitions as $group => $definitions) {
72      $variables = [];
73
74      foreach ($definitions as $definition_id => $definition) {
75        $default = $definition->getDefaultValues();
76
77        if (!isset($default[':root'])) {
78          continue;
79        }
80        $variables[$definition_id] = [
81          '#type' => $definition->getType(),
82          '#title' => $definition->getLabel(),
83          '#default_value' => $instance[$definition_id] ?? $default[':root'] ?? '',
84        ];
85      }
86
87      if (!empty($variables)) {
88        $variables['#type'] = 'details';
89        $variables['#title'] = $group;
90        $form[$group] = $variables;
91      }
92    }
93  }
94
95  /**
96   * {@inheritdoc}
97   */
98  public function validateForm(array &$form, FormStateInterface $form_state): void {
99    $variables = $form_state->getValues();
100    $variables = $this->filterValues($variables);
101    $variables = $form_state->setValues($variables);
102    // Those two lines are necessary to prevent the form from being rebuilt.
103    // if rebuilt, the form state values will have both the computed ones
104    // and the raw ones (wrapper key and values).
105    $form_state->setRebuild(FALSE);
106    $form_state->setExecuted();
107  }
108
109  /**
110   * {@inheritdoc}
111   */
112  public function alterElement(array $element, array $data = []): array {
113    $inline_css = [];
114
115    foreach ($data as $variable => $value) {
116      $variable = UiSkinsUtility::getCssVariableName($variable);
117      $inline_css[] = "{$variable}{$value};";
118    }
119    $element['#attributes']['style'] = \implode(' ', $inline_css);
120
121    return $element;
122  }
123
124  /**
125   * {@inheritdoc}
126   *
127   * No-op: a freshly attached node always has a brand-new node_id, so its
128   * own contextual panel can never already be open in the second drawer for
129   * this or any other reload to target.
130   */
131  public function onAttachToRoot(InstanceInterface $instance, string $node_id): array {
132    return [];
133  }
134
135  /**
136   * {@inheritdoc}
137   *
138   * @see self::onAttachToRoot()
139   */
140  public function onAttachToSlot(InstanceInterface $instance, string $node_id, string $parent_id): array {
141    return [];
142  }
143
144  /**
145   * {@inheritdoc}
146   */
147  public function onActive(InstanceInterface $instance, array $data): array {
148    return $this->reloadWithLocalData($instance, $data);
149  }
150
151  /**
152   * {@inheritdoc}
153   */
154  public function onDelete(InstanceInterface $instance, ?string $parent_id): array {
155    return $this->reloadWithLocalData($instance, []);
156  }
157
158  /**
159   * {@inheritdoc}
160   */
161  public function isApplicable(): bool {
162    return parent::isApplicable() && $this->moduleHandler->moduleExists('ui_skins');
163  }
164
165  /**
166   * Extract values to save in configuration.
167   *
168   * @param array $variables
169   *   The variables to filter.
170   *
171   * @return array
172   *   An array of filtered variables.
173   */
174  protected function filterValues(array $variables): array {
175    $cleaned_variables = [];
176
177    foreach ($variables as $variable => $value) {
178      /** @var \Drupal\ui_skins\Definition\CssVariableDefinition $plugin_definition */
179      $plugin_definition = $this->cssVariablePluginManager->getDefinition($variable, FALSE);
180
181      if (!$plugin_definition) {
182        continue;
183      }
184
185      // Remove values that do not differ from the default values of the plugin.
186      if ($plugin_definition->isDefaultScopeValue(':root', $value)) {
187        continue;
188      }
189
190      $cleaned_variables[$variable] = $value;
191    }
192
193    return $cleaned_variables;
194  }
195
196}