Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 94
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
TreePanel
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 4
132
0.00% covered (danger)
0.00%
0 / 1
 keyboardShortcuts
0.00% covered (danger)
0.00%
0 / 4
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 / 15
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
 buildSingleComponent
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 buildSingleBlock
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 5
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\Island\IslandType;
11use Drupal\display_builder\SourceWithSlotsInterface;
12
13/**
14 * Layers island plugin implementation.
15 */
16#[Island(
17  id: 'tree',
18  label: new TranslatableMarkup('Tree'),
19  description: new TranslatableMarkup('Hierarchical view of components and blocks.'),
20  type: IslandType::View,
21  default_region: 'main',
22  icon: 'bar-chart-steps',
23)]
24class TreePanel extends BuilderPanel {
25
26  /**
27   * {@inheritdoc}
28   */
29  public static function keyboardShortcuts(): array {
30    return [
31      'key' => 't',
32      'help' => t('Show the tree'),
33    ];
34  }
35
36  /**
37   * {@inheritdoc}
38   */
39  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
40    $builder_id = (string) $builder->id();
41
42    $build = [
43      '#type' => 'component',
44      '#component' => 'display_builder:panel_tree',
45      '#slots' => [
46        'items' => $this->digFromSlot($builder_id, $data),
47      ],
48      '#attributes' => [
49        // Required for JavaScript @see components/dropzone/dropzone.js.
50        'data-db-id' => $builder_id,
51        'data-node-title' => $this->t('Base container'),
52        'data-db-root' => TRUE,
53        // 'class' => ['db-dropzone--root', 'db-dropzone'],
54      ],
55    ];
56
57    return $build;
58  }
59
60  /**
61   * {@inheritdoc}
62   */
63  protected function buildSingleComponent(string $builder_id, string $instance_id, SourceWithSlotsInterface $source, array $data, int $index = 0): ?array {
64    $info = $this->resolveComponentInfo($source, $data, $instance_id);
65
66    if ($info === NULL) {
67      return NULL;
68    }
69
70    ['label' => $label, 'instance_id' => $instance_id] = $info;
71
72    $slots = [];
73
74    foreach ($source->getSlotDefinitions() as $slot_id => $definition) {
75      $items = [
76        '#type' => 'component',
77        '#component' => 'display_builder:tree_item',
78        '#props' => [
79          'icon' => 'box-arrow-in-right',
80        ],
81        '#slots' => [
82          'title' => $definition['title'],
83        ],
84        // Slot is needed for contextual menu paste.
85        // @see assets/js/contextual_menu.js
86        '#attributes' => [
87          'data-slot-id' => $slot_id,
88          'data-slot-title' => $definition['title'],
89          'data-node-id' => $instance_id,
90          'data-node-title' => $label,
91          'data-menu-type' => 'slot',
92        ],
93      ];
94
95      if ($sources = $source->getSlotValue($slot_id)) {
96        $items['#slots']['children'] = $this->digFromSlot($builder_id, $sources);
97      }
98
99      $slots[] = $items;
100    }
101
102    // I f a single item, expand by default.
103    if (\count($slots) === 1) {
104      $slots[0]['#props']['expanded'] = TRUE;
105    }
106
107    return [
108      '#type' => 'component',
109      '#component' => 'display_builder:tree_item',
110      '#props' => [
111        'expanded' => TRUE,
112        'icon' => 'box',
113      ],
114      '#slots' => [
115        'title' => $label,
116        'children' => $slots,
117      ],
118      // Required for the context menu label.
119      // @see assets/js/contextual_menu.js
120      '#attributes' => [
121        'data-node-id' => $instance_id,
122        'data-node-title' => $label,
123        'data-slot-position' => $index,
124        'data-menu-type' => 'component',
125        // 'class' => ['db-dropzone', 'db-tree__component'],
126      ],
127    ];
128  }
129
130  /**
131   * {@inheritdoc}
132   */
133  protected function buildSingleBlock(string $builder_id, string $instance_id, array $data, int $index = 0): array {
134    $instance_id = $instance_id ?: $data['node_id'];
135    $label = $this->slotSourceProxy->getLabelWithSummary($data, $this->configuration['contexts'] ?? []);
136
137    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
138      $label['summary'] = (string) $this->t('Field: @label', ['@label' => $label['label']]);
139    }
140
141    return [
142      '#type' => 'component',
143      '#component' => 'display_builder:tree_item',
144      '#props' => [
145        'icon' => 'view-list',
146      ],
147      '#slots' => [
148        'title' => $label['summary'],
149      ],
150      '#attributes' => [
151        'data-node-id' => $instance_id,
152        // This label is used for contextual menu.
153        // @see assets/js/contextual_menu.js
154        'data-node-title' => $label['summary'],
155        'data-slot-position' => $index,
156        'data-menu-type' => 'block',
157        // 'class' => ['db-dropzone', 'db-tree__block'],
158      ],
159    ];
160  }
161
162}