drupal/core/themes/claro/js/tabledrag.es6.js

248 lines
9.1 KiB
JavaScript

/**
* @file
* tabledrag.js overrides and functionality extensions.
*/
(($, Drupal) => {
/**
* Extends core's Tabledrag functionality.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.claroTableDrag = {
attach(context, settings) {
/**
* Refactors the table row markup to improve item label text wrapping.
*
* This addresses an issue specific to item labels that are long enough
* to be wrapped to a new line. Without this fix, a new line may start
* at the beginning of the table row, instead of the expected behavior of
* starting at the x axis of the first line.
*
* Addressing this issue requires changing the structure of a tabledrag
* cell's first row.
* @example
* <!-- Default tabledrag structure, which has the wrapping problem. -->
* <tr class="draggable">
* <td>
* <!--
* Indentations are next to each other because they are styled as
* `float: left;`
* -->
* <div class="indentation"></div>
* <div class="indentation"></div>
* <a class="tabledrag-handle"></a>
* <!-- If the text in this link wraps enough times that the element
* is taller than the floated elements preceding it, some lines
* will wrap to the beginning of the row instead of aligning with
* the beginning of the link text.
* -->
* <a class="menu-item__link">A longer label that may require wrapping</a>
* </td>
* <!-- etc. -->
* </tr>
* @example
* <!-- Claro tabledrag structure, this fixes the wrapping problem. -->
* <tr class="draggable">
* <td class="tabledrag-cell">
* <div class="tabledrag-cell-content">
* <!-- Indentations are next to each other because
* .table-drag-cell-content is styled as `display: table-row;`
* and .table-drag-cell-content > * is styled as
* `display: table-cell;`
* -->
* <div class="indentation"></div>
* <div class="indentation"></div>
* <a class="tabledrag-handle"></a>
* <div class="tabledrag-cell-content__item">
* <!-- Placing the link inside a div styled as
* `display: table-cell;` allows the text to wrap within
* the boundaries of the "cell".
* -->
* <a class="menu-item__link">A longer label that may require wrapping</a>
* </div>
* </div>
* </td>
* <!-- additional <td> -->
* </tr>
*
* @param {number} index
* The index in the loop, as provided by `jQuery.each`.
* @param {HTMLElement} row
* A draggable table row.
*
* @todo this may be removable as part of https://drupal.org/node/3083044
*/
const createItemWrapBoundaries = (row) => {
const $row = $(row);
const $firstCell = $row
.find('td:first-of-type')
.eq(0)
.wrapInner(Drupal.theme('tableDragCellContentWrapper'))
.wrapInner(
$(Drupal.theme('tableDragCellItemsWrapper')).addClass(
'js-tabledrag-cell-content',
),
);
const $targetElem = $firstCell.find('.js-tabledrag-cell-content');
// Move handle into the '.js-tabledrag-cell-content' target.
$targetElem
.eq(0)
.find(
'> .tabledrag-cell-content__item > .js-tabledrag-handle, > .tabledrag-cell-content__item > .js-indentation',
)
.prependTo($targetElem);
};
// Find each row in a draggable table and process it with
// createItemWrapBoundaries().
Object.keys(settings.tableDrag || {}).forEach((base) => {
once(
'claroTabledrag',
$(context)
.find(`#${base}`)
.find('> tr.draggable, > tbody > tr.draggable'),
).forEach(createItemWrapBoundaries);
});
},
};
$.extend(Drupal.tableDrag.prototype.row.prototype, {
/**
* Add an asterisk or other marker to the changed row.
*
* @todo this may be removable as part of https://drupal.org/node/3084910
*/
markChanged() {
const marker = $(Drupal.theme('tableDragChangedMarker')).addClass(
'js-tabledrag-changed-marker',
);
const cell = $(this.element).find('td:first-of-type');
if (cell.find('.js-tabledrag-changed-marker').length === 0) {
cell.find('.js-tabledrag-handle').after(marker);
}
},
/**
* Moves added indents into Claro's wrapper element.
*
* For indents to work properly, they must be inside the wrapper
* created by createItemWrapBoundaries(). When an indent is added via
* dragging, core's tabledrag functionality does not add it inside the
* wrapper. This function fires immediately after an indent is added, which
* moves the indent into that wrapper.
*
* @see Drupal.tableDrag.prototype.row.prototype.indent
*
* @todo this may be removable as part of https://drupal.org/node/3083044
*/
onIndent() {
$(this.table)
.find('.tabledrag-cell > .js-indentation')
.each((index, indentToMove) => {
const $indentToMove = $(indentToMove);
const $cellContent = $indentToMove.siblings(
'.tabledrag-cell-content',
);
$indentToMove.prependTo($cellContent);
});
},
});
$.extend(
Drupal.theme,
/** @lends Drupal.theme */ {
/**
* Constructs the table drag changed marker.
*
* @return {string}
* Markup for the indentation.
*/
tableDragIndentation() {
return '<div class="js-indentation indentation"><svg xmlns="http://www.w3.org/2000/svg" class="tree" width="25" height="25" viewBox="0 0 25 25"><path class="tree__item tree__item-child-ltr tree__item-child-last-ltr tree__item-horizontal tree__item-horizontal-right" d="M12,12.5 H25" stroke="#888"/><path class="tree__item tree__item-child-rtl tree__item-child-last-rtl tree__item-horizontal tree__horizontal-left" d="M0,12.5 H13" stroke="#888"/><path class="tree__item tree__item-child-ltr tree__item-child-rtl tree__item-child-last-ltr tree__item-child-last-rtl tree__vertical tree__vertical-top" d="M12.5,12 v-99" stroke="#888"/><path class="tree__item tree__item-child-ltr tree__item-child-rtl tree__vertical tree__vertical-bottom" d="M12.5,12 v99" stroke="#888"/></svg></div>';
},
/**
* Constructs the table drag changed warning.
*
* @return {string}
* Markup for the warning.
*/
tableDragChangedWarning() {
return `<div class="tabledrag-changed-warning messages messages--warning" role="alert">${Drupal.theme(
'tableDragChangedMarker',
)} ${Drupal.t('You have unsaved changes.')}</div>`;
},
/**
* Constructs the table drag handle.
*
* @return {string}
* A string representing a DOM fragment.
*/
tableDragHandle: () =>
`<a href="#" title="${Drupal.t(
'Drag to re-order',
)}" class="tabledrag-handle js-tabledrag-handle"></a>`,
/**
* The button for toggling table row weight visibility.
*
* @return {string}
* HTML markup for the weight toggle button and its container.
*/
tableDragToggle: () =>
`<div class="tabledrag-toggle-weight-wrapper" data-drupal-selector="tabledrag-toggle-weight-wrapper">
<button type="button" class="link action-link tabledrag-toggle-weight" data-drupal-selector="tabledrag-toggle-weight"></button>
</div>`,
/**
* Constructs contents of the toggle weight button.
*
* @param {boolean} show
* If the table weights are currently displayed.
*
* @return {string}
* HTML markup for the weight toggle button content.
*/
toggleButtonContent: (show) => {
const classes = [
'action-link',
'action-link--extrasmall',
'tabledrag-toggle-weight',
];
let text = '';
if (show) {
classes.push('action-link--icon-hide');
text = Drupal.t('Hide row weights');
} else {
classes.push('action-link--icon-show');
text = Drupal.t('Show row weights');
}
return `<span class="${classes.join(' ')}">${text}</a>`;
},
/**
* Constructs the wrapper for the initial content of the drag cell.
*
* @return {string}
* A string representing a DOM fragment.
*/
tableDragCellContentWrapper() {
return '<div class="tabledrag-cell-content__item"></div>';
},
/**
* Constructs the wrapper for the whole table drag cell.
*
* @return {string}
* A string representing a DOM fragment.
*/
tableDragCellItemsWrapper() {
return '<div class="tabledrag-cell-content"></div>';
},
},
);
})(jQuery, Drupal);