Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
StylesPanel
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 10
240
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 / 9
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
 validateForm
0.00% covered (danger)
0.00%
0 / 6
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 / 3
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
 getSummary
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 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 / 5
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
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\display_builder\ThirdPartySettingsInterface;
18use Drupal\ui_styles\StylePluginManagerInterface;
19use Symfony\Component\DependencyInjection\ContainerInterface;
20
21/**
22 * Styles island plugin implementation.
23 *
24 * @todo must move to UI Styles module.
25 */
26#[Island(
27  id: 'styles',
28  label: new TranslatableMarkup('Styles'),
29  description: new TranslatableMarkup('Apply style utilities to the active component or block'),
30  type: IslandType::Contextual,
31  modules: ['ui_styles'],
32  icon: 'palette',
33)]
34class StylesPanel extends IslandPluginBase implements IslandWithFormInterface, RenderableAltererInterface, ThirdPartySettingsInterface {
35
36  use IslandWithFormTrait;
37
38  /**
39   * The UI Styles styles manager.
40   */
41  protected StylePluginManagerInterface $stylesManager;
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->stylesManager = $container->get('plugin.manager.ui_styles');
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    // The data to be received here is in the form of:
64    // ['styles' => ['selected' => [], 'extra' => '']].
65    $form += [
66      'styles' => [
67        '#type' => 'ui_styles_styles',
68        '#title' => $this->t('Styles'),
69        '#wrapper_type' => 'div',
70        '#default_value' => \array_merge(['selected' => [], 'extra' => ''], $this->data ?? []),
71      ],
72      '#tree' => TRUE,
73    ];
74  }
75
76  /**
77   * {@inheritdoc}
78   */
79  public function validateForm(array &$form, FormStateInterface $form_state): void {
80    // The styles key in the values is added by BlockStylesForm.
81    // Inside the styles key, there are two keys: selected and extra.
82    // The structure here is the one produced by UI styles Form API element.
83    $values = $form_state->getValue('styles');
84    $form_state->setValue('selected', $values['selected'] ?? []);
85    $form_state->setValue('extra', $values['extra'] ?? '');
86    $form_state->unsetValue('styles');
87    // Those two lines are necessary to prevent the form from being rebuilt.
88    // if rebuilt, the form state values will have both the computed ones
89    // and the raw ones (wrapper key and values).
90    $form_state->setRebuild(FALSE);
91    $form_state->setExecuted();
92  }
93
94  /**
95   * {@inheritdoc}
96   */
97  public function alterElement(array $element, array $data = []): array {
98    $selected = $data['selected'] ?? [];
99    $extra = $data['extra'] ?? '';
100
101    return $this->stylesManager->addClasses($element, $selected, $extra);
102  }
103
104  /**
105   * {@inheritdoc}
106   */
107  public function getSummary(): ?array {
108    if (empty($this->data['selected'])) {
109      // We do not cover 'extra'.
110      return NULL;
111    }
112
113    $items = [];
114
115    foreach ($this->data['selected'] ?? [] as $style_id => $option_key) {
116      $style = $this->stylesManager->getDefinition($style_id);
117      $options = $style->getOptionsAsOptions();
118      $option = $options[$option_key] ?? $option_key;
119      $item = \sprintf('%s %s', $option, \strtolower((string) $style->getLabel()));
120      $items[] = [
121        '#type' => 'html_tag',
122        '#tag' => 'li',
123        '#value' => $item,
124      ];
125    }
126
127    if (empty($items)) {
128      return NULL;
129    }
130
131    $summary = [
132      [
133        '#type' => 'html_tag',
134        '#tag' => 'em',
135        '#value' => new TranslatableMarkup('Styles'),
136      ],
137      [
138        '#type' => 'html_tag',
139        '#tag' => 'ul',
140        '#attributes' => [
141          'class' => ['summary'],
142        ],
143        0 => $items,
144      ],
145    ];
146
147    return $summary;
148  }
149
150  /**
151   * {@inheritdoc}
152   *
153   * No-op: a freshly attached node always has a brand-new node_id, so its
154   * own contextual panel can never already be open in the second drawer for
155   * this or any other reload to target.
156   */
157  public function onAttachToRoot(InstanceInterface $instance, string $node_id): array {
158    return [];
159  }
160
161  /**
162   * {@inheritdoc}
163   *
164   * @see self::onAttachToRoot()
165   */
166  public function onAttachToSlot(InstanceInterface $instance, string $node_id, string $parent_id): array {
167    return [];
168  }
169
170  /**
171   * {@inheritdoc}
172   */
173  public function onActive(InstanceInterface $instance, array $data): array {
174    return $this->reloadWithLocalData($instance, $data);
175  }
176
177  /**
178   * {@inheritdoc}
179   */
180  public function onDelete(InstanceInterface $instance, ?string $parent_id): array {
181    return $this->reloadWithLocalData($instance, []);
182  }
183
184  /**
185   * {@inheritdoc}
186   */
187  public function isApplicable(): bool {
188    return parent::isApplicable() && !empty($this->data) && $this->moduleHandler->moduleExists('ui_styles');
189  }
190
191}