Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 242 |
|
0.00% |
0 / 72 |
|
0.00% |
0 / 246 |
|
0.00% |
0 / 12 |
CRAP | |
0.00% |
0 / 1 |
| ComponentLibraryPanel | |
0.00% |
0 / 236 |
|
0.00% |
0 / 72 |
|
0.00% |
0 / 246 |
|
0.00% |
0 / 12 |
1482 | |
0.00% |
0 / 1 |
| create | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| label | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| defaultConfiguration | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildConfigurationForm | |
0.00% |
0 / 50 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| validateConfigurationForm | |
0.00% |
0 / 8 |
|
0.00% |
0 / 5 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
20 | |||
| configurationSummary | |
0.00% |
0 / 26 |
|
0.00% |
0 / 20 |
|
0.00% |
0 / 192 |
|
0.00% |
0 / 1 |
110 | |||
| build | |
0.00% |
0 / 40 |
|
0.00% |
0 / 10 |
|
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
30 | |||
| getProviders | |
0.00% |
0 / 11 |
|
0.00% |
0 / 9 |
|
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
20 | |||
| getComponentsGrouped | |
0.00% |
0 / 20 |
|
0.00% |
0 / 7 |
|
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
| getComponentsVariants | |
0.00% |
0 / 37 |
|
0.00% |
0 / 9 |
|
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
| getComponentsMosaic | |
0.00% |
0 / 15 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| getProvidersOptions | |
0.00% |
0 / 11 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder\Plugin\display_builder\Island; |
| 6 | |
| 7 | use Drupal\Core\Extension\ModuleExtensionList; |
| 8 | use Drupal\Core\Extension\ThemeExtensionList; |
| 9 | use Drupal\Core\Form\FormStateInterface; |
| 10 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 11 | use Drupal\Core\Theme\ThemeManagerInterface; |
| 12 | use Drupal\Core\Url; |
| 13 | use Drupal\display_builder\Attribute\Island; |
| 14 | use Drupal\display_builder\ComponentLibraryDefinitionHelper; |
| 15 | use Drupal\display_builder\InstanceInterface; |
| 16 | use Drupal\display_builder\IslandConfigurationFormInterface; |
| 17 | use Drupal\display_builder\IslandConfigurationFormTrait; |
| 18 | use Drupal\display_builder\IslandPluginBase; |
| 19 | use Drupal\display_builder\IslandType; |
| 20 | use Drupal\ui_patterns\SourcePluginManager; |
| 21 | use Symfony\Component\DependencyInjection\ContainerInterface; |
| 22 | |
| 23 | /** |
| 24 | * Component library island plugin implementation. |
| 25 | */ |
| 26 | #[Island( |
| 27 | id: 'component_library', |
| 28 | enabled_by_default: TRUE, |
| 29 | label: new TranslatableMarkup('Components library'), |
| 30 | description: new TranslatableMarkup('List of available components.'), |
| 31 | type: IslandType::Library, |
| 32 | )] |
| 33 | class ComponentLibraryPanel extends IslandPluginBase implements IslandConfigurationFormInterface { |
| 34 | |
| 35 | use IslandConfigurationFormTrait; |
| 36 | |
| 37 | /** |
| 38 | * The module list extension service. |
| 39 | */ |
| 40 | protected ThemeManagerInterface $themeManager; |
| 41 | |
| 42 | /** |
| 43 | * The module list extension service. |
| 44 | */ |
| 45 | protected ThemeExtensionList $themeList; |
| 46 | |
| 47 | /** |
| 48 | * The module list extension service. |
| 49 | */ |
| 50 | protected ModuleExtensionList $moduleList; |
| 51 | |
| 52 | /** |
| 53 | * The UI Patterns source plugin manager. |
| 54 | */ |
| 55 | protected SourcePluginManager $sourceManager; |
| 56 | |
| 57 | /** |
| 58 | * The definitions filtered for current theme. |
| 59 | * |
| 60 | * @var array |
| 61 | * The definitions filtered. |
| 62 | */ |
| 63 | private array $definitionsFiltered = []; |
| 64 | |
| 65 | /** |
| 66 | * The definitions filtered and grouped for current theme. |
| 67 | * |
| 68 | * @var array |
| 69 | * The definitions filtered and grouped. |
| 70 | */ |
| 71 | private array $definitionsGrouped = []; |
| 72 | |
| 73 | /** |
| 74 | * The source data for components. |
| 75 | * |
| 76 | * @var array |
| 77 | * The source data already prepared. |
| 78 | */ |
| 79 | private array $sourcesData = []; |
| 80 | |
| 81 | /** |
| 82 | * {@inheritdoc} |
| 83 | */ |
| 84 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 85 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 86 | $instance->themeManager = $container->get('theme.manager'); |
| 87 | $instance->themeList = $container->get('extension.list.theme'); |
| 88 | $instance->moduleList = $container->get('extension.list.module'); |
| 89 | $instance->sourceManager = $container->get('plugin.manager.ui_patterns_source'); |
| 90 | |
| 91 | return $instance; |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * {@inheritdoc} |
| 96 | */ |
| 97 | public function label(): string { |
| 98 | return 'Components'; |
| 99 | } |
| 100 | |
| 101 | /** |
| 102 | * {@inheritdoc} |
| 103 | */ |
| 104 | public function defaultConfiguration(): array { |
| 105 | return [ |
| 106 | 'exclude' => [], |
| 107 | 'exclude_id' => '', |
| 108 | 'component_status' => [ |
| 109 | 'experimental', |
| 110 | ], |
| 111 | 'include_no_ui' => FALSE, |
| 112 | 'show_grouped' => TRUE, |
| 113 | 'show_variants' => TRUE, |
| 114 | 'show_mosaic' => TRUE, |
| 115 | ]; |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * {@inheritdoc} |
| 120 | */ |
| 121 | public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { |
| 122 | $configuration = $this->getConfiguration(); |
| 123 | $components = $this->sdcManager->getDefinitions(); |
| 124 | |
| 125 | $form['exclude'] = [ |
| 126 | '#type' => 'checkboxes', |
| 127 | '#title' => $this->t('Exclude providers'), |
| 128 | '#options' => $this->getProvidersOptions($components, $this->t('component'), $this->t('components')), |
| 129 | '#default_value' => $configuration['exclude'], |
| 130 | ]; |
| 131 | |
| 132 | $form['exclude_id'] = [ |
| 133 | '#type' => 'textarea', |
| 134 | '#title' => $this->t('Exclude by id'), |
| 135 | '#description' => $this->t('Provide a space separated list of components id to exclude, must be prefixed by provider. Example: "ui_suite_bootstrap:card_body<br>ui_suite_bootstrap:table_cell".'), |
| 136 | '#default_value' => $configuration['exclude_id'], |
| 137 | ]; |
| 138 | |
| 139 | // @see https://git.drupalcode.org/project/drupal/-/blob/11.x/core/assets/schemas/v1/metadata.schema.json#L217 |
| 140 | $form['component_status'] = [ |
| 141 | '#type' => 'checkboxes', |
| 142 | '#title' => $this->t('Allowed status'), |
| 143 | '#options' => [ |
| 144 | 'experimental' => $this->t('Experimental'), |
| 145 | 'deprecated' => $this->t('Deprecated'), |
| 146 | 'obsolete' => $this->t('Obsolete'), |
| 147 | ], |
| 148 | '#description' => $this->t('Components with stable or undefined status will always be available.'), |
| 149 | '#default_value' => $configuration['component_status'], |
| 150 | ]; |
| 151 | |
| 152 | $form['show_grouped'] = [ |
| 153 | '#type' => 'checkbox', |
| 154 | '#title' => $this->t('Show components grouped'), |
| 155 | '#description' => $this->t('Provide a list of grouped components for selection.'), |
| 156 | '#default_value' => $configuration['show_grouped'], |
| 157 | ]; |
| 158 | |
| 159 | $form['show_variants'] = [ |
| 160 | '#type' => 'checkbox', |
| 161 | '#title' => $this->t('Show components variants'), |
| 162 | '#description' => $this->t('Provide a list of components per variants for selection.'), |
| 163 | '#default_value' => $configuration['show_variants'], |
| 164 | ]; |
| 165 | |
| 166 | $form['show_mosaic'] = [ |
| 167 | '#type' => 'checkbox', |
| 168 | '#title' => $this->t('Show components mosaic'), |
| 169 | '#description' => $this->t('Provide a list of mosaic components for selection.'), |
| 170 | '#default_value' => $configuration['show_mosaic'], |
| 171 | ]; |
| 172 | |
| 173 | // Drupal 11.3+ new exclude feature. |
| 174 | // @see https://git.drupalcode.org/project/drupal/-/blob/11.x/core/assets/schemas/v1/metadata.schema.json#L228 |
| 175 | $form['include_no_ui'] = [ |
| 176 | '#type' => 'checkbox', |
| 177 | '#title' => $this->t('Include marked as excluded from the UI'), |
| 178 | '#description' => $this->t('Components with no ui flag are meant for internal use only. Force to include them. Drupal 11.3+ only.'), |
| 179 | '#default_value' => $configuration['include_no_ui'], |
| 180 | ]; |
| 181 | |
| 182 | return $form; |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * {@inheritdoc} |
| 187 | */ |
| 188 | public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void { |
| 189 | $values = $form_state->getValues(); |
| 190 | |
| 191 | // At least one display must be enabled. |
| 192 | $show_grouped = (bool) $values['show_grouped']; |
| 193 | $show_variants = (bool) $values['show_variants']; |
| 194 | $show_mosaic = (bool) $values['show_mosaic']; |
| 195 | |
| 196 | if (!$show_grouped && !$show_variants && !$show_mosaic) { |
| 197 | $form_state->setError($form['show_grouped'], $this->t('At least one display must be selected!')); |
| 198 | $form_state->setError($form['show_variants'], $this->t('At least one display must be selected!')); |
| 199 | $form_state->setError($form['show_mosaic'], $this->t('At least one display must be selected!')); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * {@inheritdoc} |
| 205 | */ |
| 206 | public function configurationSummary(): array { |
| 207 | $configuration = $this->getConfiguration(); |
| 208 | |
| 209 | $summary = []; |
| 210 | |
| 211 | $summary[] = $this->t('Excluded providers: @exclude', [ |
| 212 | '@exclude' => ($exclude = \array_filter($configuration['exclude'] ?? [])) ? \implode(', ', $exclude) : $this->t('None'), |
| 213 | ]); |
| 214 | |
| 215 | if (\strlen($configuration['exclude_id'] ?? '') > 5) { |
| 216 | $value = \preg_split('/\s+/', \trim($configuration['exclude_id'] ?? '')); |
| 217 | |
| 218 | if ($value === FALSE) { |
| 219 | $summary[] = $this->t('Component(s) excluded'); |
| 220 | } |
| 221 | else { |
| 222 | $num = \count($value); |
| 223 | $summary[] = $this->formatPlural($num, '@count component excluded', '@count components excluded'); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | $summary[] = $this->t('Allowed status: @status', [ |
| 228 | '@status' => \implode(', ', \array_filter(\array_unique(\array_merge(['stable', 'undefined'], $configuration['component_status'] ?? []))) ?: [$this->t('stable, undefined')]), |
| 229 | ]); |
| 230 | |
| 231 | $summary[] = $configuration['include_no_ui'] ? $this->t('Include no UI components') : $this->t('Exclude no UI components'); |
| 232 | |
| 233 | $list = []; |
| 234 | |
| 235 | if ((bool) $configuration['show_grouped']) { |
| 236 | $list[] = $this->t('grouped'); |
| 237 | } |
| 238 | |
| 239 | if ((bool) $configuration['show_variants']) { |
| 240 | $list[] = $this->t('variants'); |
| 241 | } |
| 242 | |
| 243 | if ((bool) $configuration['show_mosaic']) { |
| 244 | $list[] = $this->t('mosaic'); |
| 245 | } |
| 246 | $summary[] = $this->t('Components list as: @list', [ |
| 247 | '@list' => !empty($list) ? \implode(', ', $list) : $this->t('None selected'), |
| 248 | ]); |
| 249 | |
| 250 | return $summary; |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * {@inheritdoc} |
| 255 | */ |
| 256 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 257 | $builder_id = (string) $builder->id(); |
| 258 | // Run a single time and saved as properties to avoid repeating processing |
| 259 | // in ::getComponentsMosaic(), ::getComponentsVariants() and |
| 260 | // ::getComponentsGrouped(). |
| 261 | $configuration = $this->getConfiguration(); |
| 262 | |
| 263 | $componentDefinitions = new ComponentLibraryDefinitionHelper($this->sdcManager, $this->sourceManager); |
| 264 | $definitions = $componentDefinitions->getDefinitions($configuration); |
| 265 | |
| 266 | $this->definitionsFiltered = $definitions['filtered'] ?? []; |
| 267 | $this->definitionsGrouped = $definitions['grouped'] ?? []; |
| 268 | $this->sourcesData = $definitions['sources'] ?? []; |
| 269 | |
| 270 | $panes = []; |
| 271 | |
| 272 | if ((bool) $configuration['show_grouped']) { |
| 273 | $panes['grouped'] = [ |
| 274 | 'title' => $this->t('Grouped'), |
| 275 | 'content' => $this->getComponentsGrouped($builder_id), |
| 276 | ]; |
| 277 | } |
| 278 | |
| 279 | if ((bool) $configuration['show_variants']) { |
| 280 | $panes['variants'] = [ |
| 281 | 'title' => $this->t('Variants'), |
| 282 | 'content' => $this->getComponentsVariants($builder_id), |
| 283 | ]; |
| 284 | } |
| 285 | |
| 286 | if ((bool) $configuration['show_mosaic']) { |
| 287 | $panes['mosaic'] = [ |
| 288 | 'title' => $this->t('Mosaic'), |
| 289 | 'content' => $this->getComponentsMosaic($builder_id), |
| 290 | ]; |
| 291 | } |
| 292 | |
| 293 | $tabs = []; |
| 294 | $content = []; |
| 295 | |
| 296 | foreach ($panes as $pane_id => $pane) { |
| 297 | $id = 'db-' . $builder_id . '-components-tab---' . $pane_id; |
| 298 | $tabs[] = [ |
| 299 | 'title' => $pane['title'], |
| 300 | 'url' => '#' . $id, |
| 301 | ]; |
| 302 | $content[] = $this->wrapContent($pane['content'], $id); |
| 303 | } |
| 304 | |
| 305 | return [ |
| 306 | '#type' => 'component', |
| 307 | '#component' => 'display_builder:library_panel', |
| 308 | '#slots' => [ |
| 309 | 'tabs' => $this->buildTabs('db-' . $builder_id . '-components-tabs', $tabs), |
| 310 | 'content' => $content, |
| 311 | ], |
| 312 | ]; |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * Get all providers. |
| 317 | * |
| 318 | * @param array $definitions |
| 319 | * Plugin definitions. |
| 320 | * |
| 321 | * @return array |
| 322 | * Drupal extension definitions, keyed by extension ID |
| 323 | */ |
| 324 | protected function getProviders(array $definitions): array { |
| 325 | $themes = $this->themeList->getAllInstalledInfo(); |
| 326 | $modules = $this->moduleList->getAllInstalledInfo(); |
| 327 | $providers = []; |
| 328 | |
| 329 | foreach ($definitions as $definition) { |
| 330 | $provider_id = $definition['provider']; |
| 331 | |
| 332 | $provider = $themes[$provider_id] ?? $modules[$provider_id] ?? NULL; |
| 333 | |
| 334 | if (!$provider) { |
| 335 | continue; |
| 336 | } |
| 337 | $provider['count'] = isset($providers[$provider_id]) ? ($providers[$provider_id]['count']) + 1 : 1; |
| 338 | $providers[$provider_id] = $provider; |
| 339 | } |
| 340 | |
| 341 | return $providers; |
| 342 | } |
| 343 | |
| 344 | /** |
| 345 | * Gets the grouped components view. |
| 346 | * |
| 347 | * @param string $builder_id |
| 348 | * Builder ID. |
| 349 | * |
| 350 | * @return array |
| 351 | * A renderable array containing the grouped components. |
| 352 | */ |
| 353 | private function getComponentsGrouped(string $builder_id): array { |
| 354 | $build = []; |
| 355 | |
| 356 | foreach ($this->definitionsGrouped as $group_name => $group) { |
| 357 | $build[] = [ |
| 358 | '#type' => 'html_tag', |
| 359 | '#tag' => 'h4', |
| 360 | '#value' => $group_name, |
| 361 | '#attributes' => [ |
| 362 | 'class' => ['db-filter-hide-on-search'], |
| 363 | ], |
| 364 | ]; |
| 365 | |
| 366 | foreach ($group as $component_id => $definition) { |
| 367 | $component_id = (string) $component_id; |
| 368 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', ['component_id' => $component_id]); |
| 369 | |
| 370 | $data = [ |
| 371 | 'source_id' => 'component', |
| 372 | 'source' => $this->sourcesData[$component_id], |
| 373 | ]; |
| 374 | // Used for search filter. |
| 375 | $keywords = \sprintf('%s %s', $definition['label'], $definition['provider']); |
| 376 | $build[] = $this->buildPlaceholderButtonWithPreview($builder_id, $definition['annotated_name'], $data, $component_preview_url, $keywords); |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | return $this->buildDraggables($builder_id, $build); |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * Gets the components variants view. |
| 385 | * |
| 386 | * @param string $builder_id |
| 387 | * Builder ID. |
| 388 | * |
| 389 | * @return array |
| 390 | * A renderable array containing the variants placeholders. |
| 391 | */ |
| 392 | private function getComponentsVariants(string $builder_id): array { |
| 393 | $build = []; |
| 394 | |
| 395 | foreach ($this->definitionsFiltered as $component_id => $definition) { |
| 396 | $build[] = [ |
| 397 | '#type' => 'html_tag', |
| 398 | '#tag' => 'h4', |
| 399 | '#value' => $definition['annotated_name'], |
| 400 | '#attributes' => [ |
| 401 | 'data-filter-parent' => $definition['machineName'], |
| 402 | ], |
| 403 | ]; |
| 404 | |
| 405 | $data = [ |
| 406 | 'source_id' => 'component', |
| 407 | 'source' => $this->sourcesData[$component_id], |
| 408 | ]; |
| 409 | |
| 410 | if (!isset($definition['variants'])) { |
| 411 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', ['component_id' => $component_id]); |
| 412 | // Used for search filter. |
| 413 | $keywords = \sprintf('%s %s', $definition['label'], $definition['provider']); |
| 414 | $build_variant = $this->buildPlaceholderButtonWithPreview($builder_id, $this->t('Default'), $data, $component_preview_url, $keywords); |
| 415 | $build_variant['#attributes']['data-filter-child'] = $definition['machineName']; |
| 416 | // Label is used by default to set drawer title when dragging. It is set |
| 417 | // on RenderableBuilderTrait::buildPlaceholderButton(), so here we need |
| 418 | // to override it to have the proper label and not the variant name. |
| 419 | // @see assets/js/db_drawer.js |
| 420 | // @see src/RenderableBuilderTrait::buildPlaceholderButton() |
| 421 | $build_variant['#attributes']['data-node-title'] = $definition['label']; |
| 422 | |
| 423 | $build[] = $build_variant; |
| 424 | |
| 425 | continue; |
| 426 | } |
| 427 | |
| 428 | foreach ($definition['variants'] ?? [] as $variant_id => $variant) { |
| 429 | $params = ['component_id' => $component_id, 'variant_id' => $variant_id]; |
| 430 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', $params); |
| 431 | $data['source']['component']['variant_id'] = [ |
| 432 | 'source_id' => 'select', |
| 433 | 'source' => [ |
| 434 | 'value' => $variant_id, |
| 435 | ], |
| 436 | ]; |
| 437 | // Used for search filter. |
| 438 | $keywords = \sprintf('%s %s %s', $definition['label'], $variant['title'], $definition['provider']); |
| 439 | $build_variant = $this->buildPlaceholderButtonWithPreview($builder_id, $variant['title'], $data, $component_preview_url, $keywords); |
| 440 | $build_variant['#attributes']['data-filter-child'] = $definition['machineName']; |
| 441 | // Label is used by default to set drawer title when dragging. It is set |
| 442 | // on RenderableBuilderTrait::buildPlaceholderButton(), so here we need |
| 443 | // to override it to have the proper label and not the variant name. |
| 444 | // @see assets/js/db_drawer.js |
| 445 | // @see src/RenderableBuilderTrait::buildPlaceholderButton() |
| 446 | $build_variant['#attributes']['data-node-title'] = $definition['label']; |
| 447 | |
| 448 | $build[] = $build_variant; |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | return $this->buildDraggables($builder_id, $build); |
| 453 | } |
| 454 | |
| 455 | /** |
| 456 | * Gets the mosaic view of components. |
| 457 | * |
| 458 | * @param string $builder_id |
| 459 | * Builder ID. |
| 460 | * |
| 461 | * @return array |
| 462 | * A renderable array containing the mosaic view of components. |
| 463 | */ |
| 464 | private function getComponentsMosaic(string $builder_id): array { |
| 465 | $components = []; |
| 466 | |
| 467 | foreach (\array_keys($this->definitionsFiltered) as $component_id) { |
| 468 | $component_id = (string) $component_id; |
| 469 | $component = $this->sdcManager->find($component_id); |
| 470 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', ['component_id' => $component_id]); |
| 471 | |
| 472 | $vals = [ |
| 473 | 'source_id' => 'component', |
| 474 | 'source' => $this->sourcesData[$component_id], |
| 475 | ]; |
| 476 | $thumbnail = $component->metadata->getThumbnailPath(); |
| 477 | |
| 478 | // Used for search filter. |
| 479 | $keywords = \sprintf('%s %s', $component->metadata->name, \str_replace(':', ' ', $component_id)); |
| 480 | $build = $this->buildPlaceholderCardWithPreview($component->metadata->name, $vals, $component_preview_url, $keywords, $thumbnail); |
| 481 | // Label is used by default to set drawer title when dragging. It is set |
| 482 | // on RenderableBuilderTrait::buildPlaceholderButton(), so here we need |
| 483 | // to override it to have the proper label and not the variant name. |
| 484 | // @see assets/js/db_drawer.js |
| 485 | // @see src/RenderableBuilderTrait::buildPlaceholderButton() |
| 486 | $build['#attributes']['data-node-title'] = $component->metadata->name; |
| 487 | $components[] = $build; |
| 488 | } |
| 489 | |
| 490 | return $this->buildDraggables($builder_id, $components, 'mosaic'); |
| 491 | } |
| 492 | |
| 493 | /** |
| 494 | * Get providers options for select input. |
| 495 | * |
| 496 | * @param array $definitions |
| 497 | * Plugin definitions. |
| 498 | * @param string|TranslatableMarkup $singular |
| 499 | * Singular label of the plugins. |
| 500 | * @param string|TranslatableMarkup $plural |
| 501 | * Plural label of the plugins. |
| 502 | * |
| 503 | * @return array |
| 504 | * An associative array with extension ID as key and extension description |
| 505 | * as value. |
| 506 | */ |
| 507 | private function getProvidersOptions(array $definitions, string|TranslatableMarkup $singular = 'definition', string|TranslatableMarkup $plural = 'definitions'): array { |
| 508 | $options = []; |
| 509 | |
| 510 | foreach ($this->getProviders($definitions) as $provider_id => $provider) { |
| 511 | $params = [ |
| 512 | '@name' => $provider['name'], |
| 513 | '@type' => $provider['type'], |
| 514 | '@count' => $provider['count'], |
| 515 | '@singular' => $singular, |
| 516 | '@plural' => $plural, |
| 517 | ]; |
| 518 | $options[$provider_id] = $this->formatPlural($provider['count'], '@name (@type, @count @singular)', '@name (@type, @count @plural)', $params); |
| 519 | } |
| 520 | |
| 521 | return $options; |
| 522 | } |
| 523 | |
| 524 | } |