diff --git a/modules/quant_search/css/quant_search.admin.css b/modules/quant_search/css/quant_search.admin.css new file mode 100644 index 00000000..4c44f052 --- /dev/null +++ b/modules/quant_search/css/quant_search.admin.css @@ -0,0 +1,33 @@ +.quant-search-page-edit-form #edit-facets td { + width: 20%; +} + +.quant-search-page-edit-form #edit-facets td input[type=submit] { + min-width: 155px; +} + +.quant-search-page-edit-form #edit-facets .facet-remove { + margin-top: 2.25rem; +} + +.quant-search-page-edit-form #edit-facets .facet-add { + display: block; +} + +@media (max-width: 1100px) { + + .quant-search-page-edit-form #edit-facets td { + display: block; + width: auto; + height: auto; + margin-left: 1rem; + } + + .quant-search-page-edit-form #edit-facets td.tabledrag-cell { + margin-left: auto; + } + + .quant-search-page-edit-form #edit-facets td.tabledrag-cell div { + width: calc(100% + .25rem); + } +} diff --git a/modules/quant_search/css/quant-search.css b/modules/quant_search/css/quant_search.algolia.css similarity index 100% rename from modules/quant_search/css/quant-search.css rename to modules/quant_search/css/quant_search.algolia.css diff --git a/modules/quant_search/js/quant-search.js b/modules/quant_search/js/quant-search.js index 1de6dcbf..7f7f531f 100644 --- a/modules/quant_search/js/quant-search.js +++ b/modules/quant_search/js/quant-search.js @@ -70,7 +70,8 @@ ]); } - if (drupalSettings.quantSearch.facets.length > 0) { + var facets = drupalSettings.quantSearch.facets; + if (typeof facets === 'object' && facets !== null && !Array.isArray(facets)) { if (drupalSettings.quantSearch.display.results.show_clear_refinements) { search.addWidgets([ instantsearch.widgets.clearRefinements({ @@ -78,8 +79,8 @@ }), ]); } - for (var facet_key in drupalSettings.quantSearch.facets) { - const facet = drupalSettings.quantSearch.facets[facet_key]; + for (var facet_key in facets) { + const facet = facets[facet_key]; switch (facet.facet_display) { case "checkbox": diff --git a/modules/quant_search/quant_search.install b/modules/quant_search/quant_search.install new file mode 100644 index 00000000..0701470e --- /dev/null +++ b/modules/quant_search/quant_search.install @@ -0,0 +1,33 @@ +getStorage('quant_search_page'); + $ids = \Drupal::entityQuery('quant_search_page')->execute(); + $pages = $storage->loadMultiple($ids); + + // Combine facet_type configuration fields for better tabledrag UX. + foreach ($pages as $page) { + $facets = $page->get('facets'); + $fields = ['facet_type', 'custom_key', 'taxonomy_vocabulary']; + foreach ($facets as $i => $facet) { + foreach ($fields as $field) { + if (isset($facet[$field])) { + $facets[$i]['facet_type_config'][$field] = $facet[$field]; + unset($facets[$i][$field]); + } + } + $page->set('facets', $facets); + } + $page->save(); + } + +} diff --git a/modules/quant_search/quant_search.libraries.yml b/modules/quant_search/quant_search.libraries.yml index b0b9229a..f8623a51 100644 --- a/modules/quant_search/quant_search.libraries.yml +++ b/modules/quant_search/quant_search.libraries.yml @@ -1,8 +1,13 @@ +drupal.quant_search.admin: + css: + theme: + css/quant_search.admin.css: {} + algolia: css: theme: https://cdn.jsdelivr.net/npm/instantsearch.css@7/themes/algolia-min.css: { type: external, minified: true } - css/quant-search.css: {} + css/quant_search.algolia.css: {} js: https://cdn.jsdelivr.net/npm/algoliasearch@4/dist/algoliasearch-lite.umd.js: { type: external, minified: true } https://cdn.jsdelivr.net/npm/instantsearch.js@4: { type: external, minified: true } diff --git a/modules/quant_search/src/Controller/Search.php b/modules/quant_search/src/Controller/Search.php index 0c85524a..69b430da 100644 --- a/modules/quant_search/src/Controller/Search.php +++ b/modules/quant_search/src/Controller/Search.php @@ -323,9 +323,9 @@ public static function processTranslatedFacetKeys(array $facets) { foreach ($facets as $k => $facet) { $lang = $facet['facet_language']; - switch ($facet['facet_type']) { + switch ($facet['facet_type_config']['facet_type']) { case "taxonomy": - $key = $facet['taxonomy_vocabulary'] . '_' . $lang; + $key = $facet['facet_type_config']['taxonomy_vocabulary'] . '_' . $lang; $containerKey = $key . "_{$k}"; $facet['facet_key'] = $key; $facet['facet_container'] = $containerKey; @@ -349,7 +349,7 @@ public static function processTranslatedFacetKeys(array $facets) { break; case "custom": - // @todo This + // @todo Allow custom entity key. break; default: diff --git a/modules/quant_search/src/Form/QuantSearchPageForm.php b/modules/quant_search/src/Form/QuantSearchPageForm.php index b0b22567..42c8a790 100644 --- a/modules/quant_search/src/Form/QuantSearchPageForm.php +++ b/modules/quant_search/src/Form/QuantSearchPageForm.php @@ -39,8 +39,11 @@ public static function create(ContainerInterface $container) { * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { + $form = parent::form($form, $form_state); + $form['#attached']['library'][] = 'quant_search/drupal.quant_search.admin'; + $page = $this->entity; $form['#tree'] = TRUE; @@ -217,10 +220,24 @@ public function form(array $form, FormStateInterface $form_state) { '#default_value' => $existingDisplayConfig['pagination']['per_page'] ?? 20, ]; - // Facet configuration. + // Create tabledrag facets table. $form['facets'] = [ - '#type' => 'fieldset', - '#title' => $this->t('Facets'), + '#type' => 'table', + // Do not show the weight header as each select has a label. + '#header' => [ + [ + 'data' => $this->t('Facet configuration'), + // IMPORTANT: Must be the correct value or tabledrag doesn't work! + 'colspan' => 6, + ], + ], + '#tabledrag' => [ + [ + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'facet-sort-weight', + ], + ], '#prefix' => '
', '#suffix' => '
', ]; @@ -240,11 +257,11 @@ public function form(array $form, FormStateInterface $form_state) { // Configuration fields for all the facets. foreach ($existingFacets as $i => $facet) { - $form['facets'][$i] = [ - '#type' => 'details', - '#open' => TRUE, - '#title' => $this->t('Facet configuration'), - ]; + // Mark the table row as draggable. + $form['facets'][$i]['#attributes']['class'][] = 'draggable'; + + // Sort the table row according to its configured weight. + $form['facets'][$i]['#weight'] = $facet['weight'] ?? 10; // Facet display. $displayTypes = [ @@ -275,11 +292,11 @@ public function form(array $form, FormStateInterface $form_state) { ]; // @todo Make required. When adding '#required', it didn't save. - $form['facets'][$i]['facet_type'] = [ + $form['facets'][$i]['facet_type_config']['facet_type'] = [ '#type' => 'select', '#title' => $this->t('Facet type'), '#options' => $types, - '#default_value' => $facet['facet_type'] ?? '', + '#default_value' => $facet['facet_type_config']['facet_type'] ?? '', '#attributes' => [ 'id' => "facet_{$i}_type", ], @@ -295,11 +312,11 @@ public function form(array $form, FormStateInterface $form_state) { $vocab_options[$vocab->id()] = $vocab->label(); } - $form['facets'][$i]['taxonomy_vocabulary'] = [ + $form['facets'][$i]['facet_type_config']['taxonomy_vocabulary'] = [ '#type' => 'select', - '#title' => $this->t('Taxonomy vocabulary'), + '#title' => $this->t('Vocabulary'), '#options' => $vocab_options, - '#default_value' => $facet['taxonomy_vocabulary'] ?? '', + '#default_value' => $facet['facet_type_config']['taxonomy_vocabulary'] ?? '', '#states' => [ 'visible' => [ ':input[id="facet_' . $i . '_type"]' => ['value' => 'taxonomy'], @@ -308,11 +325,12 @@ public function form(array $form, FormStateInterface $form_state) { ]; // Allow custom option using defined entity. - $form['facets'][$i]['custom_key'] = [ + $form['facets'][$i]['facet_type_config']['custom_key'] = [ '#type' => 'textfield', '#title' => $this->t('Custom key'), - '#description' => $this->t('Provide a custom key as defined in your entity token configuration'), - '#default_value' => $facet['custom_key'] ?? '', + '#size' => 20, + '#description' => t('Entity token configuration key.'), + '#default_value' => $facet['facet_type_config']['custom_key'] ?? '', '#states' => [ 'visible' => [ ':input[id="facet_' . $i . '_type"]' => ['value' => 'custom'], @@ -325,10 +343,12 @@ public function form(array $form, FormStateInterface $form_state) { $form['facets'][$i]['facet_heading'] = [ '#type' => 'textfield', '#title' => $this->t('Facet heading'), + '#size' => 20, '#default_value' => $facet['facet_heading'] ?? '', ]; // Facet language. + // @todo Only show if more than one language but beware of colspan. $defaultLanguage = \Drupal::languageManager()->getDefaultLanguage(); $language_codes = []; @@ -340,29 +360,51 @@ public function form(array $form, FormStateInterface $form_state) { $form['facets'][$i]['facet_language'] = [ '#type' => 'select', '#title' => $this->t('Facet language'), - '#description' => $this->t('Language to use for the facet.'), '#options' => $language_codes, '#default_value' => $facet['facet_language'] ?? 'en', ]; + // Weight column element. + $form['facets'][$i]['weight'] = [ + '#type' => 'weight', + '#title' => $this->t('Facet weight'), + '#default_value' => $facet['weight'] ?? 10, + '#attributes' => [ + 'class' => [ + 'facet-sort-weight', + ], + ], + ]; + // Remove facet button. $form['facets'][$i]['actions']['remove_facet'] = [ '#type' => 'submit', '#value' => $this->t('Remove facet'), '#name' => 'remove_facet_' . $i, '#index' => $i, + '#attributes' => [ + 'class' => [ + 'facet-remove', + ], + ], '#submit' => ['::removeCallback'], '#ajax' => [ 'callback' => '::addCallback', 'wrapper' => 'facets-fieldset-wrapper', ], ]; + } // Add "Add facet" button to last item in the array. $form['facets'][$i]['actions']['add_facet'] = [ '#type' => 'submit', '#value' => $this->t('Add facet'), + '#attributes' => [ + 'class' => [ + 'facet-add', + ], + ], '#submit' => ['::addOne'], '#ajax' => [ 'callback' => '::addCallback', @@ -387,7 +429,7 @@ public function save(array $form, FormStateInterface $form_state) { $facets = $page->get('facets'); $nonEmptyFacets = []; foreach ($facets as $i => $facet) { - if ($facet['facet_display'] && $facet['facet_type']) { + if ($facet['facet_display'] && $facet['facet_type_config']['facet_type']) { $nonEmptyFacets[$i] = $facet; } }