diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 60dbf55eae..7f9236fd40 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1226,6 +1226,13 @@ function template_preprocess_splitbutton_item_list(array &$variables) {
*/
function template_preprocess_splitbutton(array &$variables) {
$variables['toggle_attributes'] = new Attribute($variables['toggle_attributes']);
+
+ // If the main element is present, ensure there is an attributes array with a
+ // class property. This facilitates the addition of classes to the main
+ // element within templates.
+ if (!empty($variables['main_element']) && !isset($variables['main_element']['#attributes']['class'])) {
+ $variables['main_element']['#attributes']['class'] = [];
+ }
}
/**
diff --git a/core/lib/Drupal/Core/Render/Element/Splitbutton.php b/core/lib/Drupal/Core/Render/Element/Splitbutton.php
index 85d3f1d2ca..226344187a 100644
--- a/core/lib/Drupal/Core/Render/Element/Splitbutton.php
+++ b/core/lib/Drupal/Core/Render/Element/Splitbutton.php
@@ -14,9 +14,9 @@
* button. All other elements will be filtered out. Elements extending this
* class can change the items that are filtered by overriding filterItems().
* - #splitbutton_type: A string or an array or strings defining a type of
- * dropbutton variant for styling purposes. This adds the class
- * `splitbutton--#splitbutton_type` to the splitbutton wrapper and the class
- * `button--#splitbutton_type` to the primary and toggle buttons.
+ * dropbutton variant for styling purposes. This is used in some themes to add
+ * the class `splitbutton--#splitbutton_type` to the splitbutton wrapper and
+ * `button--#splitbutton_type` to the main and toggle buttons.
* - #title: This changes the default splitbutton behavior of displaying a
* primary splitbutton item next a separate toggle button. When this property
* is present, there is no primary item, just a toggle.
@@ -91,7 +91,6 @@ public static function preRenderSplitbutton(array $element) {
if (!isset($element['#title'])) {
$first_item = array_shift($items);
$element['#main_element'] = $first_item;
- static::addMainElementClasses($element, $first_item['#type']);
$element['#toggle_attributes']['#attributes']['aria-label'] = t('List additional actions');
}
@@ -99,9 +98,11 @@ public static function preRenderSplitbutton(array $element) {
if (count($items)) {
static::buildItemList($element, $items);
$element['#splitbutton_multiple'] = TRUE;
+ $element['#attributes']['data-drupal-splitbutton-multiple'] = '';
}
else {
$element['#splitbutton_multiple'] = FALSE;
+ $element['#attributes']['data-drupal-splitbutton-single'] = '';
}
return $element;
@@ -112,6 +113,9 @@ public static function preRenderSplitbutton(array $element) {
*
* @param array $element
* The render element.
+ *
+ * @return array
+ * An array of splitbutton list items.
*/
public static function collectItems(array $element) {
$items = $element['#splitbutton_items'] ?? [];
@@ -147,7 +151,6 @@ public static function collectItems(array $element) {
public static function buildToggleAttributes(array &$element) {
$trigger_id = $element['#trigger_id'];
$element['#toggle_attributes'] = [
- 'class' => ['button', 'splitbutton__toggle'],
'type' => 'button',
'role' => 'button',
'aria-haspopup' => 'true',
@@ -170,9 +173,6 @@ public static function buildItemList(array &$element, array $items) {
$trigger_id = $element['#trigger_id'];
foreach ($items as &$item) {
- // Classes must be added here instead of in templates as the item could
- // be one of several different render element types.
- $item['#attributes']['class'][] = 'splitbutton__operation-list-item';
$item['#attributes']['role'] = 'menuitem';
$item['#attributes']['tabindex'] = '-1';
$item['#wrapper_attributes']['role'] = 'none';
@@ -186,19 +186,16 @@ public static function buildItemList(array &$element, array $items) {
'role' => 'menu',
'aria-labelledby' => "$trigger_id-trigger",
'id' => "$trigger_id-menu",
+ 'data-drupal-splitbutton-item-list' => '',
],
];
}
/**
- * Filters unsupported element types from a splitbutton item list.
+ * Checks for unsupported element types in a splitbutton item list.
*
* @param array $items
* The splitbutton list items.
- *
- * @return array
- * An array of only immediate children that match specific render element
- * types.
*/
public static function filterItems(array $items) {
$allowed_types = ['submit', 'button', 'link'];
@@ -209,35 +206,4 @@ public static function filterItems(array $items) {
}
}
- /**
- * Adds classes to the main splitbutton input.
- *
- * By default, the main splitbutton input is styled as a button, regardless of
- * its element type. Elements that extend splitbutton may wish to style these
- * elements differently, so this is available as a targeted, overridable
- * function.
- *
- * @param array $element
- * The splitbutton render array.
- * @param null|string $modifier
- * A modifier that can be used for adding additional classes.
- */
- public static function addMainElementClasses(array &$element, $modifier = NULL) {
- // Classes must be added here instead of templates as the main button can be
- // be one of several render element types.
- $element['#main_element']['#attributes']['class'][] = 'splitbutton__main-button';
- if (!empty($modifier)) {
- $element['#main_element']['#attributes']['class'][] = 'splitbutton__main-button--' . $modifier;
- }
-
- // The main splitbutton element will always be styled as a button,
- // regardless of its actual type.
- $element['#main_element']['#attributes']['class'][] = 'button';
-
- // Add variant classes based on #splitbutton_type to the main button.
- foreach ($element['#variants'] as $variant) {
- $element['#main_element']['#attributes']['class'][] = 'button--' . $variant;
- }
- }
-
}
diff --git a/core/misc/splitbutton/splitbutton-init.es6.js b/core/misc/splitbutton/splitbutton-init.es6.js
index 97ad2cf4b6..60eec711f7 100644
--- a/core/misc/splitbutton/splitbutton-init.es6.js
+++ b/core/misc/splitbutton/splitbutton-init.es6.js
@@ -15,7 +15,7 @@
Drupal.behaviors.splitButton = {
attach(context) {
const $splitbuttons = $(context)
- .find('.js-splitbutton-multiple')
+ .find('[data-drupal-splitbutton-multiple]')
.once('splitbutton');
if ($splitbuttons.length) {
for (let i = 0; i < $splitbuttons.length; i++) {
diff --git a/core/misc/splitbutton/splitbutton-init.js b/core/misc/splitbutton/splitbutton-init.js
index 5824d774c4..9bbcaff993 100644
--- a/core/misc/splitbutton/splitbutton-init.js
+++ b/core/misc/splitbutton/splitbutton-init.js
@@ -8,7 +8,7 @@
(function ($, Drupal) {
Drupal.behaviors.splitButton = {
attach: function attach(context) {
- var $splitbuttons = $(context).find('.js-splitbutton-multiple').once('splitbutton');
+ var $splitbuttons = $(context).find('[data-drupal-splitbutton-multiple]').once('splitbutton');
if ($splitbuttons.length) {
for (var i = 0; i < $splitbuttons.length; i++) {
diff --git a/core/misc/splitbutton/splitbutton.css b/core/misc/splitbutton/splitbutton.css
index a693cc4499..f12706d860 100644
--- a/core/misc/splitbutton/splitbutton.css
+++ b/core/misc/splitbutton/splitbutton.css
@@ -1,46 +1,20 @@
-.splitbutton {
- box-sizing: border-box;
-}
-
-.splitbutton__operation-list {
- margin: 0;
- padding: 0;
- list-style: none;
-}
-
-.splitbutton__main {
+[data-drupal-splitbutton-main] {
position: relative;
display: inline-flex;
- font-size: 0.889rem;
}
-
-.splitbutton__main .button {
- margin: 0;
-}
-
-.js .splitbutton__operation-list {
+.js [data-drupal-splitbutton-item-list] {
display: none;
}
-.js .splitbutton--enabled.open .splitbutton__operation-list {
+[data-drupal-splitbutton-open] [data-drupal-splitbutton-item-list] {
z-index: 4;
display: block;
}
-.splitbutton__operation-list-item {
- display: block;
- padding: 0;
- border: none;
- background: #fff;
-}
-.splitbutton__operation-list-item:hover {
- text-decoration: none;
-}
-
-.splitbutton__toggle {
+[data-drupal-splitbutton-trigger] {
position: relative;
}
-.splitbutton__toggle::after {
+[data-drupal-splitbutton-trigger]::after {
position: absolute;
top: 50%;
right: 6px; /* LTR */
@@ -54,24 +28,15 @@
border-bottom-color: transparent;
border-left-color: transparent;
}
-[dir="rtl"] .splitbutton__toggle::after {
+[dir="rtl"] [data-drupal-splitbutton-trigger]::after {
right: auto;
left: 6px;
}
-.splitbutton.open .splitbutton__toggle::after {
+[data-drupal-splitbutton-open] [data-drupal-splitbutton-trigger]::after {
top: 48%;
transform: translate(50%, -50%) rotate(180deg);
}
-.splitbutton__toggle--with-title {
- position: relative;
- padding-right: 15px; /* LTR */
-}
-[dir="rtl"] .splitbutton__toggle--with-title {
- padding-right: 6px;
- padding-left: 15px;
-}
-
-html:not(.js) .splitbutton__toggle {
+html:not(.js) [data-drupal-splitbutton-trigger] {
display: none;
}
diff --git a/core/misc/splitbutton/splitbutton.es6.js b/core/misc/splitbutton/splitbutton.es6.js
index 80f1cf8c47..1a7aa60912 100644
--- a/core/misc/splitbutton/splitbutton.es6.js
+++ b/core/misc/splitbutton/splitbutton.es6.js
@@ -6,10 +6,7 @@
((Drupal, Popper) => {
Drupal.SplitButton = class {
constructor(splitbutton) {
- splitbutton.classList.add(
- 'splitbutton--enabled',
- 'js-splitbutton-enabled',
- );
+ splitbutton.setAttribute('data-drupal-splitbutton-enabled', '');
this.keyCode = Object.freeze({
TAB: 9,
@@ -33,7 +30,9 @@
'[data-drupal-splitbutton-trigger]',
);
this.menu = splitbutton.querySelector('[data-drupal-splitbutton-target]');
- this.triggerContainer = splitbutton.querySelector('.splitbutton__main');
+ this.triggerContainer = splitbutton.querySelector(
+ '[data-drupal-splitbutton-main]',
+ );
this.splitbutton.addEventListener('mouseenter', () => this.activeIn());
this.splitbutton.addEventListener('focusin', () => this.activeIn());
@@ -56,6 +55,7 @@
.forEach((item, index) => {
// Add attribute to each item to identify its focus order.
item.setAttribute('data-drupal-splitbutton-item', index);
+ item.classList.add('splitbutton__operation-list-item');
this.menuItems.push(item);
const itemText =
@@ -106,7 +106,9 @@
clickToggle(e) {
e.preventDefault();
e.stopPropagation();
- const state = this.splitbutton.classList.contains('open')
+ const state = this.splitbutton.hasAttribute(
+ 'data-drupal-splitbutton-open',
+ )
? 'close'
: 'open';
this[state]();
@@ -120,9 +122,15 @@
*/
toggle(show) {
const isBool = typeof show === 'boolean';
- show = isBool ? show : !this.splitbutton.classList.contains('open');
+ show = isBool
+ ? show
+ : !this.splitbutton.hasAttribute('data-drupal-splitbutton-open');
const expanded = show ? 'true' : 'false';
- this.splitbutton.classList.toggle('open', show);
+ if (show) {
+ this.splitbutton.setAttribute('data-drupal-splitbutton-open', '');
+ } else {
+ this.splitbutton.removeAttribute('data-drupal-splitbutton-open');
+ }
this.trigger.setAttribute('aria-expanded', expanded);
}
@@ -201,7 +209,7 @@
break;
case this.keyCode.UP:
- if (this.splitbutton.classList.contains('open')) {
+ if (this.splitbutton.hasAttribute('data-drupal-splitbutton-open')) {
this.focusPrev(e);
} else {
this.open();
@@ -210,7 +218,7 @@
break;
case this.keyCode.DOWN:
- if (this.splitbutton.classList.contains('open')) {
+ if (this.splitbutton.hasAttribute('data-drupal-splitbutton-open')) {
this.focusNext(e);
} else {
this.open();
@@ -238,7 +246,7 @@
if (
char.length === 1 &&
char.match(/\S/) &&
- this.splitbutton.classList.contains('open')
+ this.splitbutton.hasAttribute('data-drupal-splitbutton-open')
) {
this.setFocusByFirstCharacter(e, char.toLowerCase());
}
diff --git a/core/misc/splitbutton/splitbutton.js b/core/misc/splitbutton/splitbutton.js
index cc3e6e68bd..a854337937 100644
--- a/core/misc/splitbutton/splitbutton.js
+++ b/core/misc/splitbutton/splitbutton.js
@@ -18,7 +18,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
_classCallCheck(this, _class);
- splitbutton.classList.add('splitbutton--enabled', 'js-splitbutton-enabled');
+ splitbutton.setAttribute('data-drupal-splitbutton-enabled', '');
this.keyCode = Object.freeze({
TAB: 9,
RETURN: 13,
@@ -38,7 +38,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
this.splitbutton = splitbutton;
this.trigger = splitbutton.querySelector('[data-drupal-splitbutton-trigger]');
this.menu = splitbutton.querySelector('[data-drupal-splitbutton-target]');
- this.triggerContainer = splitbutton.querySelector('.splitbutton__main');
+ this.triggerContainer = splitbutton.querySelector('[data-drupal-splitbutton-main]');
this.splitbutton.addEventListener('mouseenter', function () {
return _this.activeIn();
});
@@ -68,6 +68,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
var itemTags = this.menu.getAttribute('data-drupal-item-tags') || 'a, input, button';
Array.prototype.slice.call(this.menu.querySelectorAll(itemTags)).forEach(function (item, index) {
item.setAttribute('data-drupal-splitbutton-item', index);
+ item.classList.add('splitbutton__operation-list-item');
_this2.menuItems.push(item);
@@ -98,16 +99,22 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
value: function clickToggle(e) {
e.preventDefault();
e.stopPropagation();
- var state = this.splitbutton.classList.contains('open') ? 'close' : 'open';
+ var state = this.splitbutton.hasAttribute('data-drupal-splitbutton-open') ? 'close' : 'open';
this[state]();
}
}, {
key: "toggle",
value: function toggle(show) {
var isBool = typeof show === 'boolean';
- show = isBool ? show : !this.splitbutton.classList.contains('open');
+ show = isBool ? show : !this.splitbutton.hasAttribute('data-drupal-splitbutton-open');
var expanded = show ? 'true' : 'false';
- this.splitbutton.classList.toggle('open', show);
+
+ if (show) {
+ this.splitbutton.setAttribute('data-drupal-splitbutton-open', '');
+ } else {
+ this.splitbutton.removeAttribute('data-drupal-splitbutton-open');
+ }
+
this.trigger.setAttribute('aria-expanded', expanded);
}
}, {
@@ -175,7 +182,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
break;
case this.keyCode.UP:
- if (this.splitbutton.classList.contains('open')) {
+ if (this.splitbutton.hasAttribute('data-drupal-splitbutton-open')) {
this.focusPrev(e);
} else {
this.open();
@@ -185,11 +192,9 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
break;
case this.keyCode.DOWN:
- if (this.splitbutton.classList.contains('open')) {
- console.log('focusnext');
+ if (this.splitbutton.hasAttribute('data-drupal-splitbutton-open')) {
this.focusNext(e);
} else {
- console.log('open, then focus first');
this.open();
this.focusFirst();
}
@@ -214,7 +219,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
default:
preventDefault = false;
- if (char.length === 1 && char.match(/\S/) && this.splitbutton.classList.contains('open')) {
+ if (char.length === 1 && char.match(/\S/) && this.splitbutton.hasAttribute('data-drupal-splitbutton-open')) {
this.setFocusByFirstCharacter(e, char.toLowerCase());
}
diff --git a/core/modules/system/templates/splitbutton-item-list.html.twig b/core/modules/system/templates/splitbutton-item-list.html.twig
index 8c5a367de4..40483e1359 100644
--- a/core/modules/system/templates/splitbutton-item-list.html.twig
+++ b/core/modules/system/templates/splitbutton-item-list.html.twig
@@ -8,7 +8,6 @@
* - attributes: HTML attributes to be applied to each list item.
* - value: The content of the list element.
* - list_type: The tag for list element ("ul" or "ol").
- * - wrapper_attributes: HTML attributes to be applied to the list wrapper.
* - attributes: HTML attributes to be applied to the list.
*
* @see template_preprocess_splitbutton_item_list()
@@ -17,9 +16,9 @@
*/
#}
{% if items %}
- <{{ list_type }}{{ attributes.addClass('splitbutton__operation-list') }}>
- {%- for item in items -%}
-
{{ item.value }}
- {%- endfor -%}
+ <{{ list_type }}{{ attributes }}>
+ {%- for item in items -%}
+ {{ item.value }}
+ {%- endfor -%}
{{ list_type }}>
{%- endif %}
diff --git a/core/modules/system/templates/splitbutton.html.twig b/core/modules/system/templates/splitbutton.html.twig
index 1b38717184..b074c137ab 100644
--- a/core/modules/system/templates/splitbutton.html.twig
+++ b/core/modules/system/templates/splitbutton.html.twig
@@ -23,29 +23,13 @@
*
* @ingroup themeable
*/
-#}{%
- set classes = [
- 'splitbutton',
- 'js-splitbutton',
- splitbutton_multiple == true ? 'splitbutton--multiple' : 'splitbutton--single',
- splitbutton_multiple ? 'js-splitbutton-multiple',
- ]
-%}
-{%
- set button_classes = [
- title ? 'splitbutton__toggle--with-title' : 'splitbutton__toggle--no-title',
- ]
-%}
-{% for variant in variants %}
- {% set classes = classes|merge(['splitbutton--' ~ variant]) %}
- {% set button_classes = button_classes|merge(['button--' ~ variant]) %}
-{% endfor %}
+#}
-
-
+
+
{{ main_element }}
{% if splitbutton_multiple and exclude_toggle == FALSE %}
-