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