Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
LayersPanel
0.00% covered (danger)
0.00%
0 / 84
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 6
420
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
 keyboardShortcuts
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
 build
0.00% covered (danger)
0.00%
0 / 7
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
 buildSingleComponent
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
72
 buildSingleBlock
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
20
 getComponentVariantLabel
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 4
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\StringTranslation\TranslatableMarkup;
8use Drupal\display_builder\Attribute\Island;
9use Drupal\display_builder\InstanceInterface;
10use Drupal\display_builder\IslandType;
11use Drupal\display_builder\SlotSourceProxy;
12use Symfony\Component\DependencyInjection\ContainerInterface;
13
14/**
15 * Layers island plugin implementation.
16 */
17#[Island(
18  id: 'layers',
19  label: new TranslatableMarkup('Layers'),
20  description: new TranslatableMarkup('Manage hierarchical layer view of elements without preview.'),
21  type: IslandType::View,
22  icon: 'layers',
23)]
24class LayersPanel extends BuilderPanel {
25
26  /**
27   * Proxy for slot source operations.
28   */
29  protected SlotSourceProxy $slotSourceProxy;
30
31  /**
32   * {@inheritdoc}
33   */
34  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
35    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
36    $instance->slotSourceProxy = $container->get('display_builder.slot_sources_proxy');
37
38    return $instance;
39  }
40
41  /**
42   * {@inheritdoc}
43   */
44  public static function keyboardShortcuts(): array {
45    return [
46      'y' => t('Show the layer'),
47    ];
48  }
49
50  /**
51   * {@inheritdoc}
52   */
53  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
54    $build = parent::build($builder, $data, $options);
55
56    if (empty($build['#slots']['content'] ?? [])) {
57      // Load en empty component to have any assets with it.
58      $build['#slots']['content'] = [
59        '#type' => 'component',
60        '#component' => 'display_builder:layer',
61      ];
62    }
63
64    return $build;
65  }
66
67  /**
68   * {@inheritdoc}
69   */
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
75      return [];
76    }
77
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
81      return [];
82    }
83
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
139  }
140
141  /**
142   * {@inheritdoc}
143   */
144  public function buildSingleBlock(string $builder_id, string $instance_id, array $data, int $index = 0): array {
145    $label = $this->slotSourceProxy->getLabelWithSummary($data, $this->configuration['contexts'] ?? []);
146
147    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
148      $label['summary'] = (string) $this->t('Field: @label', ['@label' => $label['label']]);
149    }
150
151    $build = [
152      '#type' => 'component',
153      '#component' => 'display_builder:layer',
154      '#slots' => [
155        'title' => $label['summary'],
156      ],
157    ];
158    $instance_id = $instance_id ?: $data['node_id'];
159
160    // This label is used for contextual menu.
161    // @see assets/js/contextual_menu.js
162    $build['#attributes']['data-node-title'] = $label['summary'];
163    $build['#attributes']['data-slot-position'] = $index;
164
165    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, $label['summary'], $index);
166  }
167
168  /**
169   * Get the label for a component variant.
170   *
171   * @param array $data
172   *   The component data array.
173   * @param array $definition
174   *   The component definition array.
175   *
176   * @return string
177   *   The variant label or empty string if no variant is set.
178   */
179  private function getComponentVariantLabel(array $data, array $definition): string {
180    if (!isset($data['source']['component']['variant_id'])) {
181      return '';
182    }
183
184    if ($data['source']['component']['variant_id']['source_id'] !== 'select') {
185      return '';
186    }
187    $variant_id = $data['source']['component']['variant_id']['source']['value'] ?? '';
188
189    if (empty($variant_id)) {
190      return '';
191    }
192
193    return $definition['variants'][$variant_id]['title'] ?? '';
194  }
195
196}

Paths

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

LayersPanel->build
53  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
54    $build = parent::build($builder, $data, $options);
55
56    if (empty($build['#slots']['content'] ?? [])) {
 
58      $build['#slots']['content'] = [
59        '#type' => 'component',
60        '#component' => 'display_builder:layer',
61      ];
62    }
63
64    return $build;
 
64    return $build;
53  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
54    $build = parent::build($builder, $data, $options);
55
56    if (empty($build['#slots']['content'] ?? [])) {
 
64    return $build;
LayersPanel->buildSingleBlock
144  public function buildSingleBlock(string $builder_id, string $instance_id, array $data, int $index = 0): array {
145    $label = $this->slotSourceProxy->getLabelWithSummary($data, $this->configuration['contexts'] ?? []);
146
147    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
 
147    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
 
148      $label['summary'] = (string) $this->t('Field: @label', ['@label' => $label['label']]);
149    }
150
151    $build = [
152      '#type' => 'component',
 
152      '#type' => 'component',
153      '#component' => 'display_builder:layer',
154      '#slots' => [
155        'title' => $label['summary'],
156      ],
157    ];
158    $instance_id = $instance_id ?: $data['node_id'];
159
160    // This label is used for contextual menu.
161    // @see assets/js/contextual_menu.js
162    $build['#attributes']['data-node-title'] = $label['summary'];
163    $build['#attributes']['data-slot-position'] = $index;
164
165    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, $label['summary'], $index);
144  public function buildSingleBlock(string $builder_id, string $instance_id, array $data, int $index = 0): array {
145    $label = $this->slotSourceProxy->getLabelWithSummary($data, $this->configuration['contexts'] ?? []);
146
147    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
 
147    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
 
152      '#type' => 'component',
153      '#component' => 'display_builder:layer',
154      '#slots' => [
155        'title' => $label['summary'],
156      ],
157    ];
158    $instance_id = $instance_id ?: $data['node_id'];
159
160    // This label is used for contextual menu.
161    // @see assets/js/contextual_menu.js
162    $build['#attributes']['data-node-title'] = $label['summary'];
163    $build['#attributes']['data-slot-position'] = $index;
164
165    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, $label['summary'], $index);
144  public function buildSingleBlock(string $builder_id, string $instance_id, array $data, int $index = 0): array {
145    $label = $this->slotSourceProxy->getLabelWithSummary($data, $this->configuration['contexts'] ?? []);
146
147    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
 
152      '#type' => 'component',
153      '#component' => 'display_builder:layer',
154      '#slots' => [
155        'title' => $label['summary'],
156      ],
157    ];
158    $instance_id = $instance_id ?: $data['node_id'];
159
160    // This label is used for contextual menu.
161    // @see assets/js/contextual_menu.js
162    $build['#attributes']['data-node-title'] = $label['summary'];
163    $build['#attributes']['data-slot-position'] = $index;
164
165    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, $label['summary'], $index);
LayersPanel->buildSingleComponent
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
75      return [];
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
81      return [];
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
 
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
 
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
81      return [];
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
 
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
 
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
121      $name .= ' - ' . $variant;
122    }
123
124    $build = [
125      '#type' => 'component',
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
70  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
71    $component_id = $data['source']['component']['component_id'] ?? NULL;
72    $instance_id = $instance_id ?: $data['node_id'];
73
74    if (!$instance_id && !$component_id) {
 
78    $component = $this->sdcManager->getDefinition($component_id);
79
80    if (!$component) {
 
84    $slots = [];
85
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
 
86    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
87      $dropzone = [
88        '#type' => 'component',
89        '#component' => 'display_builder:dropzone',
90        '#props' => [
91          'title' => $definition['title'],
92          'variant' => 'highlighted',
93        ],
94        '#attributes' => [
95          // Required for JavaScript @see components/dropzone/dropzone.js.
96          'data-db-id' => $builder_id,
97          // Slot is needed for contextual menu paste.
98          // @see assets/js/contextual_menu.js
99          'data-slot-id' => $slot_id,
100          'data-slot-title' => $definition['title'],
101          'data-node-title' => $component['label'],
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $dropzone['#slots']['content'] = $this->digFromSlot($builder_id, $sources);
108      }
109      $dropzone = $this->htmxEvents->onSlotDrop($dropzone, $builder_id, $this->getPluginID(), $instance_id, $slot_id);
110      $slots[] = [
111        [
112          '#plain_text' => $definition['title'],
113        ],
114        $dropzone,
115      ];
116    }
117    $name = $component['name'];
118    $variant = $this->getComponentVariantLabel($data, $component);
119
120    if ($variant) {
 
125      '#type' => 'component',
126      '#component' => 'display_builder:layer',
127      '#slots' => [
128        'title' => $name,
129        'children' => $slots,
130      ],
131      // Required for the context menu label.
132      // @see assets/js/contextual_menu.js
133      '#attributes' => [
134        'data-node-title' => $name,
135      ],
136    ];
137
138    return $this->htmxEvents->onInstanceClick($build, $builder_id, $instance_id, (string) $component['label'], $index);
LayersPanel->create
34  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
35    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
36    $instance->slotSourceProxy = $container->get('display_builder.slot_sources_proxy');
37
38    return $instance;
LayersPanel->getComponentVariantLabel
179  private function getComponentVariantLabel(array $data, array $definition): string {
180    if (!isset($data['source']['component']['variant_id'])) {
 
181      return '';
179  private function getComponentVariantLabel(array $data, array $definition): string {
180    if (!isset($data['source']['component']['variant_id'])) {
 
184    if ($data['source']['component']['variant_id']['source_id'] !== 'select') {
 
185      return '';
179  private function getComponentVariantLabel(array $data, array $definition): string {
180    if (!isset($data['source']['component']['variant_id'])) {
 
184    if ($data['source']['component']['variant_id']['source_id'] !== 'select') {
 
187    $variant_id = $data['source']['component']['variant_id']['source']['value'] ?? '';
188
189    if (empty($variant_id)) {
 
190      return '';
179  private function getComponentVariantLabel(array $data, array $definition): string {
180    if (!isset($data['source']['component']['variant_id'])) {
 
184    if ($data['source']['component']['variant_id']['source_id'] !== 'select') {
 
187    $variant_id = $data['source']['component']['variant_id']['source']['value'] ?? '';
188
189    if (empty($variant_id)) {
 
193    return $definition['variants'][$variant_id]['title'] ?? '';
LayersPanel->keyboardShortcuts
46      'y' => t('Show the layer'),