Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 90 |
|
0.00% |
0 / 29 |
|
0.00% |
0 / 30 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
| LayersPanel | |
0.00% |
0 / 84 |
|
0.00% |
0 / 29 |
|
0.00% |
0 / 30 |
|
0.00% |
0 / 6 |
420 | |
0.00% |
0 / 1 |
| create | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| keyboardShortcuts | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| build | |
0.00% |
0 / 7 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| buildSingleComponent | |
0.00% |
0 / 49 |
|
0.00% |
0 / 13 |
|
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
72 | |||
| buildSingleBlock | |
0.00% |
0 / 14 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
20 | |||
| getComponentVariantLabel | |
0.00% |
0 / 8 |
|
0.00% |
0 / 7 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
20 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder\Plugin\display_builder\Island; |
| 6 | |
| 7 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 8 | use Drupal\display_builder\Attribute\Island; |
| 9 | use Drupal\display_builder\InstanceInterface; |
| 10 | use Drupal\display_builder\IslandType; |
| 11 | use Drupal\display_builder\SlotSourceProxy; |
| 12 | use 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 | )] |
| 24 | class 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 | } |
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.
| 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; |
| 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); |
| 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 []; |
| 78 | $component = $this->sdcManager->getDefinition($component_id); |
| 79 | |
| 80 | if (!$component) { |
| 81 | return []; |
| 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) { |
| 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); |
| 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; |
| 179 | private function getComponentVariantLabel(array $data, array $definition): string { |
| 180 | if (!isset($data['source']['component']['variant_id'])) { |
| 181 | return ''; |
| 184 | if ($data['source']['component']['variant_id']['source_id'] !== 'select') { |
| 185 | return ''; |
| 187 | $variant_id = $data['source']['component']['variant_id']['source']['value'] ?? ''; |
| 188 | |
| 189 | if (empty($variant_id)) { |
| 190 | return ''; |
| 193 | return $definition['variants'][$variant_id]['title'] ?? ''; |
| 46 | 'y' => t('Show the layer'), |