Unverified Commit f3630dd5 authored by Ignacio Sánchez Holgueras's avatar Ignacio Sánchez Holgueras Committed by Mateu Aguiló Bosch
Browse files

Issue #3323817 by isholgueras, e0ipso: Hide deprecated widgets by default

parent 42118e41
Loading
Loading
Loading
Loading
+32 −3
Original line number Diff line number Diff line
@@ -54,16 +54,19 @@
.radio-details--name {
  grid-area: 2 / 1 / span 1 / span 3;
  font-weight: bold;
  margin-bottom: 1rem;
  font-size: 0.9rem;
}

.remote_status--deprecated .radio-details--name {
  text-decoration: line-through;
}

.radio-details--wrapper .radio-details--description {
  grid-area: 3 / 1 / span 1 / span 3;
  font-size: 0.7rem;
  color: #666;
  line-height: 1rem;
  margin-bottom: 1rem;
  margin-bottom: 1.5rem;
  padding-right: 10px;
}

@@ -74,7 +77,6 @@
  overflow: hidden;
  border-radius: 5px;
  border: 1px solid #bbb;
  margin-bottom: 15px;
  background-image: url("../assets/default-thumbnail.svg");
  background-color: #cae6ef;
  background-size: 100%;
@@ -95,6 +97,22 @@
  background: #eea0c0;
}

.radio-details--wrapper .radio-details_remote-status {
  display: none
}

.radio-details--wrapper .radio-details_remote-status--deprecated {
  display: block;
  background: #ff6666a0;
  position: absolute;
  bottom: 0;
  right: 0;
  left: 0;
  text-align: center;
  font-size: 0.8rem;
  border-radius: 0 0 3px 3px;
}

.radio-details--wrapper .radio-details--source {
  padding: 0 5px;
  font-size: 0.75rem;
@@ -166,3 +184,14 @@
  width: 100%;
  height: 600px;
}

.reset-search-box {
  color: #555;
  height: 30px;
  text-align: center;
  width: 30px;
  background: none;
  border-width: 0;
  cursor: pointer;
  margin-left: -35px;
}
+257 −98
Original line number Diff line number Diff line
(function(once) {

  function toggleRadioButtons(inputQuery, fieldset) {
    if (inputQuery.length === 0) {
      for (var radio of fieldset.querySelectorAll('input[type="radio"]')) {
        radio.parentElement.parentElement.parentElement.hidden = false;
      }
      return;
  const deprecatedMessage = 'The selected widget is deprecated and will be removed. Evaluate migrating to a stable widget.';

  window.optionsFilter = {
    states: [],

    initIndex: function(index = 0) {
      optionsFilter.states[index] = {};
    },

    setContainer: function(container, index = 0) {
      optionsFilter.states[index].container = container;
      optionsFilter.states[index].widgets = Array.from(container.querySelectorAll('.js-form-type-radio'));
      var deprecationCheckbox = container.querySelector('input.deprecation-checkbox');
      optionsFilter.states[index].deprecatedFilterValue = true;
      if (deprecationCheckbox !== null) {
        optionsFilter.states[index].deprecatedFilterValue = container.querySelector(
          'input.deprecation-checkbox').checked;
      }
    var matching = Array.from(fieldset.querySelectorAll('.radio-details--search'))
      .filter(e => e.innerText.search(inputQuery) !== -1);
    // Hide all inputs, then show the matching and selected.
    for (var radio of fieldset.querySelectorAll('input[type="radio"]')) {
      // Always show the checked option.
      radio.parentElement.parentElement.parentElement.hidden = !radio.checked;
      optionsFilter.states[index].infoLayer = container.querySelector('.currently-selected');
      optionsFilter.states[index].infoLayer.hidden = true;
      optionsFilter.states[index].warningLayer = container.querySelector('.warning');
      optionsFilter.states[index].warningLayer.hidden = true;

      optionsFilter.setDefaultWidgetInDOM(index);
    },

    getContainer: function(index = 0) {
      return optionsFilter.states[index].container;
    },

    getInfoLayer: function(index = 0) {
      return optionsFilter.states[index].infoLayer;
    },

    setSearchboxValue: function(value, index = 0) {
      optionsFilter.states[index].searchboxValue = value;
    },

    getSearchboxValue: function(index = 0) {
      return optionsFilter.states[index].searchboxValue;
    },

    setDeprecatedFilterValue: function(value, index = 0) {
      optionsFilter.states[index].deprecatedFilterValue = value;
    },

    getDeprecatedFilterValue: function(index = 0) {
      return optionsFilter.states[index].deprecatedFilterValue;
    },

    setSelectedWidget: function(selectedWidget, index = 0) {
      optionsFilter.states[index].selectedWidget = selectedWidget;
    },

    getSelectedWidget: function(index = 0) {
      return optionsFilter.states[index].selectedWidget;
    },

    setDefaultWidgetInDOM: function(index = 0) {
      const container = optionsFilter.getContainer(index);
      const selectedWidget = container.querySelector('input[type="radio"]:checked');
      if (selectedWidget === null) {
        return null;
      }
    for (var matchingElement of matching) {
      optionsFilter.selectWidget(selectedWidget, index);
    },

    setSearchboxValueInDOM: function(value, index = 0) {
      const container = optionsFilter.getContainer(index);
      const searchBox = container.querySelector('input.search-box');
      searchBox.value = value;
      optionsFilter.setSearchboxValue(value);
    },

    selectWidget: function(selectedWidget, index = 0) {
      var widgetWrapper = selectedWidget.closest('.js-form-type-radio');
      optionsFilter.setSelectedWidget(widgetWrapper, index);
      optionsFilter.states[index].widgets.map(function(element) {
        element.classList.remove('form-type--radio__selected');
      });
      widgetWrapper.classList.add('form-type--radio__selected');
      optionsFilter.showWarningMessageIfNeeded(index);
      optionsFilter.setSearchboxValueInDOM(widgetWrapper.querySelector('.radio-details--machine-name').innerText);
    },

    showAllWidgets: function(index = 0) {
      optionsFilter.states[index].widgets.map(function(element) {
        element.hidden = false;
      });
    },

    showSelectedWidget: function(index = 0) {
      optionsFilter.getSelectedWidget(index).hidden = false;
    },

    hideAllWidgets: function(index = 0) {
      optionsFilter.states[index].widgets.map(function(element) {
        element.hidden = true;
      });
    },

    hideAllWidgetsBut: function(widgetsToShow, index = 0) {
      optionsFilter.hideAllWidgets(index);
      for (const matchingElement of widgetsToShow) {
        matchingElement.parentElement.parentElement.hidden = false;
      }
    },

    filterWidgetsSearched: function(index = 0) {
      const container = optionsFilter.getContainer(index);
      const searchboxValue = optionsFilter.getSearchboxValue(index);
      const matchingWidgets = Array.from(
        container.querySelectorAll('.radio-details--search')).
        filter(e => e.innerText.search(searchboxValue) !== -1);
      optionsFilter.hideAllWidgetsBut(matchingWidgets, index);
    },

    isWidgetDeprecated: function(widget) {
      return widget.innerText.search('deprecated') >= 0;
    },

    getDeprecatedWidgets: function(index = 0) {
      return optionsFilter.states[index].widgets.filter(function(widget) {
        return optionsFilter.isWidgetDeprecated(widget);
      });
    },

    filterWidgetsDeprecated: function(index = 0) {
      var showDeprecated = optionsFilter.getDeprecatedFilterValue(index);
      optionsFilter.getDeprecatedWidgets(index).map(function(widget) {
        if (!showDeprecated) {
          widget.hidden = true;
        }
      });
    },

  function subscribeToChanges(fieldset) {
    var search = fieldset.querySelector('input[type="search"]');
    var changeSearchText = (event) => toggleRadioButtons(event.target.value, fieldset);
    search.addEventListener('input', changeSearchText);
    showWarningMessageIfNeeded: function(index = 0) {
      var selectedWidget = optionsFilter.getSelectedWidget(index);
      optionsFilter.states[index].warningLayer.hidden = true;
      optionsFilter.states[index].warningLayer.innerText = '';
      if (optionsFilter.isWidgetDeprecated(selectedWidget)) {
        optionsFilter.states[index].warningLayer.hidden = false;
        optionsFilter.states[index].warningLayer.innerText = deprecatedMessage;
      }
    },

    refreshWidgets: function(index = 0) {
      optionsFilter.showAllWidgets(index);
      optionsFilter.filterWidgetsSearched(index);
      optionsFilter.filterWidgetsDeprecated(index);
      optionsFilter.showSelectedWidget(index);
    },

  var renderCurrentlySelected = (info, selectedContainer) => {
    var id = selectedContainer.querySelector('input[type="radio"]').value;
    var name = selectedContainer.querySelector('.radio-details--human-name').innerText;
    var description = selectedContainer.querySelector('.radio-details--description').innerText;
    var status = selectedContainer.querySelector('.radio-details--status').innerText;
    var languages = selectedContainer.querySelector('.radio-details--languages').innerHTML;
    var source = selectedContainer.querySelector('.radio-details--source').innerText;
    var version = selectedContainer.querySelector('.radio-details--version').innerText;
    var createdDate = selectedContainer.querySelector('.radio-details--created').innerText;
    var updatedDate = selectedContainer.querySelector('.radio-details--updated').innerText;
    const imgElement = selectedContainer.querySelector('.radio-details--image');
    renderSelectedWidget: function(index = 0) {
      var info = optionsFilter.getInfoLayer(index);
      var selectedWidget = optionsFilter.getSelectedWidget(index);

      var id = selectedWidget.querySelector('input[type="radio"]').value;
      var name = selectedWidget.querySelector('.radio-details--human-name').innerText;
      var description = selectedWidget.querySelector('.radio-details--description').innerText;
      var status = selectedWidget.querySelector('.radio-details_remote-status').innerText;
      var languages = selectedWidget.querySelector('.radio-details--languages').innerHTML;
      var source = selectedWidget.querySelector('.radio-details--source').innerText;
      var version = selectedWidget.querySelector('.radio-details--version').innerText;
      var createdDate = selectedWidget.querySelector('.radio-details--created').innerText;
      var updatedDate = selectedWidget.querySelector('.radio-details--updated').innerText;
      const imgElement = selectedWidget.querySelector('.radio-details--image');
      var img = imgElement ? imgElement.outerHTML : '';
    var previewUrl = selectedContainer.querySelector('.radio-details--preview-url').innerText;
    info.innerHTML = Drupal.theme('currentlySelectedClComponent', id, name, description, status, languages, version, source, createdDate, updatedDate, img, previewUrl);
      var previewUrl = selectedWidget.querySelector('.radio-details--preview-url').innerText;
      info.innerHTML = Drupal.theme('currentlySelectedWidget', id, name, description, status, languages, version,
        source, createdDate, updatedDate, img, previewUrl);
      if (previewUrl) {
        info.querySelector('a.button').addEventListener('click', (event) => {
          var button = event.target;
@@ -49,28 +181,88 @@
        });
      }
      info.hidden = false;
    },
  };

  const subscribeSearchboxToChanges = function(index = 0) {
    const container = optionsFilter.getContainer(index);
    const searchBox = container.querySelector('input.search-box');
    if (searchBox === null) {
      return;
    }
    searchBox.addEventListener('input', function(event) {
      optionsFilter.setSearchboxValue(event.target.value, index);
      optionsFilter.refreshWidgets(index);
    });
  };

  const includeResetButton = function(index) {
    const container = optionsFilter.getContainer(index);
    const searchBox = container.querySelector('input.search-box');
    if (searchBox === null) {
      return;
    }
    var resetButton = document.createElement('button');
    resetButton.classList.add('reset-search-box');
    resetButton.innerText = '';
    resetButton.title = 'Reset filter';
    resetButton.addEventListener('click', function(event) {
      event.preventDefault();
      container.querySelector('input.search-box').value = '';
      optionsFilter.setSearchboxValue('', index);
      optionsFilter.refreshWidgets(index);
      event.target.blur();
    });

    searchBox.parentElement.insertBefore(resetButton, searchBox.nextSibling);
  };

  const subscribeDeprecatedSearchboxToChanges = function(index = 0) {
    const container = optionsFilter.getContainer(index);
    const deprecationCheckbox = container.querySelector('input.deprecation-checkbox');
    if (deprecationCheckbox === null) {
      return;
    }
    deprecationCheckbox.addEventListener('change', function(event) {
      optionsFilter.setDeprecatedFilterValue(event.target.checked, index);
      optionsFilter.refreshWidgets(index);
    });
  };

  const subscribeRadiosToChanges = function(index = 0) {
    const container = optionsFilter.getContainer(index);
    var radios = once('radio-change-subscribed', 'input[type="radio"]', container);
    radios.map(function(radio) {
      radio.addEventListener('change', function(event) {
        optionsFilter.selectWidget(event.target, index);
        optionsFilter.refreshWidgets(index);
        optionsFilter.renderSelectedWidget(index);
      });
    });
  };

  /**
   * Set up options filter
   */
  Drupal.behaviors.optionsFilter = {
    attach: (context, settings) => {
      var fieldsets = once('options-filter', '.widget-type--selector', context);
      for (var fieldset of fieldsets) {
        var search = fieldset.querySelector('input[type="search"]');
        if (!search.value) {
          var selected = fieldset.querySelector('input[type="radio"][checked]');
          search.value = selected ? selected.parentElement.parentElement.querySelector('.radio-details--machine-name').innerText : '';
        }
        toggleRadioButtons(search.value, fieldset);
        subscribeToChanges(fieldset);
      once('options-filter', '.widget-type--selector', context).map(function(container, index) {
        optionsFilter.initIndex(index);
        optionsFilter.setContainer(container, index);
        subscribeSearchboxToChanges(index);
        subscribeDeprecatedSearchboxToChanges(index);
        subscribeRadiosToChanges(index);
        includeResetButton(index);
        optionsFilter.refreshWidgets(index);
        if (optionsFilter.getSelectedWidget(index) !== undefined) {
          optionsFilter.renderSelectedWidget(index);
        }
      });
    },
  };

  Drupal.theme.currentlySelectedClComponent = (id, name, description, status, languages, version, source, createdDate, updatedDate, img, previewUrl) => `
    <summary>${Drupal.t('ℹ️ More information about <em>@name</em>', { '@name': name })}</summary>
  Drupal.theme.currentlySelectedWidget = (
    id, name, description, status, languages, version, source, createdDate,
    updatedDate, img, previewUrl) => `
    <summary>${Drupal.t('ℹ️ More information about <em>@name</em>',
    {'@name': name})}</summary>
    <p>${description}</p>
    <div class='image-table--wrapper'>
      <table>
@@ -81,49 +273,16 @@
        <tr><th>${Drupal.t('Status')}</th><td>${status}</td></tr>
        <tr><th>${Drupal.t('Available Languages')}</th><td>${languages}</td></tr>
      </table>
      <div class='currently-selected--image--wrapper${img ? '' : ' currently-selected--image--wrapper__empty'}'>
      <div class='currently-selected--image--wrapper${img
    ? ''
    : ' currently-selected--image--wrapper__empty'}'>
        ${img ? img : ''}
      </div>
    </div>
    ${previewUrl
    ? `<div style='display: none' id='preview-url'>${previewUrl}</div>
      <div class="try-now--wrapper"><a href="#preview-url" class='try-now button button--primary'>${Drupal.t('Try now')}</a></div>`
      <div class="try-now--wrapper"><a href="#preview-url" class='try-now button button--primary'>${Drupal.t(
      'Try now')}</a></div>`
    : ''
  }`;

  /**
   * Render more info about the currently selected component.
   */
  Drupal.behaviors.currentlySelected = {
    attach: (context, settings) => {
      var fieldsets = once('currently-selected', '.widget-type--selector', context);
      for (var fieldset of fieldsets) {
        var info = fieldset.querySelector('.currently-selected');
        info.hidden = true;
        var selected = fieldset.querySelector('input[type="radio"][checked]');
        if (selected) {
          selected.parentElement.parentElement.parentElement.classList.add('form-type--radio__selected');
          renderCurrentlySelected(info, selected.parentElement.parentElement);
        }
        var radios = once('radio-change-subscribed', 'input[type="radio"]', fieldset);
        for (var radio of radios) {
          radio.addEventListener('change', (event) => {
            if (event.target.checked) {
              var all = event.target.parentElement.parentElement.parentElement.parentElement.querySelectorAll('input[type="radio"]');
              for (var item of all) {
                item.parentElement.parentElement.parentElement.classList.remove('form-type--radio__selected');
              }
              event.target.parentElement.parentElement.parentElement.classList.add('form-type--radio__selected');
              renderCurrentlySelected(info, event.target.parentElement.parentElement);
              var searchElement = fieldset.querySelector('input[type="search"]');
              var machineName = event.target.parentElement.parentElement.querySelector('.radio-details--machine-name').innerText;
              searchElement.value = machineName;
              toggleRadioButtons(machineName, fieldset);
            }
          });
        }
      }
    },
  };

}(once));
+41 −18
Original line number Diff line number Diff line
@@ -106,17 +106,39 @@ class WidgetSelectorElement extends FormElement implements ContainerFactoryPlugi
    $default_widget_type = ($default_id ? $widget_types[$default_id] : NULL) ?? NULL;
    $element += [
      '#attached' => ['library' => ['widget_type/selector']],
      'search' => [
    ];

    // Widgets types can't be change once created, so hide search and deprecated searchbox
    if (!$default_widget_type) {
      $element['search'] = [
        '#title' => $this->t('Search'),
        '#title_display' => 'hidden',
        '#type' => 'search',
        '#default_value' => $default_widget_type instanceof WidgetTypeInterface ? $default_widget_type->getRemoteId() : NULL,
        '#default_value' => $default_widget_type instanceof WidgetTypeInterface ? $default_widget_type->getRemoteId(
        ) : NULL,
        '#placeholder' => $this->t('Search for a widget type'),
        '#size' => 50,
        '#description' => $this->t('Start typing to search for a widget type.'),
        '#input' => FALSE,
        '#attributes' => [
          'class' => [
            'search-box',
          ],
        ],
      ];
      $element['show_deprecated'] = [
        '#type' => 'checkbox',
        '#title' => $this->t('Show deprecated widgets'),
        '#default_value' => FALSE,
        '#attributes' => [
          'class' => [
            'deprecation-checkbox',
          ],
      'target_id' => [
        ],
      ];
    }

    $element['target_id'] = [
      '#type' => 'radios',
      '#options' => $options,
      '#title' => $this->t('Widgets'),
@@ -126,12 +148,12 @@ class WidgetSelectorElement extends FormElement implements ContainerFactoryPlugi
        [Radios::class, 'processRadios'],
        [$this, 'processRadios'],
      ],
      '#weight' => 1,
      '#attributes' => [
        'class' => ['widget-type-selector--radios'],
      ],
      '#ajax' => $element['#ajax'] ?? FALSE,
      '#input' => FALSE,
      ],
    ];
    $classes = $element['#attributes']['class'] ?? [];
    $classes[] = 'widget-type--selector';
@@ -172,6 +194,7 @@ class WidgetSelectorElement extends FormElement implements ContainerFactoryPlugi
      $element[$key]['#human_name'] = $widget_type->getName();
      $element[$key]['#machine_name'] = $widget_type->getRemoteId();
      $element[$key]['#remote_description'] = $widget_type->getDescription();
      $element[$key]['#remote_status'] = $widget_type->getRemoteStatus();
      $field_image = $widget_type->getPreviewImage();
      $thumbnail = ['#markup' => ''];
      $image = ['#markup' => ''];
@@ -194,7 +217,7 @@ class WidgetSelectorElement extends FormElement implements ContainerFactoryPlugi
          '#uri' => $uri,
          '#attributes' => [
            'loading' => 'lazy',
            'class' => ['radio-details--image']
            'class' => ['radio-details--image'],
          ],
        ];
      }
@@ -230,11 +253,11 @@ class WidgetSelectorElement extends FormElement implements ContainerFactoryPlugi
            return [
              '#type' => 'html_tag',
              '#tag' => 'code',
              '#value' => $langcode
              '#value' => $langcode,
            ];
          },
          $widget_type->getRemoteLanguages()
        )
        ),
      ];
    }
    return $element;
+28 −4
Original line number Diff line number Diff line
@@ -233,8 +233,7 @@ final class WidgetType extends ContentEntityBase implements WidgetTypeInterface
        return [];
      }
      return $first->getValue();
    }
    catch (MissingDataException $exception) {
    } catch (MissingDataException $exception) {
      return [];
    }
  }
@@ -249,8 +248,7 @@ final class WidgetType extends ContentEntityBase implements WidgetTypeInterface
        return [];
      }
      return $first->getValue();
    }
    catch (MissingDataException $exception) {
    } catch (MissingDataException $exception) {
      return [];
    }
  }
@@ -331,6 +329,21 @@ final class WidgetType extends ContentEntityBase implements WidgetTypeInterface
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getRemoteStatus(): string {
    return $this->get('remote_widget_status')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setRemoteStatus($remote_status): WidgetTypeInterface {
    $this->set('remote_widget_status', $remote_status);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
@@ -452,6 +465,17 @@ final class WidgetType extends ContentEntityBase implements WidgetTypeInterface
      ->setDisplayConfigurable('view', TRUE)
      ->setReadOnly(TRUE);

    $fields['remote_widget_status'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Remote Widget status'))
      ->setDescription(t('The list of files of the widget.'))
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => -2,
      ])
      ->setDisplayConfigurable('view', FALSE)
      ->setReadOnly(TRUE);

    $fields['remote_widget_settings'] = BaseFieldDefinition::create('map')
      ->setLabel(t('Remote Widget Settings'))
      ->setDescription(t('The key/value settings from the widget server.'))
+15 −1
Original line number Diff line number Diff line
@@ -5,7 +5,6 @@ namespace Drupal\widget_type;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\file\FileInterface;
use Drupal\widget_type\Entity\WidgetType;

/**
 * Provides an interface defining a widget type entity type.
@@ -110,6 +109,21 @@ interface WidgetTypeInterface extends ContentEntityInterface, EntityChangedInter
   */
  public function setRemoteLanguages(array $lang_codes): self;

  /**
   * @param $remote_status
   *
   * @return $this
   */
  public function setRemoteStatus($remote_status): self;

  /**
   * Get the remote widget type status.
   *
   * @return string
   *   The widget type description.
   */
  public function getRemoteStatus(): string;

  /**
   * Get the widget type settings.
   *
Loading