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