diff --git a/modules/ui_patterns_legacy/src/Element/ComponentElementAlter.php b/modules/ui_patterns_legacy/src/Element/ComponentElementAlter.php index e52e585d279a68e94d50935b7992e591e3e8da43..16d2202d4dd9fd17bfe9255c565d8db37bcb3709 100644 --- a/modules/ui_patterns_legacy/src/Element/ComponentElementAlter.php +++ b/modules/ui_patterns_legacy/src/Element/ComponentElementAlter.php @@ -7,8 +7,8 @@ namespace Drupal\ui_patterns_legacy\Element; use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Theme\ComponentPluginManager; use Drupal\Core\Theme\ThemeManagerInterface; -use Drupal\ui_patterns\Element\ComponentElementAlter as UiPatternsComponentElementAlter; use Drupal\ui_patterns_legacy\RenderableConverter; +use Drupal\ui_patterns_library\StoriesSyntaxConverter; /** * Renders a component story. @@ -22,13 +22,13 @@ class ComponentElementAlter implements TrustedCallbackInterface { * The theme manager. * @param \Drupal\Core\Theme\ComponentPluginManager $componentPluginManager * The component plugin manager. - * @param \Drupal\ui_patterns\Element\ComponentElementAlter $componentElementAlter - * The component element alter. + * @param \Drupal\ui_patterns_library\StoriesSyntaxConverter $storiesConverter + * The stories syntax converter. */ public function __construct( protected ThemeManagerInterface $themeManager, protected ComponentPluginManager $componentPluginManager, - protected UiPatternsComponentElementAlter $componentElementAlter, + protected StoriesSyntaxConverter $storiesConverter, ) { } @@ -70,7 +70,7 @@ class ComponentElementAlter implements TrustedCallbackInterface { return $element; } $element["#story"] = $this->getStoryId($component["stories"]); - $element["#slots"] = $this->componentElementAlter->processStoriesSlots($element["#slots"] ?? []); + $element["#slots"] = $this->storiesConverter->convertSlots($element["#slots"] ?? []); return $element; } diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml index 3ada3bf59efd28561ff5ba588e4fa89c8c99aaa7..05705e5cd9252db23d9236140409c352a7fad9b5 100644 --- a/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml +++ b/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml @@ -5,3 +5,4 @@ core_version_requirement: ^10.3 || ^11 package: "User interface" dependencies: - ui_patterns:ui_patterns + - ui_patterns:ui_patterns_library diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml index 1383454385426d0b26e9a3ec2a749eb1f303d1ac..eea5e5ccc15cd5a253e7d4a4245b8f0a9103fde1 100644 --- a/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml +++ b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml @@ -22,4 +22,4 @@ services: arguments: - "@theme.manager" - "@plugin.manager.sdc" - - "@ui_patterns.component_element_alter" + - "@ui_patterns_library.stories_syntax_converter" diff --git a/modules/ui_patterns_library/src/Element/ComponentElementAlter.php b/modules/ui_patterns_library/src/Element/ComponentElementAlter.php index 82b2d94962b6147d308b1da6547d88f0b6587d3e..3357b1c615ccf7277c35571c3978a9b7da416e32 100644 --- a/modules/ui_patterns_library/src/Element/ComponentElementAlter.php +++ b/modules/ui_patterns_library/src/Element/ComponentElementAlter.php @@ -6,7 +6,7 @@ namespace Drupal\ui_patterns_library\Element; use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Theme\ComponentPluginManager; -use Drupal\ui_patterns\Element\ComponentElementAlter as UiPatternsComponentElementAlter; +use Drupal\ui_patterns_library\StoriesSyntaxConverter; /** * Renders a component story. @@ -18,12 +18,12 @@ class ComponentElementAlter implements TrustedCallbackInterface { * * @param \Drupal\Core\Theme\ComponentPluginManager $componentPluginManager * The component plugin manager. - * @param \Drupal\ui_patterns\Element\ComponentElementAlter $componentElementAlter - * The component element alter. + * @param \Drupal\ui_patterns_library\StoriesSyntaxConverter $storiesConverter + * The stories syntax converter. */ public function __construct( protected ComponentPluginManager $componentPluginManager, - protected UiPatternsComponentElementAlter $componentElementAlter, + protected StoriesSyntaxConverter $storiesConverter, ) { } @@ -59,7 +59,7 @@ class ComponentElementAlter implements TrustedCallbackInterface { } $story = $component["stories"][$story_id]; $slots = array_merge($element["#slots"] ?? [], $story["slots"] ?? []); - $element["#slots"] = $this->componentElementAlter->processStoriesSlots($slots); + $element["#slots"] = $this->storiesConverter->convertSlots($slots); $element["#props"] = array_merge($element["#props"] ?? [], $story["props"] ?? []); return $element; } diff --git a/modules/ui_patterns_library/src/StoriesSyntaxConverter.php b/modules/ui_patterns_library/src/StoriesSyntaxConverter.php new file mode 100644 index 0000000000000000000000000000000000000000..4176bcfd5f195f5391f857e52054c9038e1108b7 --- /dev/null +++ b/modules/ui_patterns_library/src/StoriesSyntaxConverter.php @@ -0,0 +1,89 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\ui_patterns_library; + +/** + * Convert a component story. + */ +class StoriesSyntaxConverter { + + /** + * Process stories slots. + * + * Stories slots have no "#" prefix in render arrays. Let's add them. + */ + public function convertSlots(array $slots): array { + foreach ($slots as $slot_id => $slot) { + if (!is_array($slot)) { + continue; + } + $slots[$slot_id] = $this->convertArray($slot); + } + return $slots; + } + + /** + * Convert an array. + */ + protected function convertArray(array $array): array { + if ($this->isRenderArray($array)) { + return $this->convertRenderArray($array); + } + foreach ($array as $index => $value) { + if (!is_array($value)) { + continue; + } + $array[$index] = $this->convertArray($value); + } + return $array; + } + + /** + * Convert a render array. + */ + protected function convertRenderArray(array $renderable): array { + foreach ($renderable as $property => $value) { + if (is_array($value)) { + $renderable[$property] = $this->convertArray($value); + } + } + $in_html_tag = (isset($renderable["type"]) && $renderable["type"] === "html_tag"); + $html_tag_allowed_render_keys = ["type", "attributes", "tag", "value", "attached"]; + foreach ($renderable as $property => $value) { + // html_tag is special. + if ($in_html_tag && !in_array($property, $html_tag_allowed_render_keys)) { + continue; + } + if (str_starts_with($property, "#")) { + continue; + } + $renderable["#" . $property] = $value; + unset($renderable[$property]); + } + return $renderable; + } + + /** + * Is the array a render array? + */ + protected function isRenderArray(array $array): bool { + if (array_is_list($array)) { + return FALSE; + } + $render_keys = ["theme", "type", "markup", "plain_text", "#theme", "#type", "#markup", "#plain_text"]; + // An array needs one, and only one, of those properties to be a render + // array. + $intersect = array_intersect(array_keys($array), $render_keys); + if (count($intersect) != 1) { + return FALSE; + } + // This property has to be a string value. + if (!is_string($array[$intersect[0]])) { + return FALSE; + } + return TRUE; + } + +} diff --git a/modules/ui_patterns_library/ui_patterns_library.services.yml b/modules/ui_patterns_library/ui_patterns_library.services.yml index 5e123e27fed3b90fdb9f8c47bd29336824c4fe05..45de47c9a6054dc0cd6d906d9124002ecb1390a5 100644 --- a/modules/ui_patterns_library/ui_patterns_library.services.yml +++ b/modules/ui_patterns_library/ui_patterns_library.services.yml @@ -3,7 +3,9 @@ services: class: Drupal\ui_patterns_library\Element\ComponentElementAlter arguments: - "@plugin.manager.sdc" - - "@ui_patterns.component_element_alter" + - "@ui_patterns_library.stories_syntax_converter" + ui_patterns_library.stories_syntax_converter: + class: Drupal\ui_patterns_library\StoriesSyntaxConverter ui_patterns_library.twig.extension: class: Drupal\ui_patterns_library\Template\TwigExtension tags: diff --git a/src/Element/ComponentElementAlter.php b/src/Element/ComponentElementAlter.php index 5e9b62e75e30a5df9fa3905025fa635ba5988697..4b7919f574fd23b468e7e2b2f6700827ed49384e 100644 --- a/src/Element/ComponentElementAlter.php +++ b/src/Element/ComponentElementAlter.php @@ -127,38 +127,4 @@ class ComponentElementAlter implements TrustedCallbackInterface { return $element; } - /** - * Process stories slots. - * - * Stories slots have no "#" prefix in render arrays. Let's add them. - * A bit like UI Patterns 1.x's PatternPreview::getPreviewMarkup() - * This method belongs here because used by both ui_patterns_library and - * ui_patterns_legacy. - */ - public function processStoriesSlots(array $slots): array { - foreach ($slots as $slot_id => $slot) { - if (!is_array($slot)) { - continue; - } - if (array_is_list($slot)) { - $slots[$slot_id] = $this->processStoriesSlots($slot); - } - $slot_keys = array_keys($slot); - $render_keys = ["theme", "type", "markup", "plain_text"]; - if (count(array_intersect($slot_keys, $render_keys)) > 0) { - foreach ($slot as $key => $value) { - if (is_array($value)) { - $value = $this->processStoriesSlots($value); - } - if (str_starts_with($key, "#")) { - continue; - } - $slots[$slot_id]["#" . $key] = $value; - unset($slots[$slot_id][$key]); - } - } - } - return $slots; - } - }