Loading cl_components.module +12 −5 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ use Drupal\cl_components\Component\Component; use Drupal\cl_components\Component\ComponentDiscovery; use Drupal\cl_components\Exception\InvalidComponentHookException; /** * @file Loading @@ -18,16 +19,22 @@ use Drupal\cl_components\Component\ComponentDiscovery; */ function cl_components_library_info_build() { // Iterate over the components' directory to find all the components. $component_repository = \Drupal::service(ComponentDiscovery::class); assert($component_repository instanceof ComponentDiscovery); $components = $component_repository->findAll(); $discovery = \Drupal::service(ComponentDiscovery::class); assert($discovery instanceof ComponentDiscovery); $components = $discovery->findAll(); $libraries = array_reduce( $components, static function (array $libraries, Component $component) use ($component_repository) { $library = $component_repository->libraryFromComponent($component); static function (array $libraries, Component $component) use ($discovery) { $library = $discovery->libraryFromComponent($component); if (empty($library)) { return $libraries; } try { $library = $component->invokeHook('library_info_alter', [$library]); } catch (InvalidComponentHookException $e) { // The component does not support this hook. It is fine, do nothing. } return array_merge($libraries, [$component->getId() => $library]); }, [] Loading cl_components_examples/templates/components/my-button/my-button.php 0 → 100644 +32 −0 Original line number Diff line number Diff line <?php /** * @file * Component PHP integration. */ use Drupal\cl_components\Component\Component; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Form\FormStateInterface; /** * Implements cl_component_COMPONENT_library_info_alter(). */ function cl_component_my_button_library_info_alter($library, Component $component): array { // Ensure all JS for the button is deferred. $library['js'] = array_map( static fn(array $value) => NestedArray::mergeDeep($value, ['attributes' => ['defer' => TRUE]]), $library['js'] ?? [] ); return $library; } /** * Implements cl_component_COMPONENT_form_alter(). */ function cl_component_my_button_form_alter($form, FormStateInterface $form_state, Component $component): array { $form['data']['iconType']['#description'] = \t('The component type will determine the icon in the button.'); $form['data']['iconType']['#default_value'] = $form['data']['iconType']['#default_value'] ?? 'power'; $form['data']['text']['#placeholder'] = \t('Click me!'); return $form; } src/Component/Component.php +27 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ namespace Drupal\cl_components\Component; use Drupal\cl_components\Exception\InvalidComponentException; use Drupal\cl_components\Exception\InvalidComponentHookException; use Drupal\cl_components\Exception\TemplateNotFoundException; use Drupal\Component\Utility\Html; use Drupal\Core\StringTranslation\StringTranslationTrait; Loading Loading @@ -213,6 +214,32 @@ class Component { return $this->metadata; } /** * Invokes a CL Component hook. * * @param string $hook * The hook to invoke. * @param array $args * The arguments for the hook. * * @return mixed * The value returned by the component hook. * * @throws \Drupal\cl_components\Exception\InvalidComponentHookException */ public function invokeHook(string $hook, array $args): mixed { $metadata = $this->getMetadata(); if (!in_array($hook, $metadata->getHooks())) { $message = sprintf('The requested hook "%s" is not supported by component "%s".', $hook, $this->getId()); throw new InvalidComponentHookException($message); } $func_name = sprintf('cl_component_%s_%s', $metadata->getSafeName(), $hook); if (!function_exists($func_name)) { require_once $this->getMetadata()->getPhpPath(); } return $func_name(...[...$args, $this]); } /** * Calculates additional context for this template. * Loading src/Component/ComponentMetadata.php +59 −4 Original line number Diff line number Diff line Loading @@ -3,9 +3,9 @@ namespace Drupal\cl_components\Component; use Drupal\cl_components\Exception\InvalidComponentException; use Drupal\cl_components\Parser\ComponentPhpFile; use Drupal\Component\Serialization\Json; use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Url; use JsonSchema\Validator; /** Loading Loading @@ -121,6 +121,27 @@ final class ComponentMetadata { */ private string $description; /** * The hook names this component implements. * * @var string[] */ private array $hooks = []; /** * The name safe for using in PHP functions. * * @var string */ private string $safeName; /** * The full path to the PHP file. * * @var string */ private string $phpPath; /** * ComponentMetadata constructor. * Loading Loading @@ -169,6 +190,13 @@ final class ComponentMetadata { // Save the schemas. $this->parseSchemaInfo($metadata_info); // Parse the hooks. $this->safeName = preg_replace('@[^a-z0-9]+@', '_', strtolower($this->machineName)); $php_file = new ComponentPhpFile($this->path, $this->machineName, $this->safeName); $this->phpPath = $php_file->path; if ($php_file->hasPhpFile()) { $this->hooks = $php_file->parseHooks(); } } /** Loading Loading @@ -227,9 +255,7 @@ final class ComponentMetadata { if (!is_array($type)) { $type = [$type]; } $type = array_merge($type, ['object']); $type = array_unique($type); $schema['type'] = $type; $schema['type'] = array_unique([...$type, 'object']); $this->schemas['props']['properties'][$name]['type'] = $type; } $this->schemas['named_blocks'] = $this->parseNamedBlockSchemaInfo(); Loading Loading @@ -465,4 +491,33 @@ final class ComponentMetadata { return $this->description; } /** * Gets the machine name safe to use in PHP function names. * * @return string */ public function getSafeName(): string { return $this->safeName; } /** * Gets the hook names. * * @return string[] * The hook names. */ public function getHooks(): array { return $this->hooks; } /** * The path to the PHP file. * * @return string * The path to the PHP file. */ public function getPhpPath(): string { return $this->phpPath; } } src/Controller/ComponentAudit.php +27 −4 Original line number Diff line number Diff line Loading @@ -166,10 +166,31 @@ final class ComponentAudit extends ControllerBase { $assets = $this->discovery->discoverDistAssets($path); $assets_message = [ '#theme' => 'item_list', '#items' => array_map(static fn(string $file) => preg_replace('@.*/' . $component->getId() . '/@', '', $file), [ '#items' => array_map( static fn(string $file) => [ '#type' => 'html_tag', '#tag' => 'code', '#value' => preg_replace('@.*/' . $component->getId() . '/@', '', $file), ], [ ...$assets['js'] ?? [], ...$assets['css'] ?? [], ]), ] ), ]; $hooks = $metadata->getHooks(); $hooks_message = empty($hooks) ? [ '#type' => 'html_tag', '#tag' => 'em', '#value' => $this->t('No hooks in <code>@filename</code>.', ['@filename' => $component->getId() . '.php']), ] : [ '#theme' => 'item_list', '#items' => array_map( static fn(string $hook) => ['#type' => 'html_tag', '#tag' => 'code', '#value' => $hook], $hooks ), ]; $card_build = [ 'title' => [ Loading @@ -195,6 +216,7 @@ final class ComponentAudit extends ControllerBase { $this->t('Default Template'), $this->t('Variants'), $this->t('Assets'), $this->t('Implemented Hooks'), ], '#rows' => [ [ Loading @@ -202,6 +224,7 @@ final class ComponentAudit extends ControllerBase { $template_message, $variants_message, ['data' => $assets_message], ['data' => $hooks_message], ], ], ], Loading Loading
cl_components.module +12 −5 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ use Drupal\cl_components\Component\Component; use Drupal\cl_components\Component\ComponentDiscovery; use Drupal\cl_components\Exception\InvalidComponentHookException; /** * @file Loading @@ -18,16 +19,22 @@ use Drupal\cl_components\Component\ComponentDiscovery; */ function cl_components_library_info_build() { // Iterate over the components' directory to find all the components. $component_repository = \Drupal::service(ComponentDiscovery::class); assert($component_repository instanceof ComponentDiscovery); $components = $component_repository->findAll(); $discovery = \Drupal::service(ComponentDiscovery::class); assert($discovery instanceof ComponentDiscovery); $components = $discovery->findAll(); $libraries = array_reduce( $components, static function (array $libraries, Component $component) use ($component_repository) { $library = $component_repository->libraryFromComponent($component); static function (array $libraries, Component $component) use ($discovery) { $library = $discovery->libraryFromComponent($component); if (empty($library)) { return $libraries; } try { $library = $component->invokeHook('library_info_alter', [$library]); } catch (InvalidComponentHookException $e) { // The component does not support this hook. It is fine, do nothing. } return array_merge($libraries, [$component->getId() => $library]); }, [] Loading
cl_components_examples/templates/components/my-button/my-button.php 0 → 100644 +32 −0 Original line number Diff line number Diff line <?php /** * @file * Component PHP integration. */ use Drupal\cl_components\Component\Component; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Form\FormStateInterface; /** * Implements cl_component_COMPONENT_library_info_alter(). */ function cl_component_my_button_library_info_alter($library, Component $component): array { // Ensure all JS for the button is deferred. $library['js'] = array_map( static fn(array $value) => NestedArray::mergeDeep($value, ['attributes' => ['defer' => TRUE]]), $library['js'] ?? [] ); return $library; } /** * Implements cl_component_COMPONENT_form_alter(). */ function cl_component_my_button_form_alter($form, FormStateInterface $form_state, Component $component): array { $form['data']['iconType']['#description'] = \t('The component type will determine the icon in the button.'); $form['data']['iconType']['#default_value'] = $form['data']['iconType']['#default_value'] ?? 'power'; $form['data']['text']['#placeholder'] = \t('Click me!'); return $form; }
src/Component/Component.php +27 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ namespace Drupal\cl_components\Component; use Drupal\cl_components\Exception\InvalidComponentException; use Drupal\cl_components\Exception\InvalidComponentHookException; use Drupal\cl_components\Exception\TemplateNotFoundException; use Drupal\Component\Utility\Html; use Drupal\Core\StringTranslation\StringTranslationTrait; Loading Loading @@ -213,6 +214,32 @@ class Component { return $this->metadata; } /** * Invokes a CL Component hook. * * @param string $hook * The hook to invoke. * @param array $args * The arguments for the hook. * * @return mixed * The value returned by the component hook. * * @throws \Drupal\cl_components\Exception\InvalidComponentHookException */ public function invokeHook(string $hook, array $args): mixed { $metadata = $this->getMetadata(); if (!in_array($hook, $metadata->getHooks())) { $message = sprintf('The requested hook "%s" is not supported by component "%s".', $hook, $this->getId()); throw new InvalidComponentHookException($message); } $func_name = sprintf('cl_component_%s_%s', $metadata->getSafeName(), $hook); if (!function_exists($func_name)) { require_once $this->getMetadata()->getPhpPath(); } return $func_name(...[...$args, $this]); } /** * Calculates additional context for this template. * Loading
src/Component/ComponentMetadata.php +59 −4 Original line number Diff line number Diff line Loading @@ -3,9 +3,9 @@ namespace Drupal\cl_components\Component; use Drupal\cl_components\Exception\InvalidComponentException; use Drupal\cl_components\Parser\ComponentPhpFile; use Drupal\Component\Serialization\Json; use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Url; use JsonSchema\Validator; /** Loading Loading @@ -121,6 +121,27 @@ final class ComponentMetadata { */ private string $description; /** * The hook names this component implements. * * @var string[] */ private array $hooks = []; /** * The name safe for using in PHP functions. * * @var string */ private string $safeName; /** * The full path to the PHP file. * * @var string */ private string $phpPath; /** * ComponentMetadata constructor. * Loading Loading @@ -169,6 +190,13 @@ final class ComponentMetadata { // Save the schemas. $this->parseSchemaInfo($metadata_info); // Parse the hooks. $this->safeName = preg_replace('@[^a-z0-9]+@', '_', strtolower($this->machineName)); $php_file = new ComponentPhpFile($this->path, $this->machineName, $this->safeName); $this->phpPath = $php_file->path; if ($php_file->hasPhpFile()) { $this->hooks = $php_file->parseHooks(); } } /** Loading Loading @@ -227,9 +255,7 @@ final class ComponentMetadata { if (!is_array($type)) { $type = [$type]; } $type = array_merge($type, ['object']); $type = array_unique($type); $schema['type'] = $type; $schema['type'] = array_unique([...$type, 'object']); $this->schemas['props']['properties'][$name]['type'] = $type; } $this->schemas['named_blocks'] = $this->parseNamedBlockSchemaInfo(); Loading Loading @@ -465,4 +491,33 @@ final class ComponentMetadata { return $this->description; } /** * Gets the machine name safe to use in PHP function names. * * @return string */ public function getSafeName(): string { return $this->safeName; } /** * Gets the hook names. * * @return string[] * The hook names. */ public function getHooks(): array { return $this->hooks; } /** * The path to the PHP file. * * @return string * The path to the PHP file. */ public function getPhpPath(): string { return $this->phpPath; } }
src/Controller/ComponentAudit.php +27 −4 Original line number Diff line number Diff line Loading @@ -166,10 +166,31 @@ final class ComponentAudit extends ControllerBase { $assets = $this->discovery->discoverDistAssets($path); $assets_message = [ '#theme' => 'item_list', '#items' => array_map(static fn(string $file) => preg_replace('@.*/' . $component->getId() . '/@', '', $file), [ '#items' => array_map( static fn(string $file) => [ '#type' => 'html_tag', '#tag' => 'code', '#value' => preg_replace('@.*/' . $component->getId() . '/@', '', $file), ], [ ...$assets['js'] ?? [], ...$assets['css'] ?? [], ]), ] ), ]; $hooks = $metadata->getHooks(); $hooks_message = empty($hooks) ? [ '#type' => 'html_tag', '#tag' => 'em', '#value' => $this->t('No hooks in <code>@filename</code>.', ['@filename' => $component->getId() . '.php']), ] : [ '#theme' => 'item_list', '#items' => array_map( static fn(string $hook) => ['#type' => 'html_tag', '#tag' => 'code', '#value' => $hook], $hooks ), ]; $card_build = [ 'title' => [ Loading @@ -195,6 +216,7 @@ final class ComponentAudit extends ControllerBase { $this->t('Default Template'), $this->t('Variants'), $this->t('Assets'), $this->t('Implemented Hooks'), ], '#rows' => [ [ Loading @@ -202,6 +224,7 @@ final class ComponentAudit extends ControllerBase { $template_message, $variants_message, ['data' => $assets_message], ['data' => $hooks_message], ], ], ], Loading