annotate core/modules/quickedit/js/views/EntityToolbarView.es6.js @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents a9cd425dd02b
children
rev   line source
Chris@0 1 /**
Chris@0 2 * @file
Chris@0 3 * A Backbone View that provides an entity level toolbar.
Chris@0 4 */
Chris@0 5
Chris@4 6 (function($, _, Backbone, Drupal, debounce) {
Chris@4 7 Drupal.quickedit.EntityToolbarView = Backbone.View.extend(
Chris@4 8 /** @lends Drupal.quickedit.EntityToolbarView# */ {
Chris@4 9 /**
Chris@4 10 * @type {jQuery}
Chris@4 11 */
Chris@4 12 _fieldToolbarRoot: null,
Chris@0 13
Chris@4 14 /**
Chris@4 15 * @return {object}
Chris@4 16 * A map of events.
Chris@4 17 */
Chris@4 18 events() {
Chris@4 19 const map = {
Chris@4 20 'click button.action-save': 'onClickSave',
Chris@4 21 'click button.action-cancel': 'onClickCancel',
Chris@4 22 mouseenter: 'onMouseenter',
Chris@4 23 };
Chris@4 24 return map;
Chris@4 25 },
Chris@0 26
Chris@4 27 /**
Chris@4 28 * @constructs
Chris@4 29 *
Chris@4 30 * @augments Backbone.View
Chris@4 31 *
Chris@4 32 * @param {object} options
Chris@4 33 * Options to construct the view.
Chris@4 34 * @param {Drupal.quickedit.AppModel} options.appModel
Chris@4 35 * A quickedit `AppModel` to use in the view.
Chris@4 36 */
Chris@4 37 initialize(options) {
Chris@4 38 const that = this;
Chris@4 39 this.appModel = options.appModel;
Chris@4 40 this.$entity = $(this.model.get('el'));
Chris@0 41
Chris@4 42 // Rerender whenever the entity state changes.
Chris@4 43 this.listenTo(
Chris@4 44 this.model,
Chris@4 45 'change:isActive change:isDirty change:state',
Chris@4 46 this.render,
Chris@4 47 );
Chris@4 48 // Also rerender whenever a different field is highlighted or activated.
Chris@4 49 this.listenTo(
Chris@4 50 this.appModel,
Chris@4 51 'change:highlightedField change:activeField',
Chris@4 52 this.render,
Chris@4 53 );
Chris@4 54 // Rerender when a field of the entity changes state.
Chris@4 55 this.listenTo(
Chris@4 56 this.model.get('fields'),
Chris@4 57 'change:state',
Chris@4 58 this.fieldStateChange,
Chris@4 59 );
Chris@0 60
Chris@4 61 // Reposition the entity toolbar as the viewport and the position within
Chris@4 62 // the viewport changes.
Chris@4 63 $(window).on(
Chris@4 64 'resize.quickedit scroll.quickedit drupalViewportOffsetChange.quickedit',
Chris@4 65 debounce($.proxy(this.windowChangeHandler, this), 150),
Chris@4 66 );
Chris@0 67
Chris@4 68 // Adjust the fence placement within which the entity toolbar may be
Chris@4 69 // positioned.
Chris@4 70 $(document).on(
Chris@4 71 'drupalViewportOffsetChange.quickedit',
Chris@4 72 (event, offsets) => {
Chris@4 73 if (that.$fence) {
Chris@4 74 that.$fence.css(offsets);
Chris@4 75 }
Chris@4 76 },
Chris@4 77 );
Chris@0 78
Chris@4 79 // Set the entity toolbar DOM element as the el for this view.
Chris@4 80 const $toolbar = this.buildToolbarEl();
Chris@4 81 this.setElement($toolbar);
Chris@4 82 this._fieldToolbarRoot = $toolbar
Chris@4 83 .find('.quickedit-toolbar-field')
Chris@4 84 .get(0);
Chris@4 85
Chris@4 86 // Initial render.
Chris@4 87 this.render();
Chris@4 88 },
Chris@4 89
Chris@4 90 /**
Chris@4 91 * @inheritdoc
Chris@4 92 *
Chris@4 93 * @return {Drupal.quickedit.EntityToolbarView}
Chris@4 94 * The entity toolbar view.
Chris@4 95 */
Chris@4 96 render() {
Chris@4 97 if (this.model.get('isActive')) {
Chris@4 98 // If the toolbar container doesn't exist, create it.
Chris@4 99 const $body = $('body');
Chris@4 100 if ($body.children('#quickedit-entity-toolbar').length === 0) {
Chris@4 101 $body.append(this.$el);
Chris@4 102 }
Chris@4 103 // The fence will define a area on the screen that the entity toolbar
Chris@4 104 // will be position within.
Chris@4 105 if ($body.children('#quickedit-toolbar-fence').length === 0) {
Chris@4 106 this.$fence = $(Drupal.theme('quickeditEntityToolbarFence'))
Chris@4 107 .css(Drupal.displace())
Chris@4 108 .appendTo($body);
Chris@4 109 }
Chris@4 110 // Adds the entity title to the toolbar.
Chris@4 111 this.label();
Chris@4 112
Chris@4 113 // Show the save and cancel buttons.
Chris@4 114 this.show('ops');
Chris@4 115 // If render is being called and the toolbar is already visible, just
Chris@4 116 // reposition it.
Chris@4 117 this.position();
Chris@0 118 }
Chris@0 119
Chris@4 120 // The save button text and state varies with the state of the entity
Chris@4 121 // model.
Chris@4 122 const $button = this.$el.find('.quickedit-button.action-save');
Chris@4 123 const isDirty = this.model.get('isDirty');
Chris@4 124 // Adjust the save button according to the state of the model.
Chris@4 125 switch (this.model.get('state')) {
Chris@4 126 // Quick editing is active, but no field is being edited.
Chris@4 127 case 'opened':
Chris@4 128 // The saving throbber is not managed by AJAX system. The
Chris@4 129 // EntityToolbarView manages this visual element.
Chris@4 130 $button
Chris@4 131 .removeClass('action-saving icon-throbber icon-end')
Chris@4 132 .text(Drupal.t('Save'))
Chris@4 133 .removeAttr('disabled')
Chris@4 134 .attr('aria-hidden', !isDirty);
Chris@0 135 break;
Chris@0 136
Chris@4 137 // The changes to the fields of the entity are being committed.
Chris@4 138 case 'committing':
Chris@4 139 $button
Chris@4 140 .addClass('action-saving icon-throbber icon-end')
Chris@4 141 .text(Drupal.t('Saving'))
Chris@4 142 .attr('disabled', 'disabled');
Chris@0 143 break;
Chris@0 144
Chris@4 145 default:
Chris@4 146 $button.attr('aria-hidden', true);
Chris@4 147 break;
Chris@4 148 }
Chris@4 149
Chris@4 150 return this;
Chris@4 151 },
Chris@4 152
Chris@4 153 /**
Chris@4 154 * @inheritdoc
Chris@4 155 */
Chris@4 156 remove() {
Chris@4 157 // Remove additional DOM elements controlled by this View.
Chris@4 158 this.$fence.remove();
Chris@4 159
Chris@4 160 // Stop listening to additional events.
Chris@4 161 $(window).off(
Chris@4 162 'resize.quickedit scroll.quickedit drupalViewportOffsetChange.quickedit',
Chris@4 163 );
Chris@4 164 $(document).off('drupalViewportOffsetChange.quickedit');
Chris@4 165
Chris@4 166 Backbone.View.prototype.remove.call(this);
Chris@4 167 },
Chris@4 168
Chris@4 169 /**
Chris@4 170 * Repositions the entity toolbar on window scroll and resize.
Chris@4 171 *
Chris@4 172 * @param {jQuery.Event} event
Chris@4 173 * The scroll or resize event.
Chris@4 174 */
Chris@4 175 windowChangeHandler(event) {
Chris@4 176 this.position();
Chris@4 177 },
Chris@4 178
Chris@4 179 /**
Chris@4 180 * Determines the actions to take given a change of state.
Chris@4 181 *
Chris@4 182 * @param {Drupal.quickedit.FieldModel} model
Chris@4 183 * The `FieldModel` model.
Chris@4 184 * @param {string} state
Chris@4 185 * The state of the associated field. One of
Chris@4 186 * {@link Drupal.quickedit.FieldModel.states}.
Chris@4 187 */
Chris@4 188 fieldStateChange(model, state) {
Chris@4 189 switch (state) {
Chris@4 190 case 'active':
Chris@4 191 this.render();
Chris@0 192 break;
Chris@0 193
Chris@4 194 case 'invalid':
Chris@4 195 this.render();
Chris@0 196 break;
Chris@0 197 }
Chris@4 198 },
Chris@0 199
Chris@0 200 /**
Chris@4 201 * Uses the jQuery.ui.position() method to position the entity toolbar.
Chris@0 202 *
Chris@4 203 * @param {HTMLElement} [element]
Chris@4 204 * The element against which the entity toolbar is positioned.
Chris@0 205 */
Chris@4 206 position(element) {
Chris@4 207 clearTimeout(this.timer);
Chris@4 208
Chris@4 209 const that = this;
Chris@4 210 // Vary the edge of the positioning according to the direction of language
Chris@4 211 // in the document.
Chris@4 212 const edge = document.documentElement.dir === 'rtl' ? 'right' : 'left';
Chris@4 213 // A time unit to wait until the entity toolbar is repositioned.
Chris@4 214 let delay = 0;
Chris@4 215 // Determines what check in the series of checks below should be
Chris@4 216 // evaluated.
Chris@4 217 let check = 0;
Chris@4 218 // When positioned against an active field that has padding, we should
Chris@4 219 // ignore that padding when positioning the toolbar, to not unnecessarily
Chris@4 220 // move the toolbar horizontally, which feels annoying.
Chris@4 221 let horizontalPadding = 0;
Chris@4 222 let of;
Chris@4 223 let activeField;
Chris@4 224 let highlightedField;
Chris@4 225 // There are several elements in the page that the entity toolbar might be
Chris@4 226 // positioned against. They are considered below in a priority order.
Chris@4 227 do {
Chris@4 228 switch (check) {
Chris@4 229 case 0:
Chris@4 230 // Position against a specific element.
Chris@4 231 of = element;
Chris@4 232 break;
Chris@4 233
Chris@4 234 case 1:
Chris@4 235 // Position against a form container.
Chris@4 236 activeField = Drupal.quickedit.app.model.get('activeField');
Chris@4 237 of =
Chris@4 238 activeField &&
Chris@4 239 activeField.editorView &&
Chris@4 240 activeField.editorView.$formContainer &&
Chris@4 241 activeField.editorView.$formContainer.find('.quickedit-form');
Chris@4 242 break;
Chris@4 243
Chris@4 244 case 2:
Chris@4 245 // Position against an active field.
Chris@4 246 of =
Chris@4 247 activeField &&
Chris@4 248 activeField.editorView &&
Chris@4 249 activeField.editorView.getEditedElement();
Chris@4 250 if (
Chris@4 251 activeField &&
Chris@4 252 activeField.editorView &&
Chris@4 253 activeField.editorView.getQuickEditUISettings().padding
Chris@4 254 ) {
Chris@4 255 horizontalPadding = 5;
Chris@4 256 }
Chris@4 257 break;
Chris@4 258
Chris@4 259 case 3:
Chris@4 260 // Position against a highlighted field.
Chris@4 261 highlightedField = Drupal.quickedit.app.model.get(
Chris@4 262 'highlightedField',
Chris@4 263 );
Chris@4 264 of =
Chris@4 265 highlightedField &&
Chris@4 266 highlightedField.editorView &&
Chris@4 267 highlightedField.editorView.getEditedElement();
Chris@4 268 delay = 250;
Chris@4 269 break;
Chris@4 270
Chris@4 271 default: {
Chris@4 272 const fieldModels = this.model.get('fields').models;
Chris@4 273 let topMostPosition = 1000000;
Chris@4 274 let topMostField = null;
Chris@4 275 // Position against the topmost field.
Chris@4 276 for (let i = 0; i < fieldModels.length; i++) {
Chris@4 277 const pos = fieldModels[i].get('el').getBoundingClientRect()
Chris@4 278 .top;
Chris@4 279 if (pos < topMostPosition) {
Chris@4 280 topMostPosition = pos;
Chris@4 281 topMostField = fieldModels[i];
Chris@4 282 }
Chris@4 283 }
Chris@4 284 of = topMostField.get('el');
Chris@4 285 delay = 50;
Chris@4 286 break;
Chris@4 287 }
Chris@0 288 }
Chris@4 289 // Prepare to check the next possible element to position against.
Chris@4 290 check++;
Chris@4 291 } while (!of);
Chris@4 292
Chris@4 293 /**
Chris@4 294 * Refines the positioning algorithm of jquery.ui.position().
Chris@4 295 *
Chris@4 296 * Invoked as the 'using' callback of jquery.ui.position() in
Chris@4 297 * positionToolbar().
Chris@4 298 *
Chris@4 299 * @param {*} view
Chris@4 300 * The view the positions will be calculated from.
Chris@4 301 * @param {object} suggested
Chris@4 302 * A hash of top and left values for the position that should be set. It
Chris@4 303 * can be forwarded to .css() or .animate().
Chris@4 304 * @param {object} info
Chris@4 305 * The position and dimensions of both the 'my' element and the 'of'
Chris@4 306 * elements, as well as calculations to their relative position. This
Chris@4 307 * object contains the following properties:
Chris@4 308 * @param {object} info.element
Chris@4 309 * A hash that contains information about the HTML element that will be
Chris@4 310 * positioned. Also known as the 'my' element.
Chris@4 311 * @param {object} info.target
Chris@4 312 * A hash that contains information about the HTML element that the
Chris@4 313 * 'my' element will be positioned against. Also known as the 'of'
Chris@4 314 * element.
Chris@4 315 */
Chris@4 316 function refinePosition(view, suggested, info) {
Chris@4 317 // Determine if the pointer should be on the top or bottom.
Chris@4 318 const isBelow = suggested.top > info.target.top;
Chris@4 319 info.element.element.toggleClass(
Chris@4 320 'quickedit-toolbar-pointer-top',
Chris@4 321 isBelow,
Chris@4 322 );
Chris@4 323 // Don't position the toolbar past the first or last editable field if
Chris@4 324 // the entity is the target.
Chris@4 325 if (view.$entity[0] === info.target.element[0]) {
Chris@4 326 // Get the first or last field according to whether the toolbar is
Chris@4 327 // above or below the entity.
Chris@4 328 const $field = view.$entity
Chris@4 329 .find('.quickedit-editable')
Chris@4 330 .eq(isBelow ? -1 : 0);
Chris@4 331 if ($field.length > 0) {
Chris@4 332 suggested.top = isBelow
Chris@4 333 ? $field.offset().top + $field.outerHeight(true)
Chris@4 334 : $field.offset().top - info.element.element.outerHeight(true);
Chris@4 335 }
Chris@4 336 }
Chris@4 337 // Don't let the toolbar go outside the fence.
Chris@4 338 const fenceTop = view.$fence.offset().top;
Chris@4 339 const fenceHeight = view.$fence.height();
Chris@4 340 const toolbarHeight = info.element.element.outerHeight(true);
Chris@4 341 if (suggested.top < fenceTop) {
Chris@4 342 suggested.top = fenceTop;
Chris@4 343 } else if (suggested.top + toolbarHeight > fenceTop + fenceHeight) {
Chris@4 344 suggested.top = fenceTop + fenceHeight - toolbarHeight;
Chris@4 345 }
Chris@4 346 // Position the toolbar.
Chris@4 347 info.element.element.css({
Chris@4 348 left: Math.floor(suggested.left),
Chris@4 349 top: Math.floor(suggested.top),
Chris@4 350 });
Chris@0 351 }
Chris@4 352
Chris@4 353 /**
Chris@4 354 * Calls the jquery.ui.position() method on the $el of this view.
Chris@4 355 */
Chris@4 356 function positionToolbar() {
Chris@4 357 that.$el
Chris@4 358 .position({
Chris@4 359 my: `${edge} bottom`,
Chris@4 360 // Move the toolbar 1px towards the start edge of the 'of' element,
Chris@4 361 // plus any horizontal padding that may have been added to the
Chris@4 362 // element that is being added, to prevent unwanted horizontal
Chris@4 363 // movement.
Chris@4 364 at: `${edge}+${1 + horizontalPadding} top`,
Chris@4 365 of,
Chris@4 366 collision: 'flipfit',
Chris@4 367 using: refinePosition.bind(null, that),
Chris@4 368 within: that.$fence,
Chris@4 369 })
Chris@4 370 // Resize the toolbar to match the dimensions of the field, up to a
Chris@4 371 // maximum width that is equal to 90% of the field's width.
Chris@4 372 .css({
Chris@4 373 'max-width':
Chris@4 374 document.documentElement.clientWidth < 450
Chris@4 375 ? document.documentElement.clientWidth
Chris@4 376 : 450,
Chris@4 377 // Set a minimum width of 240px for the entity toolbar, or the width
Chris@4 378 // of the client if it is less than 240px, so that the toolbar
Chris@4 379 // never folds up into a squashed and jumbled mess.
Chris@4 380 'min-width':
Chris@4 381 document.documentElement.clientWidth < 240
Chris@4 382 ? document.documentElement.clientWidth
Chris@4 383 : 240,
Chris@4 384 width: '100%',
Chris@4 385 });
Chris@0 386 }
Chris@4 387
Chris@4 388 // Uses the jQuery.ui.position() method. Use a timeout to move the toolbar
Chris@4 389 // only after the user has focused on an editable for 250ms. This prevents
Chris@4 390 // the toolbar from jumping around the screen.
Chris@4 391 this.timer = setTimeout(() => {
Chris@4 392 // Render the position in the next execution cycle, so that animations
Chris@4 393 // on the field have time to process. This is not strictly speaking, a
Chris@4 394 // guarantee that all animations will be finished, but it's a simple
Chris@4 395 // way to get better positioning without too much additional code.
Chris@4 396 _.defer(positionToolbar);
Chris@4 397 }, delay);
Chris@4 398 },
Chris@0 399
Chris@0 400 /**
Chris@4 401 * Set the model state to 'saving' when the save button is clicked.
Chris@4 402 *
Chris@4 403 * @param {jQuery.Event} event
Chris@4 404 * The click event.
Chris@0 405 */
Chris@4 406 onClickSave(event) {
Chris@4 407 event.stopPropagation();
Chris@4 408 event.preventDefault();
Chris@4 409 // Save the model.
Chris@4 410 this.model.set('state', 'committing');
Chris@4 411 },
Chris@0 412
Chris@4 413 /**
Chris@4 414 * Sets the model state to candidate when the cancel button is clicked.
Chris@4 415 *
Chris@4 416 * @param {jQuery.Event} event
Chris@4 417 * The click event.
Chris@4 418 */
Chris@4 419 onClickCancel(event) {
Chris@4 420 event.preventDefault();
Chris@4 421 this.model.set('state', 'deactivating');
Chris@4 422 },
Chris@0 423
Chris@4 424 /**
Chris@4 425 * Clears the timeout that will eventually reposition the entity toolbar.
Chris@4 426 *
Chris@4 427 * Without this, it may reposition itself, away from the user's cursor!
Chris@4 428 *
Chris@4 429 * @param {jQuery.Event} event
Chris@4 430 * The mouse event.
Chris@4 431 */
Chris@4 432 onMouseenter(event) {
Chris@4 433 clearTimeout(this.timer);
Chris@4 434 },
Chris@0 435
Chris@4 436 /**
Chris@4 437 * Builds the entity toolbar HTML; attaches to DOM; sets starting position.
Chris@4 438 *
Chris@4 439 * @return {jQuery}
Chris@4 440 * The toolbar element.
Chris@4 441 */
Chris@4 442 buildToolbarEl() {
Chris@4 443 const $toolbar = $(
Chris@4 444 Drupal.theme('quickeditEntityToolbar', {
Chris@4 445 id: 'quickedit-entity-toolbar',
Chris@4 446 }),
Chris@4 447 );
Chris@0 448
Chris@4 449 $toolbar
Chris@4 450 .find('.quickedit-toolbar-entity')
Chris@4 451 // Append the "ops" toolgroup into the toolbar.
Chris@4 452 .prepend(
Chris@4 453 Drupal.theme('quickeditToolgroup', {
Chris@4 454 classes: ['ops'],
Chris@4 455 buttons: [
Chris@4 456 {
Chris@4 457 label: Drupal.t('Save'),
Chris@4 458 type: 'submit',
Chris@4 459 classes: 'action-save quickedit-button icon',
Chris@4 460 attributes: {
Chris@4 461 'aria-hidden': true,
Chris@4 462 },
Chris@4 463 },
Chris@4 464 {
Chris@4 465 label: Drupal.t('Close'),
Chris@4 466 classes:
Chris@4 467 'action-cancel quickedit-button icon icon-close icon-only',
Chris@4 468 },
Chris@4 469 ],
Chris@4 470 }),
Chris@4 471 );
Chris@0 472
Chris@4 473 // Give the toolbar a sensible starting position so that it doesn't
Chris@4 474 // animate on to the screen from a far off corner.
Chris@4 475 $toolbar.css({
Chris@0 476 left: this.$entity.offset().left,
Chris@0 477 top: this.$entity.offset().top,
Chris@0 478 });
Chris@0 479
Chris@4 480 return $toolbar;
Chris@4 481 },
Chris@4 482
Chris@4 483 /**
Chris@4 484 * Returns the DOM element that fields will attach their toolbars to.
Chris@4 485 *
Chris@4 486 * @return {jQuery}
Chris@4 487 * The DOM element that fields will attach their toolbars to.
Chris@4 488 */
Chris@4 489 getToolbarRoot() {
Chris@4 490 return this._fieldToolbarRoot;
Chris@4 491 },
Chris@4 492
Chris@4 493 /**
Chris@4 494 * Generates a state-dependent label for the entity toolbar.
Chris@4 495 */
Chris@4 496 label() {
Chris@4 497 // The entity label.
Chris@4 498 let label = '';
Chris@4 499 const entityLabel = this.model.get('label');
Chris@4 500
Chris@4 501 // Label of an active field, if it exists.
Chris@4 502 const activeField = Drupal.quickedit.app.model.get('activeField');
Chris@4 503 const activeFieldLabel =
Chris@4 504 activeField && activeField.get('metadata').label;
Chris@4 505 // Label of a highlighted field, if it exists.
Chris@4 506 const highlightedField = Drupal.quickedit.app.model.get(
Chris@4 507 'highlightedField',
Chris@4 508 );
Chris@4 509 const highlightedFieldLabel =
Chris@4 510 highlightedField && highlightedField.get('metadata').label;
Chris@4 511 // The label is constructed in a priority order.
Chris@4 512 if (activeFieldLabel) {
Chris@4 513 label = Drupal.theme('quickeditEntityToolbarLabel', {
Chris@4 514 entityLabel,
Chris@4 515 fieldLabel: activeFieldLabel,
Chris@4 516 });
Chris@4 517 } else if (highlightedFieldLabel) {
Chris@4 518 label = Drupal.theme('quickeditEntityToolbarLabel', {
Chris@4 519 entityLabel,
Chris@4 520 fieldLabel: highlightedFieldLabel,
Chris@4 521 });
Chris@4 522 } else {
Chris@4 523 // @todo Add XSS regression test coverage in https://www.drupal.org/node/2547437
Chris@4 524 label = Drupal.checkPlain(entityLabel);
Chris@4 525 }
Chris@4 526
Chris@4 527 this.$el.find('.quickedit-toolbar-label').html(label);
Chris@4 528 },
Chris@4 529
Chris@4 530 /**
Chris@4 531 * Adds classes to a toolgroup.
Chris@4 532 *
Chris@4 533 * @param {string} toolgroup
Chris@4 534 * A toolgroup name.
Chris@4 535 * @param {string} classes
Chris@4 536 * A string of space-delimited class names that will be applied to the
Chris@4 537 * wrapping element of the toolbar group.
Chris@4 538 */
Chris@4 539 addClass(toolgroup, classes) {
Chris@4 540 this._find(toolgroup).addClass(classes);
Chris@4 541 },
Chris@4 542
Chris@4 543 /**
Chris@4 544 * Removes classes from a toolgroup.
Chris@4 545 *
Chris@4 546 * @param {string} toolgroup
Chris@4 547 * A toolgroup name.
Chris@4 548 * @param {string} classes
Chris@4 549 * A string of space-delimited class names that will be removed from the
Chris@4 550 * wrapping element of the toolbar group.
Chris@4 551 */
Chris@4 552 removeClass(toolgroup, classes) {
Chris@4 553 this._find(toolgroup).removeClass(classes);
Chris@4 554 },
Chris@4 555
Chris@4 556 /**
Chris@4 557 * Finds a toolgroup.
Chris@4 558 *
Chris@4 559 * @param {string} toolgroup
Chris@4 560 * A toolgroup name.
Chris@4 561 *
Chris@4 562 * @return {jQuery}
Chris@4 563 * The toolgroup DOM element.
Chris@4 564 */
Chris@4 565 _find(toolgroup) {
Chris@4 566 return this.$el.find(
Chris@4 567 `.quickedit-toolbar .quickedit-toolgroup.${toolgroup}`,
Chris@4 568 );
Chris@4 569 },
Chris@4 570
Chris@4 571 /**
Chris@4 572 * Shows a toolgroup.
Chris@4 573 *
Chris@4 574 * @param {string} toolgroup
Chris@4 575 * A toolgroup name.
Chris@4 576 */
Chris@4 577 show(toolgroup) {
Chris@4 578 this.$el.removeClass('quickedit-animate-invisible');
Chris@4 579 },
Chris@0 580 },
Chris@4 581 );
Chris@4 582 })(jQuery, _, Backbone, Drupal, Drupal.debounce);