diff --git a/js/overview.js b/js/overview.js new file mode 100644 index 0000000..623be64 --- /dev/null +++ b/js/overview.js @@ -0,0 +1,76 @@ +/** + * @file + * Paragraphs Collection overview behaviors. + */ + +(function ($, Drupal) { + + "use strict"; + + /** + * Filters the monitoring sensor overview table by input search filters. + * + * Text search input: input.table-filter-text + * Select sensor type: select.table-filter-select-sensor-type + * Select group: select.table-filter-select-group + * Target table: input.table-filter-text[data-table] + * Source text: .table-filter-text-source, .table-filter-group + * + * @type {Drupal~behavior} + */ + Drupal.behaviors.tableFilterByText = { + attach: function (context, settings) { + var $input = $('input.table-filter-text').once('table-filter-text'); + var $select_group = $('select.table-filter-select-group').once('table-filter-select-group'); + var $table = $($input.attr('data-table')); + var $rows; + var $details; + + function filterSensorList() { + var group_value = $select_group.val().toLowerCase(); + var input_value = $input.val().toLowerCase(); + + function showSensorRow(index, row) { + var $row = $(row); + var $group_sources = $row.find('.table-filter-group'); + var $input_value_sources = $row.find('.table-filter-text-source'); + var group_array = $group_sources.map(function() { + return $(this).text().toLowerCase(); + // return $.trim($(this).text().toLowerCase()); + }).get(); + + if (group_value && group_array.indexOf(group_value) == -1) { + $row.hide(); + return; + } + if (input_value && $input_value_sources.text().toLowerCase().indexOf(input_value) == -1) { + $row.hide(); + return; + } + $row.show(); + } + + $rows.each(showSensorRow); + + // // Filter if the length of the input field is longer than 2 characters + // // or an option is selected. + // if (group_value || input_value.length >= 2) { + // $rows.each(showSensorRow); + // } + // else { + // $rows.show(); + // $details.attr('open', false); + // } + } + + if ($table.length) { + $rows = $table.find('tbody tr'); + $input.on('keyup', filterSensorList); + $select_group.on('change', filterSensorList); + $select_sensor_type.on('change', filterSensorList); + $details = $table.find('details'); + } + } + }; + +}(jQuery, Drupal)); diff --git a/paragraphs_collection.libraries.yml b/paragraphs_collection.libraries.yml index 957af7e..6a869d4 100644 --- a/paragraphs_collection.libraries.yml +++ b/paragraphs_collection.libraries.yml @@ -1,3 +1,7 @@ +overview: + js: + js/overview.js: {} + background: css: theme: diff --git a/src/Controller/OverviewController.php b/src/Controller/OverviewController.php index 78316a8..160b1f4 100644 --- a/src/Controller/OverviewController.php +++ b/src/Controller/OverviewController.php @@ -10,6 +10,17 @@ use Symfony\Component\DependencyInjection\ContainerInterface; /** * The controller for overviews of Paragraphs Collection's discoverable items. + * + * The grid layout and style behavior plugins use pluggable YAML files which + * define the concrete layouts and styles that can be then used by specific + * Paragraph entities. There are plugin-specific classes for the discovery of + * these files. Paragraphs Types can restrict the available items. This + * controller creates overview pages for these items. + * + * @see \Drupal\paragraphs_collection\Plugin\paragraphs\Behavior\ParagraphsGridLayoutPlugin + * @see \Drupal\paragraphs_collection\Plugin\paragraphs\Behavior\ParagraphsStylePlugin + * @see \Drupal\paragraphs_collection\GridLayoutDiscoveryInterface + * @see \Drupal\paragraphs_collection\StyleDiscoveryInterface */ class OverviewController extends ControllerBase { @@ -28,25 +39,26 @@ class OverviewController extends ControllerBase { protected $styleDiscovery; /** - * Arrays of grid layout machine names keyed by Paragraph Type IDs. + * Discoverable items keyed by Paragraphs Types keyed by behavior plugins. * - * Each Paragraph Type has all enabled layouts in its array. Exception: An - * empty array means that all layouts are enabled for that Paragraphs Type. + * The structure is: + * @code + * ['' => ['' => ]] + * @endcode * - * @var array - */ - protected $paragraphsTypesLayoutsEnabled; - - /** - * Machine names of style groups keyed by IDs of Paragraph Types. + * Items: * - * Each Paragraph Type has the style group is uses as the value. Exception: An - * empty string means that all style groups are enabled for that Paragraphs - * Type. + * For the grid layout plugin, each Paragraphs Type has the machine names of + * all enabled layouts in an array. Exception: An empty array means that all + * layouts are enabled for that Paragraphs Type. + * + * For the style plugin, each Paragraphs Type has the machine name of the + * style group is uses as its value. Exception: An empty string means that all + * style groups are enabled for that Paragraphs Type. * * @var array */ - protected $paragraphsTypesStyleGroupsUsed; + protected $itemsPerParagraphsTypesPerPlugins; /** * Constructs a \Drupal\paragraphs_collection\Controller\OverviewController object. @@ -72,49 +84,31 @@ class OverviewController extends ControllerBase { } /** - * Finds all Paragraphs Types which allow a grid layout behaviour. + * Lists Paragraphs Types with discoverable items they allow for a plugin. * - * @return array - * Arrays of grid layout machine names keyed by IDs of Paragraph Types which - * have them enabled. A Paragraphs Type ID with an empty array as its value - * means that all grid layouts are enabled for that Paragraphs Type. - */ - public function getParagraphsTypesLayoutsEnabled() { - if (isset($this->paragraphsTypesLayoutsEnabled)) { - return $this->paragraphsTypesLayoutsEnabled; - } - - $paragraph_type_ids = \Drupal::entityQuery('paragraphs_type')->execute(); - $paragraphs_types_enabled = []; - foreach ($paragraph_type_ids as $paragraph_type_id) { - /** @var ParagraphsType $paragraphs_type */ - $paragraphs_type = ParagraphsType::load($paragraph_type_id); - $configuration = $paragraphs_type->getBehaviorPlugin('grid_layout')->getConfiguration(); - if (isset($configuration['enabled']) && $configuration['enabled']) { - $paragraphs_types_enabled[$paragraph_type_id] = []; - foreach ($configuration['available_grid_layouts'] as $key => $value) { - if ($value) { - $paragraphs_types_enabled[$paragraph_type_id][] = $key; - } - } - } - } - $this->paragraphsTypesLayoutsEnabled = $paragraphs_types_enabled; - - return $this->paragraphsTypesLayoutsEnabled; - } - - /** - * Finds all Paragraphs Types which allow a style behaviour. + * @param string + * The ID of the behaviour plugin that the discoverable items belong to. + * Only "grid_layout" and "style" are currently supported. * * @return array - * Style groups machine names keyed by IDs of Paragraph Types which have - * them enabled. A Paragraphs Type ID with an empty sting as its value means - * that all styles are enabled for that Paragraphs Type. + * Discoverable items keyed by by Paragraphs Type IDs. The form of the items + * depends on the behaviour plugin. + * For the grid layout plugin, these are arrays of grid layout machine names + * keyed by the IDs of Paragraph Types which have them enabled. A Paragraphs + * Type ID with an empty array as its value means that all grid layouts are + * enabled for that Paragraphs Type. + * For the style plugin, the items are style groups machine names keyed by + * the IDs of Paragraph Types which use them. A Paragraphs Type ID with an + * empty sting as its value means that all styles are enabled for that + * Paragraphs Type. + * For an an unsupported plugin, an empty array is returned. + * + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + * If the behaviour plugin ID is invalid. */ - public function getParagraphsTypesStyleGroupsUsed() { - if (isset($this->paragraphsTypesStyleGroupsUsed)) { - return $this->paragraphsTypesStyleGroupsUsed; + public function getItemsPerParagraphsTypesPerPlugin($plugin_id) { + if (isset($this->itemsPerParagraphsTypesPerPlugins[$plugin_id])) { + return $this->itemsPerParagraphsTypesPerPlugins[$plugin_id]; } $paragraph_type_ids = \Drupal::entityQuery('paragraphs_type')->execute(); @@ -122,14 +116,26 @@ class OverviewController extends ControllerBase { foreach ($paragraph_type_ids as $paragraph_type_id) { /** @var ParagraphsType $paragraphs_type */ $paragraphs_type = ParagraphsType::load($paragraph_type_id); - $configuration = $paragraphs_type->getBehaviorPlugin('style')->getConfiguration(); + $configuration = $paragraphs_type->getBehaviorPlugin($plugin_id)->getConfiguration(); if (isset($configuration['enabled']) && $configuration['enabled']) { - $paragraphs_types_enabled[$paragraph_type_id] = $configuration['group']; + switch ($plugin_id) { + case 'grid_layout': + $paragraphs_types_enabled[$paragraph_type_id] = []; + foreach ($configuration['available_grid_layouts'] as $key => $value) { + if ($value) { + $paragraphs_types_enabled[$paragraph_type_id][] = $key; + } + } + break; + case 'style': + $paragraphs_types_enabled[$paragraph_type_id] = $configuration['group']; + break; + default: + return $this->itemsPerParagraphsTypesPerPlugins[$plugin_id] = []; + } } } - $this->paragraphsTypesStyleGroupsUsed = $paragraphs_types_enabled; - - return $this->paragraphsTypesStyleGroupsUsed; + return $this->itemsPerParagraphsTypesPerPlugins[$plugin_id] = $paragraphs_types_enabled; } /** @@ -138,12 +144,12 @@ class OverviewController extends ControllerBase { * @param string $layout * The machine name of the grid layout. * -*@return array + * @return array * Array of IDs of Paragraphs Types that use the grid layout. */ public function getParagraphsTypesPerLayout($layout) { $paragraphs_types = []; - foreach ($this->getParagraphsTypesLayoutsEnabled() as $paragraphs_type => $enabled_layouts) { + foreach ($this->getItemsPerParagraphsTypesPerPlugin('grid_layout') as $paragraphs_type => $enabled_layouts) { if ($enabled_layouts == [] || in_array($layout, $enabled_layouts)) { $paragraphs_types[] = $paragraphs_type; } @@ -157,12 +163,13 @@ class OverviewController extends ControllerBase { * * @param string $style * The machine name of the style. + * * @return array * Array of IDs of Paragraphs Types that use the style. */ public function getParagraphsTypesPerStyle($style) { $paragraphs_types = []; - foreach ($this->getParagraphsTypesStyleGroupsUsed() as $paragraphs_type => $used_style_group) { + foreach ($this->getItemsPerParagraphsTypesPerPlugin('style') as $paragraphs_type => $used_style_group) { $enabled_styles = array_keys($this->styleDiscovery->getStyleOptions($used_style_group)); if (in_array($style, $enabled_styles)) { $paragraphs_types[] = $paragraphs_type; @@ -179,16 +186,53 @@ class OverviewController extends ControllerBase { * The output render array. */ public function layouts() { + $header = [ + 'label' => $this->t('Style'), + 'description' => $this->t('Description'), + 'use' => $this->t('Used in'), + ]; + $layouts = $this->gridDiscovery->getGridLayouts(); - foreach (array_keys($layouts) as $layout_id) { + $rows =[]; + foreach ($layouts as $layout_id => $layout) { $paragraphs_type_ids = $this->getParagraphsTypesPerLayout($layout_id); + $paragraphs_type_links = []; foreach ($paragraphs_type_ids as $paragraphs_type_id) { $paragraphs_type = ParagraphsType::load($paragraphs_type_id); - $path = $paragraphs_type->toUrl()->getInternalPath(); + + if($paragraphs_type_links != []) { + $paragraphs_type_links[] = ['#plain_text' => ', ']; + } + + $paragraphs_type_links[] = [ + '#type' => 'link', + '#title' => $paragraphs_type->label(), + '#url' => $paragraphs_type->toUrl(), + ]; } + + $rows[] = [ + 'label' => [ + '#plain_text' => $layout['title'], + ], + 'description' => [ + '#plain_text' => $layout['description'], + ], + 'use' => $paragraphs_type_links, + ]; } - return $this->content(['layouts']); + $table = [ + '#type' => 'table', + '#header' => $header, + '#sticky' => TRUE, + ]; + $table += $rows; + + $build['table'] = $table; + $build['#attached']['library'] = ['paragraphs_collection/overview']; + + return $build; } /** @@ -198,23 +242,100 @@ class OverviewController extends ControllerBase { * The output render array. */ public function styles() { + $header = [ + 'label' => $this->t('Style'), + 'description' => $this->t('Description'), + 'groups' => $this->t('Groups'), + 'use' => $this->t('Used in'), + ]; + $styles = $this->styleDiscovery->getStyles(); - foreach (array_keys($styles) as $style_id) { + $rows =[]; + foreach ($styles as $style_id => $style) { $paragraphs_type_ids = $this->getParagraphsTypesPerStyle($style_id); + $paragraphs_type_links = []; foreach ($paragraphs_type_ids as $paragraphs_type_id) { $paragraphs_type = ParagraphsType::load($paragraphs_type_id); - $path = $paragraphs_type->toUrl()->getInternalPath(); + + if($paragraphs_type_links != []) { + $paragraphs_type_links[] = ['#plain_text' => ', ']; + } + + $paragraphs_type_links[] = [ + '#type' => 'link', + '#title' => $paragraphs_type->label(), + '#url' => $paragraphs_type->toUrl(), + // @todo Figure out what classes and IDs we need. And find good names. +// '#prefix' => '', +// '#suffix' => '', + ]; } + + $row['label']['#plain_text'] = $style['title']; + $row['description']['#plain_text'] = $style['description']; + $row['groups']['#plain_text'] = implode(', ', $style['groups']); + $row['use'] = $paragraphs_type_links; + + $rows[] = $row; } - return $this->content(['styles']); + $table = [ + '#type' => 'table', + '#header' => $header, + '#sticky' => TRUE, + // @todo Figure out what classes and IDs we need. And find good names. +// '#attributes' => [ +// 'id' => 'overview-table', +// ], + ]; + $table += $rows; + + $build['table'] = $table; + $build['table']['#attributes']['class'][] = 'paragraphs-collection-overview'; + $build['#attached']['library'] = ['paragraphs_collection/overview']; + + $groups = $this->styleDiscovery->getStyleGroups(); + + $build['filters'] = [ + '#type' => 'fieldset', + '#attributes' => [ + 'class' => ['table-filter', 'js-show', 'form--inline'], + ], + '#weight' => -10, + '#title' => $this->t('Filter'), + ]; + $build['filters']['category'] = [ + '#type' => 'select', + '#empty_option' => $this->t('- All -'), + '#title' => $this->t('Group'), + '#options' => $groups, + '#attributes' => [ + 'class' => ['table-filter-select-group'], + ], + ]; + $build['filters']['text'] = [ + '#type' => 'search', + '#title' => $this->t('Style label or ID'), + '#size' => 40, + '#attributes' => [ + 'class' => ['table-filter-text'], + 'data-table' => '.paragraphs-collection-overview', + 'autocomplete' => 'off', + 'title' => $this->t('Enter a part of the style label or ID to filter by.'), + ], + ]; + + return $build; +// return $this->content(['styles']); } + // @todo This is currently no longer called. Delete/reuse documentation. /** * Generates an overview page of available layouts and/or styles. * * @param array $item_types * Array of item types to list. Supported types are 'styles' and 'layouts'. + * * @return array * The output render array. */ @@ -223,6 +344,7 @@ class OverviewController extends ControllerBase { '#type' => 'markup', '#markup' => t('Hello World! (' . implode('', $item_types) . ')'), ]; + return $build; }