Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
83.62% covered (warning)
83.62%
97 / 116
80.33% covered (warning)
80.33%
49 / 61
31.25% covered (danger)
31.25%
15 / 48
52.63% covered (warning)
52.63%
10 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
LayoutSource
83.62% covered (warning)
83.62%
97 / 116
80.33% covered (warning)
80.33%
49 / 61
31.25% covered (danger)
31.25%
15 / 48
57.89% covered (warning)
57.89%
11 / 19
457.14
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 defaultSettings
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
 getPropValue
83.33% covered (warning)
83.33%
10 / 12
81.82% covered (warning)
81.82%
9 / 11
14.29% covered (danger)
14.29%
1 / 7
0.00% covered (danger)
0.00%
0 / 1
20.74
 settingsForm
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
3 / 3
50.00% covered (danger)
50.00%
1 / 2
100.00% covered (success)
100.00%
1 / 1
2.50
 settingsSummary
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
 getChoices
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
9 / 9
16.67% covered (danger)
16.67%
1 / 6
100.00% covered (success)
100.00%
1 / 1
13.26
 getChoiceSettings
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
 getChoice
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 calculateDependencies
100.00% covered (success)
100.00%
21 / 21
91.67% covered (success)
91.67%
11 / 12
8.33% covered (danger)
8.33%
1 / 12
100.00% covered (success)
100.00%
1 / 1
24.26
 getSlotDefinitions
87.50% covered (warning)
87.50%
7 / 8
83.33% covered (warning)
83.33%
5 / 6
25.00% covered (danger)
25.00%
1 / 4
0.00% covered (danger)
0.00%
0 / 1
6.80
 getSlotValues
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSlotValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSlotValue
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSlotRenderable
0.00% covered (danger)
0.00%
0 / 2
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
 getSlotPath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 settingsFormPropsOnly
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLayout
75.00% covered (warning)
75.00%
3 / 4
66.67% covered (warning)
66.67%
2 / 3
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 getExtensionType
40.00% covered (danger)
40.00%
2 / 5
40.00% covered (danger)
40.00%
2 / 5
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
5.67
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\UiPatterns\Source;
6
7use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;
8use Drupal\Component\Render\MarkupInterface;
9use Drupal\Core\Extension\ModuleHandlerInterface;
10use Drupal\Core\Extension\ThemeHandlerInterface;
11use Drupal\Core\Form\FormStateInterface;
12use Drupal\Core\Layout\LayoutInterface;
13use Drupal\Core\Layout\LayoutPluginManagerInterface;
14use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
15use Drupal\Core\Plugin\PluginFormInterface;
16use Drupal\Core\Routing\RouteMatchInterface;
17use Drupal\Core\StringTranslation\TranslatableMarkup;
18use Drupal\Core\Utility\Token;
19use Drupal\display_builder\SourceWithSlotsInterface;
20use Drupal\ui_patterns\Attribute\Source;
21use Drupal\ui_patterns\Element\ComponentElementBuilder;
22use Drupal\ui_patterns\Entity\SampleEntityGeneratorInterface;
23use Drupal\ui_patterns\PropTypePluginManager;
24use Drupal\ui_patterns\SourcePluginBase;
25use Drupal\ui_patterns\SourcePluginManager;
26use Drupal\ui_patterns\SourceWithChoicesInterface;
27use Drupal\ui_patterns\UiPatternsNormalizerInterface;
28use Symfony\Component\DependencyInjection\ContainerInterface;
29
30/**
31 * Plugin implementation of the source.
32 */
33#[Source(
34  id: 'layout',
35  label: new TranslatableMarkup('Layout'),
36  prop_types: ['slot'],
37  context_requirements: ['layout_discovery'],
38)]
39class LayoutSource extends SourcePluginBase implements SourceWithChoicesInterface, SourceWithSlotsInterface {
40
41  /**
42   * {@inheritdoc}
43   */
44  public function __construct(
45    array $configuration,
46    $plugin_id,
47    $plugin_definition,
48    PropTypePluginManager $propTypeManager,
49    ContextRepositoryInterface $contextRepository,
50    RouteMatchInterface $routeMatch,
51    SampleEntityGeneratorInterface $sampleEntityGenerator,
52    ModuleHandlerInterface $moduleHandler,
53    Token $token,
54    UiPatternsNormalizerInterface $normalizer,
55    protected ComponentElementBuilder $componentElementBuilder,
56    protected LayoutPluginManagerInterface $layoutManager,
57    protected SourcePluginManager $sourceManager,
58    protected ThemeHandlerInterface $themeHandler,
59  ) {
60    parent::__construct($configuration, $plugin_id, $plugin_definition, $propTypeManager, $contextRepository, $routeMatch, $sampleEntityGenerator, $moduleHandler, $token, $normalizer);
61  }
62
63  /**
64   * {@inheritdoc}
65   */
66  public static function create(
67    ContainerInterface $container,
68    array $configuration,
69    $plugin_id,
70    $plugin_definition,
71  ): static {
72    $instance = new static(
73      $configuration,
74      $plugin_id,
75      $plugin_definition,
76      $container->get('plugin.manager.ui_patterns_prop_type'),
77      $container->get('context.repository'),
78      $container->get('current_route_match'),
79      $container->get('ui_patterns.sample_entity_generator'),
80      $container->get('module_handler'),
81      $container->get('token'),
82      $container->get('ui_patterns.normalizer'),
83      $container->get('ui_patterns.component_element_builder'),
84      $container->get('plugin.manager.core.layout'),
85      $container->get('plugin.manager.ui_patterns_source'),
86      $container->get('theme_handler'),
87    );
88
89    return $instance;
90  }
91
92  /**
93   * {@inheritdoc}
94   */
95  public function defaultSettings(): array {
96    $layout = $this->getLayout();
97
98    return [
99      'layout_id' => NULL,
100      'settings' => $layout->defaultConfiguration() ?? [],
101      'regions' => [],
102    ];
103  }
104
105  /**
106   * {@inheritdoc}
107   */
108  public function getPropValue(): mixed {
109    $layout = $this->getLayout();
110
111    if (!$layout) {
112      return [];
113    }
114
115    $regions = [];
116
117    foreach ($this->configuration['settings']['regions'] ?? [] as $region_id => $region) {
118      foreach ($region as $source) {
119        $content = $this->componentElementBuilder->buildSource([], 'content', [], $source, $this->configuration['contexts'] ?? []) ?? [];
120        $content = $content['#slots']['content'][0] ?? [];
121
122        // An empty render array is enough to cancel the rendering of the full
123        // layout plugins, so let's remove them from the renderable.
124        if (empty($content)) {
125          continue;
126        }
127        $regions[$region_id][] = $content;
128      }
129    }
130
131    return $layout->build($regions);
132  }
133
134  /**
135   * {@inheritdoc}
136   */
137  public function settingsForm(array $form, FormStateInterface $form_state): array {
138    // Careful with the configuration/settings/settings hierarchy where:
139    // - $this->configuration has everything UI Patterns needs to work properly
140    //   (prop_id, prop_definition, contexts..)
141    // - $this->configuration['settings'] is the source data from the source
142    //   tree
143    // - $this->configuration['settings']['settings'] is the layout plugin
144    //   configuration.
145    $form = parent::settingsForm($form, $form_state);
146    $form['#tree'] = TRUE;
147
148    // We are not implementing a slot sources form for $forms['regions'],
149    // because Display Builder doesn't need layout regions to be available in
150    // the component form.
151    // @todo However, for compatibility with UI Patterns ecosystem, it would be
152    // better to implement it anyway. It will be removed by
153    // ::settingsFormPropsOnly() anyway.
154    $form['regions'] = [];
155    $layout = $this->getLayout();
156
157    if ($layout instanceof PluginFormInterface) {
158      $form['settings'] = $layout->buildConfigurationForm($form, $form_state);
159      // Hide administrative label textfield.
160      $form['settings']['label']['#type'] = 'hidden';
161    }
162
163    return $form;
164  }
165
166  /**
167   * {@inheritdoc}
168   */
169  public function settingsSummary(): array {
170    // @todo settings summary
171    return [];
172  }
173
174  /**
175   * {@inheritdoc}
176   */
177  public function getChoices(): array {
178    $definitions = $this->layoutManager->getGroupedDefinitions();
179    $choices = [];
180
181    foreach ($definitions as $group_id => $group) {
182      foreach ($group as $layout_id => $definition) {
183        /** @var \Drupal\Core\Layout\LayoutDefinition $definition */
184        $choice = [
185          'label' => $definition->getLabel(),
186          'original_id' => $layout_id,
187          'group' => $group_id,
188          'provider' => $definition,
189        ];
190
191        if ($choice['label'] instanceof MarkupInterface) {
192          $choice['label'] = (string) $choice['label'];
193        }
194        $choices[$layout_id] = $choice;
195      }
196    }
197
198    return $choices;
199  }
200
201  /**
202   * {@inheritdoc}
203   */
204  public function getChoiceSettings(string $choice_id): array {
205    return [
206      'layout_id' => $choice_id,
207    ];
208  }
209
210  /**
211   * {@inheritdoc}
212   */
213  public function getChoice(array $settings): string {
214    return $settings['layout_id'] ?? '';
215  }
216
217  /**
218   * {@inheritdoc}
219   */
220  public function calculateDependencies(): array {
221    $dependencies = parent::calculateDependencies();
222    $layout = $this->getLayout();
223    $provider = [];
224
225    // 1. Dependency of plugin.manager.core.layout.
226    $provider['module'][] = 'layout_discovery';
227
228    // 2. Layout provider extension (module or theme)
229    $definition = $layout->getPluginDefinition();
230    $provider = ($definition instanceof PluginDefinitionInterface) ? $definition->getProvider() : (string) ($definition['provider'] ?? '');
231    $extension_type = $this->getExtensionType($provider);
232    $dependencies[$extension_type][] = $provider;
233
234    // 3. Layout plugin dependencies.
235    SourcePluginBase::mergeConfigDependencies(
236      $dependencies,
237      $layout->calculateDependencies()
238    );
239
240    // 4. Sources in slots.
241    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
242
243    foreach ($this->getSlotValues() as $slot_id => $slot) {
244      foreach ($slot as $source) {
245        if ($source = $this->sourceManager->getSource($slot_id, $slot_definition, $source, [])) {
246          SourcePluginBase::mergeConfigDependencies(
247            $dependencies,
248            $source->calculateDependencies()
249          );
250        }
251      }
252    }
253
254    return $dependencies;
255  }
256
257  /**
258   * {@inheritdoc}
259   */
260  public function getSlotDefinitions(): array {
261    $slots = [];
262    $layout_id = $this->configuration['settings']['layout_id'] ?? NULL;
263
264    if (!$layout_id) {
265      return [];
266    }
267    $definition = $this->layoutManager->getDefinition($layout_id);
268
269    foreach ($definition->getRegions() as $region_id => $region) {
270      $slots[$region_id]['title'] = (string) $region['label'];
271    }
272
273    return $slots;
274  }
275
276  /**
277   * {@inheritdoc}
278   */
279  public function getSlotValues(): array {
280    return $this->configuration['settings']['regions'] ?? [];
281  }
282
283  /**
284   * {@inheritdoc}
285   */
286  public function getSlotValue(string $slot_id): array {
287    return $this->configuration['settings']['regions'][$slot_id] ?? [];
288  }
289
290  /**
291   * {@inheritdoc}
292   */
293  public function setSlotValue(string $slot_id, array $slot): array {
294    $this->configuration['settings']['regions'][$slot_id] = $slot;
295
296    return $this->configuration['settings'];
297  }
298
299  /**
300   * {@inheritdoc}
301   */
302  public function setSlotRenderable(array $build, string $slot_id, array $slot): array {
303    $build[$slot_id] = $slot;
304
305    return $build;
306  }
307
308  /**
309   * {@inheritdoc}
310   */
311  public static function getSlotPath(string $slot_id): array {
312    return ['regions', $slot_id];
313  }
314
315  /**
316   * {@inheritdoc}
317   */
318  public function settingsFormPropsOnly(array $form, FormStateInterface $form_state): array {
319    $form = $this->settingsForm($form, $form_state);
320    unset($form['regions']);
321    $layout = $this->getLayout();
322    $form['layout_id'] = [
323      '#type' => 'hidden',
324      '#value' => $layout->getPluginId(),
325    ];
326
327    return $form;
328  }
329
330  /**
331   * Get layout plugin.
332   *
333   * @return \Drupal\Core\Layout\LayoutInterface|null
334   *   The layout plugin.
335   */
336  private function getLayout(): ?LayoutInterface {
337    $layout_id = $this->configuration['settings']['layout_id'] ?? NULL;
338
339    if (!$layout_id) {
340      return NULL;
341    }
342
343    return $this->layoutManager->createInstance($layout_id, $this->configuration['settings']['settings'] ?? []);
344  }
345
346  /**
347   * Get extension type (theme or module).
348   *
349   * @param string $extension
350   *   Extension (module or theme) machine name.
351   *
352   * @return string
353   *   Extension type.
354   */
355  private function getExtensionType(string $extension): string {
356    if ($this->moduleHandler->moduleExists($extension)) {
357      return 'module';
358    }
359
360    if ($this->themeHandler->themeExists($extension)) {
361      return 'theme';
362    }
363
364    return '';
365  }
366
367}

Branches

Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once. Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

LayoutSource->__construct
45    array $configuration,
46    $plugin_id,
47    $plugin_definition,
48    PropTypePluginManager $propTypeManager,
49    ContextRepositoryInterface $contextRepository,
50    RouteMatchInterface $routeMatch,
51    SampleEntityGeneratorInterface $sampleEntityGenerator,
52    ModuleHandlerInterface $moduleHandler,
53    Token $token,
54    UiPatternsNormalizerInterface $normalizer,
55    protected ComponentElementBuilder $componentElementBuilder,
56    protected LayoutPluginManagerInterface $layoutManager,
57    protected SourcePluginManager $sourceManager,
58    protected ThemeHandlerInterface $themeHandler,
59  ) {
60    parent::__construct($configuration, $plugin_id, $plugin_definition, $propTypeManager, $contextRepository, $routeMatch, $sampleEntityGenerator, $moduleHandler, $token, $normalizer);
61  }
LayoutSource->calculateDependencies
221    $dependencies = parent::calculateDependencies();
222    $layout = $this->getLayout();
223    $provider = [];
224
225    // 1. Dependency of plugin.manager.core.layout.
226    $provider['module'][] = 'layout_discovery';
227
228    // 2. Layout provider extension (module or theme)
229    $definition = $layout->getPluginDefinition();
230    $provider = ($definition instanceof PluginDefinitionInterface) ? $definition->getProvider() : (string) ($definition['provider'] ?? '');
230    $provider = ($definition instanceof PluginDefinitionInterface) ? $definition->getProvider() : (string) ($definition['provider'] ?? '');
230    $provider = ($definition instanceof PluginDefinitionInterface) ? $definition->getProvider() : (string) ($definition['provider'] ?? '');
230    $provider = ($definition instanceof PluginDefinitionInterface) ? $definition->getProvider() : (string) ($definition['provider'] ?? '');
231    $extension_type = $this->getExtensionType($provider);
232    $dependencies[$extension_type][] = $provider;
233
234    // 3. Layout plugin dependencies.
235    SourcePluginBase::mergeConfigDependencies(
236      $dependencies,
237      $layout->calculateDependencies()
238    );
239
240    // 4. Sources in slots.
241    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
242
243    foreach ($this->getSlotValues() as $slot_id => $slot) {
243    foreach ($this->getSlotValues() as $slot_id => $slot) {
243    foreach ($this->getSlotValues() as $slot_id => $slot) {
244      foreach ($slot as $source) {
244      foreach ($slot as $source) {
245        if ($source = $this->sourceManager->getSource($slot_id, $slot_definition, $source, [])) {
244      foreach ($slot as $source) {
245        if ($source = $this->sourceManager->getSource($slot_id, $slot_definition, $source, [])) {
246          SourcePluginBase::mergeConfigDependencies(
244      foreach ($slot as $source) {
243    foreach ($this->getSlotValues() as $slot_id => $slot) {
244      foreach ($slot as $source) {
243    foreach ($this->getSlotValues() as $slot_id => $slot) {
244      foreach ($slot as $source) {
245        if ($source = $this->sourceManager->getSource($slot_id, $slot_definition, $source, [])) {
246          SourcePluginBase::mergeConfigDependencies(
247            $dependencies,
248            $source->calculateDependencies()
249          );
250        }
251      }
252    }
253
254    return $dependencies;
255  }
LayoutSource->create
67    ContainerInterface $container,
68    array $configuration,
69    $plugin_id,
70    $plugin_definition,
71  ): static {
72    $instance = new static(
73      $configuration,
74      $plugin_id,
75      $plugin_definition,
76      $container->get('plugin.manager.ui_patterns_prop_type'),
77      $container->get('context.repository'),
78      $container->get('current_route_match'),
79      $container->get('ui_patterns.sample_entity_generator'),
80      $container->get('module_handler'),
81      $container->get('token'),
82      $container->get('ui_patterns.normalizer'),
83      $container->get('ui_patterns.component_element_builder'),
84      $container->get('plugin.manager.core.layout'),
85      $container->get('plugin.manager.ui_patterns_source'),
86      $container->get('theme_handler'),
87    );
88
89    return $instance;
90  }
LayoutSource->defaultSettings
96    $layout = $this->getLayout();
97
98    return [
99      'layout_id' => NULL,
100      'settings' => $layout->defaultConfiguration() ?? [],
101      'regions' => [],
102    ];
103  }
LayoutSource->getChoice
213  public function getChoice(array $settings): string {
214    return $settings['layout_id'] ?? '';
215  }
LayoutSource->getChoiceSettings
204  public function getChoiceSettings(string $choice_id): array {
205    return [
206      'layout_id' => $choice_id,
207    ];
208  }
LayoutSource->getChoices
178    $definitions = $this->layoutManager->getGroupedDefinitions();
179    $choices = [];
180
181    foreach ($definitions as $group_id => $group) {
181    foreach ($definitions as $group_id => $group) {
181    foreach ($definitions as $group_id => $group) {
182      foreach ($group as $layout_id => $definition) {
182      foreach ($group as $layout_id => $definition) {
182      foreach ($group as $layout_id => $definition) {
183        /** @var \Drupal\Core\Layout\LayoutDefinition $definition */
184        $choice = [
185          'label' => $definition->getLabel(),
186          'original_id' => $layout_id,
187          'group' => $group_id,
188          'provider' => $definition,
189        ];
190
191        if ($choice['label'] instanceof MarkupInterface) {
192          $choice['label'] = (string) $choice['label'];
193        }
194        $choices[$layout_id] = $choice;
182      foreach ($group as $layout_id => $definition) {
183        /** @var \Drupal\Core\Layout\LayoutDefinition $definition */
184        $choice = [
185          'label' => $definition->getLabel(),
186          'original_id' => $layout_id,
187          'group' => $group_id,
188          'provider' => $definition,
189        ];
190
191        if ($choice['label'] instanceof MarkupInterface) {
192          $choice['label'] = (string) $choice['label'];
193        }
194        $choices[$layout_id] = $choice;
181    foreach ($definitions as $group_id => $group) {
182      foreach ($group as $layout_id => $definition) {
181    foreach ($definitions as $group_id => $group) {
182      foreach ($group as $layout_id => $definition) {
183        /** @var \Drupal\Core\Layout\LayoutDefinition $definition */
184        $choice = [
185          'label' => $definition->getLabel(),
186          'original_id' => $layout_id,
187          'group' => $group_id,
188          'provider' => $definition,
189        ];
190
191        if ($choice['label'] instanceof MarkupInterface) {
192          $choice['label'] = (string) $choice['label'];
193        }
194        $choices[$layout_id] = $choice;
195      }
196    }
197
198    return $choices;
199  }
LayoutSource->getExtensionType
355  private function getExtensionType(string $extension): string {
356    if ($this->moduleHandler->moduleExists($extension)) {
357      return 'module';
360    if ($this->themeHandler->themeExists($extension)) {
361      return 'theme';
364    return '';
365  }
LayoutSource->getLayout
337    $layout_id = $this->configuration['settings']['layout_id'] ?? NULL;
338
339    if (!$layout_id) {
340      return NULL;
343    return $this->layoutManager->createInstance($layout_id, $this->configuration['settings']['settings'] ?? []);
344  }
LayoutSource->getPropValue
109    $layout = $this->getLayout();
110
111    if (!$layout) {
112      return [];
115    $regions = [];
116
117    foreach ($this->configuration['settings']['regions'] ?? [] as $region_id => $region) {
117    foreach ($this->configuration['settings']['regions'] ?? [] as $region_id => $region) {
117    foreach ($this->configuration['settings']['regions'] ?? [] as $region_id => $region) {
118      foreach ($region as $source) {
118      foreach ($region as $source) {
119        $content = $this->componentElementBuilder->buildSource([], 'content', [], $source, $this->configuration['contexts'] ?? []) ?? [];
120        $content = $content['#slots']['content'][0] ?? [];
121
122        // An empty render array is enough to cancel the rendering of the full
123        // layout plugins, so let's remove them from the renderable.
124        if (empty($content)) {
125          continue;
118      foreach ($region as $source) {
119        $content = $this->componentElementBuilder->buildSource([], 'content', [], $source, $this->configuration['contexts'] ?? []) ?? [];
120        $content = $content['#slots']['content'][0] ?? [];
121
122        // An empty render array is enough to cancel the rendering of the full
123        // layout plugins, so let's remove them from the renderable.
124        if (empty($content)) {
125          continue;
126        }
127        $regions[$region_id][] = $content;
117    foreach ($this->configuration['settings']['regions'] ?? [] as $region_id => $region) {
118      foreach ($region as $source) {
117    foreach ($this->configuration['settings']['regions'] ?? [] as $region_id => $region) {
118      foreach ($region as $source) {
119        $content = $this->componentElementBuilder->buildSource([], 'content', [], $source, $this->configuration['contexts'] ?? []) ?? [];
120        $content = $content['#slots']['content'][0] ?? [];
121
122        // An empty render array is enough to cancel the rendering of the full
123        // layout plugins, so let's remove them from the renderable.
124        if (empty($content)) {
125          continue;
126        }
127        $regions[$region_id][] = $content;
128      }
129    }
130
131    return $layout->build($regions);
132  }
LayoutSource->getSlotDefinitions
261    $slots = [];
262    $layout_id = $this->configuration['settings']['layout_id'] ?? NULL;
263
264    if (!$layout_id) {
265      return [];
267    $definition = $this->layoutManager->getDefinition($layout_id);
268
269    foreach ($definition->getRegions() as $region_id => $region) {
269    foreach ($definition->getRegions() as $region_id => $region) {
269    foreach ($definition->getRegions() as $region_id => $region) {
269    foreach ($definition->getRegions() as $region_id => $region) {
270      $slots[$region_id]['title'] = (string) $region['label'];
271    }
272
273    return $slots;
274  }
LayoutSource->getSlotPath
311  public static function getSlotPath(string $slot_id): array {
312    return ['regions', $slot_id];
313  }
LayoutSource->getSlotValue
286  public function getSlotValue(string $slot_id): array {
287    return $this->configuration['settings']['regions'][$slot_id] ?? [];
288  }
LayoutSource->getSlotValues
280    return $this->configuration['settings']['regions'] ?? [];
281  }
LayoutSource->setSlotRenderable
302  public function setSlotRenderable(array $build, string $slot_id, array $slot): array {
303    $build[$slot_id] = $slot;
304
305    return $build;
306  }
LayoutSource->setSlotValue
293  public function setSlotValue(string $slot_id, array $slot): array {
294    $this->configuration['settings']['regions'][$slot_id] = $slot;
295
296    return $this->configuration['settings'];
297  }
LayoutSource->settingsForm
137  public function settingsForm(array $form, FormStateInterface $form_state): array {
138    // Careful with the configuration/settings/settings hierarchy where:
139    // - $this->configuration has everything UI Patterns needs to work properly
140    //   (prop_id, prop_definition, contexts..)
141    // - $this->configuration['settings'] is the source data from the source
142    //   tree
143    // - $this->configuration['settings']['settings'] is the layout plugin
144    //   configuration.
145    $form = parent::settingsForm($form, $form_state);
146    $form['#tree'] = TRUE;
147
148    // We are not implementing a slot sources form for $forms['regions'],
149    // because Display Builder doesn't need layout regions to be available in
150    // the component form.
151    // @todo However, for compatibility with UI Patterns ecosystem, it would be
152    // better to implement it anyway. It will be removed by
153    // ::settingsFormPropsOnly() anyway.
154    $form['regions'] = [];
155    $layout = $this->getLayout();
156
157    if ($layout instanceof PluginFormInterface) {
158      $form['settings'] = $layout->buildConfigurationForm($form, $form_state);
159      // Hide administrative label textfield.
160      $form['settings']['label']['#type'] = 'hidden';
161    }
162
163    return $form;
163    return $form;
164  }
LayoutSource->settingsFormPropsOnly
318  public function settingsFormPropsOnly(array $form, FormStateInterface $form_state): array {
319    $form = $this->settingsForm($form, $form_state);
320    unset($form['regions']);
321    $layout = $this->getLayout();
322    $form['layout_id'] = [
323      '#type' => 'hidden',
324      '#value' => $layout->getPluginId(),
325    ];
326
327    return $form;
328  }
LayoutSource->settingsSummary
171    return [];
172  }