diff --git a/components/alert/alert.component.yml b/components/alert/alert.component.yml new file mode 100644 index 000000000..8aee9c201 --- /dev/null +++ b/components/alert/alert.component.yml @@ -0,0 +1,43 @@ +name: Alert +description: 'Provide contextual feedback messages for typical user actions with the handful of available and flexible alert messages. https://v5.getbootstrap.com/docs/5.0/components/alerts/' +variants: + primary: + title: 'Primary (default)' + secondary: + title: Secondary + success: + title: Success + danger: + title: Danger + warning: + title: Warning + info: + title: Info + light: + title: Light + dark: + title: Dark + link: + title: Link +slots: + heading: + title: Heading + description: 'The alert heading. Optional.' + message: + title: Message + description: 'The alert message.' +props: + type: object + properties: + icon: + title: 'Icon name' + description: 'Add an icon to the alert. Optional.' + type: string + dismissible: + title: Dismissible + description: 'Add close button to dismiss the alert inline. Defaults to true.' + type: boolean + animated_dismiss: + title: 'Animated dismiss' + description: 'Add fade animation to the dismissible alert. Defaults to true.' + type: boolean diff --git a/components/alert/alert.preview-default.story.yml b/components/alert/alert.preview-default.story.yml new file mode 100644 index 000000000..3d6d15e86 --- /dev/null +++ b/components/alert/alert.preview-default.story.yml @@ -0,0 +1,6 @@ +name: Preview +slots: + heading: 'Well done!' + message: 'A simple alert—check it out!' +props: + icon: info-circle diff --git a/components/alert/alert.twig b/components/alert/alert.twig new file mode 100644 index 000000000..b545b2188 --- /dev/null +++ b/components/alert/alert.twig @@ -0,0 +1,20 @@ +{# +/** + * @file + * Alert component. + */ +#} + +{% set _bcl_icon_path = file_url(get_theme_path('oe_bootstrap_theme') ~ '/assets/icons/bcl-default-icons.svg') %} +{% set _icon_path = icon_path|default(_bcl_icon_path) %} + +{% include '@oe-bcl/alert' with { + 'heading': heading, + 'message': message, + 'variant': variant, + 'dismissible': dismissible, + 'animated_dismiss': animated_dismiss, + 'icon_path': _icon_path, + 'icon_name': icon, + 'attributes': attributes, +} only %} diff --git a/components/description_list/description_list.component.yml b/components/description_list/description_list.component.yml new file mode 100644 index 000000000..24f246542 --- /dev/null +++ b/components/description_list/description_list.component.yml @@ -0,0 +1,77 @@ +name: 'Description list' +description: 'Description lists are used to display and organise content that belongs to a category with a descriptions. Common use cases include glossary type lists or a list of speakers with their biographies.' +slots: + title: + title: Title + type: string + description: 'Title of the description list.' +props: + type: object + properties: + title_tag: + title: 'Title tag' + type: string + description: 'The tag to use for the title. Defaults to h2.' + items: + title: 'List items' + type: array + description: 'Each item is composed by one or more terms and one or more definitions. Each term entry can have an icon.' + items: + type: object + properties: + term: + title: 'Terms' + type: [string, array] + description: 'Can be a single string or an array of term objects.' + items: + type: object + properties: + label: + title: Label + type: [string, array, object] + description: 'Label text or render array.' + icon: + type: [object, string] + properties: + name: + type: string + size: + type: string + path: + type: string + definition: + title: 'Definition' + type: [string, array] + items: + type: object + properties: + label: + type: [string, array, object] + icon_path: + type: string + title: "Icons path" + description: "Path to icons" + orientation: + title: Orientation + type: string + description: 'Orientation of the list.' + enum: + - vertical + - horizontal + 'meta:enum': + vertical: Vertical + horizontal: Horizontal + bordered: + title: Bordered + type: boolean + description: 'Adds a border to the description list.' + col_classes: + title: 'Column classes' + type: string + description: 'Classes for the columns.' + title_attributes: + title: 'Title attributes' + type: Drupal\Core\Template\Attribute + wrapper_attributes: + title: Attributes + type: Drupal\Core\Template\Attribute diff --git a/components/description_list/description_list.preview-default.story.yml b/components/description_list/description_list.preview-default.story.yml new file mode 100644 index 000000000..92ed6e9f3 --- /dev/null +++ b/components/description_list/description_list.preview-default.story.yml @@ -0,0 +1,44 @@ +name: Preview +slots: + title: 'This is the title of this description list.' +props: + items: + - + term: + - + label: 'Single label with icon' + icon: + name: 'geo-alt-fill' + definition: 'Description of first term text goes here.' + - + term: + - + label: 'First label without icon' + - + label: 'Second label' + icon: + name: 'upload' + definition: 'Description of second term text goes here.' + - + term: + - + label: + - + '#markup': 'A label with some markup.' + definition: + - + label: 'First description of third term text goes here.' + - + label: 'Second description of third term text goes here.' + - + term: + - + label: 'Custom icon size' + icon: + name: calendar-x + size: xl + definition: 'This is a bigger icon.' + - + term: 'A single term without icon.' + definition: 'Yet another definition text.' + orientation: horizontal diff --git a/components/description_list/description_list.twig b/components/description_list/description_list.twig new file mode 100644 index 000000000..650866c2a --- /dev/null +++ b/components/description_list/description_list.twig @@ -0,0 +1,40 @@ +{# +/** + * @file + * Description list component. + */ +#} + +{% set _bcl_icon_path = file_url(get_theme_path('oe_bootstrap_theme') ~ '/assets/icons/bcl-default-icons.svg') %} +{% set _icon_path = icon_path|default(_bcl_icon_path) %} + +{% set _items = [] %} +{% for item in items %} + {% set _terms = [] %} + {% set item_terms = item.term is iterable ? item.term : [item.term] %} + {% for term in item_terms %} + {% set _term = term is iterable ? term : {'label': term} %} + + {% if _term.icon is defined and _term.icon %} + {% set _icon = _term.icon is iterable ? _term.icon : {'name': _term.icon} %} + {% set _term = _term|merge({ + 'icon': {'size': 'xs', 'path': _icon_path}|merge(_icon) + }) %} + {% endif %} + + {% set _terms = _terms|merge([_term]) %} + {% endfor %} + + {% set _items = _items|merge([item|merge({'term': _terms})]) %} +{% endfor %} + +{% include '@oe-bcl/description-list' with { + title: title, + title_tag: title_tag|default('h2'), + title_attributes: title_attributes, + items: _items, + variant: orientation, + bordered: bordered, + col_classes: col_classes, + attributes: wrapper_attributes, +} %} diff --git a/composer.json b/composer.json index 93ec45136..ab4f5a8e9 100644 --- a/composer.json +++ b/composer.json @@ -10,8 +10,8 @@ "cweagans/composer-patches": "^1.7", "drupal/core": "^10.3 || ^11", "drupal/file_link": "^2.2", - "drupal/ui_patterns": "^1.10", - "drupal/ui_patterns_settings": "^2.3", + "drupal/ui_patterns": "^2.0", + "drupal/ui_patterns_settings": "3.0.x-dev", "openeuropa/ecl-twig-loader": "^4.0" }, "require-dev": { @@ -54,20 +54,23 @@ "composer-exit-on-patch-failure": true, "enable-patching": true, "installer-paths": { - "build/core": ["type:drupal-core"], - "build/profiles/contrib/{$name}": ["type:drupal-profile"], - "build/modules/contrib/{$name}": ["type:drupal-module"], - "build/themes/contrib/{$name}": ["type:drupal-theme"] + "build/core": [ + "type:drupal-core" + ], + "build/profiles/contrib/{$name}": [ + "type:drupal-profile" + ], + "build/modules/contrib/{$name}": [ + "type:drupal-module" + ], + "build/themes/contrib/{$name}": [ + "type:drupal-theme" + ] }, "drupal-scaffold": { "locations": { "web-root": "./build" } - }, - "patches": { - "drupal/ui_patterns_settings": { - "Case mismatch between loaded and declared class names @see https://www.drupal.org/project/ui_patterns_settings/issues/3508198": "https://www.drupal.org/files/issues/2025-02-21/3508198-case-mismatch.patch" - } } }, "config": { diff --git a/includes/pattern.inc b/includes/pattern.inc index eec8b6000..a1b8dd2dc 100644 --- a/includes/pattern.inc +++ b/includes/pattern.inc @@ -59,39 +59,6 @@ function oe_bootstrap_theme_preprocess_pattern_offcanvas(array &$variables): voi $variables['offcanvas_id'] = $variables['attributes']['id'] ?? Html::getUniqueId('bcl_offcanvas'); } -/** - * Implements hook_preprocess_HOOK() for description list pattern. - */ -function oe_bootstrap_theme_preprocess_pattern_description_list(&$variables) { - // Multiple terms can be passed for each item. Process all the entries to - // add path and size to icons, if not yet specified. - foreach ($variables['items'] as &$item) { - // Allow to pass a single term, without forcing users to pass an array with - // the "label" key in it. This covers for a bug in the BCL pattern. - if (!is_array($item['term'])) { - $item['term'] = [ - ['label' => $item['term']], - ]; - continue; - } - - foreach ($item['term'] as &$term) { - if (is_array($term) && !empty($term['icon'])) { - // If the icon is not an array, the icon name has been passed. - if (!is_array($term['icon'])) { - $term['icon'] = [ - 'name' => $term['icon'], - ]; - } - $term['icon'] += [ - 'size' => 'xs', - 'path' => $variables['bcl_icon_path'], - ]; - } - } - } -} - /** * Implements hook_preprocess_HOOK(). */ diff --git a/modules/oe_bootstrap_theme_helper/oe_bootstrap_theme_helper.info.yml b/modules/oe_bootstrap_theme_helper/oe_bootstrap_theme_helper.info.yml index 553faebb7..65b56f2f4 100644 --- a/modules/oe_bootstrap_theme_helper/oe_bootstrap_theme_helper.info.yml +++ b/modules/oe_bootstrap_theme_helper/oe_bootstrap_theme_helper.info.yml @@ -7,7 +7,7 @@ dependencies: - drupal:responsive_image - ui_patterns:ui_patterns - ui_patterns:ui_patterns_library - - ui_patterns_settings:ui_patterns_settings + - ui_patterns:ui_patterns_legacy config_devel: install: - image.style.oe_bootstrap_theme_medium_no_crop diff --git a/modules/oe_bootstrap_theme_helper/oe_bootstrap_theme_helper.services.yml b/modules/oe_bootstrap_theme_helper/oe_bootstrap_theme_helper.services.yml index 081e0f002..a2089eec5 100644 --- a/modules/oe_bootstrap_theme_helper/oe_bootstrap_theme_helper.services.yml +++ b/modules/oe_bootstrap_theme_helper/oe_bootstrap_theme_helper.services.yml @@ -6,6 +6,6 @@ services: - { name: twig.loader, priority: -100 } oe_bootstrap_theme_helper.twig_extension: class: Drupal\oe_bootstrap_theme_helper\TwigExtension\TwigExtension - arguments: ['@language_manager', '@twig', '@renderer'] + arguments: ['@language_manager', '@twig', '@renderer', '@extension.list.theme'] tags: - { name: twig.extension } diff --git a/modules/oe_bootstrap_theme_helper/src/TwigExtension/TwigExtension.php b/modules/oe_bootstrap_theme_helper/src/TwigExtension/TwigExtension.php index 6985438ff..a8636092d 100644 --- a/modules/oe_bootstrap_theme_helper/src/TwigExtension/TwigExtension.php +++ b/modules/oe_bootstrap_theme_helper/src/TwigExtension/TwigExtension.php @@ -5,6 +5,7 @@ namespace Drupal\oe_bootstrap_theme_helper\TwigExtension; use Drupal\Component\Utility\Html; +use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Link; use Drupal\Core\Render\BubbleableMetadata; @@ -48,6 +49,13 @@ class TwigExtension extends AbstractExtension { */ protected TwigEnvironment $twigEnvironment; + /** + * The theme extension list service. + * + * @var \Drupal\Core\Extension\ThemeExtensionList + */ + protected $themeList; + /** * Constructs a new TwigExtension object. * @@ -57,11 +65,14 @@ class TwigExtension extends AbstractExtension { * The Drupal Twig environment. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer. + * @param \Drupal\Core\Extension\ThemeExtensionList $theme_list + * The theme extension list. */ - public function __construct(LanguageManagerInterface $languageManager, TwigEnvironment $twigEnvironment, RendererInterface $renderer) { + public function __construct(LanguageManagerInterface $languageManager, TwigEnvironment $twigEnvironment, RendererInterface $renderer, ThemeExtensionList $theme_list) { $this->languageManager = $languageManager; $this->twigEnvironment = $twigEnvironment; $this->renderer = $renderer; + $this->themeList = $theme_list; } /** @@ -93,6 +104,7 @@ public function getFunctions(): array { 'needs_environment' => TRUE, ]), new TwigFunction('bcl_gallery_items', [$this, 'bclGalleryItems']), + new TwigFunction('get_theme_path', [$this, 'getThemePath']), ]; } @@ -341,6 +353,24 @@ public function bclGalleryItems(array $items): array { return $items; } + /** + * Returns the path to a given theme. + * + * @param string $theme_name + * The machine name of the theme. + * + * @return string + * The relative path to the theme. + */ + public function getThemePath(string $theme_name) { + try { + return $this->themeList->getPath($theme_name); + } + catch (\Exception) { + return ''; + } + } + /** * Returns the children of an element array, optionally sorted by weight. * diff --git a/templates/overrides/menu/menu--main.html.twig b/templates/overrides/menu/menu--main.html.twig index 51d01f92a..57084b13e 100644 --- a/templates/overrides/menu/menu--main.html.twig +++ b/templates/overrides/menu/menu--main.html.twig @@ -4,10 +4,10 @@ */ #} -{{ - pattern('navigation', { - 'variant': 'navbar', - 'items': items, - 'attributes': attributes, - }) -}} +{#{{#} +{# pattern('navigation', {#} +{# 'variant': 'navbar',#} +{# 'items': items,#} +{# 'attributes': attributes,#} +{# })#} +{#}}#} diff --git a/templates/overrides/menu/menu-local-tasks.html.twig b/templates/overrides/menu/menu-local-tasks.html.twig index 314f0dcc1..e65fc5a87 100644 --- a/templates/overrides/menu/menu-local-tasks.html.twig +++ b/templates/overrides/menu/menu-local-tasks.html.twig @@ -6,10 +6,10 @@ #} {% if primary %}