diff --git a/docs/components.md b/docs/components.md index a6b2239ffc3eb2b36049ebfef811a368c1f52061..eed919398a8fbfd19df23be6365d91880b23b24a 100644 --- a/docs/components.md +++ b/docs/components.md @@ -31,6 +31,7 @@ This uses the terms defined above. - MUST support `SDC` and `Block` today - MUST be evolvable to [support other component types later](https://www.drupal.org/project/experience_builder/issues/3454519) - MUST support existing `SDC`s and `Block`s, if they meet certain criteria necessary for XB to provide a good UX +- MUST support categorization of `component`s - MAY require API additions and perhaps even changes to `SDC`s (such as: defining restrictions for `component slot`s, schema references and more) ⚠️ [an overview of what has been identified is constantly updated](https://www.drupal.org/project/experience_builder/issues/3462705) ⚠️ ⚠️ The [supported component modeling approaches](https://www.drupal.org/project/experience_builder/issues/3446083) @@ -96,3 +97,26 @@ refer to "blocks" and not "block plugins", under the hood, they actually _are_ b ### 3.3 Other `component type`s Nothing yet, this will change when we [support other `component type`s later](https://www.drupal.org/project/experience_builder/issues/3454519). + +### 3.4 Categorization + +Each `component` can be categorized in order to group them in the UI. Some `component type`s have shared categories, as follows: + +```mermaid +stateDiagram-v2 +classdef source color:white,fill:purple + +state "JavaScript component" as JavaScript +state "Theme component" as Theme +state "Theme component categories" as ThemeCat +state "Module component" as Module +state "Module component categories" as ModuleCat +state "Element categories" as ElementCat +state "Block categories" as BlockCat + +JavaScript:::source --> ThemeCat +Theme:::source --> ThemeCat +Module:::source --> ModuleCat +Element:::source --> ElementCat +Block:::source --> BlockCat +``` diff --git a/openapi.yml b/openapi.yml index f706aec74ddaa4a0560ab61e134e35685491b4e6..b6434014761f47a34e30456b22995ebe9874438c 100644 --- a/openapi.yml +++ b/openapi.yml @@ -91,6 +91,7 @@ paths: 'sdc_test:my-cta': id: 'sdc_test:my-cta' name: Call to Action + source: Module component metadata: path: >- core/modules/system/tests/modules/sdc_test/components/my-cta diff --git a/src/Controller/ApiComponentsController.php b/src/Controller/ApiComponentsController.php index 849f5b4d75415e66d6ddb07efe39b60e26bc8693..12f499917d4e6c1b419a86a8f43834fc7b24b026 100644 --- a/src/Controller/ApiComponentsController.php +++ b/src/Controller/ApiComponentsController.php @@ -19,8 +19,8 @@ use Symfony\Component\DependencyInjection\Attribute\Autowire; * * @see ui/src/types/Component.ts * @phpstan-import-type ComponentConfigEntityId from \Drupal\experience_builder\Entity\Component - * @phpstan-type ComponentClientSideTypeAny array{'id': string, 'name': string, 'default_markup': string|\Stringable, 'css': string|\Stringable, 'js_header': string|\Stringable, 'js_footer': string|\Stringable} - * @phpstan-type ComponentClientSideTypeSdc array{'id': string, 'name': string, 'default_markup': string|\Stringable, 'css': string|\Stringable, 'js_header': string|\Stringable, 'js_footer': string|\Stringable, 'metadata': array<string, mixed>, 'field_data': array<string, mixed>, 'dynamic_prop_source_candidates': array<string, mixed>,} + * @phpstan-type ComponentClientSideTypeAny array{'id': string, 'name': string, 'source': string, 'default_markup': string|\Stringable, 'css': string|\Stringable, 'js_header': string|\Stringable, 'js_footer': string|\Stringable} + * @phpstan-type ComponentClientSideTypeSdc array{'id': string, 'name': string, 'source': string, 'default_markup': string|\Stringable, 'css': string|\Stringable, 'js_header': string|\Stringable, 'js_footer': string|\Stringable, 'metadata': array<string, mixed>, 'field_data': array<string, mixed>, 'dynamic_prop_source_candidates': array<string, mixed>,} * * This controller provides a critical response for the XB UI. Therefore it * should hence be as fast and cacheable as possible. High-cardinality cache @@ -62,6 +62,7 @@ final class ApiComponentsController { * array of XB Component config entities, with for each: * - `id`: the Component config entity ID * - `name`: human-readable name + * - `source`: human-readable component type * - `default_markup`: without providing user input, this is what * Component's markup would look like — used to preview the Component * prior to placing it diff --git a/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php b/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php index 1af6bfe521866c28cb9c4626cbd6f318a0370afa..ed74d617e761fdabc0b3a759304868ac43a46fd1 100644 --- a/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php +++ b/src/Plugin/ExperienceBuilder/ComponentSource/BlockComponent.php @@ -151,6 +151,7 @@ final class BlockComponent extends ComponentSourceBase implements ContainerFacto return [ 'id' => $component->id(), 'name' => (string) $definition['admin_label'], + 'source' => (string) $this->t('Block'), // @todo Allow components to pass build arrays back? 'default_markup' => $this->renderer->render($build), // @todo CSS and JS diff --git a/src/Plugin/ExperienceBuilder/ComponentSource/SingleDirectoryComponent.php b/src/Plugin/ExperienceBuilder/ComponentSource/SingleDirectoryComponent.php index 7f490f6621e49996f35bb02429a06a9fb2dfd47b..17b0d3292a48e178b717152c015de25926d5314c 100644 --- a/src/Plugin/ExperienceBuilder/ComponentSource/SingleDirectoryComponent.php +++ b/src/Plugin/ExperienceBuilder/ComponentSource/SingleDirectoryComponent.php @@ -17,6 +17,7 @@ use Drupal\Core\Render\RendererInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Theme\Component\ComponentValidator; use Drupal\Core\Theme\ComponentPluginManager; +use Drupal\Core\Theme\ExtensionType; use Drupal\experience_builder\AssetRenderer; use Drupal\experience_builder\Attribute\ComponentSource; use Drupal\experience_builder\ComponentSource\ComponentSourceBase; @@ -315,6 +316,7 @@ final class SingleDirectoryComponent extends ComponentSourceBase implements Comp return [ 'id' => $component->id(), 'name' => $component_plugin->metadata->name, + 'source' => (string) $this->getSourceLabel(), // A pre-rendered version of the component is provided so no requests // are needed when adding it to the layout which includes a default markup, // CSS files, JS files in the header and JS files in the footer. @@ -330,6 +332,26 @@ final class SingleDirectoryComponent extends ComponentSourceBase implements Comp ]; } + /** + * Returns the source label for this component. + * + * @return \Drupal\Core\StringTranslation\TranslatableMarkup + * The source label. + */ + protected function getSourceLabel(): TranslatableMarkup { + $component_plugin = $this->getComponentPlugin(); + assert(is_array($component_plugin->getPluginDefinition())); + + // The 'extension_type' key is guaranteed to be set. + // @see \Drupal\Core\Theme\ComponentPluginManager::alterDefinition() + $extension_type = $component_plugin->getPluginDefinition()['extension_type']; + assert($extension_type instanceof ExtensionType); + return match ($extension_type) { + ExtensionType::Module => $this->t('Module component'), + ExtensionType::Theme => $this->t('Theme component'), + }; + } + /** * Converts an SDC plugin machine name into a config entity ID. *