From 2f438310aa0796811fbf13b7f411ca19a6537537 Mon Sep 17 00:00:00 2001
From: dabbor <dabbor@2214664.no-reply.drupal.org>
Date: Wed, 25 Aug 2021 13:39:08 +0200
Subject: [PATCH] Issue #3052574 by dabbor, Mschudders, czigor, ctrlADel,
 matiasmiranda, raman.b, MPetkovFFW, dealancer, allaprishchepa, lukasss,
 Oleksiy, amoebanath, KarlShea, balis_m, silajit, yuraosn, p-neyens: Facets
 with AJAX not working in most of situations

---
 facets-ajax-3052574-117.patch                 | 698 ++++++++++++++++++
 facets.module                                 |  19 +-
 js/facets-views-ajax.js                       | 105 ++-
 modules/facets_summary/facets_summary.module  |  23 +
 .../facets_summary.services.yml               |   2 +
 .../DefaultFacetsSummaryManager.php           |  66 +-
 .../src/Plugin/Block/FacetsSummaryBlock.php   |  46 +-
 src/Controller/FacetBlockAjaxController.php   | 103 +--
 src/Plugin/Block/FacetBlock.php               |  64 +-
 9 files changed, 1012 insertions(+), 114 deletions(-)
 create mode 100644 facets-ajax-3052574-117.patch

diff --git a/facets-ajax-3052574-117.patch b/facets-ajax-3052574-117.patch
new file mode 100644
index 00000000..55e47777
--- /dev/null
+++ b/facets-ajax-3052574-117.patch
@@ -0,0 +1,698 @@
+diff --git a/facets.module b/facets.module
+index 6c04f21..7b43909 100644
+--- a/facets.module
++++ b/facets.module
+@@ -124,20 +124,27 @@ function facets_entity_presave(EntityInterface $entity) {
+ /**
+  * Implements hook_preprocess_block().
+  *
+- * Adds a class for the widget to the facet block to allow for more specific
+- * styling.
++ * Adds some classes for hiding empty blocks, working ajax, and more.
+  */
+ function facets_preprocess_block(&$variables) {
+   if ($variables['configuration']['provider'] == 'facets') {
+     // Hide the block if it's empty.
+-    if (!empty($variables['elements']['content'][0]['#attributes']['class']) && in_array('facet-hidden', $variables['elements']['content'][0]['#attributes']['class'])) {
+-      // Add the Drupal class for hiding this for everyone, including screen
+-      // readers. See hidden.module.css in the core system module.
+-      $variables['attributes']['class'][] = 'hidden';
++    if (!empty($variables['content']['facet_block']['#attributes'])) {
++      $attributes = $variables['content']['facet_block']['#attributes'];
++      if (isset($attributes['class'])) {
++        if (in_array('hidden', $attributes['class'])) {
++          // Add the Drupal class for hiding this for everyone, including screen
++          // readers. See hidden.module.css in the core system module.
++          $variables['attributes']['class'][] = 'hidden';
++        }
++      }
+     }
++
+     if (!empty($variables['derivative_plugin_id'])) {
+       $facet = Facet::load($variables['derivative_plugin_id']);
+       $variables['attributes']['class'][] = 'block-facet--' . Html::cleanCssIdentifier($facet->getWidget()['type']);
++      // For use ajax.
++      $variables['attributes']['data-drupal-block-facet-id'] = $facet->id();
+     }
+   }
+ }
+diff --git a/js/facets-views-ajax.js b/js/facets-views-ajax.js
+index e0e4f2c..0af8c8e 100644
+--- a/js/facets-views-ajax.js
++++ b/js/facets-views-ajax.js
+@@ -37,17 +37,72 @@
+           return;
+         }
+ 
+-        // Update view on summary block click.
+-        if (updateFacetsSummaryBlock() && (facetId === 'facets_summary_ajax')) {
+-          $('[data-drupal-facets-summary-id=' + facetSettings.facets_summary_id + ']').children('ul').children('li').once().click(function (e) {
+-            e.preventDefault();
+-            var facetLink = $(this).find('a');
+-            updateFacetsView(facetLink.attr('href'), current_dom_id, view_path);
+-          });
++        // Update view on range slider stop event
++        if (typeof settings.facets !== "undefined" && settings.facets.sliders && settings.facets.sliders[facetId]) {
++          settings.facets.sliders[facetId].stop = function (e, ui) {
++            var href = settings.facets.sliders[facetId].url.replace('__range_slider_min__', ui.values[0]).replace('__range_slider_max__', ui.values[1]);
++
++            // Update facet query params on the request.
++            var currentHref = window.location.href;
++            var currentQueryParams = Drupal.Views.parseQueryString(currentHref);
++            var newQueryParams = Drupal.Views.parseQueryString(href);
++
++            var queryParams = {};
++            var facetPositions = [];
++            var fCount = 0;
++            var value = '';
++            var facetKey = '';
++            for (var paramName in currentQueryParams) {
++              if (paramName.substr(0, 1) === 'f') {
++                value = currentQueryParams[paramName];
++                // Store the facet position so we can override it later.
++                facetKey = value.substr(0, value.indexOf(':'));
++                facetPositions[facetKey] = fCount;
++                queryParams['f[' + fCount + ']'] = value;
++                fCount++;
++              }
++              else {
++                queryParams[paramName] = currentQueryParams[paramName];
++              }
++            }
++
++            var paramKey = '';
++            for (let paramName in newQueryParams) {
++              if (paramName.substr(0, 1) === 'f') {
++                value = newQueryParams[paramName];
++                // replace
++                facetKey = value.substr(0, value.indexOf(':'));
++                if (typeof facetPositions[facetKey] !== 'undefined') {
++                  paramKey = 'f[' + facetPositions[facetKey] + ']';
++                }
++                else {
++                  paramKey = 'f[' + fCount + ']';
++                  fCount++;
++                }
++                queryParams[paramKey] = newQueryParams[paramName];
++              }
++              else {
++                queryParams[paramName] = newQueryParams[paramName];
++              }
++            }
++
++            href = '/' + Drupal.Views.getPath(href) + '?' + $.param(queryParams);
++
++            updateFacetsView(href, current_dom_id, view_path);
++          };
++        }
++        else if (facetId == 'facets_summary_ajax_summary' || facetId == 'facets_summary_ajax_summary_count') {
++          if (updateFacetsSummaryBlock()) {
++            $('[data-drupal-facets-summary-id=' + facetSettings.facets_summary_id + ']').children('ul').children('li').once().click(function (e) {
++              e.preventDefault();
++              var facetLink = $(this).find('a');
++              updateFacetsView(facetLink.attr('href'), current_dom_id, view_path);
++            });
++          }
+         }
+         // Update view on facet item click.
+         else {
+-          $('[data-drupal-facet-id=' + facetId + ']').each(function (index, facet_item) {
++          $('[data-drupal-facet-id |= ' + facetId + ']').each(function (index, facet_item) {
+             if ($(facet_item).hasClass('js-facets-widget')) {
+               $(facet_item).unbind('facets_filter.facets');
+               $(facet_item).on('facets_filter.facets', function (event, url) {
+@@ -104,8 +159,8 @@
+     var facets_blocks = facetsBlocks();
+ 
+     // Remove All Range Input Form Facet Blocks from being updated.
+-    if(settings.facets && settings.facets.rangeInput) {
+-      $.each(settings.facets.rangeInput, function (index, value){
++    if (settings.facets && settings.facets.rangeInput) {
++      $.each(settings.facets.rangeInput, function (index, value) {
+         delete facets_blocks[value.facetId];
+       });
+     }
+@@ -121,17 +176,17 @@
+ 
+     // Update facets summary block.
+     if (updateFacetsSummaryBlock()) {
+-      var facet_summary_wrapper_id = $('[data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax.facets_summary_id + ']').attr('id');
+-      var facet_summary_block_id = '';
+-      if (facet_summary_wrapper_id.indexOf('--') !== -1) {
+-        facet_summary_block_id = facet_summary_wrapper_id.substring(0, facet_summary_wrapper_id.indexOf('--')).replace('block-', '');
+-      }
+-      else {
+-        facet_summary_block_id = facet_summary_wrapper_id.replace('block-', '');
+-      }
+       facet_settings.submit.update_summary_block = true;
+-      facet_settings.submit.facet_summary_block_id = facet_summary_block_id;
+-      facet_settings.submit.facet_summary_wrapper_id = settings.facets_views_ajax.facets_summary_ajax.facets_summary_id;
++      facet_settings.submit.facet_summary_plugin_ids = {};
++      let summary_selector = '[data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax_summary.facets_summary_id + ']';
++      if (settings.facets_views_ajax.facets_summary_ajax_summary_count !== undefined) {
++        summary_selector += ', [data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax_summary_count.facets_summary_id + ']';
++      }
++      $(summary_selector).each(function (index, summaryWrapper) {
++        let summaryPluginId = $(summaryWrapper).attr('data-drupal-facets-summary-plugin-id');
++        let summaryPluginIdWrapper = $(summaryWrapper).attr('id');
++        facet_settings.submit.facet_summary_plugin_ids[summaryPluginIdWrapper] = summaryPluginId;
++      });
+     }
+ 
+     Drupal.ajax(facet_settings).execute();
+@@ -143,7 +198,7 @@
+     var settings = drupalSettings;
+     var update_summary = false;
+ 
+-    if (settings.facets_views_ajax.facets_summary_ajax) {
++    if (settings.facets_views_ajax.facets_summary_ajax_summary || settings.facets_views_ajax.facets_summary_ajax_summary_count) {
+       update_summary = true;
+     }
+ 
+@@ -162,8 +217,8 @@
+           return v.slice(block_id_start.length, v.length);
+         }
+       }).join();
+-      var block_selector = '#' + $(this).attr('id');
+-      facets_blocks[block_id] = block_selector;
++      var block_selector = $(this).attr('id');
++      facets_blocks[block_selector] = block_id;
+     });
+ 
+     return facets_blocks;
+@@ -204,12 +259,12 @@
+   }
+ 
+   // Helper function to add exposed form data to facets url
+-  var addExposedFiltersToFacetsUrl = function(href, view_name, view_display_id) {
++  var addExposedFiltersToFacetsUrl = function (href, view_name, view_display_id) {
+     var $exposed_form = $('form#views-exposed-form-' + view_name.replace(/_/g, '-') + '-' + view_display_id.replace(/_/g, '-'));
+ 
+     var params = Drupal.Views.parseQueryString(href);
+ 
+-    $.each($exposed_form.serializeArray(), function() {
++    $.each($exposed_form.serializeArray(), function () {
+       params[this.name] = this.value;
+     });
+ 
+diff --git a/modules/facets_summary/facets_summary.module b/modules/facets_summary/facets_summary.module
+index 6720862..af2dc3f 100644
+--- a/modules/facets_summary/facets_summary.module
++++ b/modules/facets_summary/facets_summary.module
+@@ -98,3 +98,26 @@ function facets_summary_theme_suggestions_facets_summary_item_list(array $variab
+ function facets_summary_preprocess_facets_summary_item_list(array &$variables) {
+   template_preprocess_item_list($variables);
+ }
++
++/**
++ * Implements hook_preprocess_block().
++ *
++ * Adds some classes for hiding empty blocks, working ajax, and more.
++ */
++function facets_summary_preprocess_block(&$variables) {
++  if ($variables['configuration']['provider'] == 'facets_summary') {
++    // Hide the block if it's empty.
++    if (!empty($variables['content']['facets_summary']['#attributes'])) {
++      $attributes = $variables['content']['facets_summary']['#attributes'];
++      if (isset($attributes['class'])) {
++        if (in_array('hidden', $attributes['class'])) {
++          // Add the Drupal class for hiding this for everyone, including screen
++          // readers. See hidden.module.css in the core system module.
++          $variables['attributes']['class'][] = 'hidden';
++        }
++      }
++    }
++    // For use ajax.
++    $variables['attributes']['data-drupal-block-facet-summary-id'] = $variables['configuration']['id'];
++  }
++}
+diff --git a/modules/facets_summary/facets_summary.services.yml b/modules/facets_summary/facets_summary.services.yml
+index ba1a6f5..bd8f14e 100644
+--- a/modules/facets_summary/facets_summary.services.yml
++++ b/modules/facets_summary/facets_summary.services.yml
+@@ -8,6 +8,8 @@ services:
+       - '@plugin.manager.facets.facet_source'
+       - '@plugin.manager.facets_summary.processor'
+       - '@facets.manager'
++      - '@plugin.manager.facets.url_processor'
++      - '@facets.utility.url_generator'
+   facets_summary.search_api_subscriber:
+     class: Drupal\facets_summary\EventSubscriber\SearchApiSubscriber
+     arguments: ['@entity_type.manager']
+diff --git a/modules/facets_summary/src/FacetsSummaryManager/DefaultFacetsSummaryManager.php b/modules/facets_summary/src/FacetsSummaryManager/DefaultFacetsSummaryManager.php
+index 105c438..0adafc0 100644
+--- a/modules/facets_summary/src/FacetsSummaryManager/DefaultFacetsSummaryManager.php
++++ b/modules/facets_summary/src/FacetsSummaryManager/DefaultFacetsSummaryManager.php
+@@ -4,13 +4,17 @@ namespace Drupal\facets_summary\FacetsSummaryManager;
+ 
+ use Drupal\Core\Link;
+ use Drupal\Core\StringTranslation\StringTranslationTrait;
++use Drupal\Core\Url;
+ use Drupal\facets\Exception\InvalidProcessorException;
+ use Drupal\facets\FacetManager\DefaultFacetManager;
+ use Drupal\facets\FacetSource\FacetSourcePluginManager;
++use Drupal\facets\Result\Result;
+ use Drupal\facets_summary\Processor\BuildProcessorInterface;
+ use Drupal\facets_summary\Processor\ProcessorInterface;
+ use Drupal\facets_summary\Processor\ProcessorPluginManager;
+ use Drupal\facets_summary\FacetsSummaryInterface;
++use Drupal\facets\UrlProcessor\UrlProcessorPluginManager;
++use Drupal\facets\Utility\FacetsUrlGenerator;
+ 
+ /**
+  * The facet summary manager.
+@@ -45,6 +49,20 @@ class DefaultFacetsSummaryManager {
+    */
+   protected $facetManager;
+ 
++  /**
++   * The url processor plugin manager.
++   *
++   * @var \Drupal\facets\UrlProcessor\UrlProcessorPluginManager
++   */
++  protected $urlProcessorPluginManager;
++
++  /**
++   * The facets url generator.
++   *
++   * @var \Drupal\facets\Utility\FacetsUrlGenerator
++   */
++  protected $facetsUrlGenerator;
++
+   /**
+    * Constructs a new instance of the DefaultFacetManager.
+    *
+@@ -54,11 +72,17 @@ class DefaultFacetsSummaryManager {
+    *   The facets summary processor plugin manager.
+    * @param \Drupal\facets\FacetManager\DefaultFacetManager $facet_manager
+    *   The facet manager service.
++   * @param \Drupal\facets\UrlProcessor\UrlProcessorPluginManager $url_processor_plugin_manager
++   *   The url processor plugin manager.
++   * @param \Drupal\facets\Utility\FacetsUrlGenerator $facets_url_generator
++   *   The facets url generator.
+    */
+-  public function __construct(FacetSourcePluginManager $facet_source_manager, ProcessorPluginManager $processor_plugin_manager, DefaultFacetManager $facet_manager) {
++  public function __construct(FacetSourcePluginManager $facet_source_manager, ProcessorPluginManager $processor_plugin_manager, DefaultFacetManager $facet_manager, UrlProcessorPluginManager $url_processor_plugin_manager, FacetsUrlGenerator $facets_url_generator) {
+     $this->facetSourcePluginManager = $facet_source_manager;
+     $this->processorPluginManager = $processor_plugin_manager;
+     $this->facetManager = $facet_manager;
++    $this->urlProcessorPluginManager = $url_processor_plugin_manager;
++    $this->facetsUrlGenerator = $facets_url_generator;
+   }
+ 
+   /**
+@@ -117,16 +141,48 @@ class DefaultFacetsSummaryManager {
+       '#theme' => 'facets_summary_item_list',
+       '#facet_summary_id' => $facets_summary->id(),
+       '#attributes' => [
+-        'data-drupal-facets-summary-id' => $facets_summary->id(),
++        'class' => ['facet-summary__items'],
+       ],
+     ];
+ 
+     $results = [];
+     foreach ($facets as $facet) {
+-      $show_count = $facets_config[$facet->id()]['show_count'];
+-      $results = array_merge($results, $this->buildResultTree($show_count, $facet->getResults()));
++      $enabled_processors = $facet->getProcessors(TRUE);
++      if (isset($enabled_processors["range_slider"])) {
++        $active_values = $facet->getActiveItems();
++        if ($active_values) {
++          $min = $active_values[0][0];
++          $max = $active_values[0][1];
++          $url_processor = $this->urlProcessorPluginManager->createInstance($facet->getFacetSourceConfig()->getUrlProcessorName(), ['facet' => $facet]);
++          $active_filters = $url_processor->getActiveFilters();
++          if (isset($active_filters[''])) {
++            unset($active_filters['']);
++          }
++          unset($active_filters[$facet->id()]);
++          // Only if there are still active filters, use url generator.
++          if ($active_filters) {
++            $url = $this->facetsUrlGenerator->getUrl($active_filters, FALSE);
++          }
++          else {
++            // @todo Keep non-facet related get params.
++            $url = Url::fromUserInput($facets_summary->getFacetSource()->getPath());
++          }
++          $result_without_current_rangeslider = new Result($facet, "(min:$min,max:$max)", " $min - $max", 1);
++          $result_without_current_rangeslider->setActiveState(TRUE);
++          $result_without_current_rangeslider->setUrl($url);
++          $results = array_merge($results, $this->buildResultTree(FALSE, [$result_without_current_rangeslider]));
++        }
++      }
++      else {
++        $show_count = $facets_config[$facet->id()]['show_count'];
++        $results = array_merge($results, $this->buildResultTree($show_count, $facet->getResults()));
++      }
++
++    }
++
++    if ($results) {
++      $build['#items'] = $results;
+     }
+-    $build['#items'] = $results;
+ 
+     // Allow our Facets Summary processors to alter the build array in a
+     // configured order.
+diff --git a/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php b/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php
+index 5b9255a..4ace992 100644
+--- a/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php
++++ b/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php
+@@ -6,6 +6,7 @@ use Drupal\Core\Block\BlockBase;
+ use Drupal\Core\Cache\UncacheableDependencyTrait;
+ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+ use Drupal\Core\Url;
++use Drupal\Component\Utility\Html;
+ use Drupal\facets_summary\Entity\FacetsSummary;
+ use Drupal\facets_summary\FacetsSummaryBlockInterface;
+ use Drupal\facets_summary\FacetsSummaryManager\DefaultFacetsSummaryManager;
+@@ -88,25 +89,46 @@ class FacetsSummaryBlock extends BlockBase implements FacetsSummaryBlockInterfac
+     $facets_summary = $this->getEntity();
+ 
+     // Let the facet_manager build the facets.
+-    $build = $this->facetsSummaryManager->build($facets_summary);
++    $build = [];
+ 
+-    // Add contextual links only when we have results.
+-    if (!empty($build)) {
+-      $build['#contextual_links']['facets_summary'] = [
+-        'route_parameters' => ['facets_summary' => $facets_summary->id()],
++    // Let the facet_manager build the facets.
++    $summary_build = $this->facetsSummaryManager->build($facets_summary);
++
++    if ($summary_build) {
++      $build = [
++        'facets_summary' => [
++          '#type' => 'container',
++          '#contextual_links' => [
++            'facets_summary' => [
++              'route_parameters' => ['facets_summary' => $facets_summary->id()],
++            ],
++          ],
++          '#attributes' => [
++            'data-drupal-facets-summary-id' => $facets_summary->id(),
++            'data-drupal-facets-summary-plugin-id' => $this->getPluginId(),
++            'id' => Html::getUniqueId(str_replace(':', '-', $this->getPluginId())),
++            'class' => [
++              'facets-summary-block__wrapper',
++            ],
++          ],
++          'summary_build' => $summary_build,
++        ],
+       ];
+-    }
+ 
+-    /** @var \Drupal\views\ViewExecutable $view */
+-    if ($view = $facets_summary->getFacetSource()->getViewsDisplay()) {
+-      $build['#attached']['drupalSettings']['facets_views_ajax'] = [
+-        'facets_summary_ajax' => [
++      // Hidden empty result.
++      if (!isset($summary_build['#items']) && !isset($summary_build['#message'])) {
++        $build['facets_summary']['#attributes']['class'][] = 'hidden';
++      }
++
++      /** @var \Drupal\views\ViewExecutable $view */
++      if ($view = $facets_summary->getFacetSource()->getViewsDisplay()) {
++        $build['#attached']['drupalSettings']['facets_views_ajax']['facets_summary_ajax_' . $facets_summary->id()] = [
+           'facets_summary_id' => $facets_summary->id(),
+           'view_id' => $view->id(),
+           'current_display_id' => $view->current_display,
+           'ajax_path' => Url::fromRoute('views.ajax')->toString(),
+-        ],
+-      ];
++        ];
++      }
+     }
+ 
+     return $build;
+diff --git a/src/Controller/FacetBlockAjaxController.php b/src/Controller/FacetBlockAjaxController.php
+index d1d446d..9b2cf1a 100644
+--- a/src/Controller/FacetBlockAjaxController.php
++++ b/src/Controller/FacetBlockAjaxController.php
+@@ -5,10 +5,10 @@ namespace Drupal\facets\Controller;
+ use Drupal\Core\Ajax\AjaxResponse;
+ use Drupal\Core\Ajax\InvokeCommand;
+ use Drupal\Core\Ajax\ReplaceCommand;
++use Drupal\Core\Block\BlockManager;
+ use Drupal\Core\Controller\ControllerBase;
+ use Drupal\Core\Path\CurrentPathStack;
+ use Drupal\Core\PathProcessor\PathProcessorManager;
+-use Drupal\Core\Render\RendererInterface;
+ use Drupal\Core\Routing\CurrentRouteMatch;
+ use Symfony\Component\DependencyInjection\ContainerInterface;
+ use Symfony\Component\HttpFoundation\Request;
+@@ -28,13 +28,6 @@ class FacetBlockAjaxController extends ControllerBase {
+    */
+   protected $storage;
+ 
+-  /**
+-   * The renderer.
+-   *
+-   * @var \Drupal\Core\Render\RendererInterface
+-   */
+-  protected $renderer;
+-
+   /**
+    * The current path.
+    *
+@@ -63,11 +56,16 @@ class FacetBlockAjaxController extends ControllerBase {
+    */
+   protected $currentRouteMatch;
+ 
++  /**
++   * The block manager service.
++   *
++   * @var \Drupal\Core\Block\BlockManager
++   */
++  protected $blockManager;
++
+   /**
+    * Constructs a FacetBlockAjaxController object.
+    *
+-   * @param \Drupal\Core\Render\RendererInterface $renderer
+-   *   The renderer service.
+    * @param \Drupal\Core\Path\CurrentPathStack $currentPath
+    *   The current path service.
+    * @param \Symfony\Component\Routing\RouterInterface $router
+@@ -76,14 +74,16 @@ class FacetBlockAjaxController extends ControllerBase {
+    *   The path processor manager.
+    * @param \Drupal\Core\Routing\CurrentRouteMatch $currentRouteMatch
+    *   The current route match service.
++   * @param \Drupal\Core\Block\BlockManager $blockManager
++   *   The block manager service.
+    */
+-  public function __construct(RendererInterface $renderer, CurrentPathStack $currentPath, RouterInterface $router, PathProcessorManager $pathProcessor, CurrentRouteMatch $currentRouteMatch) {
++  public function __construct(CurrentPathStack $currentPath, RouterInterface $router, PathProcessorManager $pathProcessor, CurrentRouteMatch $currentRouteMatch, BlockManager $blockManager) {
+     $this->storage = $this->entityTypeManager()->getStorage('block');
+-    $this->renderer = $renderer;
+     $this->currentPath = $currentPath;
+     $this->router = $router;
+     $this->pathProcessor = $pathProcessor;
+     $this->currentRouteMatch = $currentRouteMatch;
++    $this->blockManager = $blockManager;
+   }
+ 
+   /**
+@@ -91,11 +91,11 @@ class FacetBlockAjaxController extends ControllerBase {
+    */
+   public static function create(ContainerInterface $container) {
+     return new static(
+-      $container->get('renderer'),
+       $container->get('path.current'),
+       $container->get('router'),
+       $container->get('path_processor_manager'),
+-      $container->get('current_route_match')
++      $container->get('current_route_match'),
++      $container->get('plugin.manager.block')
+     );
+   }
+ 
+@@ -122,9 +122,6 @@ class FacetBlockAjaxController extends ControllerBase {
+       throw new NotFoundHttpException('No facet link or facet blocks found.');
+     }
+ 
+-    // Make sure we are not updating blocks multiple times.
+-    $facets_blocks = array_unique($facets_blocks);
+-
+     $new_request = Request::create($path);
+     $request_stack = new RequestStack();
+     $processed = $this->pathProcessor->processInbound($path, $new_request);
+@@ -136,40 +133,56 @@ class FacetBlockAjaxController extends ControllerBase {
+ 
+     $container = \Drupal::getContainer();
+     $container->set('request_stack', $request_stack);
+-    $active_facet = $request->request->get('active_facet');
+-
+-    // Build the facets blocks found for the current request and update.
+-    foreach ($facets_blocks as $block_id => $block_selector) {
+-      $block_entity = $this->storage->load($block_id);
+-
+-      if ($block_entity) {
+-        // Render a block, then add it to the response as a replace command.
+-        $block_view = $this->entityTypeManager
+-          ->getViewBuilder('block')
+-          ->view($block_entity);
+-
+-        $block_view = (string) $this->renderer->renderPlain($block_view);
+-        $response->addCommand(new ReplaceCommand($block_selector, $block_view));
++    foreach ($facets_blocks as $block_selector => $block_id) {
++      // Facet block render array.
++      $block_view = NULL;
++      // Re prepare from css standarts.
++      $block_id = str_replace(['--', '-'], [':', '_'], $block_id);
++
++      // @todo We should not create an instance if we have already created one.
++      $block_instance = $this->blockManager->createInstance($block_id);
++      if ($block_instance) {
++        $block_view = $block_instance->build();
++        if ($block_view) {
++          // Replace content current ID selector.
++          $response->addCommand(new ReplaceCommand('#' . $block_selector, $block_view));
++          // Hide or show block.
++          $facet_id = explode(':', $block_id)[1];
++          $hide_show_selector = '[data-drupal-block-facet-id = "' . $facet_id . '"]';
++          if (!empty($block_view['facet_block']['#attributes']['class']) && in_array('hidden', $block_view['facet_block']['#attributes']['class'])) {
++            $response->addCommand(new InvokeCommand($hide_show_selector, 'addClass', ['hidden']));
++          }
++          else {
++            $response->addCommand(new InvokeCommand($hide_show_selector, 'removeClass', ['hidden']));
++          }
++        }
+       }
+     }
+ 
+-    $response->addCommand(new InvokeCommand('[data-block-plugin-id="' . $active_facet . '"]', 'addClass', ['facet-active']));
+-
+     // Update filter summary block.
+     $update_summary_block = $request->request->get('update_summary_block');
+     if ($update_summary_block) {
+-      $facet_summary_block_id = $request->request->get('facet_summary_block_id');
+-      $facet_summary_wrapper_id = $request->request->get('facet_summary_wrapper_id');
+-      $facet_summary_block_id = str_replace('-', '_', $facet_summary_block_id);
+-
+-      if ($facet_summary_block_id) {
+-        $block_entity = $this->storage->load($facet_summary_block_id);
+-        $block_view = $this->entityTypeManager
+-          ->getViewBuilder('block')
+-          ->view($block_entity);
+-        $block_view = (string) $this->renderer->renderPlain($block_view);
+-
+-        $response->addCommand(new ReplaceCommand('[data-drupal-facets-summary-id=' . $facet_summary_wrapper_id . ']', $block_view));
++      $facet_summary_plugin_ids = $request->request->get('facet_summary_plugin_ids');
++      foreach ($facet_summary_plugin_ids as $block_selector => $summary_plugin_id) {
++        // Facet summary block render array.
++        $block_view = NULL;
++        // @todo We should not create an instance if we have already created one.
++        $block_instance = $this->blockManager->createInstance($summary_plugin_id);
++        if ($block_instance) {
++          $block_view = $block_instance->build();
++          if ($block_view) {
++            // Replace content facets summary plugin ID selector.
++            $response->addCommand(new ReplaceCommand('[data-drupal-facets-summary-plugin-id = "' . $summary_plugin_id . '"]', $block_view));
++            // Hide or show block.
++            $hide_show_selector = '[data-drupal-block-facet-summary-id = "' . $summary_plugin_id . '"]';
++            if (!empty($block_view['facets_summary']['#attributes']['class']) && in_array('hidden', $block_view['facets_summary']['#attributes']['class'])) {
++              $response->addCommand(new InvokeCommand($hide_show_selector, 'addClass', ['hidden']));
++            }
++            else {
++              $response->addCommand(new InvokeCommand($hide_show_selector, 'removeClass', ['hidden']));
++            }
++          }
++        }
+       }
+     }
+ 
+diff --git a/src/Plugin/Block/FacetBlock.php b/src/Plugin/Block/FacetBlock.php
+index 676c573..509552f 100644
+--- a/src/Plugin/Block/FacetBlock.php
++++ b/src/Plugin/Block/FacetBlock.php
+@@ -3,6 +3,7 @@
+ namespace Drupal\facets\Plugin\Block;
+ 
+ use Drupal\Core\Block\BlockBase;
++use Drupal\Component\Utility\Html;
+ use Drupal\Core\Entity\EntityStorageInterface;
+ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+ use Drupal\Core\Form\FormStateInterface;
+@@ -79,39 +80,60 @@ class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface {
+       return [];
+     }
+ 
++    $build = [];
++
+     // Let the facet_manager build the facets.
+-    $build = $this->facetManager->build($facet);
++    $facet_build = $this->facetManager->build($facet);
+ 
+-    if (!empty($build)) {
++    if ($facet_build) {
+       // Add extra elements from facet source, for example, ajax scripts.
+       // @see Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay
+-      /* @var \Drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source */
++      /** @var \Drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source */
+       $facet_source = $facet->getFacetSource();
+-      $build += $facet_source->buildFacet();
+-
+-      // Add contextual links only when we have results.
+-      $build['#contextual_links']['facets_facet'] = [
+-        'route_parameters' => ['facets_facet' => $facet->id()],
++      $facet_build += $facet_source->buildFacet();
++
++      $build = [
++        '#type' => 'container',
++        '#contextual_links' => [
++          'facets_facet' => [
++            'route_parameters' => ['facets_facet' => $facet->id()],
++          ],
++        ],
++        '#attributes' => [
++          'class' => ['block-facet__wrapper'],
++        ],
++        $facet_build,
+       ];
+ 
+-      if (!empty($build[0]['#attributes']['class']) && in_array('facet-active', $build[0]['#attributes']['class'], TRUE)) {
+-        $build['#attributes']['class'][] = 'facet-active';
+-      }
+-      else {
+-        $build['#attributes']['class'][] = 'facet-inactive';
++      // Add css classes.
++      if (!empty($facet_build[0]['#attributes']['class'])) {
++        $css_classes = $facet_build[0]['#attributes']['class'];
++        // Active/inactive css classes.
++        if (in_array('facet-active', $css_classes)) {
++          $build['#attributes']['class'][] = 'facet-active';
++        }
++        else {
++          $build['#attributes']['class'][] = 'facet-inactive';
++        }
++        // Whether it is necessary to add hide css class.
++        if (in_array('facet-hidden', $css_classes)) {
++          $build['#attributes']['class'][] = 'hidden';
++        }
+       }
+ 
+       // Add classes needed for ajax.
+-      if (!empty($build['#use_ajax'])) {
++      if (!empty($facet_build['#use_ajax'])) {
+         $build['#attributes']['class'][] = 'block-facets-ajax';
+-        // The configuration block id isn't always set in the configuration.
+-        if (isset($this->configuration['block_id'])) {
+-          $build['#attributes']['class'][] = 'js-facet-block-id-' . $this->configuration['block_id'];
+-        }
+-        else {
+-          $build['#attributes']['class'][] = 'js-facet-block-id-' . $this->pluginId;
+-        }
++        $block_id = str_replace(':', '--', $this->pluginId);
++        $block_id = Html::cleanCssIdentifier($block_id);
++        $build['#attributes']['class'][] = 'js-facet-block-id-' . $block_id;
++        $build['#attributes']['id'] = Html::getUniqueId($block_id);
+       }
++
++      // To render correctly in different situations.
++      $build = [
++        'facet_block' => $build,
++      ];
+     }
+ 
+     return $build;
diff --git a/facets.module b/facets.module
index 6c04f21a..7b43909e 100644
--- a/facets.module
+++ b/facets.module
@@ -124,20 +124,27 @@ function facets_entity_presave(EntityInterface $entity) {
 /**
  * Implements hook_preprocess_block().
  *
- * Adds a class for the widget to the facet block to allow for more specific
- * styling.
+ * Adds some classes for hiding empty blocks, working ajax, and more.
  */
 function facets_preprocess_block(&$variables) {
   if ($variables['configuration']['provider'] == 'facets') {
     // Hide the block if it's empty.
-    if (!empty($variables['elements']['content'][0]['#attributes']['class']) && in_array('facet-hidden', $variables['elements']['content'][0]['#attributes']['class'])) {
-      // Add the Drupal class for hiding this for everyone, including screen
-      // readers. See hidden.module.css in the core system module.
-      $variables['attributes']['class'][] = 'hidden';
+    if (!empty($variables['content']['facet_block']['#attributes'])) {
+      $attributes = $variables['content']['facet_block']['#attributes'];
+      if (isset($attributes['class'])) {
+        if (in_array('hidden', $attributes['class'])) {
+          // Add the Drupal class for hiding this for everyone, including screen
+          // readers. See hidden.module.css in the core system module.
+          $variables['attributes']['class'][] = 'hidden';
+        }
+      }
     }
+
     if (!empty($variables['derivative_plugin_id'])) {
       $facet = Facet::load($variables['derivative_plugin_id']);
       $variables['attributes']['class'][] = 'block-facet--' . Html::cleanCssIdentifier($facet->getWidget()['type']);
+      // For use ajax.
+      $variables['attributes']['data-drupal-block-facet-id'] = $facet->id();
     }
   }
 }
diff --git a/js/facets-views-ajax.js b/js/facets-views-ajax.js
index e0e4f2ce..0af8c8e9 100644
--- a/js/facets-views-ajax.js
+++ b/js/facets-views-ajax.js
@@ -37,17 +37,72 @@
           return;
         }
 
-        // Update view on summary block click.
-        if (updateFacetsSummaryBlock() && (facetId === 'facets_summary_ajax')) {
-          $('[data-drupal-facets-summary-id=' + facetSettings.facets_summary_id + ']').children('ul').children('li').once().click(function (e) {
-            e.preventDefault();
-            var facetLink = $(this).find('a');
-            updateFacetsView(facetLink.attr('href'), current_dom_id, view_path);
-          });
+        // Update view on range slider stop event
+        if (typeof settings.facets !== "undefined" && settings.facets.sliders && settings.facets.sliders[facetId]) {
+          settings.facets.sliders[facetId].stop = function (e, ui) {
+            var href = settings.facets.sliders[facetId].url.replace('__range_slider_min__', ui.values[0]).replace('__range_slider_max__', ui.values[1]);
+
+            // Update facet query params on the request.
+            var currentHref = window.location.href;
+            var currentQueryParams = Drupal.Views.parseQueryString(currentHref);
+            var newQueryParams = Drupal.Views.parseQueryString(href);
+
+            var queryParams = {};
+            var facetPositions = [];
+            var fCount = 0;
+            var value = '';
+            var facetKey = '';
+            for (var paramName in currentQueryParams) {
+              if (paramName.substr(0, 1) === 'f') {
+                value = currentQueryParams[paramName];
+                // Store the facet position so we can override it later.
+                facetKey = value.substr(0, value.indexOf(':'));
+                facetPositions[facetKey] = fCount;
+                queryParams['f[' + fCount + ']'] = value;
+                fCount++;
+              }
+              else {
+                queryParams[paramName] = currentQueryParams[paramName];
+              }
+            }
+
+            var paramKey = '';
+            for (let paramName in newQueryParams) {
+              if (paramName.substr(0, 1) === 'f') {
+                value = newQueryParams[paramName];
+                // replace
+                facetKey = value.substr(0, value.indexOf(':'));
+                if (typeof facetPositions[facetKey] !== 'undefined') {
+                  paramKey = 'f[' + facetPositions[facetKey] + ']';
+                }
+                else {
+                  paramKey = 'f[' + fCount + ']';
+                  fCount++;
+                }
+                queryParams[paramKey] = newQueryParams[paramName];
+              }
+              else {
+                queryParams[paramName] = newQueryParams[paramName];
+              }
+            }
+
+            href = '/' + Drupal.Views.getPath(href) + '?' + $.param(queryParams);
+
+            updateFacetsView(href, current_dom_id, view_path);
+          };
+        }
+        else if (facetId == 'facets_summary_ajax_summary' || facetId == 'facets_summary_ajax_summary_count') {
+          if (updateFacetsSummaryBlock()) {
+            $('[data-drupal-facets-summary-id=' + facetSettings.facets_summary_id + ']').children('ul').children('li').once().click(function (e) {
+              e.preventDefault();
+              var facetLink = $(this).find('a');
+              updateFacetsView(facetLink.attr('href'), current_dom_id, view_path);
+            });
+          }
         }
         // Update view on facet item click.
         else {
-          $('[data-drupal-facet-id=' + facetId + ']').each(function (index, facet_item) {
+          $('[data-drupal-facet-id |= ' + facetId + ']').each(function (index, facet_item) {
             if ($(facet_item).hasClass('js-facets-widget')) {
               $(facet_item).unbind('facets_filter.facets');
               $(facet_item).on('facets_filter.facets', function (event, url) {
@@ -104,8 +159,8 @@
     var facets_blocks = facetsBlocks();
 
     // Remove All Range Input Form Facet Blocks from being updated.
-    if(settings.facets && settings.facets.rangeInput) {
-      $.each(settings.facets.rangeInput, function (index, value){
+    if (settings.facets && settings.facets.rangeInput) {
+      $.each(settings.facets.rangeInput, function (index, value) {
         delete facets_blocks[value.facetId];
       });
     }
@@ -121,17 +176,17 @@
 
     // Update facets summary block.
     if (updateFacetsSummaryBlock()) {
-      var facet_summary_wrapper_id = $('[data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax.facets_summary_id + ']').attr('id');
-      var facet_summary_block_id = '';
-      if (facet_summary_wrapper_id.indexOf('--') !== -1) {
-        facet_summary_block_id = facet_summary_wrapper_id.substring(0, facet_summary_wrapper_id.indexOf('--')).replace('block-', '');
-      }
-      else {
-        facet_summary_block_id = facet_summary_wrapper_id.replace('block-', '');
-      }
       facet_settings.submit.update_summary_block = true;
-      facet_settings.submit.facet_summary_block_id = facet_summary_block_id;
-      facet_settings.submit.facet_summary_wrapper_id = settings.facets_views_ajax.facets_summary_ajax.facets_summary_id;
+      facet_settings.submit.facet_summary_plugin_ids = {};
+      let summary_selector = '[data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax_summary.facets_summary_id + ']';
+      if (settings.facets_views_ajax.facets_summary_ajax_summary_count !== undefined) {
+        summary_selector += ', [data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax_summary_count.facets_summary_id + ']';
+      }
+      $(summary_selector).each(function (index, summaryWrapper) {
+        let summaryPluginId = $(summaryWrapper).attr('data-drupal-facets-summary-plugin-id');
+        let summaryPluginIdWrapper = $(summaryWrapper).attr('id');
+        facet_settings.submit.facet_summary_plugin_ids[summaryPluginIdWrapper] = summaryPluginId;
+      });
     }
 
     Drupal.ajax(facet_settings).execute();
@@ -143,7 +198,7 @@
     var settings = drupalSettings;
     var update_summary = false;
 
-    if (settings.facets_views_ajax.facets_summary_ajax) {
+    if (settings.facets_views_ajax.facets_summary_ajax_summary || settings.facets_views_ajax.facets_summary_ajax_summary_count) {
       update_summary = true;
     }
 
@@ -162,8 +217,8 @@
           return v.slice(block_id_start.length, v.length);
         }
       }).join();
-      var block_selector = '#' + $(this).attr('id');
-      facets_blocks[block_id] = block_selector;
+      var block_selector = $(this).attr('id');
+      facets_blocks[block_selector] = block_id;
     });
 
     return facets_blocks;
@@ -204,12 +259,12 @@
   }
 
   // Helper function to add exposed form data to facets url
-  var addExposedFiltersToFacetsUrl = function(href, view_name, view_display_id) {
+  var addExposedFiltersToFacetsUrl = function (href, view_name, view_display_id) {
     var $exposed_form = $('form#views-exposed-form-' + view_name.replace(/_/g, '-') + '-' + view_display_id.replace(/_/g, '-'));
 
     var params = Drupal.Views.parseQueryString(href);
 
-    $.each($exposed_form.serializeArray(), function() {
+    $.each($exposed_form.serializeArray(), function () {
       params[this.name] = this.value;
     });
 
diff --git a/modules/facets_summary/facets_summary.module b/modules/facets_summary/facets_summary.module
index 6720862c..af2dc3f6 100644
--- a/modules/facets_summary/facets_summary.module
+++ b/modules/facets_summary/facets_summary.module
@@ -98,3 +98,26 @@ function facets_summary_theme_suggestions_facets_summary_item_list(array $variab
 function facets_summary_preprocess_facets_summary_item_list(array &$variables) {
   template_preprocess_item_list($variables);
 }
+
+/**
+ * Implements hook_preprocess_block().
+ *
+ * Adds some classes for hiding empty blocks, working ajax, and more.
+ */
+function facets_summary_preprocess_block(&$variables) {
+  if ($variables['configuration']['provider'] == 'facets_summary') {
+    // Hide the block if it's empty.
+    if (!empty($variables['content']['facets_summary']['#attributes'])) {
+      $attributes = $variables['content']['facets_summary']['#attributes'];
+      if (isset($attributes['class'])) {
+        if (in_array('hidden', $attributes['class'])) {
+          // Add the Drupal class for hiding this for everyone, including screen
+          // readers. See hidden.module.css in the core system module.
+          $variables['attributes']['class'][] = 'hidden';
+        }
+      }
+    }
+    // For use ajax.
+    $variables['attributes']['data-drupal-block-facet-summary-id'] = $variables['configuration']['id'];
+  }
+}
diff --git a/modules/facets_summary/facets_summary.services.yml b/modules/facets_summary/facets_summary.services.yml
index ba1a6f5f..bd8f14ed 100644
--- a/modules/facets_summary/facets_summary.services.yml
+++ b/modules/facets_summary/facets_summary.services.yml
@@ -8,6 +8,8 @@ services:
       - '@plugin.manager.facets.facet_source'
       - '@plugin.manager.facets_summary.processor'
       - '@facets.manager'
+      - '@plugin.manager.facets.url_processor'
+      - '@facets.utility.url_generator'
   facets_summary.search_api_subscriber:
     class: Drupal\facets_summary\EventSubscriber\SearchApiSubscriber
     arguments: ['@entity_type.manager']
diff --git a/modules/facets_summary/src/FacetsSummaryManager/DefaultFacetsSummaryManager.php b/modules/facets_summary/src/FacetsSummaryManager/DefaultFacetsSummaryManager.php
index 105c4380..0adafc09 100644
--- a/modules/facets_summary/src/FacetsSummaryManager/DefaultFacetsSummaryManager.php
+++ b/modules/facets_summary/src/FacetsSummaryManager/DefaultFacetsSummaryManager.php
@@ -4,13 +4,17 @@ namespace Drupal\facets_summary\FacetsSummaryManager;
 
 use Drupal\Core\Link;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Url;
 use Drupal\facets\Exception\InvalidProcessorException;
 use Drupal\facets\FacetManager\DefaultFacetManager;
 use Drupal\facets\FacetSource\FacetSourcePluginManager;
+use Drupal\facets\Result\Result;
 use Drupal\facets_summary\Processor\BuildProcessorInterface;
 use Drupal\facets_summary\Processor\ProcessorInterface;
 use Drupal\facets_summary\Processor\ProcessorPluginManager;
 use Drupal\facets_summary\FacetsSummaryInterface;
+use Drupal\facets\UrlProcessor\UrlProcessorPluginManager;
+use Drupal\facets\Utility\FacetsUrlGenerator;
 
 /**
  * The facet summary manager.
@@ -45,6 +49,20 @@ class DefaultFacetsSummaryManager {
    */
   protected $facetManager;
 
+  /**
+   * The url processor plugin manager.
+   *
+   * @var \Drupal\facets\UrlProcessor\UrlProcessorPluginManager
+   */
+  protected $urlProcessorPluginManager;
+
+  /**
+   * The facets url generator.
+   *
+   * @var \Drupal\facets\Utility\FacetsUrlGenerator
+   */
+  protected $facetsUrlGenerator;
+
   /**
    * Constructs a new instance of the DefaultFacetManager.
    *
@@ -54,11 +72,17 @@ class DefaultFacetsSummaryManager {
    *   The facets summary processor plugin manager.
    * @param \Drupal\facets\FacetManager\DefaultFacetManager $facet_manager
    *   The facet manager service.
+   * @param \Drupal\facets\UrlProcessor\UrlProcessorPluginManager $url_processor_plugin_manager
+   *   The url processor plugin manager.
+   * @param \Drupal\facets\Utility\FacetsUrlGenerator $facets_url_generator
+   *   The facets url generator.
    */
-  public function __construct(FacetSourcePluginManager $facet_source_manager, ProcessorPluginManager $processor_plugin_manager, DefaultFacetManager $facet_manager) {
+  public function __construct(FacetSourcePluginManager $facet_source_manager, ProcessorPluginManager $processor_plugin_manager, DefaultFacetManager $facet_manager, UrlProcessorPluginManager $url_processor_plugin_manager, FacetsUrlGenerator $facets_url_generator) {
     $this->facetSourcePluginManager = $facet_source_manager;
     $this->processorPluginManager = $processor_plugin_manager;
     $this->facetManager = $facet_manager;
+    $this->urlProcessorPluginManager = $url_processor_plugin_manager;
+    $this->facetsUrlGenerator = $facets_url_generator;
   }
 
   /**
@@ -117,16 +141,48 @@ class DefaultFacetsSummaryManager {
       '#theme' => 'facets_summary_item_list',
       '#facet_summary_id' => $facets_summary->id(),
       '#attributes' => [
-        'data-drupal-facets-summary-id' => $facets_summary->id(),
+        'class' => ['facet-summary__items'],
       ],
     ];
 
     $results = [];
     foreach ($facets as $facet) {
-      $show_count = $facets_config[$facet->id()]['show_count'];
-      $results = array_merge($results, $this->buildResultTree($show_count, $facet->getResults()));
+      $enabled_processors = $facet->getProcessors(TRUE);
+      if (isset($enabled_processors["range_slider"])) {
+        $active_values = $facet->getActiveItems();
+        if ($active_values) {
+          $min = $active_values[0][0];
+          $max = $active_values[0][1];
+          $url_processor = $this->urlProcessorPluginManager->createInstance($facet->getFacetSourceConfig()->getUrlProcessorName(), ['facet' => $facet]);
+          $active_filters = $url_processor->getActiveFilters();
+          if (isset($active_filters[''])) {
+            unset($active_filters['']);
+          }
+          unset($active_filters[$facet->id()]);
+          // Only if there are still active filters, use url generator.
+          if ($active_filters) {
+            $url = $this->facetsUrlGenerator->getUrl($active_filters, FALSE);
+          }
+          else {
+            // @todo Keep non-facet related get params.
+            $url = Url::fromUserInput($facets_summary->getFacetSource()->getPath());
+          }
+          $result_without_current_rangeslider = new Result($facet, "(min:$min,max:$max)", " $min - $max", 1);
+          $result_without_current_rangeslider->setActiveState(TRUE);
+          $result_without_current_rangeslider->setUrl($url);
+          $results = array_merge($results, $this->buildResultTree(FALSE, [$result_without_current_rangeslider]));
+        }
+      }
+      else {
+        $show_count = $facets_config[$facet->id()]['show_count'];
+        $results = array_merge($results, $this->buildResultTree($show_count, $facet->getResults()));
+      }
+
+    }
+
+    if ($results) {
+      $build['#items'] = $results;
     }
-    $build['#items'] = $results;
 
     // Allow our Facets Summary processors to alter the build array in a
     // configured order.
diff --git a/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php b/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php
index 5b9255af..4ace9922 100644
--- a/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php
+++ b/modules/facets_summary/src/Plugin/Block/FacetsSummaryBlock.php
@@ -6,6 +6,7 @@ use Drupal\Core\Block\BlockBase;
 use Drupal\Core\Cache\UncacheableDependencyTrait;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Url;
+use Drupal\Component\Utility\Html;
 use Drupal\facets_summary\Entity\FacetsSummary;
 use Drupal\facets_summary\FacetsSummaryBlockInterface;
 use Drupal\facets_summary\FacetsSummaryManager\DefaultFacetsSummaryManager;
@@ -88,25 +89,46 @@ class FacetsSummaryBlock extends BlockBase implements FacetsSummaryBlockInterfac
     $facets_summary = $this->getEntity();
 
     // Let the facet_manager build the facets.
-    $build = $this->facetsSummaryManager->build($facets_summary);
+    $build = [];
 
-    // Add contextual links only when we have results.
-    if (!empty($build)) {
-      $build['#contextual_links']['facets_summary'] = [
-        'route_parameters' => ['facets_summary' => $facets_summary->id()],
+    // Let the facet_manager build the facets.
+    $summary_build = $this->facetsSummaryManager->build($facets_summary);
+
+    if ($summary_build) {
+      $build = [
+        'facets_summary' => [
+          '#type' => 'container',
+          '#contextual_links' => [
+            'facets_summary' => [
+              'route_parameters' => ['facets_summary' => $facets_summary->id()],
+            ],
+          ],
+          '#attributes' => [
+            'data-drupal-facets-summary-id' => $facets_summary->id(),
+            'data-drupal-facets-summary-plugin-id' => $this->getPluginId(),
+            'id' => Html::getUniqueId(str_replace(':', '-', $this->getPluginId())),
+            'class' => [
+              'facets-summary-block__wrapper',
+            ],
+          ],
+          'summary_build' => $summary_build,
+        ],
       ];
-    }
 
-    /** @var \Drupal\views\ViewExecutable $view */
-    if ($view = $facets_summary->getFacetSource()->getViewsDisplay()) {
-      $build['#attached']['drupalSettings']['facets_views_ajax'] = [
-        'facets_summary_ajax' => [
+      // Hidden empty result.
+      if (!isset($summary_build['#items']) && !isset($summary_build['#message'])) {
+        $build['facets_summary']['#attributes']['class'][] = 'hidden';
+      }
+
+      /** @var \Drupal\views\ViewExecutable $view */
+      if ($view = $facets_summary->getFacetSource()->getViewsDisplay()) {
+        $build['#attached']['drupalSettings']['facets_views_ajax']['facets_summary_ajax_' . $facets_summary->id()] = [
           'facets_summary_id' => $facets_summary->id(),
           'view_id' => $view->id(),
           'current_display_id' => $view->current_display,
           'ajax_path' => Url::fromRoute('views.ajax')->toString(),
-        ],
-      ];
+        ];
+      }
     }
 
     return $build;
diff --git a/src/Controller/FacetBlockAjaxController.php b/src/Controller/FacetBlockAjaxController.php
index d1d446d1..9b2cf1ad 100644
--- a/src/Controller/FacetBlockAjaxController.php
+++ b/src/Controller/FacetBlockAjaxController.php
@@ -5,10 +5,10 @@ namespace Drupal\facets\Controller;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\InvokeCommand;
 use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Core\Block\BlockManager;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Path\CurrentPathStack;
 use Drupal\Core\PathProcessor\PathProcessorManager;
-use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Routing\CurrentRouteMatch;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -28,13 +28,6 @@ class FacetBlockAjaxController extends ControllerBase {
    */
   protected $storage;
 
-  /**
-   * The renderer.
-   *
-   * @var \Drupal\Core\Render\RendererInterface
-   */
-  protected $renderer;
-
   /**
    * The current path.
    *
@@ -63,11 +56,16 @@ class FacetBlockAjaxController extends ControllerBase {
    */
   protected $currentRouteMatch;
 
+  /**
+   * The block manager service.
+   *
+   * @var \Drupal\Core\Block\BlockManager
+   */
+  protected $blockManager;
+
   /**
    * Constructs a FacetBlockAjaxController object.
    *
-   * @param \Drupal\Core\Render\RendererInterface $renderer
-   *   The renderer service.
    * @param \Drupal\Core\Path\CurrentPathStack $currentPath
    *   The current path service.
    * @param \Symfony\Component\Routing\RouterInterface $router
@@ -76,14 +74,16 @@ class FacetBlockAjaxController extends ControllerBase {
    *   The path processor manager.
    * @param \Drupal\Core\Routing\CurrentRouteMatch $currentRouteMatch
    *   The current route match service.
+   * @param \Drupal\Core\Block\BlockManager $blockManager
+   *   The block manager service.
    */
-  public function __construct(RendererInterface $renderer, CurrentPathStack $currentPath, RouterInterface $router, PathProcessorManager $pathProcessor, CurrentRouteMatch $currentRouteMatch) {
+  public function __construct(CurrentPathStack $currentPath, RouterInterface $router, PathProcessorManager $pathProcessor, CurrentRouteMatch $currentRouteMatch, BlockManager $blockManager) {
     $this->storage = $this->entityTypeManager()->getStorage('block');
-    $this->renderer = $renderer;
     $this->currentPath = $currentPath;
     $this->router = $router;
     $this->pathProcessor = $pathProcessor;
     $this->currentRouteMatch = $currentRouteMatch;
+    $this->blockManager = $blockManager;
   }
 
   /**
@@ -91,11 +91,11 @@ class FacetBlockAjaxController extends ControllerBase {
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('renderer'),
       $container->get('path.current'),
       $container->get('router'),
       $container->get('path_processor_manager'),
-      $container->get('current_route_match')
+      $container->get('current_route_match'),
+      $container->get('plugin.manager.block')
     );
   }
 
@@ -122,9 +122,6 @@ class FacetBlockAjaxController extends ControllerBase {
       throw new NotFoundHttpException('No facet link or facet blocks found.');
     }
 
-    // Make sure we are not updating blocks multiple times.
-    $facets_blocks = array_unique($facets_blocks);
-
     $new_request = Request::create($path);
     $request_stack = new RequestStack();
     $processed = $this->pathProcessor->processInbound($path, $new_request);
@@ -136,40 +133,56 @@ class FacetBlockAjaxController extends ControllerBase {
 
     $container = \Drupal::getContainer();
     $container->set('request_stack', $request_stack);
-    $active_facet = $request->request->get('active_facet');
-
-    // Build the facets blocks found for the current request and update.
-    foreach ($facets_blocks as $block_id => $block_selector) {
-      $block_entity = $this->storage->load($block_id);
-
-      if ($block_entity) {
-        // Render a block, then add it to the response as a replace command.
-        $block_view = $this->entityTypeManager
-          ->getViewBuilder('block')
-          ->view($block_entity);
-
-        $block_view = (string) $this->renderer->renderPlain($block_view);
-        $response->addCommand(new ReplaceCommand($block_selector, $block_view));
+    foreach ($facets_blocks as $block_selector => $block_id) {
+      // Facet block render array.
+      $block_view = NULL;
+      // Re prepare from css standarts.
+      $block_id = str_replace(['--', '-'], [':', '_'], $block_id);
+
+      // @todo We should not create an instance if we have already created one.
+      $block_instance = $this->blockManager->createInstance($block_id);
+      if ($block_instance) {
+        $block_view = $block_instance->build();
+        if ($block_view) {
+          // Replace content current ID selector.
+          $response->addCommand(new ReplaceCommand('#' . $block_selector, $block_view));
+          // Hide or show block.
+          $facet_id = explode(':', $block_id)[1];
+          $hide_show_selector = '[data-drupal-block-facet-id = "' . $facet_id . '"]';
+          if (!empty($block_view['facet_block']['#attributes']['class']) && in_array('hidden', $block_view['facet_block']['#attributes']['class'])) {
+            $response->addCommand(new InvokeCommand($hide_show_selector, 'addClass', ['hidden']));
+          }
+          else {
+            $response->addCommand(new InvokeCommand($hide_show_selector, 'removeClass', ['hidden']));
+          }
+        }
       }
     }
 
-    $response->addCommand(new InvokeCommand('[data-block-plugin-id="' . $active_facet . '"]', 'addClass', ['facet-active']));
-
     // Update filter summary block.
     $update_summary_block = $request->request->get('update_summary_block');
     if ($update_summary_block) {
-      $facet_summary_block_id = $request->request->get('facet_summary_block_id');
-      $facet_summary_wrapper_id = $request->request->get('facet_summary_wrapper_id');
-      $facet_summary_block_id = str_replace('-', '_', $facet_summary_block_id);
-
-      if ($facet_summary_block_id) {
-        $block_entity = $this->storage->load($facet_summary_block_id);
-        $block_view = $this->entityTypeManager
-          ->getViewBuilder('block')
-          ->view($block_entity);
-        $block_view = (string) $this->renderer->renderPlain($block_view);
-
-        $response->addCommand(new ReplaceCommand('[data-drupal-facets-summary-id=' . $facet_summary_wrapper_id . ']', $block_view));
+      $facet_summary_plugin_ids = $request->request->get('facet_summary_plugin_ids');
+      foreach ($facet_summary_plugin_ids as $block_selector => $summary_plugin_id) {
+        // Facet summary block render array.
+        $block_view = NULL;
+        // @todo We should not create an instance if we have already created one.
+        $block_instance = $this->blockManager->createInstance($summary_plugin_id);
+        if ($block_instance) {
+          $block_view = $block_instance->build();
+          if ($block_view) {
+            // Replace content facets summary plugin ID selector.
+            $response->addCommand(new ReplaceCommand('[data-drupal-facets-summary-plugin-id = "' . $summary_plugin_id . '"]', $block_view));
+            // Hide or show block.
+            $hide_show_selector = '[data-drupal-block-facet-summary-id = "' . $summary_plugin_id . '"]';
+            if (!empty($block_view['facets_summary']['#attributes']['class']) && in_array('hidden', $block_view['facets_summary']['#attributes']['class'])) {
+              $response->addCommand(new InvokeCommand($hide_show_selector, 'addClass', ['hidden']));
+            }
+            else {
+              $response->addCommand(new InvokeCommand($hide_show_selector, 'removeClass', ['hidden']));
+            }
+          }
+        }
       }
     }
 
diff --git a/src/Plugin/Block/FacetBlock.php b/src/Plugin/Block/FacetBlock.php
index 676c5731..509552fe 100644
--- a/src/Plugin/Block/FacetBlock.php
+++ b/src/Plugin/Block/FacetBlock.php
@@ -3,6 +3,7 @@
 namespace Drupal\facets\Plugin\Block;
 
 use Drupal\Core\Block\BlockBase;
+use Drupal\Component\Utility\Html;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Form\FormStateInterface;
@@ -79,39 +80,60 @@ class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface {
       return [];
     }
 
+    $build = [];
+
     // Let the facet_manager build the facets.
-    $build = $this->facetManager->build($facet);
+    $facet_build = $this->facetManager->build($facet);
 
-    if (!empty($build)) {
+    if ($facet_build) {
       // Add extra elements from facet source, for example, ajax scripts.
       // @see Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay
-      /* @var \Drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source */
+      /** @var \Drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source */
       $facet_source = $facet->getFacetSource();
-      $build += $facet_source->buildFacet();
-
-      // Add contextual links only when we have results.
-      $build['#contextual_links']['facets_facet'] = [
-        'route_parameters' => ['facets_facet' => $facet->id()],
+      $facet_build += $facet_source->buildFacet();
+
+      $build = [
+        '#type' => 'container',
+        '#contextual_links' => [
+          'facets_facet' => [
+            'route_parameters' => ['facets_facet' => $facet->id()],
+          ],
+        ],
+        '#attributes' => [
+          'class' => ['block-facet__wrapper'],
+        ],
+        $facet_build,
       ];
 
-      if (!empty($build[0]['#attributes']['class']) && in_array('facet-active', $build[0]['#attributes']['class'], TRUE)) {
-        $build['#attributes']['class'][] = 'facet-active';
-      }
-      else {
-        $build['#attributes']['class'][] = 'facet-inactive';
+      // Add css classes.
+      if (!empty($facet_build[0]['#attributes']['class'])) {
+        $css_classes = $facet_build[0]['#attributes']['class'];
+        // Active/inactive css classes.
+        if (in_array('facet-active', $css_classes)) {
+          $build['#attributes']['class'][] = 'facet-active';
+        }
+        else {
+          $build['#attributes']['class'][] = 'facet-inactive';
+        }
+        // Whether it is necessary to add hide css class.
+        if (in_array('facet-hidden', $css_classes)) {
+          $build['#attributes']['class'][] = 'hidden';
+        }
       }
 
       // Add classes needed for ajax.
-      if (!empty($build['#use_ajax'])) {
+      if (!empty($facet_build['#use_ajax'])) {
         $build['#attributes']['class'][] = 'block-facets-ajax';
-        // The configuration block id isn't always set in the configuration.
-        if (isset($this->configuration['block_id'])) {
-          $build['#attributes']['class'][] = 'js-facet-block-id-' . $this->configuration['block_id'];
-        }
-        else {
-          $build['#attributes']['class'][] = 'js-facet-block-id-' . $this->pluginId;
-        }
+        $block_id = str_replace(':', '--', $this->pluginId);
+        $block_id = Html::cleanCssIdentifier($block_id);
+        $build['#attributes']['class'][] = 'js-facet-block-id-' . $block_id;
+        $build['#attributes']['id'] = Html::getUniqueId($block_id);
       }
+
+      // To render correctly in different situations.
+      $build = [
+        'facet_block' => $build,
+      ];
     }
 
     return $build;
-- 
GitLab