views_faceted_filters_js-0.0.18/src/Plugin/views/style/ViewsFacetedFiltersJsGrid.php
src/Plugin/views/style/ViewsFacetedFiltersJsGrid.php
<?php
namespace Drupal\views_faceted_filters_js\Plugin\views\style;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\style\Grid;
/**
* Grid with Client-Side Faceted Filters style plugin.
*
* @ViewsStyle(
* id = "views_view_faceted_filters_js_grid",
* title = @Translation("Grid, with Client-Side Faceted Filters"),
* help = @Translation("Provides a very simple data-attributes based client-side JavaScript Faceted Search capabilities for views."),
* theme = "views_view_faceted_filters_js_grid",
* display_types = {"normal"}
* )
*/
class ViewsFacetedFiltersJsGrid extends Grid {
/**
* Does the style plugin for itself support to add fields to its output.
*
* @var bool
*/
protected $usesFields = TRUE;
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['injection_selector'] = ['default' => '#your-facets-container'];
$options['injection_method'] = ['default' => 'beforeend'];
$options['facets_show'] = ['default' => TRUE];
$options['facets'] = ['default' => []];
$options['fulltext_search_show'] = ['default' => FALSE];
$options['fulltext_search_title'] = ['default' => 'Search'];
$options['fulltext_search_description'] = ['default' => ''];
$options['fulltext_search_placeholder'] = ['default' => ''];
$options['fulltext_search_delay'] = ['default' => 500];
$options['reset_button_show'] = ['default' => TRUE];
$options['reset_button_label'] = ['default' => 'Reset'];
$options['results_empty_text'] = ['default' => 'No matching results.'];
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
// @todo POSTPONED on: https://www.drupal.org/project/drupal/issues/2190333
// $form['csff'] = [
// '#type' => 'details',
// '#title' => t('client-side faceted filters'),
// '#open' => TRUE,
// ];.
$form['injection_selector'] = [
'#type' => 'textfield',
// '#group' => 'csff',
'#title' => $this->t('Injection selector'),
'#description' => $this->t('Enter a unique CSS (preferrably "#id") selector where to inject the facets in the DOM. Region example: ".region-sidebar-first"'),
'#size' => '30',
'#required' => TRUE,
'#default_value' => $this->options['injection_selector'],
];
$form['injection_method'] = [
'#type' => 'radios',
// '#group' => 'csff',
'#title' => $this->t('Injection method'),
'#options' => [
'afterbegin' => $this->t('Prepend'),
'beforeend' => $this->t('Append (Default)'),
'beforebegin' => $this->t('Before'),
'afterend' => $this->t('After'),
'replace' => $this->t('Replace'),
],
'#required' => TRUE,
'#default_value' => $this->options['injection_method'],
'#description' => $this->t('Select how to inject the facets on the selected DOM element.'),
];
// Facets:
// @todo POSTPONED on: https://www.drupal.org/project/drupal/issues/2190333
// $form['facets'] = [
// '#type' => 'details',
// '#title' => t('Faceted Filters'),
// '#open' => TRUE,
// '#group' => 'csff',
// ];.
$form['facets_show'] = [
'#type' => 'checkbox',
// '#group' => 'facets',
'#title' => $this->t('Show faceted filters'),
'#description' => $this->t('Shows as the faceted filters'),
'#default_value' => $this->options['facets_show'],
];
// To create the complex facets selection table
// we use a helper method for better readability:
$this->buildOptionsFormFacetsTable($form, $form_state);
// Fulltext search:
// @todo POSTPONED on: https://www.drupal.org/project/drupal/issues/2190333
// $form['fulltext_search'] = [
// '#type' => 'details',
// '#title' => t('Fulltext search'),
// '#open' => TRUE,
// '#group' => 'csff',
// ];.
$form['fulltext_search_show'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show fulltext search'),
// '#group' => 'fulltext_search',
'#description' => $this->t('Show the fulltext search input field.'),
'#default_value' => $this->options['fulltext_search_show'],
];
$form['fulltext_search_title'] = [
'#type' => 'textfield',
// '#group' => 'fulltext_search',
'#title' => $this->t('Fulltext search block label'),
'#description' => $this->t('Label of the fulltext search block.'),
'#size' => '30',
'#default_value' => $this->options['fulltext_search_title'],
'#states' => [
'visible' => [
':input[name="style_options[fulltext_search_show]"]' => ['checked' => TRUE],
],
],
];
$form['fulltext_search_description'] = [
'#type' => 'textfield',
'#title' => $this->t('Fulltext search block description'),
// '#group' => 'fulltext_search',
'#description' => $this->t('Description text of the fulltext search block.'),
'#size' => '30',
'#default_value' => $this->options['fulltext_search_description'],
'#states' => [
'visible' => [
':input[name="style_options[fulltext_search_show]"]' => ['checked' => TRUE],
],
],
];
$form['fulltext_search_placeholder'] = [
'#type' => 'textfield',
'#title' => $this->t('Fulltext search placeholder'),
// '#group' => 'fulltext_search',
'#description' => $this->t('Placeholder text for the fulltext search input field.'),
'#size' => '30',
'#default_value' => $this->options['fulltext_search_placeholder'],
'#states' => [
'visible' => [
':input[name="style_options[fulltext_search_show]"]' => ['checked' => TRUE],
],
],
];
$form['fulltext_search_delay'] = [
'#type' => 'textfield',
'#title' => $this->t('Fulltext search delay (ms)'),
// '#group' => 'fulltext_search',
'#description' => $this->t('The delay in miliseconds, before the search input value is submitted.'),
'#required' => TRUE,
'#size' => '5',
'#default_value' => $this->options['fulltext_search_delay'],
'#states' => [
'visible' => [
':input[name="style_options[fulltext_search_show]"]' => ['checked' => TRUE],
],
],
];
// Reset button:
// @todo POSTPONED on: https://www.drupal.org/project/drupal/issues/2190333
// $form['reset_button'] = [
// '#type' => 'details',
// '#title' => t('Reset button'),
// '#open' => TRUE,
// '#group' => 'csff',
// ];.
$form['reset_button_show'] = [
'#type' => 'checkbox',
// '#group' => 'reset_button',
'#title' => $this->t('Show reset button'),
'#description' => $this->t('Displays a reset button.'),
'#default_value' => $this->options['reset_button_show'],
];
$form['reset_button_title'] = [
'#type' => 'textfield',
// '#group' => 'fulltext_search',
'#title' => $this->t('Reset button block label'),
'#description' => $this->t('Label of the reset button block.'),
'#size' => '30',
'#default_value' => $this->options['reset_button_title'],
'#states' => [
'visible' => [
':input[name="style_options[reset_button_show]"]' => ['checked' => TRUE],
],
],
];
$form['reset_button_description'] = [
'#type' => 'textfield',
'#title' => $this->t('Reset button block description'),
// '#group' => 'fulltext_search',
'#description' => $this->t('Description text of the reset button block.'),
'#size' => '30',
'#default_value' => $this->options['reset_button_description'],
'#states' => [
'visible' => [
':input[name="style_options[reset_button_show]"]' => ['checked' => TRUE],
],
],
];
$form['reset_button_label'] = [
'#type' => 'textfield',
'#title' => $this->t('Reset button label'),
// '#group' => 'reset_button',
'#description' => $this->t('Label of the reset button.'),
'#size' => '30',
'#required' => TRUE,
'#default_value' => $this->options['reset_button_label'],
// Only show if reset button is used:
'#states' => [
'visible' => [
':input[name="style_options[reset_button_show]"]' => ['checked' => TRUE],
],
],
];
$form['results_empty_text'] = [
'#type' => 'textarea',
'#title' => $this->t('Empty results text'),
'#description' => $this->t('Text to display when there are no matching results.'),
'#required' => TRUE,
'#default_value' => $this->options['results_empty_text'],
];
}
/**
* Builds the Options Form Facets Table.
*
* @param mixed $form
* The form.
* @param Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
protected function buildOptionsFormFacetsTable(&$form, FormStateInterface $form_state) {
$facets = $this->sanitizeColumns($this->options['facets']);
$field_names = $this->displayHandler->getFieldLabels();
$form['facets'] = [
'#type' => 'table',
'#caption' => $this
->t('Expose Fields as Client-side Facets:'),
'#description' => $this->t('Select and configure the fields you want to expose as Client-side facets.'),
'#header' => [
$this->t('Field name'),
$this->t('Expose as facet'),
$this->t('Facet title'),
$this->t('Facet description'),
$this->t('Show result count'),
$this->t('Sort values by'),
$this->t('Sort order'),
$this->t('Multivalue separator'),
$this->t('Facet weight'),
],
// Does not work as expected, hides the whole form:
// '#states' => [
// 'visible' => [
// ':input[name="style_options[facets_show]"]' => ['checked' => TRUE],
// ],
// ],
];
foreach ($facets as $field_id => $facet) {
$form['facets'][$field_id]['name'] = [
'#type' => 'markup',
'#markup' => $field_names[$field_id] . ' (' . $field_id . ')',
'#title' => $this->t('Field name'),
'#title_display' => 'invisible',
];
$form['facets'][$field_id]['enabled'] = [
'#type' => 'checkbox',
'#title' => $this->t('Expose as facet'),
'#title_display' => 'invisible',
'#default_value' => $this->options['facets'][$field_id]['enabled'],
];
$form['facets'][$field_id]['title'] = [
'#type' => 'textfield',
'#title' => $this->t('Facet title'),
'#title_display' => 'invisible',
'#default_value' => $this->options['facets'][$field_id]['title'] ?: '',
'#states' => [
'visible' => [
':input[name="style_options[facets][' . $field_id . '][enabled]"]' => ['checked' => TRUE],
],
],
];
$form['facets'][$field_id]['description'] = [
'#type' => 'textfield',
'#title' => $this->t('Facet description'),
'#title_display' => 'invisible',
'#default_value' => $this->options['facets'][$field_id]['description'] ?: '',
'#states' => [
'visible' => [
':input[name="style_options[facets][' . $field_id . '][enabled]"]' => ['checked' => TRUE],
],
],
];
$form['facets'][$field_id]['count_show'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show result count'),
'#title_display' => 'invisible',
'#default_value' => $this->options['facets'][$field_id]['count_show'] ?? TRUE,
'#states' => [
'visible' => [
':input[name="style_options[facets][' . $field_id . '][enabled]"]' => ['checked' => TRUE],
],
],
];
$form['facets'][$field_id]['values_sort'] = [
'#type' => 'radios',
// '#group' => 'facets',
'#title' => $this->t('Sort values by'),
'#title_display' => 'invisible',
'#options' => [
'' => $this->t('None'),
'value' => $this->t('Value (natural)'),
'count' => $this->t('Count'),
],
// '#description' => $this->t('Select if / how to sort the facet values.'),
'#default_value' => $this->options['facets'][$field_id]['values_sort'] ?: '',
'#states' => [
'visible' => [
':input[name="style_options[facets][' . $field_id . '][enabled]"]' => ['checked' => TRUE],
],
],
];
$form['facets'][$field_id]['values_sort_direction'] = [
'#type' => 'radios',
// '#group' => 'facets',
'#title' => $this->t('Sort ascending / descending'),
'#title_display' => 'invisible',
'#options' => [
'asc' => $this->t('Ascending'),
'desc' => $this->t('Descending'),
],
// '#description' => $this->t('Select the sort order.'),
'#default_value' => $this->options['facets'][$field_id]['values_sort_direction'] ?: 'asc',
'#states' => [
'visible' => [
':input[name="style_options[facets][' . $field_id . '][enabled]"]' => ['checked' => TRUE],
],
],
];
$form['facets'][$field_id]['multivalue_separator'] = [
'#type' => 'textfield',
// '#group' => 'csff',
'#title' => $this->t('Multivalue separator'),
'#title_display' => 'invisible',
// '#description' => $this->t('Separator for multi-value fields. Do not use a string that is used in regular facets texts or they might be split.'),
'#size' => '5',
'#required' => TRUE,
'#default_value' => $this->options['facets'][$field_id]['multivalue_separator'] ?: ', ',
'#states' => [
'visible' => [
':input[name="style_options[facets][' . $field_id . '][enabled]"]' => ['checked' => TRUE],
],
],
];
$form['facets'][$field_id]['weight'] = [
'#type' => 'select',
'#title' => $this->t('Facet weight'),
'#title_display' => 'invisible',
'#options' => range(0, 500),
'#default_value' => !empty($this->options['facets'][$field_id]['weight']) ? $this->options['facets'][$field_id]['weight'] : 0,
'#states' => [
'visible' => [
':input[name="style_options[facets][' . $field_id . '][enabled]"]' => ['checked' => TRUE],
],
],
];
}
}
/**
* Returns the value for the given facet (=field) id.
*
* Returns the value for the given facet (=field) id for
* the current row to build data attributes.
*
* Internally uses tokens as there's currently no better way to retrieve the
* field values.
*
* @param string $facet_id
* The facet (=field) id.
* @param bool $row_index
* The current row index.
*
* @return string
* The facet value for the current row.
*/
public function getFacetValue(string $facet_id, $row_index) {
return trim($this->tokenizeValue('{{ ' . $facet_id . ' }}', $row_index));
}
/**
* Copied from Drupal\views\Plugin\views\style\Table::sanitizeColumns()!
*
* Normalize a list of columns based upon the fields that are
* available. This compares the fields stored in the style handler
* to the list of fields actually in the view, removing fields that
* have been removed and adding new fields in their own column.
*
* - Each field must be in a column.
* - Each column must be based upon a field, and that field
* is somewhere in the column.
* - Any fields not currently represented must be added.
* - Columns must be re-ordered to match the fields.
*
* @param string[] $columns
* An array of all fields; the key is the id of the field and the
* value is the id of the column the field should be in.
* @param array $fields
* The fields to use for the columns. If not provided, they will
* be requested from the current display. The running render should
* send the fields through, as they may be different than what the
* display has listed due to access control or other changes.
*
* @return array
* An array of all the sanitized columns.
*/
public function sanitizeColumns(array $columns, array $fields = NULL) {
$sanitized = [];
if ($fields === NULL) {
$fields = $this->displayHandler->getOption('fields');
}
// Preconfigure the sanitized array so that the order is retained.
foreach ($fields as $field => $info) {
// Set to itself so that if it isn't touched, it gets column
// status automatically.
$sanitized[$field] = $field;
}
foreach ($columns as $field => $column) {
// first, make sure the field still exists.
if (!isset($sanitized[$field])) {
continue;
}
// If the field is the column, mark it so, or the column
// it's set to is a column, that's ok.
if ($field == $column) {
$sanitized[$field] = $column;
}
// Since we set the field to itself initially, ignoring
// the condition is ok; the field will get its column
// status back.
}
return $sanitized;
}
}
