Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 282
0.00% covered (danger)
0.00%
0 / 106
0.00% covered (danger)
0.00%
0 / 228
0.00% covered (danger)
0.00%
0 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
ProfileViewBuilder
0.00% covered (danger)
0.00%
0 / 282
0.00% covered (danger)
0.00%
0 / 106
0.00% covered (danger)
0.00%
0 / 228
0.00% covered (danger)
0.00%
0 / 16
2756
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 createInstance
0.00% covered (danger)
0.00%
0 / 9
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
 view
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 buildSlots
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 120
0.00% covered (danger)
0.00%
0 / 1
90
 buildFlatLibraryPanels
0.00% covered (danger)
0.00%
0 / 30
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
 buildButtons
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 prepareViewIslands
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
42
 buildContextualIslands
0.00% covered (danger)
0.00%
0 / 16
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
 buildPanes
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 buildDeferrableAttribute
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 buildFloatingControlsRegion
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
56
 buildStartButtons
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
 buildDynamicTabs
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 resolvePanelDisplay
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
 buildMenuWrapper
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getIslandsEnableSorted
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder;
6
7use Drupal\Component\Utility\Html;
8use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
9use Drupal\Core\Entity\EntityInterface;
10use Drupal\Core\Entity\EntityRepositoryInterface;
11use Drupal\Core\Entity\EntityTypeInterface;
12use Drupal\Core\Entity\EntityTypeManagerInterface;
13use Drupal\Core\Entity\EntityViewBuilder;
14use Drupal\Core\Language\LanguageManagerInterface;
15use Drupal\Core\Security\TrustedCallbackInterface;
16use Drupal\Core\Theme\Registry;
17use Drupal\display_builder\Entity\ProfileInterface;
18use Drupal\display_builder\Island\IslandInterface;
19use Drupal\display_builder\Island\IslandPluginManagerInterface;
20use Drupal\display_builder\Island\IslandType;
21use Symfony\Component\DependencyInjection\ContainerInterface;
22
23/**
24 * View builder handler for display builder profiles.
25 */
26class ProfileViewBuilder extends EntityViewBuilder implements TrustedCallbackInterface {
27
28  use RenderableBuilderTrait;
29
30  /**
31   * The entity we are building the view for.
32   */
33  protected ProfileInterface $entity;
34
35  /**
36   * {@inheritdoc}
37   */
38  public function __construct(
39    EntityTypeInterface $entity_type,
40    EntityRepositoryInterface $entity_repository,
41    LanguageManagerInterface $language_manager,
42    Registry $theme_registry,
43    EntityDisplayRepositoryInterface $entity_display_repository,
44    protected EntityTypeManagerInterface $entityTypeManager,
45    protected IslandPluginManagerInterface $islandPluginManager,
46  ) {
47    parent::__construct($entity_type, $entity_repository, $language_manager, $theme_registry, $entity_display_repository);
48  }
49
50  /**
51   * {@inheritdoc}
52   */
53  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type): static {
54    return new static(
55      $entity_type,
56      $container->get('entity.repository'),
57      $container->get('language_manager'),
58      $container->get('theme.registry'),
59      $container->get('entity_display.repository'),
60      $container->get('entity_type.manager'),
61      $container->get('plugin.manager.db_island'),
62    );
63  }
64
65  /**
66   * {@inheritdoc}
67   */
68  public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL): array {
69    // We have 'hacked' the interface by using $view_mode as a way of passing
70    // the Instance entity ID.
71    $builder_id = $view_mode;
72
73    /** @var \Drupal\display_builder\Entity\ProfileInterface $entity */
74    $entity = $entity;
75    $this->entity = $entity;
76
77    /** @var \Drupal\display_builder\InstanceInterface $builder */
78    $builder = $this->entityTypeManager->getStorage('display_builder_instance')->load($builder_id);
79    $buildable_id = $builder->get('buildable')->first()->get('plugin_id')->getValue() ?? '';
80    $contexts = $builder->getAvailableContexts() ?? [];
81
82    $islands_enabled_sorted = $this->getIslandsEnableSorted($contexts);
83    $build = [
84      '#type' => 'component',
85      '#component' => 'display_builder:display_builder',
86      '#props' => [
87        'builder_id' => $builder_id,
88        'buildable_id' => Html::getClass($buildable_id),
89        'hash' => (string) $builder->getHash(),
90      ],
91      '#slots' => $this->buildSlots($builder, $islands_enabled_sorted),
92      '#attached' => [],
93      '#cache' => [
94        'tags' => \array_merge(
95          $builder->getCacheTags(),
96          $entity->getCacheTags()
97        ),
98      ],
99    ];
100
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
103        $build = $island->alterRenderable($builder, $build);
104      }
105    }
106
107    return $build;
108  }
109
110  /**
111   * Builds and returns the value of each slot.
112   *
113   * @param \Drupal\display_builder\InstanceInterface $builder
114   *   Display builder instance.
115   * @param array $islands_enabled_sorted
116   *   An array of enabled islands.
117   *
118   * @return array
119   *   An associative array with the value of each slot.
120   */
121  private function buildSlots(InstanceInterface $builder, array $islands_enabled_sorted): array {
122    $builder_data = $builder->getCurrentState();
123
124    $button_islands = $islands_enabled_sorted[IslandType::Button->value] ?? [];
125    $library_islands = $islands_enabled_sorted[IslandType::Library->value] ?? [];
126    $contextual_islands = $islands_enabled_sorted[IslandType::Contextual->value] ?? [];
127    $menu_islands = $islands_enabled_sorted[IslandType::Menu->value] ?? [];
128    $view_islands = $islands_enabled_sorted[IslandType::View->value] ?? [];
129    $floating_islands = $islands_enabled_sorted[IslandType::Floating->value] ?? [];
130
131    if (!empty($menu_islands)) {
132      $menu_islands = $this->buildMenuWrapper($builder, $menu_islands);
133    }
134
135    if (!empty($library_islands)) {
136      $library_islands = $this->entity->isLibraryFlat()
137        ? $this->buildFlatLibraryPanels($builder, $library_islands, $builder_data)
138        : [
139          $this->buildDynamicTabs($builder, $library_islands, TRUE, $this->entity->getLibraryTabsDisplay()),
140          $this->buildPanes($builder, $library_islands, $builder_data),
141        ];
142    }
143
144    $view_islands_data = $this->prepareViewIslands($builder, $view_islands, $floating_islands);
145    $view_sidebar = $view_islands_data['view_sidebar'];
146    $view_main = $view_islands_data['view_main'];
147
148    // Library content can be in main or sidebar.
149    // @todo Move the logic to LibrariesPanel::build().
150    // @see https://www.drupal.org/project/display_builder/issues/3542866
151    if (isset($view_sidebar['library']) && !empty($library_islands)) {
152      $view_sidebar['library']['content'] = $library_islands;
153    }
154    elseif (isset($view_main['library']) && !empty($library_islands)) {
155      $view_main['library']['content'] = $library_islands;
156    }
157
158    if (!empty($contextual_islands)) {
159      $contextual_islands = $this->buildContextualIslands($builder, $islands_enabled_sorted);
160    }
161
162    return [
163      'view_sidebar_buttons' => $view_islands_data['view_sidebar_buttons'],
164      'view_sidebar' => $view_sidebar,
165      'view_main_tabs' => $view_islands_data['view_main_tabs'],
166      'view_main' => $view_main,
167      'view_floating_controls' => $view_islands_data['view_floating_controls'],
168      'start_buttons' => $this->buildButtons($builder, $button_islands, 'start'),
169      'end_buttons' => $this->buildButtons($builder, $button_islands),
170      'contextual_islands' => $contextual_islands,
171      'menu_islands' => $menu_islands,
172    ];
173  }
174
175  /**
176   * Build library panels merged into a single flat list with one search box.
177   *
178   * @param \Drupal\display_builder\InstanceInterface $builder
179   *   The builder instance.
180   * @param \Drupal\display_builder\Island\IslandInterface[] $library_islands
181   *   The enabled library islands.
182   * @param array $builder_data
183   *   The builder current state.
184   *
185   * @return array
186   *   A two-element render array: the shared search input, and the panes
187   *   wrapped in a container the search input targets.
188   */
189  private function buildFlatLibraryPanels(InstanceInterface $builder, array $library_islands, array $builder_data): array {
190    $container_id = 'db-library-flat-' . $builder->id();
191
192    $search = [
193      '#type' => 'component',
194      '#component' => 'display_builder:input',
195      '#props' => [
196        'variant' => 'search',
197        // Names the field for assistive tech, which a placeholder cannot do.
198        // Visually hidden, @see components/library_panel/search.css.
199        'label' => $this->t('Search the library'),
200        'placeholder' => $this->t('Search'),
201        'size' => 'medium',
202        'autocomplete_off' => TRUE,
203        'clearable' => TRUE,
204        'icon' => 'search',
205      ],
206      '#attributes' => [
207        'class' => ['db-search-library'],
208        'data-search-container-id' => $container_id,
209        'data-elements-selector' => '.db-placeholder',
210        'autofocus' => TRUE,
211      ],
212    ];
213
214    $content = [
215      '#type' => 'html_tag',
216      '#tag' => 'div',
217      '#attributes' => [
218        'id' => $container_id,
219        'class' => ['db-library-flat'],
220      ],
221      'panes' => $this->buildPanes($builder, $library_islands, $builder_data),
222    ];
223
224    return [$search, $content];
225  }
226
227  /**
228   * Build buttons for a region.
229   *
230   * @param \Drupal\display_builder\InstanceInterface $builder
231   *   The builder instance.
232   * @param \Drupal\display_builder\Island\IslandInterface[] $buttonIslands
233   *   The button islands.
234   * @param string $region
235   *   The button region.
236   *
237   * @return array
238   *   The buttons.
239   */
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
254      $buttons = $this->buildPanes($builder, $islands, [], [], 'span');
255    }
256
257    return $buttons;
258  }
259
260  /**
261   * Prepares view islands data.
262   *
263   * @param \Drupal\display_builder\InstanceInterface $builder
264   *   Display builder instance.
265   * @param array $islands
266   *   The sorted, enabled View islands.
267   * @param array $floating_islands
268   *   The sorted, enabled Floating islands, to attach to their target main
269   *   View island(s), @see buildFloatingControlsRegion().
270   *
271   * @return array
272   *   The prepared view islands data.
273   */
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
321
322  /**
323   * Build contextual islands which are tabbed sub islands.
324   *
325   * @param \Drupal\display_builder\InstanceInterface $builder
326   *   Display builder instance.
327   * @param array $islands_enabled_sorted
328   *   The islands enabled sorted.
329   *
330   * @return array
331   *   The contextual islands render array.
332   */
333  private function buildContextualIslands(InstanceInterface $builder, array $islands_enabled_sorted): array {
334    $contextual_islands = $islands_enabled_sorted[IslandType::Contextual->value] ?? [];
335
336    if (empty($contextual_islands)) {
337      return [];
338    }
339
340    $filter = $this->buildInput((string) $builder->id(), '', 'search', 'medium', 'off', $this->t('Search'), TRUE, 'search');
341    // @see components/library_panel/search.js
342    $filter['#attributes']['class'] = ['db-search-instance'];
343
344    return [
345      '#type' => 'html_tag',
346      '#tag' => 'div',
347      // Used for custom styling in components/display_builder/form.css.
348      '#attributes' => [
349        'id' => \sprintf('%s-contextual', $builder->id()),
350        'class' => ['db-form'],
351      ],
352      'tabs' => $this->buildDynamicTabs($builder, $contextual_islands, FALSE, $this->entity->getContextualTabsDisplay()),
353      'filter' => $filter,
354      'panes' => $this->buildPanes($builder, $contextual_islands, $builder->getCurrentState()),
355    ];
356  }
357
358  /**
359   * Builds panes.
360   *
361   * @param \Drupal\display_builder\InstanceInterface $builder
362   *   Display builder instance.
363   * @param \Drupal\display_builder\Island\IslandInterface[] $islands
364   *   The islands to build tabs for.
365   * @param array $data
366   *   (Optional) The data to pass to the islands.
367   * @param array $classes
368   *   (Optional) The HTML classes to start with.
369   * @param string $tag
370   *   (Optional) The HTML tag, defaults to 'div'.
371   *
372   * @return array
373   *   The tabs render array.
374   */
375  private function buildPanes(InstanceInterface $builder, array $islands, array $data = [], array $classes = [], string $tag = 'div'): array {
376    $panes = [];
377
378    foreach ($islands as $island_id => $island) {
379      $island_classes = \array_merge($classes, [
380        'db-island',
381        \sprintf('db-island-%s', $island->getTypeId()),
382        \sprintf('db-island-%s', $island->getPluginId()),
383      ]);
384
385      $panes[$island_id] = [
386        '#type' => 'html_tag',
387        '#tag' => $tag,
388        'children' => $island->build($builder, $data),
389        '#attributes' => [
390          // `id` attribute is used by HTMX OOB swap.
391          'id' => $island->getHtmlId((string) $builder->id()),
392          // `sse-swap` attribute is used by HTMX SSE swap.
393          'sse-swap' => $island->getHtmlId((string) $builder->id()),
394          'class' => $island_classes,
395          'data-testid' => \sprintf('%s_%s', $island->getTypeId(), $island->getPluginId()),
396        ] + $this->buildDeferrableAttribute($island),
397      ];
398    }
399
400    return $panes;
401  }
402
403  /**
404   * Marks a pane the client may leave stale while it is off screen.
405   *
406   * The server owns this decision, so the client does not have to re-derive it
407   * from island types and stay in sync with them - it just looks for the
408   * attribute.
409   *
410   * @param \Drupal\display_builder\Island\IslandInterface $island
411   *   The island the pane belongs to.
412   *
413   * @return array
414   *   The attribute to merge in, or an empty array if the island always
415   *   renders.
416   *
417   * @see components/display_builder/js/deferred_islands.js
418   * @see \Drupal\display_builder\Island\IslandInterface::isDeferrable()
419   */
420  private function buildDeferrableAttribute(IslandInterface $island): array {
421    return $island->isDeferrable() ? ['data-db-deferrable' => 'true'] : [];
422  }
423
424  /**
425   * Builds the floating controls region for the main-region View islands.
426   *
427   * Each Floating island is rendered exactly once, as a flex child of a
428   * single `.db-island-floating-controls` box, no matter how many View panes
429   * it attaches to. It carries a comma-separated `data-attached-to` listing
430   * every attached pane present in this profile, and stays visible while ANY
431   * of them is the active tab (@see components/shoelace/tabs/tabs.js
432   * syncPanes()). Rendering per Floating island rather than per View pane is
433   * what keeps its `id` unique - a pane that attached the same Floating island
434   * twice (e.g. Highlight on both Canvas and Scaffold) used to emit duplicate
435   * ids, an invalid-HTML/axe failure, and it also keeps the reload/OOB target
436   * (@see IslandPluginBase::reloadWithGlobalData(), which swaps `#{getHtmlId}`)
437   * pointing at a single element.
438   *
439   * @param \Drupal\display_builder\InstanceInterface $builder
440   *   Display builder instance.
441   * @param \Drupal\display_builder\Island\IslandInterface[] $view_islands
442   *   The enabled main-region View islands, keyed by plugin ID.
443   * @param \Drupal\display_builder\Island\IslandInterface[] $floating_islands
444   *   The sorted, enabled Floating islands.
445   *
446   * @return array
447   *   A single wrapper render array holding one child per Floating island, or
448   *   an empty array when none attach to a present pane.
449   */
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
500      return [];
501    }
502
503    return [
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
512
513  /**
514   * Build the buttons to hide/show the drawer.
515   *
516   * @param \Drupal\display_builder\InstanceInterface $builder
517   *   Display builder instance.
518   * @param \Drupal\display_builder\Island\IslandInterface[] $islands
519   *   An array of island objects for which buttons will be created.
520   * @param string $display
521   *   (Optional) How to show each island: 'label', 'icon' or 'icon_label'.
522   *   Default 'icon_label'.
523   *
524   * @return array
525   *   An array of render arrays for the drawer buttons.
526   */
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
558
559  /**
560   * Builds tabs.
561   *
562   * @param \Drupal\display_builder\InstanceInterface $builder
563   *   Display builder instance.
564   * @param \Drupal\display_builder\Island\IslandInterface[] $islands
565   *   The islands to build tabs for.
566   * @param bool $contextual
567   *   (Optional) Is the tabs contextual? See component for details. Default no.
568   * @param string $display
569   *   (Optional) How to show each island: 'label', 'icon' or 'icon_label'.
570   *   Default 'label'.
571   *
572   * @return array
573   *   The tabs render array.
574   */
575  private function buildDynamicTabs(InstanceInterface $builder, array $islands, bool $contextual = FALSE, string $display = 'label'): array {
576    // Global id is based on last island.
577    $id = '';
578    $tabs = [];
579    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
580
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
598        'show_label' => $show_label,
599      ];
600    }
601
602    // Id is needed for storage tabs state, @see component tabs.js file.
603    return $this->buildTabs($id, $tabs, $contextual);
604  }
605
606  /**
607   * Resolves a 'label'/'icon'/'icon_label' display mode into show flags.
608   *
609   * @param string $display
610   *   One of 'label', 'icon' or 'icon_label'.
611   *
612   * @return array
613   *   An associative array with 'icon' and 'label' boolean flags.
614   */
615  private static function resolvePanelDisplay(string $display): array {
616    return [
617      'icon' => \in_array($display, ['icon', 'icon_label'], TRUE),
618      'label' => \in_array($display, ['label', 'icon_label'], TRUE),
619    ];
620  }
621
622  /**
623   * Builds menu with islands as entries.
624   *
625   * @param \Drupal\display_builder\InstanceInterface $builder
626   *   Display builder instance.
627   * @param \Drupal\display_builder\Island\IslandInterface[] $islands
628   *   The islands to build tabs for.
629   *
630   * @return array
631   *   The islands render array.
632   *
633   * @see components/contextual_menu/contextual_menu.js
634   */
635  private function buildMenuWrapper(InstanceInterface $builder, array $islands): array {
636    $build = [
637      '#type' => 'component',
638      '#component' => 'display_builder:contextual_menu',
639      '#slots' => [
640        'label' => $this->t('Select an action'),
641      ],
642      '#attributes' => [
643        'class' => ['db-background', 'db-menu'],
644        // Require for JavaScript.
645        // @see components/contextual_menu/contextual_menu.js
646        'data-db-id' => (string) $builder->id(),
647      ],
648    ];
649
650    $items = [];
651
652    foreach ($islands as $island) {
653      $items = \array_merge($items, $island->build($builder, $builder->getCurrentState()));
654    }
655    $build['#slots']['items'] = $items;
656
657    return $build;
658  }
659
660  /**
661   * Get enabled panes sorted by weight.
662   *
663   * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
664   *   An array of contexts, keyed by context name.
665   *
666   * @return array
667   *   The list of enabled islands sorted.
668   *
669   * @todo just key by weight and default weight in Island?
670   */
671  private function getIslandsEnableSorted(array $contexts): array {
672    // Set island by weight.
673    // @todo just key by weight and default weight in Island?
674    $islands_enable_by_weight = $this->entity->getEnabledIslands();
675
676    return $this->islandPluginManager->getIslandsByTypes($contexts, $this->entity->getIslandConfigurations(), $islands_enable_by_weight);
677  }
678
679}

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.

ProfileViewBuilder->__construct
39    EntityTypeInterface $entity_type,
40    EntityRepositoryInterface $entity_repository,
41    LanguageManagerInterface $language_manager,
42    Registry $theme_registry,
43    EntityDisplayRepositoryInterface $entity_display_repository,
44    protected EntityTypeManagerInterface $entityTypeManager,
45    protected IslandPluginManagerInterface $islandPluginManager,
46  ) {
47    parent::__construct($entity_type, $entity_repository, $language_manager, $theme_registry, $entity_display_repository);
48  }
ProfileViewBuilder->buildButtons
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
 
254      $buttons = $this->buildPanes($builder, $islands, [], [], 'span');
255    }
256
257    return $buttons;
 
257    return $buttons;
258  }
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
 
257    return $buttons;
258  }
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
 
254      $buttons = $this->buildPanes($builder, $islands, [], [], 'span');
255    }
256
257    return $buttons;
 
257    return $buttons;
258  }
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
 
257    return $buttons;
258  }
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
 
254      $buttons = $this->buildPanes($builder, $islands, [], [], 'span');
255    }
256
257    return $buttons;
 
257    return $buttons;
258  }
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
 
257    return $buttons;
258  }
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
 
254      $buttons = $this->buildPanes($builder, $islands, [], [], 'span');
255    }
256
257    return $buttons;
 
257    return $buttons;
258  }
240  private function buildButtons(InstanceInterface $builder, array $buttonIslands, string $region = 'end'): array {
241    $islands = [];
242
243    foreach ($buttonIslands as $id => $island) {
 
243    foreach ($buttonIslands as $id => $island) {
244      $islandRegion = $island->getConfiguration()['region'] ?? 'end';
245
246      if ($islandRegion === $region) {
247        $islands[$id] = $island;
248      }
249    }
250
251    $buttons = [];
252
253    if (!empty($islands)) {
 
257    return $buttons;
258  }
ProfileViewBuilder->buildContextualIslands
333  private function buildContextualIslands(InstanceInterface $builder, array $islands_enabled_sorted): array {
334    $contextual_islands = $islands_enabled_sorted[IslandType::Contextual->value] ?? [];
335
336    if (empty($contextual_islands)) {
 
337      return [];
333  private function buildContextualIslands(InstanceInterface $builder, array $islands_enabled_sorted): array {
334    $contextual_islands = $islands_enabled_sorted[IslandType::Contextual->value] ?? [];
335
336    if (empty($contextual_islands)) {
 
340    $filter = $this->buildInput((string) $builder->id(), '', 'search', 'medium', 'off', $this->t('Search'), TRUE, 'search');
341    // @see components/library_panel/search.js
342    $filter['#attributes']['class'] = ['db-search-instance'];
343
344    return [
345      '#type' => 'html_tag',
346      '#tag' => 'div',
347      // Used for custom styling in components/display_builder/form.css.
348      '#attributes' => [
349        'id' => \sprintf('%s-contextual', $builder->id()),
350        'class' => ['db-form'],
351      ],
352      'tabs' => $this->buildDynamicTabs($builder, $contextual_islands, FALSE, $this->entity->getContextualTabsDisplay()),
353      'filter' => $filter,
354      'panes' => $this->buildPanes($builder, $contextual_islands, $builder->getCurrentState()),
355    ];
356  }
ProfileViewBuilder->buildDeferrableAttribute
420  private function buildDeferrableAttribute(IslandInterface $island): array {
421    return $island->isDeferrable() ? ['data-db-deferrable' => 'true'] : [];
 
421    return $island->isDeferrable() ? ['data-db-deferrable' => 'true'] : [];
 
421    return $island->isDeferrable() ? ['data-db-deferrable' => 'true'] : [];
422  }
420  private function buildDeferrableAttribute(IslandInterface $island): array {
421    return $island->isDeferrable() ? ['data-db-deferrable' => 'true'] : [];
 
421    return $island->isDeferrable() ? ['data-db-deferrable' => 'true'] : [];
 
421    return $island->isDeferrable() ? ['data-db-deferrable' => 'true'] : [];
422  }
ProfileViewBuilder->buildDynamicTabs
575  private function buildDynamicTabs(InstanceInterface $builder, array $islands, bool $contextual = FALSE, string $display = 'label'): array {
576    // Global id is based on last island.
577    $id = '';
578    $tabs = [];
579    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
580
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
 
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
 
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
 
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
 
597        'icon' => $show_icon ? $island->getIcon() : NULL,
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
 
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
598        'show_label' => $show_label,
599      ];
600    }
601
602    // Id is needed for storage tabs state, @see component tabs.js file.
603    return $this->buildTabs($id, $tabs, $contextual);
604  }
575  private function buildDynamicTabs(InstanceInterface $builder, array $islands, bool $contextual = FALSE, string $display = 'label'): array {
576    // Global id is based on last island.
577    $id = '';
578    $tabs = [];
579    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
580
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
 
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
 
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
 
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
 
594        'title' => $island->label(),
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
 
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
598        'show_label' => $show_label,
599      ];
600    }
601
602    // Id is needed for storage tabs state, @see component tabs.js file.
603    return $this->buildTabs($id, $tabs, $contextual);
604  }
575  private function buildDynamicTabs(InstanceInterface $builder, array $islands, bool $contextual = FALSE, string $display = 'label'): array {
576    // Global id is based on last island.
577    $id = '';
578    $tabs = [];
579    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
580
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
 
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
 
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
 
597        'icon' => $show_icon ? $island->getIcon() : NULL,
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
 
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
598        'show_label' => $show_label,
599      ];
600    }
601
602    // Id is needed for storage tabs state, @see component tabs.js file.
603    return $this->buildTabs($id, $tabs, $contextual);
604  }
575  private function buildDynamicTabs(InstanceInterface $builder, array $islands, bool $contextual = FALSE, string $display = 'label'): array {
576    // Global id is based on last island.
577    $id = '';
578    $tabs = [];
579    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
580
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
 
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
 
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
 
594        'title' => $island->label(),
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
 
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
598        'show_label' => $show_label,
599      ];
600    }
601
602    // Id is needed for storage tabs state, @see component tabs.js file.
603    return $this->buildTabs($id, $tabs, $contextual);
604  }
575  private function buildDynamicTabs(InstanceInterface $builder, array $islands, bool $contextual = FALSE, string $display = 'label'): array {
576    // Global id is based on last island.
577    $id = '';
578    $tabs = [];
579    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
580
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
598        'show_label' => $show_label,
599      ];
600    }
601
602    // Id is needed for storage tabs state, @see component tabs.js file.
603    return $this->buildTabs($id, $tabs, $contextual);
604  }
575  private function buildDynamicTabs(InstanceInterface $builder, array $islands, bool $contextual = FALSE, string $display = 'label'): array {
576    // Global id is based on last island.
577    $id = '';
578    $tabs = [];
579    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
580
581    foreach ($islands as $island) {
 
581    foreach ($islands as $island) {
582      $id = $island_id = $island->getHtmlId((string) $builder->id());
583      $attributes = [
584        'data-testid' => \sprintf('tab_%s_%s', $island->getTypeId(), $island->getPluginId()),
585      ];
586
587      if ($keyboard = $island::keyboardShortcuts()) {
588        $attributes['data-keyboard-key'] = $keyboard['key'] ?? '';
589        $attributes['data-keyboard-help'] = $keyboard['help'] ?? '';
590        $attributes['aria-keyshortcuts'] = $keyboard['key'] ?? '';
591      }
592
593      $tabs[] = [
594        'title' => $island->label(),
595        'url' => '#' . $island_id,
596        'attributes' => $attributes,
597        'icon' => $show_icon ? $island->getIcon() : NULL,
598        'show_label' => $show_label,
599      ];
600    }
601
602    // Id is needed for storage tabs state, @see component tabs.js file.
603    return $this->buildTabs($id, $tabs, $contextual);
604  }
ProfileViewBuilder->buildFlatLibraryPanels
189  private function buildFlatLibraryPanels(InstanceInterface $builder, array $library_islands, array $builder_data): array {
190    $container_id = 'db-library-flat-' . $builder->id();
191
192    $search = [
193      '#type' => 'component',
194      '#component' => 'display_builder:input',
195      '#props' => [
196        'variant' => 'search',
197        // Names the field for assistive tech, which a placeholder cannot do.
198        // Visually hidden, @see components/library_panel/search.css.
199        'label' => $this->t('Search the library'),
200        'placeholder' => $this->t('Search'),
201        'size' => 'medium',
202        'autocomplete_off' => TRUE,
203        'clearable' => TRUE,
204        'icon' => 'search',
205      ],
206      '#attributes' => [
207        'class' => ['db-search-library'],
208        'data-search-container-id' => $container_id,
209        'data-elements-selector' => '.db-placeholder',
210        'autofocus' => TRUE,
211      ],
212    ];
213
214    $content = [
215      '#type' => 'html_tag',
216      '#tag' => 'div',
217      '#attributes' => [
218        'id' => $container_id,
219        'class' => ['db-library-flat'],
220      ],
221      'panes' => $this->buildPanes($builder, $library_islands, $builder_data),
222    ];
223
224    return [$search, $content];
225  }
ProfileViewBuilder->buildFloatingControlsRegion
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
465        if (isset($view_islands[$view_island_id])) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
471        continue;
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
 
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
 
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
500      return [];
450  private function buildFloatingControlsRegion(InstanceInterface $builder, array $view_islands, array $floating_islands): array {
451    $data = $builder->getCurrentState();
452    $children = [];
453
454    foreach ($floating_islands as $floating_island) {
 
454    foreach ($floating_islands as $floating_island) {
455      $definition = $floating_island->getPluginDefinition();
456      $attach_to = \is_array($definition) ? ($definition['attach_to'] ?? []) : [];
457
458      // The selectors of the panes this island attaches to that are actually
459      // present in this profile. The tabs system already uses the same
460      // selector for a pane's data-target, so syncPanes() shows/hides this
461      // control in step with those panes.
462      $targets = [];
463
464      foreach ($attach_to as $view_island_id) {
465        if (isset($view_islands[$view_island_id])) {
466          $targets[] = '#' . $view_islands[$view_island_id]->getHtmlId((string) $builder->id());
467        }
468      }
469
470      if (empty($targets)) {
471        continue;
472      }
473
474      $floating_island_id = $floating_island->getPluginId();
475      // Same db-island-{type}/db-island-{plugin_id} classing as buildPanes()
476      // - other JS (e.g. assets/js/viewport_switcher.js) selects on it.
477      $children[$floating_island_id] = [
478        '#type' => 'html_tag',
479        '#tag' => 'div',
480        '#attributes' => [
481          'id' => $floating_island->getHtmlId((string) $builder->id()),
482          // `sse-swap` attribute is used by HTMX SSE swap.
483          'sse-swap' => $floating_island->getHtmlId((string) $builder->id()),
484          'class' => [
485            'db-island',
486            \sprintf('db-island-%s', $floating_island->getTypeId()),
487            \sprintf('db-island-%s', $floating_island_id),
488            // Starts hidden like the panes do; the initial syncPanes() call on
489            // page load corrects it.
490            'shoelace-tabs__tab--hidden',
491          ],
492          'data-testid' => \sprintf('%s_%s', $floating_island->getTypeId(), $floating_island_id),
493          'data-attached-to' => \implode(',', $targets),
494        ] + $this->buildDeferrableAttribute($floating_island),
495        'children' => $floating_island->build($builder, $data),
496      ];
497    }
498
499    if (empty($children)) {
 
504      '#type' => 'html_tag',
505      '#tag' => 'div',
506      '#attributes' => [
507        'class' => ['db-island-floating-controls'],
508      ],
509      'children' => $children,
510    ];
511  }
ProfileViewBuilder->buildMenuWrapper
635  private function buildMenuWrapper(InstanceInterface $builder, array $islands): array {
636    $build = [
637      '#type' => 'component',
638      '#component' => 'display_builder:contextual_menu',
639      '#slots' => [
640        'label' => $this->t('Select an action'),
641      ],
642      '#attributes' => [
643        'class' => ['db-background', 'db-menu'],
644        // Require for JavaScript.
645        // @see components/contextual_menu/contextual_menu.js
646        'data-db-id' => (string) $builder->id(),
647      ],
648    ];
649
650    $items = [];
651
652    foreach ($islands as $island) {
 
652    foreach ($islands as $island) {
 
652    foreach ($islands as $island) {
653      $items = \array_merge($items, $island->build($builder, $builder->getCurrentState()));
 
652    foreach ($islands as $island) {
 
652    foreach ($islands as $island) {
653      $items = \array_merge($items, $island->build($builder, $builder->getCurrentState()));
654    }
655    $build['#slots']['items'] = $items;
656
657    return $build;
658  }
635  private function buildMenuWrapper(InstanceInterface $builder, array $islands): array {
636    $build = [
637      '#type' => 'component',
638      '#component' => 'display_builder:contextual_menu',
639      '#slots' => [
640        'label' => $this->t('Select an action'),
641      ],
642      '#attributes' => [
643        'class' => ['db-background', 'db-menu'],
644        // Require for JavaScript.
645        // @see components/contextual_menu/contextual_menu.js
646        'data-db-id' => (string) $builder->id(),
647      ],
648    ];
649
650    $items = [];
651
652    foreach ($islands as $island) {
 
652    foreach ($islands as $island) {
 
652    foreach ($islands as $island) {
653      $items = \array_merge($items, $island->build($builder, $builder->getCurrentState()));
654    }
655    $build['#slots']['items'] = $items;
656
657    return $build;
658  }
635  private function buildMenuWrapper(InstanceInterface $builder, array $islands): array {
636    $build = [
637      '#type' => 'component',
638      '#component' => 'display_builder:contextual_menu',
639      '#slots' => [
640        'label' => $this->t('Select an action'),
641      ],
642      '#attributes' => [
643        'class' => ['db-background', 'db-menu'],
644        // Require for JavaScript.
645        // @see components/contextual_menu/contextual_menu.js
646        'data-db-id' => (string) $builder->id(),
647      ],
648    ];
649
650    $items = [];
651
652    foreach ($islands as $island) {
 
652    foreach ($islands as $island) {
653      $items = \array_merge($items, $island->build($builder, $builder->getCurrentState()));
654    }
655    $build['#slots']['items'] = $items;
656
657    return $build;
658  }
ProfileViewBuilder->buildPanes
375  private function buildPanes(InstanceInterface $builder, array $islands, array $data = [], array $classes = [], string $tag = 'div'): array {
376    $panes = [];
377
378    foreach ($islands as $island_id => $island) {
 
378    foreach ($islands as $island_id => $island) {
 
378    foreach ($islands as $island_id => $island) {
 
378    foreach ($islands as $island_id => $island) {
 
378    foreach ($islands as $island_id => $island) {
379      $island_classes = \array_merge($classes, [
380        'db-island',
381        \sprintf('db-island-%s', $island->getTypeId()),
382        \sprintf('db-island-%s', $island->getPluginId()),
383      ]);
384
385      $panes[$island_id] = [
386        '#type' => 'html_tag',
387        '#tag' => $tag,
388        'children' => $island->build($builder, $data),
389        '#attributes' => [
390          // `id` attribute is used by HTMX OOB swap.
391          'id' => $island->getHtmlId((string) $builder->id()),
392          // `sse-swap` attribute is used by HTMX SSE swap.
393          'sse-swap' => $island->getHtmlId((string) $builder->id()),
394          'class' => $island_classes,
395          'data-testid' => \sprintf('%s_%s', $island->getTypeId(), $island->getPluginId()),
396        ] + $this->buildDeferrableAttribute($island),
397      ];
398    }
399
400    return $panes;
401  }
375  private function buildPanes(InstanceInterface $builder, array $islands, array $data = [], array $classes = [], string $tag = 'div'): array {
376    $panes = [];
377
378    foreach ($islands as $island_id => $island) {
 
378    foreach ($islands as $island_id => $island) {
 
378    foreach ($islands as $island_id => $island) {
379      $island_classes = \array_merge($classes, [
380        'db-island',
381        \sprintf('db-island-%s', $island->getTypeId()),
382        \sprintf('db-island-%s', $island->getPluginId()),
383      ]);
384
385      $panes[$island_id] = [
386        '#type' => 'html_tag',
387        '#tag' => $tag,
388        'children' => $island->build($builder, $data),
389        '#attributes' => [
390          // `id` attribute is used by HTMX OOB swap.
391          'id' => $island->getHtmlId((string) $builder->id()),
392          // `sse-swap` attribute is used by HTMX SSE swap.
393          'sse-swap' => $island->getHtmlId((string) $builder->id()),
394          'class' => $island_classes,
395          'data-testid' => \sprintf('%s_%s', $island->getTypeId(), $island->getPluginId()),
396        ] + $this->buildDeferrableAttribute($island),
397      ];
398    }
399
400    return $panes;
401  }
375  private function buildPanes(InstanceInterface $builder, array $islands, array $data = [], array $classes = [], string $tag = 'div'): array {
376    $panes = [];
377
378    foreach ($islands as $island_id => $island) {
 
378    foreach ($islands as $island_id => $island) {
379      $island_classes = \array_merge($classes, [
380        'db-island',
381        \sprintf('db-island-%s', $island->getTypeId()),
382        \sprintf('db-island-%s', $island->getPluginId()),
383      ]);
384
385      $panes[$island_id] = [
386        '#type' => 'html_tag',
387        '#tag' => $tag,
388        'children' => $island->build($builder, $data),
389        '#attributes' => [
390          // `id` attribute is used by HTMX OOB swap.
391          'id' => $island->getHtmlId((string) $builder->id()),
392          // `sse-swap` attribute is used by HTMX SSE swap.
393          'sse-swap' => $island->getHtmlId((string) $builder->id()),
394          'class' => $island_classes,
395          'data-testid' => \sprintf('%s_%s', $island->getTypeId(), $island->getPluginId()),
396        ] + $this->buildDeferrableAttribute($island),
397      ];
398    }
399
400    return $panes;
401  }
ProfileViewBuilder->buildStartButtons
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
535        '#type' => 'component',
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
535        '#type' => 'component',
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
535        '#type' => 'component',
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
535        '#type' => 'component',
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
535        '#type' => 'component',
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
535        '#type' => 'component',
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
535        '#type' => 'component',
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
535        '#type' => 'component',
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
 
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
 
535        '#type' => 'component',
 
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
 
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
527  private function buildStartButtons(InstanceInterface $builder, array $islands, string $display = 'icon_label'): array {
528    $build = [];
529    ['icon' => $show_icon, 'label' => $show_label] = self::resolvePanelDisplay($display);
530
531    foreach ($islands as $island) {
 
531    foreach ($islands as $island) {
532      $island_id = $island->getPluginId();
533
534      $build[$island_id] = [
535        '#type' => 'component',
536        '#component' => 'display_builder:button',
537        '#props' => [
538          'id' => \sprintf('start-btn-%s-%s', $builder->id(), $island_id),
539          'label' => $show_label ? (string) $island->label() : '',
540          'icon' => $show_icon ? $island->getIcon() : NULL,
541          'tooltip' => $show_label ? NULL : (string) $island->label(),
542          'attributes' => [
543            'data-open-first-drawer' => TRUE,
544            'data-target' => $island_id,
545          ],
546        ],
547      ];
548
549      if ($keyboard = $island::keyboardShortcuts()) {
550        $build[$island_id]['#attributes']['data-keyboard-key'] = $keyboard['key'] ?? '';
551        $build[$island_id]['#attributes']['data-keyboard-help'] = $keyboard['help'] ?? '';
552        $build[$island_id]['#attributes']['aria-keyshortcuts'] = $keyboard['key'] ?? '';
553      }
554    }
555
556    return $build;
557  }
ProfileViewBuilder->createInstance
53  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type): static {
54    return new static(
55      $entity_type,
56      $container->get('entity.repository'),
57      $container->get('language_manager'),
58      $container->get('theme.registry'),
59      $container->get('entity_display.repository'),
60      $container->get('entity_type.manager'),
61      $container->get('plugin.manager.db_island'),
62    );
63  }
ProfileViewBuilder->getIslandsEnableSorted
671  private function getIslandsEnableSorted(array $contexts): array {
672    // Set island by weight.
673    // @todo just key by weight and default weight in Island?
674    $islands_enable_by_weight = $this->entity->getEnabledIslands();
675
676    return $this->islandPluginManager->getIslandsByTypes($contexts, $this->entity->getIslandConfigurations(), $islands_enable_by_weight);
677  }
ProfileViewBuilder->prepareViewIslands
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
282        continue;
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
282        continue;
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
282        continue;
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
282        continue;
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
 
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
 
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
 
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
 
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
 
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
300      $view_sidebar_buttons = $this->buildStartButtons($builder, $view_sidebar_buttons, $view_panels_display);
301    }
302
303    if (!empty($view_main_tabs)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
304      $view_main_tabs = $this->buildDynamicTabs($builder, $view_main_tabs, FALSE, $view_panels_display);
305    }
306
307    $builder_data = $builder->getCurrentState();
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
274  private function prepareViewIslands(InstanceInterface $builder, array $islands, array $floating_islands = []): array {
275    $view_islands_sidebar = [];
276    $view_islands_main = [];
277    $view_sidebar_buttons = [];
278    $view_main_tabs = [];
279
280    foreach ($islands as $id => $island) {
 
280    foreach ($islands as $id => $island) {
281      if ($island->getTypeId() !== IslandType::View->value) {
282        continue;
283      }
284
285      $configuration = $island->getConfiguration();
286
287      if ($configuration['region'] === 'sidebar') {
288        $view_islands_sidebar[$id] = $islands[$id];
289        $view_sidebar_buttons[$id] = $islands[$id];
290      }
291      else {
292        $view_islands_main[$id] = $islands[$id];
293        $view_main_tabs[$id] = $islands[$id];
294      }
295    }
296
297    $view_panels_display = $this->entity->getViewPanelsDisplay();
298
299    if (!empty($view_sidebar_buttons)) {
 
303    if (!empty($view_main_tabs)) {
 
307    $builder_data = $builder->getCurrentState();
308    $view_sidebar = $this->buildPanes($builder, $view_islands_sidebar, $builder_data);
309    // Default hidden.
310    $view_main = $this->buildPanes($builder, $view_islands_main, $builder_data, ['shoelace-tabs__tab--hidden']);
311    $view_floating_controls = $this->buildFloatingControlsRegion($builder, $view_islands_main, $floating_islands);
312
313    return [
314      'view_sidebar_buttons' => $view_sidebar_buttons,
315      'view_main_tabs' => $view_main_tabs,
316      'view_sidebar' => $view_sidebar,
317      'view_main' => $view_main,
318      'view_floating_controls' => $view_floating_controls,
319    ];
320  }
ProfileViewBuilder->resolvePanelDisplay
615  private static function resolvePanelDisplay(string $display): array {
616    return [
617      'icon' => \in_array($display, ['icon', 'icon_label'], TRUE),
618      'label' => \in_array($display, ['label', 'icon_label'], TRUE),
619    ];
620  }
ProfileViewBuilder->view
68  public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL): array {
69    // We have 'hacked' the interface by using $view_mode as a way of passing
70    // the Instance entity ID.
71    $builder_id = $view_mode;
72
73    /** @var \Drupal\display_builder\Entity\ProfileInterface $entity */
74    $entity = $entity;
75    $this->entity = $entity;
76
77    /** @var \Drupal\display_builder\InstanceInterface $builder */
78    $builder = $this->entityTypeManager->getStorage('display_builder_instance')->load($builder_id);
79    $buildable_id = $builder->get('buildable')->first()->get('plugin_id')->getValue() ?? '';
80    $contexts = $builder->getAvailableContexts() ?? [];
81
82    $islands_enabled_sorted = $this->getIslandsEnableSorted($contexts);
83    $build = [
84      '#type' => 'component',
85      '#component' => 'display_builder:display_builder',
86      '#props' => [
87        'builder_id' => $builder_id,
88        'buildable_id' => Html::getClass($buildable_id),
89        'hash' => (string) $builder->getHash(),
90      ],
91      '#slots' => $this->buildSlots($builder, $islands_enabled_sorted),
92      '#attached' => [],
93      '#cache' => [
94        'tags' => \array_merge(
95          $builder->getCacheTags(),
96          $entity->getCacheTags()
97        ),
98      ],
99    ];
100
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
 
102      foreach ($islands as $island) {
 
102      foreach ($islands as $island) {
 
102      foreach ($islands as $island) {
103        $build = $island->alterRenderable($builder, $build);
 
102      foreach ($islands as $island) {
 
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
 
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
103        $build = $island->alterRenderable($builder, $build);
104      }
105    }
106
107    return $build;
108  }
68  public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL): array {
69    // We have 'hacked' the interface by using $view_mode as a way of passing
70    // the Instance entity ID.
71    $builder_id = $view_mode;
72
73    /** @var \Drupal\display_builder\Entity\ProfileInterface $entity */
74    $entity = $entity;
75    $this->entity = $entity;
76
77    /** @var \Drupal\display_builder\InstanceInterface $builder */
78    $builder = $this->entityTypeManager->getStorage('display_builder_instance')->load($builder_id);
79    $buildable_id = $builder->get('buildable')->first()->get('plugin_id')->getValue() ?? '';
80    $contexts = $builder->getAvailableContexts() ?? [];
81
82    $islands_enabled_sorted = $this->getIslandsEnableSorted($contexts);
83    $build = [
84      '#type' => 'component',
85      '#component' => 'display_builder:display_builder',
86      '#props' => [
87        'builder_id' => $builder_id,
88        'buildable_id' => Html::getClass($buildable_id),
89        'hash' => (string) $builder->getHash(),
90      ],
91      '#slots' => $this->buildSlots($builder, $islands_enabled_sorted),
92      '#attached' => [],
93      '#cache' => [
94        'tags' => \array_merge(
95          $builder->getCacheTags(),
96          $entity->getCacheTags()
97        ),
98      ],
99    ];
100
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
 
102      foreach ($islands as $island) {
 
102      foreach ($islands as $island) {
 
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
 
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
103        $build = $island->alterRenderable($builder, $build);
104      }
105    }
106
107    return $build;
108  }
68  public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL): array {
69    // We have 'hacked' the interface by using $view_mode as a way of passing
70    // the Instance entity ID.
71    $builder_id = $view_mode;
72
73    /** @var \Drupal\display_builder\Entity\ProfileInterface $entity */
74    $entity = $entity;
75    $this->entity = $entity;
76
77    /** @var \Drupal\display_builder\InstanceInterface $builder */
78    $builder = $this->entityTypeManager->getStorage('display_builder_instance')->load($builder_id);
79    $buildable_id = $builder->get('buildable')->first()->get('plugin_id')->getValue() ?? '';
80    $contexts = $builder->getAvailableContexts() ?? [];
81
82    $islands_enabled_sorted = $this->getIslandsEnableSorted($contexts);
83    $build = [
84      '#type' => 'component',
85      '#component' => 'display_builder:display_builder',
86      '#props' => [
87        'builder_id' => $builder_id,
88        'buildable_id' => Html::getClass($buildable_id),
89        'hash' => (string) $builder->getHash(),
90      ],
91      '#slots' => $this->buildSlots($builder, $islands_enabled_sorted),
92      '#attached' => [],
93      '#cache' => [
94        'tags' => \array_merge(
95          $builder->getCacheTags(),
96          $entity->getCacheTags()
97        ),
98      ],
99    ];
100
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
 
102      foreach ($islands as $island) {
 
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
 
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
103        $build = $island->alterRenderable($builder, $build);
104      }
105    }
106
107    return $build;
108  }
68  public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL): array {
69    // We have 'hacked' the interface by using $view_mode as a way of passing
70    // the Instance entity ID.
71    $builder_id = $view_mode;
72
73    /** @var \Drupal\display_builder\Entity\ProfileInterface $entity */
74    $entity = $entity;
75    $this->entity = $entity;
76
77    /** @var \Drupal\display_builder\InstanceInterface $builder */
78    $builder = $this->entityTypeManager->getStorage('display_builder_instance')->load($builder_id);
79    $buildable_id = $builder->get('buildable')->first()->get('plugin_id')->getValue() ?? '';
80    $contexts = $builder->getAvailableContexts() ?? [];
81
82    $islands_enabled_sorted = $this->getIslandsEnableSorted($contexts);
83    $build = [
84      '#type' => 'component',
85      '#component' => 'display_builder:display_builder',
86      '#props' => [
87        'builder_id' => $builder_id,
88        'buildable_id' => Html::getClass($buildable_id),
89        'hash' => (string) $builder->getHash(),
90      ],
91      '#slots' => $this->buildSlots($builder, $islands_enabled_sorted),
92      '#attached' => [],
93      '#cache' => [
94        'tags' => \array_merge(
95          $builder->getCacheTags(),
96          $entity->getCacheTags()
97        ),
98      ],
99    ];
100
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
103        $build = $island->alterRenderable($builder, $build);
104      }
105    }
106
107    return $build;
108  }
68  public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL): array {
69    // We have 'hacked' the interface by using $view_mode as a way of passing
70    // the Instance entity ID.
71    $builder_id = $view_mode;
72
73    /** @var \Drupal\display_builder\Entity\ProfileInterface $entity */
74    $entity = $entity;
75    $this->entity = $entity;
76
77    /** @var \Drupal\display_builder\InstanceInterface $builder */
78    $builder = $this->entityTypeManager->getStorage('display_builder_instance')->load($builder_id);
79    $buildable_id = $builder->get('buildable')->first()->get('plugin_id')->getValue() ?? '';
80    $contexts = $builder->getAvailableContexts() ?? [];
81
82    $islands_enabled_sorted = $this->getIslandsEnableSorted($contexts);
83    $build = [
84      '#type' => 'component',
85      '#component' => 'display_builder:display_builder',
86      '#props' => [
87        'builder_id' => $builder_id,
88        'buildable_id' => Html::getClass($buildable_id),
89        'hash' => (string) $builder->getHash(),
90      ],
91      '#slots' => $this->buildSlots($builder, $islands_enabled_sorted),
92      '#attached' => [],
93      '#cache' => [
94        'tags' => \array_merge(
95          $builder->getCacheTags(),
96          $entity->getCacheTags()
97        ),
98      ],
99    ];
100
101    foreach ($islands_enabled_sorted as $islands) {
 
101    foreach ($islands_enabled_sorted as $islands) {
102      foreach ($islands as $island) {
103        $build = $island->alterRenderable($builder, $build);
104      }
105    }
106
107    return $build;
108  }