annotate core/misc/tableselect.es6.js @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 /**
Chris@0 2 * @file
Chris@0 3 * Table select functionality.
Chris@0 4 */
Chris@0 5
Chris@17 6 (function($, Drupal) {
Chris@0 7 /**
Chris@0 8 * Initialize tableSelects.
Chris@0 9 *
Chris@0 10 * @type {Drupal~behavior}
Chris@0 11 *
Chris@0 12 * @prop {Drupal~behaviorAttach} attach
Chris@0 13 * Attaches tableSelect functionality.
Chris@0 14 */
Chris@0 15 Drupal.behaviors.tableSelect = {
Chris@0 16 attach(context, settings) {
Chris@0 17 // Select the inner-most table in case of nested tables.
Chris@14 18 $(context)
Chris@14 19 .find('th.select-all')
Chris@14 20 .closest('table')
Chris@14 21 .once('table-select')
Chris@14 22 .each(Drupal.tableSelect);
Chris@0 23 },
Chris@0 24 };
Chris@0 25
Chris@0 26 /**
Chris@0 27 * Callback used in {@link Drupal.behaviors.tableSelect}.
Chris@0 28 */
Chris@17 29 Drupal.tableSelect = function() {
Chris@0 30 // Do not add a "Select all" checkbox if there are no rows with checkboxes
Chris@0 31 // in the table.
Chris@0 32 if ($(this).find('td input[type="checkbox"]').length === 0) {
Chris@0 33 return;
Chris@0 34 }
Chris@0 35
Chris@0 36 // Keep track of the table, which checkbox is checked and alias the
Chris@0 37 // settings.
Chris@0 38 const table = this;
Chris@0 39 let checkboxes;
Chris@0 40 let lastChecked;
Chris@0 41 const $table = $(table);
Chris@0 42 const strings = {
Chris@0 43 selectAll: Drupal.t('Select all rows in this table'),
Chris@0 44 selectNone: Drupal.t('Deselect all rows in this table'),
Chris@0 45 };
Chris@17 46 const updateSelectAll = function(state) {
Chris@0 47 // Update table's select-all checkbox (and sticky header's if available).
Chris@17 48 $table
Chris@17 49 .prev('table.sticky-header')
Chris@17 50 .addBack()
Chris@17 51 .find('th.select-all input[type="checkbox"]')
Chris@17 52 .each(function() {
Chris@17 53 const $checkbox = $(this);
Chris@17 54 const stateChanged = $checkbox.prop('checked') !== state;
Chris@0 55
Chris@17 56 $checkbox.attr(
Chris@17 57 'title',
Chris@17 58 state ? strings.selectNone : strings.selectAll,
Chris@17 59 );
Chris@0 60
Chris@0 61 /**
Chris@0 62 * @checkbox {HTMLElement}
Chris@0 63 */
Chris@0 64 if (stateChanged) {
Chris@17 65 $checkbox.prop('checked', state).trigger('change');
Chris@0 66 }
Chris@17 67 });
Chris@17 68 };
Chris@0 69
Chris@17 70 // Find all <th> with class select-all, and insert the check all checkbox.
Chris@17 71 $table
Chris@17 72 .find('th.select-all')
Chris@17 73 .prepend(
Chris@17 74 $('<input type="checkbox" class="form-checkbox" />').attr(
Chris@17 75 'title',
Chris@17 76 strings.selectAll,
Chris@17 77 ),
Chris@17 78 )
Chris@17 79 .on('click', event => {
Chris@17 80 if ($(event.target).is('input[type="checkbox"]')) {
Chris@17 81 // Loop through all checkboxes and set their state to the select all
Chris@17 82 // checkbox' state.
Chris@17 83 checkboxes.each(function() {
Chris@17 84 const $checkbox = $(this);
Chris@17 85 const stateChanged =
Chris@17 86 $checkbox.prop('checked') !== event.target.checked;
Chris@17 87
Chris@17 88 /**
Chris@17 89 * @checkbox {HTMLElement}
Chris@17 90 */
Chris@17 91 if (stateChanged) {
Chris@17 92 $checkbox.prop('checked', event.target.checked).trigger('change');
Chris@17 93 }
Chris@17 94 // Either add or remove the selected class based on the state of the
Chris@17 95 // check all checkbox.
Chris@17 96
Chris@17 97 /**
Chris@17 98 * @checkbox {HTMLElement}
Chris@17 99 */
Chris@17 100 $checkbox.closest('tr').toggleClass('selected', this.checked);
Chris@17 101 });
Chris@17 102 // Update the title and the state of the check all box.
Chris@17 103 updateSelectAll(event.target.checked);
Chris@17 104 }
Chris@17 105 });
Chris@0 106
Chris@0 107 // For each of the checkboxes within the table that are not disabled.
Chris@17 108 checkboxes = $table
Chris@17 109 .find('td input[type="checkbox"]:enabled')
Chris@17 110 .on('click', function(e) {
Chris@17 111 // Either add or remove the selected class based on the state of the
Chris@17 112 // check all checkbox.
Chris@0 113
Chris@17 114 /**
Chris@17 115 * @this {HTMLElement}
Chris@17 116 */
Chris@17 117 $(this)
Chris@17 118 .closest('tr')
Chris@17 119 .toggleClass('selected', this.checked);
Chris@0 120
Chris@17 121 // If this is a shift click, we need to highlight everything in the
Chris@17 122 // range. Also make sure that we are actually checking checkboxes
Chris@17 123 // over a range and that a checkbox has been checked or unchecked before.
Chris@17 124 if (e.shiftKey && lastChecked && lastChecked !== e.target) {
Chris@17 125 // We use the checkbox's parent <tr> to do our range searching.
Chris@17 126 Drupal.tableSelectRange(
Chris@17 127 $(e.target).closest('tr')[0],
Chris@17 128 $(lastChecked).closest('tr')[0],
Chris@17 129 e.target.checked,
Chris@17 130 );
Chris@17 131 }
Chris@0 132
Chris@17 133 // If all checkboxes are checked, make sure the select-all one is checked
Chris@17 134 // too, otherwise keep unchecked.
Chris@17 135 updateSelectAll(
Chris@17 136 checkboxes.length === checkboxes.filter(':checked').length,
Chris@17 137 );
Chris@0 138
Chris@17 139 // Keep track of the last checked checkbox.
Chris@17 140 lastChecked = e.target;
Chris@17 141 });
Chris@0 142
Chris@0 143 // If all checkboxes are checked on page load, make sure the select-all one
Chris@0 144 // is checked too, otherwise keep unchecked.
Chris@17 145 updateSelectAll(checkboxes.length === checkboxes.filter(':checked').length);
Chris@0 146 };
Chris@0 147
Chris@0 148 /**
Chris@0 149 * @param {HTMLElement} from
Chris@0 150 * The HTML element representing the "from" part of the range.
Chris@0 151 * @param {HTMLElement} to
Chris@0 152 * The HTML element representing the "to" part of the range.
Chris@0 153 * @param {bool} state
Chris@0 154 * The state to set on the range.
Chris@0 155 */
Chris@17 156 Drupal.tableSelectRange = function(from, to, state) {
Chris@0 157 // We determine the looping mode based on the order of from and to.
Chris@17 158 const mode =
Chris@17 159 from.rowIndex > to.rowIndex ? 'previousSibling' : 'nextSibling';
Chris@0 160
Chris@0 161 // Traverse through the sibling nodes.
Chris@0 162 for (let i = from[mode]; i; i = i[mode]) {
Chris@14 163 const $i = $(i);
Chris@0 164 // Make sure that we're only dealing with elements.
Chris@0 165 if (i.nodeType !== 1) {
Chris@0 166 continue;
Chris@0 167 }
Chris@0 168 // Either add or remove the selected class based on the state of the
Chris@0 169 // target checkbox.
Chris@0 170 $i.toggleClass('selected', state);
Chris@0 171 $i.find('input[type="checkbox"]').prop('checked', state);
Chris@0 172
Chris@0 173 if (to.nodeType) {
Chris@0 174 // If we are at the end of the range, stop.
Chris@0 175 if (i === to) {
Chris@0 176 break;
Chris@0 177 }
Chris@0 178 }
Chris@0 179 // A faster alternative to doing $(i).filter(to).length.
Chris@0 180 else if ($.filter(to, [i]).r.length) {
Chris@0 181 break;
Chris@0 182 }
Chris@0 183 }
Chris@0 184 };
Chris@17 185 })(jQuery, Drupal);