diff --git a/css/file_browser.view.css b/css/file_browser.view.css index 1d4aca9..840817b 100644 --- a/css/file_browser.view.css +++ b/css/file_browser.view.css @@ -91,9 +91,9 @@ left: 0; right: 0; top: 0; - width: calc(100% - 4px); - height: calc(100% - 4px); - border: 2px solid #37802f; + width: calc(100% - 6px); + height: calc(100% - 6px); + border: 3px solid #73b355; overflow: hidden; } @@ -101,6 +101,11 @@ box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 7px 3px rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.2); } +.grid-item.disabled { + opacity: .3; + filter: saturate(10%); +} + .view-content { margin: 0 auto; width: 100% !important; @@ -186,6 +191,9 @@ input[type="submit"][name="filter"] { background: white; border-top: 1px solid #b8b8b8; } +.form-actions.file-browser-actions { + margin-bottom: 0; +} input.entity-browser-show-selection { display: none; diff --git a/file_browser.libraries.yml b/file_browser.libraries.yml index 17c5a19..95e54b5 100644 --- a/file_browser.libraries.yml +++ b/file_browser.libraries.yml @@ -18,6 +18,7 @@ view: theme: css/file_browser.view.css: {} dependencies: + - core/backbone - core/jquery - core/drupal - core/jquery.once diff --git a/file_browser.module b/file_browser.module index 3033a48..40af4bd 100644 --- a/file_browser.module +++ b/file_browser.module @@ -45,11 +45,14 @@ function file_browser_attach_file_browser_to_form(&$form) { // Attach our library. $form['#attached']['library'][] = 'file_browser/view'; $form['#attached']['library'][] = 'file_browser/dropzone_css'; - - if (isset($form['selection_display'])) { + if (!empty($form['selection_display'])) { $form['selection_display']['#type'] = 'container'; $form['selection_display']['#attributes']['class'] = ['file-browser-actions']; } + if (!empty($form['widget']['actions'])) { + // $form['widget']['actions']['#type'] = 'container'; + $form['widget']['actions']['#attributes']['class'] = ['file-browser-actions']; + } // Add a class for generic styling. $form['#attributes']['class'][] = 'file-browser-form'; diff --git a/js/file_browser.view.js b/js/file_browser.view.js index c596dcd..d3447a7 100644 --- a/js/file_browser.view.js +++ b/js/file_browser.view.js @@ -2,7 +2,7 @@ * @file file_browser.view.js */ -(function ($, Drupal) { +(function ($, _, Backbone, Drupal) { "use strict"; @@ -79,29 +79,6 @@ }; /** - * Checks the hidden Entity Browser checkbox when an item is clicked. - * - * This behavior provides backwards-compatibility for users not using - * auto-select and multi-step. - */ - Drupal.behaviors.fileBrowserClickProxy = { - attach: function (context, settings) { - if (!settings.entity_browser_widget.auto_select) { - $('.grid-item', context).once('bind-click-event').click(function () { - var input = $(this).find('.views-field-entity-browser-select input'); - input.prop('checked', !input.prop('checked')); - if (input.prop('checked')) { - $(this).addClass('checked'); - } - else { - $(this).removeClass('checked'); - } - }); - } - } - }; - - /** * Tracks when entities have been added or removed in the multi-step form, * and displays that information on each grid item. */ @@ -125,4 +102,149 @@ } }; -}(jQuery, Drupal)); + var Selection = Backbone.View.extend({ + + events: { + 'click .grid-item': 'onClick', + 'dblclick .grid-item': 'onClick' + }, + + initialize: function () { + // This view must be created on an element which has this attribute. + // Otherwise, things will blow up and rightfully so. + this.uuid = this.el.getAttribute('data-entity-browser-uuid'); + + // If we're in an iFrame, reach into the parent window context to get the + // settings for this entity browser. + var settings = (frameElement ? parent : window).drupalSettings.entity_browser[this.uuid]; + + // Assume a single-cardinality field with no existing selection. + this.count = settings.count || 0; + this.cardinality = settings.cardinality || 1; + }, + + deselect: function (item) { + this.$(item) + .removeClass('checked') + .find('input[name ^= "entity_browser_select"]') + .prop('checked', false); + }, + + /** + * Deselects all items in the entity browser. + */ + deselectAll: function () { + // Create a version of deselect() that can be called within each() with + // this as its context. + var _deselect = jQuery.proxy(this.deselect, this); + + this.$('.grid-item').each(function (undefined, item) { + _deselect(item); + }); + }, + + select: function (item) { + this.$(item) + .addClass('checked') + .find('input[name ^= "entity_browser_select"]') + .prop('checked', true); + }, + + /** + * Marks unselected items in the entity browser as disabled. + */ + lock: function () { + this.$('.grid-item:not(.checked)').addClass('disabled'); + }, + + /** + * Marks all items in the entity browser as enabled. + */ + unlock: function () { + this.$('.grid-item').removeClass('disabled'); + }, + + /** + * Handles click events for any item in the entity browser. + * + * @param {jQuery.Event} event + */ + onClick: function (event) { + + var chosen_one = this.$(event.currentTarget); + + if (chosen_one.hasClass('disabled')) { + return false; + } + else if (this.cardinality === 1) { + this.deselectAll(); + this.select(chosen_one); + + if (event.type === 'dblclick') { + this.$('.form-actions input').click().prop('disabled', true); + } + } + else if (chosen_one.hasClass('checked')) { + this.deselect(chosen_one); + this.count--; + this.unlock(); + } + else { + this.select(chosen_one); + + // If cardinality is unlimited, this will never be fulfilled. Good. + if (++this.count === this.cardinality) { + this.lock(); + } + } + } + + }); + + Drupal.behaviors.fileBrowserSelection = { + + getElement: function (context) { + // If we're in a document context, search for the first available entity + // browser form. Otherwise, ensure that the context is itself an entity + // browser form. + return $(context)[context === document ? 'find' : 'filter']('form[data-entity-browser-uuid]').get(0); + }, + + attach: function (context) { + var element = this.getElement(context); + if (element) { + $(element).data('view', new Selection({ el: element })); + } + }, + + detach: function (context) { + var element = this.getElement(context); + + if (element) { + var view = $(element).data('view'); + + if (view instanceof Selection) { + view.undelegateEvents(); + } + } + } + + }; + + Drupal.behaviors.changeOnKeyUp = { + + onKeyUp: _.debounce(function () { + $(this).trigger('change'); + }, 600), + + attach: function (context) { + $('.keyup-change', context).on('keyup', this.onKeyUp); + }, + + detach: function (context) { + $('.keyup-change', context).off('keyup', this.onKeyUp); + } + + }; + +})(jQuery, _, Backbone, Drupal);