Skip to content
Snippets Groups Projects
Commit b743313c authored by Florent Torregrosa's avatar Florent Torregrosa
Browse files

Issue #3503645 by grimreaper, laetitia_al, tocab: Contextual links: use dropdown

parent 30d00eb1
No related branches found
No related tags found
1 merge request!266Issue #3503645 by grimreaper, laetitia_al: Contextual links: use popover
Pipeline #447603 passed
.contextual .dropdown-menu {
--bs-dropdown-min-width: auto;
}
.contextual.open .trigger {
border-bottom-color: #ccc !important;
border-radius: 13px !important;
}
(($, Drupal) => {
/**
* Theme function for a contextual trigger.
*
* @return {string}
* A string representing a DOM fragment.
*/
Drupal.theme.contextualTrigger = () => {
return (
'<button class="trigger focusable visually-hidden dropdown-toggle"' +
'type="button"' +
'data-bs-toggle="dropdown"' +
'aria-expanded="false"' +
'></button>'
);
};
})(jQuery, Drupal);
<?php
declare(strict_types=1);
namespace Drupal\ui_suite_bootstrap\Element;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Component\Utility\Html;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\Template\Attribute;
/**
* Element Prerender methods for contextual_links_placeholder.
*/
class ElementPreRenderContextualLinksPlaceholder implements TrustedCallbackInterface {
/**
* Add classes for dropdown styling.
*/
public static function preRenderContextualLinksPlaceholder(array $element): array {
if (!isset($element['#markup']) || !($element['#markup'] instanceof MarkupInterface)) {
return $element;
}
$placeholder = (string) $element['#markup'];
$attributes = static::extractAttributes($placeholder);
$attributes['class'][] = 'dropdown';
$attributes['class'][] = 'position-absolute';
$attribute = new Attribute($attributes);
$element['#markup'] = new FormattableMarkup('<div@attributes></div>', ['@attributes' => $attribute]);
return $element;
}
/**
* {@inheritdoc}
*/
public static function trustedCallbacks(): array {
return ['preRenderContextualLinksPlaceholder'];
}
/**
* Extract attributes.
*
* @param string $html
* The HTML to parse. Expected to be one div.
*
* @return array
* The array of attributes.
*/
protected static function extractAttributes(string $html): array {
$attributes = [];
// Extract existing attributes.
/** @var \DOMElement $div */
foreach (Html::load($html)->getElementsByTagName('div') as $div) {
/** @var \DOMAttr $attr */
foreach ($div->attributes as $attr) {
$attributes[$attr->nodeName] = $attr->nodeValue;
}
}
return $attributes;
}
}
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\ui_suite_bootstrap\HookHandler;
use Drupal\ui_suite_bootstrap\Element\ElementPreRenderContextualLinksPlaceholder;
use Drupal\ui_suite_bootstrap\Element\ElementPreRenderDropbutton;
use Drupal\ui_suite_bootstrap\Element\ElementPreRenderLayoutBuilder;
use Drupal\ui_suite_bootstrap\Element\ElementPreRenderLink;
......@@ -190,6 +191,14 @@ class ElementInfoAlter {
];
}
// Contextual link placeholder.
if (isset($info['contextual_links_placeholder'])) {
$info['contextual_links_placeholder']['#pre_render'][] = [
ElementPreRenderContextualLinksPlaceholder::class,
'preRenderContextualLinksPlaceholder',
];
}
// Dropbutton.
if (isset($info['dropbutton'])) {
// Remove Core pre_render to remove wrapper and classes.
......
<?php
declare(strict_types=1);
namespace Drupal\ui_suite_bootstrap\HookHandler;
/**
* Ensure links structure fits into list group structure.
*/
class PreprocessLinksContextual extends PreprocessLinksMediaLibraryMenu {
}
{#
/**
* @file
* Default theme implementation for a set of links.
*
* Available variables:
* - attributes: Attributes for the UL containing the list of links.
* - links: Links to be output.
* Each link will have the following elements:
* - link: (optional) A render array that returns a link. See
* template_preprocess_links() for details how it is generated.
* - text: The link text.
* - attributes: HTML attributes for the list item element.
* - text_attributes: (optional) HTML attributes for the span element if no
* 'url' was supplied.
* - heading: (optional) A heading to precede the links.
* - text: The heading text.
* - level: The heading level (e.g. 'h2', 'h3').
* - attributes: (optional) A keyed list of attributes for the heading.
* If the heading is a string, it will be used as the text of the heading and
* the level will default to 'h2'.
*
* Headings should be used on navigation menus and any list of links that
* consistently appears on multiple pages. To make the heading invisible use
* the 'visually-hidden' CSS class. Do not use 'display:none', which
* removes it from screen readers and assistive technology. Headings allow
* screen reader and keyboard only users to navigate to or skip the links.
* See http://juicystudio.com/article/screen-readers-display-none.php and
* http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
*
* @see template_preprocess_links()
*
* @ingroup themeable
*/
#}
{% if links -%}
{%- if heading -%}
{%- if heading.level -%}
<{{ heading.level }}{{ heading.attributes }}>{{ heading.text }}</{{ heading.level }}>
{%- else -%}
<h2{{ heading.attributes }}>{{ heading.text }}</h2>
{%- endif -%}
{%- endif -%}
{% if preprocessed_items -%}
{#
Can't use the dropdown component directly because it handles the dropdown
button which is handled by JS in contextual links.
#}
<ul{{ attributes.addClass('dropdown-menu').removeClass('contextual-links') }}>
{% for item in preprocessed_items %}
{% set item_attributes = create_attribute(item.attributes|default({})) %}
<li{{ item_attributes }}>
{% set link_attributes = create_attribute(item.link_attributes|default({})) %}
{% if item.title and item.url %}
<a{{ link_attributes.setAttribute('href', item.url).addClass('dropdown-item') }}>{{ item.title }}</a>
{% elseif (not item.title and not item.url) or link_attributes.hasClass('dropdown-divider') %}
<hr{{ link_attributes.addClass('dropdown-divider') }}>
{% elseif item.title and not item.url %}
{% if link_attributes.hasClass('dropdown-header') %}
<h{{ heading_level }}{{ link_attributes }}>{{ item.title }}</h{{ heading_level }}>
{% elseif link_attributes.hasClass('dropdown-item') %}
<button{{ link_attributes.setAttribute('type', 'button') }}>{{ item.title }}</button>
{% else %}
<span{{ link_attributes.addClass('dropdown-item-text') }}>{{ item.title }}</span>
{% endif %}
{% endif %}
</li>
{% endfor %}
</ul>
{%- else -%}
<ul{{ attributes }}>
{%- for item in links -%}
<li{{ item.attributes }}>
{%- if item.link -%}
{{ item.link }}
{%- elseif item.text_attributes -%}
<span{{ item.text_attributes }}>{{ item.text }}</span>
{%- else -%}
{{ item.text }}
{%- endif -%}
</li>
{%- endfor -%}
</ul>
{%- endif -%}
{%- endif %}
......@@ -117,6 +117,8 @@ libraries-extend:
- ui_suite_bootstrap/drupal.progress
core/drupal.tabledrag:
- ui_suite_bootstrap/drupal.tabledrag
contextual/drupal.contextual-links:
- ui_suite_bootstrap/drupal.contextual-links
layout_builder/drupal.layout_builder:
- ui_suite_bootstrap/drupal.layout_builder
media_library/view:
......
......@@ -46,6 +46,17 @@ drupal.checkbox:
dependencies:
- core/drupal
drupal.contextual-links:
js:
assets/js/contextual/contextual.js: {}
css:
theme:
assets/css/contextual/contextual.dropdown.css: {}
assets/css/contextual/contextual.theme.css: {}
dependencies:
- core/drupal
- core/jquery
drupal.dialog:
js:
assets/js/misc/dialog/dialog.js: {}
......
......@@ -22,6 +22,7 @@ use Drupal\ui_suite_bootstrap\HookHandler\PreprocessFileLink;
use Drupal\ui_suite_bootstrap\HookHandler\PreprocessFilterTips;
use Drupal\ui_suite_bootstrap\HookHandler\PreprocessFormElement;
use Drupal\ui_suite_bootstrap\HookHandler\PreprocessInput;
use Drupal\ui_suite_bootstrap\HookHandler\PreprocessLinksContextual;
use Drupal\ui_suite_bootstrap\HookHandler\PreprocessLinksDropbutton;
use Drupal\ui_suite_bootstrap\HookHandler\PreprocessLinksLayoutBuilderLinks;
use Drupal\ui_suite_bootstrap\HookHandler\PreprocessLinksMediaLibraryMenu;
......@@ -191,6 +192,16 @@ function ui_suite_bootstrap_preprocess_input(array &$variables): void {
$instance->preprocess($variables);
}
/**
* Implements hook_preprocess_HOOK() for 'links__contextual'.
*/
function ui_suite_bootstrap_preprocess_links__contextual(array &$variables): void {
/** @var \Drupal\ui_suite_bootstrap\HookHandler\PreprocessLinksContextual $instance */
$instance = \Drupal::service('class_resolver')
->getInstanceFromDefinition(PreprocessLinksContextual::class);
$instance->preprocess($variables);
}
/**
* Implements hook_preprocess_HOOK() for 'links__dropbutton'.
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment