Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 107
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 162
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
PresetLibraryPanel
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 162
0.00% covered (danger)
0.00%
0 / 9
462
0.00% covered (danger)
0.00%
0 / 1
 create
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
 defaultConfiguration
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
 buildConfigurationForm
0.00% covered (danger)
0.00%
0 / 12
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
 configurationSummary
0.00% covered (danger)
0.00%
0 / 5
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
 build
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 onPresetSave
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
 buildPresets
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 144
0.00% covered (danger)
0.00%
0 / 1
56
 buildPresetItem
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getPresetGroup
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\display_builder\Island;
6
7use Drupal\Core\Entity\EntityStorageInterface;
8use Drupal\Core\Form\FormStateInterface;
9use Drupal\Core\StringTranslation\TranslatableMarkup;
10use Drupal\Core\Url;
11use Drupal\display_builder\Attribute\Island;
12use Drupal\display_builder\BlockLibrarySourceHelper;
13use Drupal\display_builder\Entity\PatternPresetInterface;
14use Drupal\display_builder\InstanceInterface;
15use Drupal\display_builder\Island\IslandConfigurationFormInterface;
16use Drupal\display_builder\Island\IslandConfigurationFormTrait;
17use Drupal\display_builder\Island\IslandPluginBase;
18use Drupal\display_builder\Island\IslandType;
19use Symfony\Component\DependencyInjection\ContainerInterface;
20
21/**
22 * Preset island plugin implementation.
23 */
24#[Island(
25  id: 'preset_library',
26  label: new TranslatableMarkup('Presets'),
27  description: new TranslatableMarkup('List of preset, already build group of components.'),
28  type: IslandType::Library,
29  icon: 'boxes',
30)]
31class PresetLibraryPanel extends IslandPluginBase implements IslandConfigurationFormInterface {
32
33  use IslandConfigurationFormTrait;
34
35  /**
36   * The Pattern preset storage.
37   */
38  protected EntityStorageInterface $presetConfigStorage;
39
40  /**
41   * {@inheritdoc}
42   */
43  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
44    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
45    $instance->presetConfigStorage = $container->get('entity_type.manager')->getStorage('pattern_preset');
46
47    return $instance;
48  }
49
50  /**
51   * {@inheritdoc}
52   */
53  public function defaultConfiguration(): array {
54    return [
55      'preview' => TRUE,
56    ];
57  }
58
59  /**
60   * {@inheritdoc}
61   */
62  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
63    $configuration = $this->getConfiguration();
64
65    $form['display'] = [
66      '#type' => 'fieldset',
67      '#title' => $this->t('Display'),
68    ];
69
70    $form['display']['preview'] = [
71      '#type' => 'checkbox',
72      '#title' => $this->t('Enable preview on hover'),
73      '#description' => $this->t('Enable or disable the preview of components when hovering over them.'),
74      '#default_value' => $configuration['preview'],
75    ];
76
77    return $form;
78  }
79
80  /**
81   * {@inheritdoc}
82   */
83  public function configurationSummary(): array {
84    $configuration = $this->getConfiguration();
85
86    $summary = [];
87
88    if ($configuration['preview']) {
89      $summary[] = $this->t('Preview on hover enabled');
90    }
91
92    return $summary;
93  }
94
95  /**
96   * {@inheritdoc}
97   */
98  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
99    $builder_id = (string) $builder->id();
100    $entity_ids = $this->presetConfigStorage->getQuery()
101      ->accessCheck(TRUE)
102      ->condition('status', TRUE)
103      ->sort('weight', 'ASC')
104      ->execute();
105    /** @var \Drupal\display_builder\Entity\PatternPresetInterface[] $presets */
106    $presets = $this->presetConfigStorage->loadMultiple($entity_ids);
107    $contexts = $this->configuration['contexts'] ?? [];
108
109    foreach ($presets as $preset_id => $preset) {
110      if (!$preset->areContextsSatisfied($contexts)) {
111        unset($presets[$preset_id]);
112      }
113    }
114
115    if (empty($presets)) {
116      $content = [
117        [
118          '#type' => 'html_tag',
119          '#tag' => 'p',
120        ],
121        [
122          '#type' => 'html_tag',
123          '#tag' => 'p',
124          '#value' => $this->t('Pattern presets are reusable arrangements of components and blocks.'),
125        ],
126        [
127          '#type' => 'html_tag',
128          '#tag' => 'p',
129          '#value' => $this->t('Add presets from the contextual menu.'),
130        ],
131      ];
132    }
133    else {
134      $content = $this->buildPresets($builder_id, $presets);
135    }
136
137    return [
138      '#type' => 'component',
139      '#component' => 'display_builder:library_panel',
140      '#slots' => [
141        'content' => $content,
142      ],
143      '#cache' => [
144        'tags' => $this->presetConfigStorage->getEntityType()->getListCacheTags(),
145      ],
146    ];
147  }
148
149  /**
150   * {@inheritdoc}
151   */
152  public function onPresetSave(InstanceInterface $instance): array {
153    return $this->reloadWithGlobalData($instance);
154  }
155
156  /**
157   * Build preset as placeholder grouped.
158   *
159   * @param string $builder_id
160   *   Builder ID.
161   * @param \Drupal\display_builder\Entity\PatternPresetInterface[] $presets
162   *   The presets to build.
163   *
164   * @return array
165   *   Array of grouped preset plugins.
166   */
167  private function buildPresets(string $builder_id, array $presets): array {
168    $configuration = $this->getConfiguration();
169    $build = [];
170    $grouped_presets = [];
171
172    foreach ($presets as $preset_id => $preset) {
173      $group_key = $this->getPresetGroup($preset);
174      $grouped_presets[$group_key][$preset_id] = $preset;
175    }
176
177    $formatted_groups = [];
178
179    foreach ($grouped_presets as $group_name => $presets_in_group) {
180      $formatted_groups[$group_name] = [
181        'label' => $group_name,
182        'choices' => $presets_in_group,
183      ];
184    }
185
186    $is_single_group = \count($formatted_groups) === 1;
187
188    if (!$is_single_group) {
189      BlockLibrarySourceHelper::sortGroupedChoices($formatted_groups);
190    }
191
192    foreach ($formatted_groups as $group_data) {
193      if (!$is_single_group) {
194        $build[] = [
195          '#type' => 'html_tag',
196          '#tag' => 'h4',
197          '#value' => $group_data['label'],
198          '#attributes' => [
199            'class' => ['db-placeholder__group', 'db-filter-hide-on-search'],
200          ],
201        ];
202      }
203
204      foreach ($group_data['choices'] as $preset) {
205        $build[] = $this->buildPresetItem($builder_id, $preset, $configuration['preview']);
206      }
207    }
208
209    $build = $this->buildDraggables($builder_id, $build);
210    $build['#source_contexts'] = $this->configuration['contexts'] ?? [];
211
212    return $build;
213  }
214
215  /**
216   * Build a preset item.
217   *
218   * @param string $builder_id
219   *   Builder ID.
220   * @param \Drupal\display_builder\Entity\PatternPresetInterface $preset
221   *   The preset entity.
222   * @param bool $preview
223   *   Whether to include preview attributes.
224   *
225   * @return array
226   *   The render array for the preset item.
227   */
228  private function buildPresetItem(string $builder_id, PatternPresetInterface $preset, bool $preview): array {
229    $keywords = \sprintf('%s %s', $preset->get('label'), $preset->get('description') ?? '');
230    $vals = ['preset_id' => $preset->id()];
231
232    if ($preview) {
233      $url = Url::fromRoute('display_builder.api_preset_preview', $vals);
234
235      $build = $this->buildPlaceholderListWithPreview($builder_id, $preset->get('label'), $vals, $url, $keywords);
236    }
237    else {
238      $build = $this->buildPlaceholderList($preset->get('label'), $vals, $keywords);
239    }
240
241    return $build;
242  }
243
244  /**
245   * Gets the preset's group from the preset entity in the database.
246   *
247   * @param \Drupal\display_builder\Entity\PatternPresetInterface $preset
248   *   The preset entity.
249   *
250   * @return string
251   *   The group name, fallback to 'Others'.
252   */
253  private function getPresetGroup(PatternPresetInterface $preset): string {
254    return $preset->getGroup() ? (string) $preset->getGroup() : (string) $this->t('Others');
255  }
256
257}