menex.ru
← Все статьи

Фильтр записей в Oxygen с помощью ISOTOPE

27.05.2021 • Теги: #jQuery #Oxygen #wordpress

На одном из проектов, необходимо было реализовать фотогалерею по разделам. Она была реализована с помощью кастомных записей и плагина Search & Filter. Но при переводе проекта на Oxygen, плагин Search & Filter отказался работать. Поэтому переделали фильтр на основе кастомных записей и библиотеки Isotope.

<!-- more -->

Реализация фильтра записей в Oxygen

С помощью плагина Custom Post Type UI создали кастомные записи и кастомные такстономии.

Приведу сразу код который можно вставить в functions.php или сниппет.

Записи

function cptui_register_my_cpts_fotootchet() {

    /**
     * Post Type: Фотоотчёты.
     */

    $labels = [
        "name" => __( "Фотоотчёты", "twentytwentyone" ),
        "singular_name" => __( "Фотоотчёт", "twentytwentyone" ),
    ];

    $args = [
        "label" => __( "Фотоотчёты", "twentytwentyone" ),
        "labels" => $labels,
        "description" => "",
        "public" => true,
        "publicly_queryable" => true,
        "show_ui" => true,
        "show_in_rest" => true,
        "rest_base" => "",
        "rest_controller_class" => "WP_REST_Posts_Controller",
        "has_archive" => true,
        "show_in_menu" => true,
        "show_in_nav_menus" => true,
        "delete_with_user" => false,
        "exclude_from_search" => false,
        "capability_type" => "post",
        "map_meta_cap" => true,
        "hierarchical" => false,
        "rewrite" => [ "slug" => "fotootchet", "with_front" => true ],
        "query_var" => true,
        "menu_icon" => "dashicons-camera-alt",
        "supports" => [ "title", "editor", "thumbnail" ],
        "show_in_graphql" => false,
    ];

    register_post_type( "fotootchet", $args );
}

add_action( 'init', 'cptui_register_my_cpts_fotootchet' );

Таксономии

function cptui_register_my_taxes() {

    /**
     * Taxonomy: Годы.
     */

    $labels = [
        "name" => __( "Годы", "twentytwentyone" ),
        "singular_name" => __( "Год", "twentytwentyone" ),
    ];


    $args = [
        "label" => __( "Годы", "twentytwentyone" ),
        "labels" => $labels,
        "public" => true,
        "publicly_queryable" => true,
        "hierarchical" => true,
        "show_ui" => true,
        "show_in_menu" => true,
        "show_in_nav_menus" => true,
        "query_var" => true,
        "rewrite" => [ 'slug' => 'foto_year', 'with_front' => true, ],
        "show_admin_column" => false,
        "show_in_rest" => true,
        "rest_base" => "foto_year",
        "rest_controller_class" => "WP_REST_Terms_Controller",
        "show_in_quick_edit" => false,
        "show_in_graphql" => false,
    ];
    register_taxonomy( "foto_year", [ "fotootchet" ], $args );

    /**
     * Taxonomy: Месяцы.
     */

    $labels = [
        "name" => __( "Месяцы", "twentytwentyone" ),
        "singular_name" => __( "Месяц", "twentytwentyone" ),
    ];


    $args = [
        "label" => __( "Месяцы", "twentytwentyone" ),
        "labels" => $labels,
        "public" => true,
        "publicly_queryable" => true,
        "hierarchical" => true,
        "show_ui" => true,
        "show_in_menu" => true,
        "show_in_nav_menus" => true,
        "query_var" => true,
        "rewrite" => [ 'slug' => 'foto_month', 'with_front' => true, ],
        "show_admin_column" => false,
        "show_in_rest" => true,
        "rest_base" => "foto_month",
        "rest_controller_class" => "WP_REST_Terms_Controller",
        "show_in_quick_edit" => false,
        "show_in_graphql" => false,
    ];
    register_taxonomy( "foto_month", [ "fotootchet" ], $args );
}
add_action( 'init', 'cptui_register_my_taxes' );

Через плагин Code Snippets вставляем код, для того чтобы подключить библиотеку Isotope и создать шорткод самого фильтра, которые вставляется в шаблон Oxygen для этих записей.

Сам код сниппета

// Подключаеи ISOTOPE 
add_action( 'wp_footer', function () { ?>
<script src="https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js"></script>
<script type="text/javascript">
// Инициализация Isotope
var $grid = jQuery('.oxy-posts').isotope({
  // Опции
  itemSelector: '.element-item',
  layoutMode: 'fitRows'
});

// Фильтруем по нажатию кнопок
var filters = {};
jQuery('.filter-button-group').on( 'click', 'button', function() {
  var $this = jQuery(this);
  // get group key
  var $buttonGroup = $this.parents('.button-group');
  var filterGroup = $buttonGroup.attr('data-filter-group');
  // set filter for group
  filters[ filterGroup ] = $this.attr('data-filter');
  // combine filters
  var filterValue = concatValues( filters );
  $grid.isotope({ filter: filterValue });
});

// flatten object by concatting values
function concatValues( obj ) {
  var value = '';
  for ( var prop in obj ) {
    value += obj[ prop ];
  }
  return value;
}

// Добавляем и убираем класс is-checked для активных кнопок
jQuery('.button-group').each( function( i, buttonGroup ) {
  var $buttonGroup = jQuery( buttonGroup );
  $buttonGroup.on( 'click', 'button', function( event ) {
    $buttonGroup.find('.is-checked').removeClass('is-checked');
    var $button = jQuery( event.currentTarget );
    $button.addClass('is-checked');
  });
});

jQuery('.filter-foto-button').click(function(){ jQuery('.filter-button-group .filter-foto-button:not(.is-checked)').toggleClass("default");
 });


</script>
<style>

/* Фильтр выпадашкой

    .filter-button-group .filter-foto-button.is-checked {background: #f0c114;}
    .filter-button-group {
        width: 150px;
        height: 30px;
        overflow: hidden;
        float: left;
        margin-right:20px;
    }
    .filter-button-group button {
        width: 150px;
        height: 30px;
        display: block;
    }
    .filter-button-group:hover{
        overflow: auto;
        z-index: 999999;
        height: min-content;
    }
    .filter-group {
        position: absolute;
        z-index: 1;
    }
    .filter-button-group .filter-foto-button {
        background: #f9f9f9;
        border: none;
    }
    .filter-button-group .filter-foto-button.is-checked {
        display:block;
    }
    .filter-button-group .filter-foto-button:not(.is-checked) {
        display:none;
    }
    .filter-button-group:hover .filter-foto-button:not(.is-checked),
    .filter-button-group:focus .filter-foto-button:not(.is-checked) {
        display:block;
    }
    .filter-button-group .filter-foto-button:first-child {
        background: #f9f9f9;
        border-bottom: 1px solid #999999;
        position: relative;
    }
    .filter-button-group .filter-foto-button:first-child:after {
        content: "";
        position: absolute;
        top: 10px;
        right: 0px;
        height: 15px;
        width: 15px;
        margin: 0;
        transform: translate(-50%,-50%) rotate(45deg);
        border: none;
        border-right: 1px solid #999;
        border-bottom: 1px solid #999;
    }
@media screen and (max-width: 767px) {
    .filter-button-group, .filter-button-group button  {
        width: 100px;
        font-size:12px;
    }
    .filter-button-group {
        margin-right: 10px;
    }

    .filter-button-group .filter-foto-button:first-child:after {
        height: 7px;
        width: 7px;
        top: 12px;
    }
}*/


/* фильтр кнопками */
.filter-button-group .filter-foto-button.is-checked {
    background: rgba(47,94,26,0.2);
    color:#0f0f0f;
    }
.filter-button-group .filter-foto-button {
    background: transparent;
    border: 1px solid #686868;
    color: #686868;
    border-radius: 10px;
    padding: 5px 30px;
    margin: 0 10px 10px 0;
    font-size: 14px;
    font-family: 'Roboto';
}
    .filter-button-group .filter-foto-button:hover {
        background: rgba(47,94,26,0.32);
    }
    body.single-fotootchet .ct-section-inner-wrap h1.ct-headline {
        margin-bottom: 17px;
    }

</style>
<?php } );

// Шорткод фильтра для вставки перед Easy Post
add_shortcode( 'filter-foto', function () {

    echo '<div class="filter-group">';

// Код для вывода списка кнопок с определенной таксономией  

// Выводим список кнопок с домами   
$args = array( 'hide_empty=0', 'orderby' => 'slug', 'order' => 'ASC' );
$terms = get_terms('foto_house', $args);
// собираем их и выводим
if ( !empty( $terms ) && !is_wp_error( $terms ) ) {
    $count = count($terms);
    $i=0;
    $term_list = '<div class="button-group filter-button-group"  data-filter-group="house"><button class="filter-foto-button is-checked" data-filter="*">Все дома</button>';
    foreach ($terms as $term) {
        $i++;
        $term_list .= '<button class="filter-foto-button" data-filter=".'. $term->slug .'">' . $term->name . '</button>';
        if ($count != $i) {
            $term_list .= '';
        }
        else {
            $term_list .= '</div>';
        }
    }
    echo $term_list;
}   
// Выводим список кнопок с годами   
$args = array( 'hide_empty=0', 'orderby' => 'slug', 'order' => 'ASC' );
$terms = get_terms('foto_year', $args);
// собираем их и выводим
if ( !empty( $terms ) && !is_wp_error( $terms ) ) {
    $count = count($terms);
    $i=0;
    $term_list = '<div class="button-group filter-button-group" data-filter-group="year"><button class="filter-foto-button is-checked" data-filter="*">Все годы</button>';
    foreach ($terms as $term) {
        $i++;
        $term_list .= '<button class="filter-foto-button" data-filter=".'. $term->slug .'">' . $term->name . '</button>';
        if ($count != $i) {
            $term_list .= '';
        }
        else {
            $term_list .= '</div>';
        }
    }
    echo $term_list;
}

// Выводим список кнопок с месяцами     
$args = array( 'hide_empty=0', 'orderby' => 'slug', 'order' => 'ASC' );
$terms = get_terms('foto_month', $args);
// собираем их и выводим
if ( !empty( $terms ) && !is_wp_error( $terms ) ) {
    $count = count($terms);
    $i=0;
    $term_list = '<div class="button-group filter-button-group" data-filter-group="month"><button class="filter-foto-button is-checked" data-filter="*">Все месяцы</button>';
    foreach ($terms as $term) {
        $i++;
        $term_list .= '<button class="filter-foto-button" data-filter=".'. $term->slug .'">' . $term->name . '</button>';
        if ($count != $i) {
            $term_list .= '';
        }
        else {
            $term_list .= '</div>';
        }
    }
    echo $term_list;
}

// END Код для вывода списка кнопок с определенной таксономией  

    echo '</div>';

} );
// END Шорткод фильтра для вставки перед Easy Post

Создаем шаблон для архива этих записей Oxygen -> Templates

В самом шаблоне вставляем шорткод и блок Easy Posts.

Шорткод вставляем который прописали в сниппете выше.

[filter-foto]

А в блоке Easy Posts выбираем Template PHP и вставляем туда этот код:

<div class='oxy-post element-item <?php
  $cur_terms = get_the_terms( $post->ID, 'foto_year' );
    if( is_array( $cur_terms ) ){
        foreach( $cur_terms as $cur_term ){
            echo $cur_term->slug;
            echo ' ';
        }
    }

  $cur_terms = get_the_terms( $post->ID, 'foto_month' );
    if( is_array( $cur_terms ) ){
        foreach( $cur_terms as $cur_term ){
            echo $cur_term->slug;
              echo ' ';
        }
    }

  $cur_terms = get_the_terms( $post->ID, 'foto_house' );
    if( is_array( $cur_terms ) ){
        foreach( $cur_terms as $cur_term ){
            echo $cur_term->slug;
            echo ' ';
        }
    }?> '>


    <a class='oxy-post-image' href='<?php the_permalink(); ?>'>
      <div class='oxy-post-image-fixed-ratio' style='background-image: url(<?php echo get_the_post_thumbnail_url($post,'full'); ?>);'>

        <div class="foto-otchet-title">
          <div><?php the_title(); ?></div>
        </div>


      </div>

    </a>


</div>

А в блоке Easy Posts -> Template CSS вставляем этот код:

%%EPID%% .oxy-posts {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

%%EPID%% .oxy-post {
  display: flex;
  flex-direction: column;
  text-align: left;
  align-items: flex-start;
  margin-bottom: 0;
  width: 33.33%;
  padding: 1em;
}

%%EPID%% .oxy-post-image {
  margin-bottom: 0;
  position: relative;
  background-color: grey;
  width: 100%;
}

%%EPID%% .oxy-post-image-fixed-ratio {
  height: 200px;
  background-size: cover;
  background-position: center center;
}

%%EPID%% .oxy-post-image-date-overlay {
  position: absolute;
  top: 1em;
  right: 1em;
  font-size: .7em;
  color: white;
  background-color: rgba(0,0,0,0.5);
  padding: .7em 1em;
  font-weight: bold;
  -webkit-font-smoothing: antialiased;
}

%%EPID%% .oxy-post-title {
  font-size: 1.5em;
  line-height: 1.2em;
}

%%EPID%% .oxy-post-meta {
  margin-top: 0.5em;
  font-size: .8em;
  display: flex;
  flex-direction: row;
}

%%EPID%% .oxy-post-meta-item::after {
    content: "\00b7";
    margin-right: .5em;
    margin-left: .5em;
}

%%EPID%% .oxy-post-meta-item:last-child::after {
    content: "";
    display: none;
}

%%EPID%% .oxy-post-content {
  margin-top: 1em;
  margin-bottom: 1em;
}

%%EPID%% .oxy-post-content p {
  margin: 0;
}
%%EPID%% .foto-otchet-title {
    width: 100%;
  height: 100%;
  background-color: rgba(0,0,0,0.7);
  display: flex;
  align-items: center;
    justify-content: center;
    color:#fff;
  font-size: 20px;
  font-weight: 300;
  -webkit-transition: all 0.8s ease;;
  -moz-transition: all 0.8s ease;;
  -o-transition: all 0.8s ease;;
  transition: all 0.8s ease;
}
%%EPID%% a:hover .foto-otchet-title {
  background-color: rgba(0,0,0,0.0);
}




@media (max-width: 1120px) {
  %%EPID%% .oxy-post-meta {
    display: none;
  }
}

В итоге получаем рубрику с фильтром для записей и кнопками. Если в сниппете закомментировать стиль для кнопок и разкомментировать стиль для выпадашек, то вместо кнопок в фильтре будут выпадашки. Но они не корректно работают на мобильном устройстве.