component_library-1.0.x-dev/component_library.module
component_library.module
<?php // phpcs:ignore Drupal.Commenting.FileComment.Missing
declare(strict_types=1);
/**
* @file
* Provides a component library and component library variant entity type.
*/
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Serialization\Yaml;
use Drupal\component_library\Entity\ComponentLibraryAsset;
use Drupal\component_library\Entity\ComponentLibraryPattern;
use Drupal\component_library\Form\ComponentOverrideForm;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Extension\Extension;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\Core\Entity\EntityInterface;
/**
* Implements hook_library_info_build().
*/
function component_library_library_info_build(): array {
$libraries = [];
foreach (ComponentLibraryAsset::loadMultiple() as $asset) {
foreach ($asset->getFiles() as $file) {
if (!file_exists($file['path'])) {
\Drupal::service('component_library.asset')->generateLibraryFiles($asset);
}
if ($file['type'] === 'css') {
$libraries["component_library.{$asset->id()}"][$file['type']]['theme'][$file['path']] = [];
}
else {
$libraries["component_library.{$asset->id()}"][$file['type']][$file['path']] = [];
}
}
}
return $libraries;
}
/**
* Implements hook_css_alter().
*/
function component_library_css_alter(&$css, AttachedAssetsInterface $assets): void {
foreach (ComponentLibraryAsset::loadMultiple() as $asset) {
foreach ($asset->getFiles() as $file) {
if ($file['type'] === 'css' && !empty($css[$file['path']])) {
// Ensure our css overrides are loaded after the theme.
$css[$file['path']]['group'] = CSS_AGGREGATE_THEME;
$css[$file['path']]['weight'] = 999_999_999_999;
}
}
}
}
/**
* Implements hook_theme().
*/
function component_library_theme(): array {
return [
'html__component_library__variant_preview' => [
'render element' => 'html',
],
'component_library_variant' => [
'render element' => 'elements',
],
'component_library_component' => [
'render element' => 'elements',
],
];
}
/**
* Implements hook_theme_registry_alter().
*/
function component_library_theme_registry_alter(&$theme_registry): void {
// Ensure there is a theme registry entry for each override.
$theme = Drupal::theme()->getActiveTheme();
if ($theme) {
$theme_name = $theme->getName();
$overrides = \Drupal::entityTypeManager()->getStorage('component_override')->loadByProperties(['theme' => $theme_name]);
if ($overrides) {
$override_manager = Drupal::service('plugin.manager.component_override');
foreach ($overrides as $override) {
/** @var \Drupal\component_library\ComponentOverrideManager $override_manager */
$override_manager->ensureThemeRegistry($override, $theme_registry);
}
}
}
}
function template_preprocess_html__component_library__variant_preview(&$variables): void {
template_preprocess_html($variables);
}
/**
* Prepares variables for component template.
*/
function template_preprocess_component_library_component(array &$variables, string $hook, array $info): void {
$context = [];
$pattern_entity = ComponentLibraryPattern::load($info['component_id']);
if ($pattern_entity) {
/** @var \Drupal\ui_patterns\Element\PatternContext $pattern_context */
$pattern_context = $variables['context'];
foreach ($pattern_entity->getFields() as $name => $preview_value) {
$context[$name] = $pattern_context->isOfType('preview') ? $preview_value : $variables[$name] ?? '';
}
$component_variant = $pattern_entity->getVariant($variables['variant']);
if ($component_variant) {
$variables['content'] = [
'#type' => 'inline_template',
'#template' => $component_variant->get('template'),
'#context' => $context,
];
$cache_metadata = new CacheableMetadata();
$cache_metadata->addCacheableDependency($component_variant);
$cache_metadata->addCacheableDependency($component_variant->getPatternEntity());
$cache_metadata->applyTo($variables);
}
}
}
/**
* Prepares variables for component library variant template.
*
* Default template: component-library-variant.html.twig.
*
* @param array $variables
* An associative array containing:
* - elements: An associative array containing the component library variant
* information and any fields attached to the entity.
* - attributes: HTML attributes for the containing element.
*/
function template_preprocess_component_library_variant(array &$variables): void {
$context = [];
$component_library_variant = $variables['elements']['#component_library_variant'];
if ($pattern_entity = $component_library_variant->getPatternEntity()) {
try {
$context = Yaml::decode($pattern_entity->get('data')) ?? [];
}
catch (InvalidDataTypeException $exception) {
// Intentionally empty.
}
}
$variables['content'] = [
'#type' => 'inline_template',
'#template' => $component_library_variant->get('template'),
'#context' => $context,
];
}
/**
* Implements hook_codemirror_editor_assets_alter().
*/
function component_library_codemirror_editor_assets_alter(array &$assets): void {
$assets['js'][] = 'addon/hint/css-hint.js';
$assets['js'][] = 'addon/hint/html-hint.js';
$assets['js'][] = 'addon/hint/show-hint.js';
$assets['js'][] = 'addon/hint/xml-hint.js';
$assets['js'][] = 'addon/lint/css-lint.js';
$assets['js'][] = 'addon/lint/html-lint.js';
$assets['js'][] = 'addon/lint/lint.js';
$assets['js'][] = 'addon/lint/yaml-lint.js';
$assets['css'][] = 'addon/hint/show-hint.css';
$assets['css'][] = 'addon/lint/lint.css';
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*
* Adds list of theme's component_override entities to the theme settings.
*/
function component_library_form_system_theme_settings_alter(&$form, FormStateInterface $form_state): void {
$theme = $form_state->getBuildInfo()['args'][0];
if ($theme) {
$entity_type_manager = \Drupal::entityTypeManager();
$entity_type = 'component_override';
$overrides = $entity_type_manager->getStorage($entity_type)
->loadByProperties(['theme' => $theme]);
if ($overrides) {
$form['component_overrides'] = [
'#type' => 'details',
'#title' => t('Component overrides'),
'#open' => TRUE,
'#weight' => -50,
];
$form['component_overrides']['list'] = $entity_type_manager
->getListBuilder($entity_type)
->render();
}
}
}
/**
* Implements hook_toolbar().
*/
function component_library_toolbar(): array {
$items = [];
if (!\Drupal::config('component_library.override_mode.settings')->get('override_mode')) {
return $items;
}
if (!\Drupal::currentUser()->hasPermission('access override mode')) {
return $items;
}
$items['component_override'] = [
'#cache' => [
'contexts' => [
'user.permissions',
],
],
];
$items['component_override'] += [
'#type' => 'toolbar_item',
'tab' => [
'#type' => 'link',
'#title' => t('Override'),
'#url' => Url::fromRoute('component_library.override_mode'),
'#attributes' => [
'id' => 'enable-override-mode',
],
],
'#wrapper_attributes' => [
'class' => ['float-right'],
],
'#attached' => [
'library' => [
'component_library/override_mode',
'core/drupal.dialog.ajax',
],
],
];
return $items;
}
/**
* Implements hook_system_info_alter().
*/
function component_library_system_info_alter(array &$info, Extension $file, $type): void {
if ($type === 'theme' && $info['engine'] === 'twig') {
$info['engine'] = 'component_library_engine';
}
}
