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 | } |
Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not
necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once.
Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 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']), |
| 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 | } |
| 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'; |
| 112 | $configuration['show'] = $configuration['show_mosaic'] ? 'mosaic' : 'grouped'; |
| 112 | $configuration['show'] = $configuration['show_mosaic'] ? 'mosaic' : 'grouped'; |
| 112 | $configuration['show'] = $configuration['show_mosaic'] ? 'mosaic' : 'grouped'; |
| 113 | $configuration['show'] = $configuration['show_variants'] ? 'variants' : $configuration['show']; |
| 113 | $configuration['show'] = $configuration['show_variants'] ? 'variants' : $configuration['show']; |
| 113 | $configuration['show'] = $configuration['show_variants'] ? 'variants' : $configuration['show']; |
| 113 | $configuration['show'] = $configuration['show_variants'] ? 'variants' : $configuration['show']; |
| 114 | $configuration['show'] = $configuration['show_grouped'] ? 'grouped' : $configuration['show']; |
| 114 | $configuration['show'] = $configuration['show_grouped'] ? 'grouped' : $configuration['show']; |
| 114 | $configuration['show'] = $configuration['show_grouped'] ? 'grouped' : $configuration['show']; |
| 114 | $configuration['show'] = $configuration['show_grouped'] ? 'grouped' : $configuration['show']; |
| 115 | } |
| 116 | |
| 117 | $form['display'] = [ |
| 118 | '#type' => 'fieldset', |
| 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 | } |
| 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'), |
| 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', [ |
| 207 | $summary[] = $this->t('Excluded providers: @exclude', [ |
| 208 | '@exclude' => ($exclude = \array_filter($configuration['exclude'] ?? [])) ? \implode(', ', $exclude) : $this->t('None'), |
| 208 | '@exclude' => ($exclude = \array_filter($configuration['exclude'] ?? [])) ? \implode(', ', $exclude) : $this->t('None'), |
| 208 | '@exclude' => ($exclude = \array_filter($configuration['exclude'] ?? [])) ? \implode(', ', $exclude) : $this->t('None'), |
| 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) { |
| 214 | if ($value === FALSE) { |
| 215 | $summary[] = $this->t('Component(s) excluded'); |
| 218 | $summary[] = $this->formatPlural(\count($value), '@count component excluded', '@count components excluded by id'); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | $summary[] = $this->t('Allowed status: @status', [ |
| 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') : ''; |
| 226 | $summary[] = $configuration['include_no_ui'] ? $this->t('Include `No UI` components') : ''; |
| 226 | $summary[] = $configuration['include_no_ui'] ? $this->t('Include `No UI` components') : ''; |
| 226 | $summary[] = $configuration['include_no_ui'] ? $this->t('Include `No UI` components') : ''; |
| 227 | |
| 228 | return $summary; |
| 229 | } |
| 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 | } |
| 93 | 'exclude' => [], |
| 94 | 'exclude_id' => '', |
| 95 | 'component_status' => [], |
| 96 | 'include_no_ui' => FALSE, |
| 97 | 'show' => 'grouped', |
| 98 | 'preview' => TRUE, |
| 99 | ]; |
| 100 | } |
| 351 | private function getComponentsFlat(string $builder_id, bool $preview): array { |
| 352 | $build = []; |
| 353 | |
| 354 | foreach ($this->definitionsFiltered as $component_id => $definition) { |
| 354 | foreach ($this->definitionsFiltered as $component_id => $definition) { |
| 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) { |
| 364 | if ($preview) { |
| 365 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', ['component_id' => $component_id]); |
| 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); |
| 354 | foreach ($this->definitionsFiltered as $component_id => $definition) { |
| 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 | } |
| 304 | private function getComponentsGrouped(string $builder_id, bool $preview): array { |
| 305 | $build = []; |
| 306 | |
| 307 | foreach ($this->definitionsGrouped as $group_name => $group) { |
| 307 | foreach ($this->definitionsGrouped as $group_name => $group) { |
| 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) { |
| 317 | foreach ($group as $component_id => $definition) { |
| 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) { |
| 327 | if ($preview) { |
| 328 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', ['component_id' => $component_id]); |
| 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); |
| 317 | foreach ($group as $component_id => $definition) { |
| 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) { |
| 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 | } |
| 466 | private function getComponentsMosaic(string $builder_id, bool $preview): array { |
| 467 | $components = []; |
| 468 | |
| 469 | foreach (\array_keys($this->definitionsFiltered) as $component_id) { |
| 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) { |
| 482 | if ($preview) { |
| 483 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', ['component_id' => $component_id]); |
| 487 | $build = $this->buildPlaceholderCard($component->metadata->name, $vals, $keywords, $thumbnail); |
| 488 | } |
| 489 | |
| 490 | $build['#attributes']['data-node-title'] = $component->metadata->name; |
| 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; |
| 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 | } |
| 387 | private function getComponentsVariants(string $builder_id, bool $preview): array { |
| 388 | $build = []; |
| 389 | |
| 390 | foreach ($this->definitionsFiltered as $component_id => $definition) { |
| 390 | foreach ($this->definitionsFiltered as $component_id => $definition) { |
| 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'])) { |
| 408 | $keywords = \sprintf('%s %s', $definition['label'], $definition['provider']); |
| 409 | |
| 410 | if ($preview) { |
| 410 | if ($preview) { |
| 411 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', ['component_id' => $component_id]); |
| 415 | $build_variant = $this->buildPlaceholderList($this->t('Default'), $data, $keywords); |
| 416 | } |
| 417 | |
| 418 | $build_variant['#attributes']['data-filter-child'] = $definition['machineName']; |
| 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; |
| 426 | foreach ($definition['variants'] ?? [] as $variant_id => $variant) { |
| 426 | foreach ($definition['variants'] ?? [] as $variant_id => $variant) { |
| 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) { |
| 437 | if ($preview) { |
| 438 | $component_preview_url = Url::fromRoute('display_builder.api_component_preview', $params); |
| 442 | $build_variant = $this->buildPlaceholderList($variant['title'], $data, $keywords); |
| 443 | } |
| 444 | |
| 445 | $build_variant['#attributes']['data-filter-child'] = $definition['machineName']; |
| 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']; |
| 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) { |
| 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 | } |
| 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) { |
| 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; |
| 286 | $provider['count'] = isset($providers[$provider_id]) ? ($providers[$provider_id]['count']) + 1 : 1; |
| 286 | $provider['count'] = isset($providers[$provider_id]) ? ($providers[$provider_id]['count']) + 1 : 1; |
| 286 | $provider['count'] = isset($providers[$provider_id]) ? ($providers[$provider_id]['count']) + 1 : 1; |
| 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; |
| 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 | } |
| 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) { |
| 514 | foreach ($this->getProviders($definitions) as $provider_id => $provider) { |
| 514 | foreach ($this->getProviders($definitions) as $provider_id => $provider) { |
| 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 | } |