bs-swiper icon indicating copy to clipboard operation
bs-swiper copied to clipboard

Define default columns count

Open Jonowa opened this issue 7 months ago • 3 comments

Hi,

I love using bootscore with the additional plugins. I've a question for the swiper plugin. Can you make it possible to define a default columns count dependent on the screen size? Currently it's hard coded in the javascript file. With every update I have to fix it for my personal use.

Regards Joerg

Jonowa avatar May 19 '25 14:05 Jonowa

Hi,

Currently it's hard coded in the javascript file. With every update I have to fix it for my personal use.

Why not dequeue the init script and enqueue own in child as discussed here https://github.com/orgs/bootscore/discussions/768?

Can you make it possible to define a default columns count dependent on the screen size?

That's a good point! And I am having something like this in mind since a while to add more options to the shortcode to control the columns and more. For example all Bootstrap columns xs, sm, md, lg, xl and xxl can be added to the shortcode cols="2,3,4,5,6,8". If they are not added, the columns fallback to the default settings:

[bs-swiper-card type="post" category="post-templates" cols="2,3,4,5,6,8"]
[bs-swiper-card type="post" category="post-templates"]

Please check the modified PHP and init JS (default card template):

PHP

// Card Slider Shortcode
add_shortcode('bs-swiper-card', 'bootscore_swiper');
function bootscore_swiper($atts) {
  ob_start();

  $atts = shortcode_atts(array(
    'type'        => 'post',
    'post_status' => 'publish',
    'order'       => 'date',
    'orderby'     => 'date',
    'posts'       => -1,
    'category'    => '',
    'post_parent' => '',
    'tax'         => '',
    'terms'       => '',
    'id'          => '',
    'excerpt'     => 'true',
    'tags'        => 'true',
    'categories'  => 'true',
    'cols'        => '', // new attribute: e.g., "2,3,4,6"
  ), $atts);

  $options = array(
    'post_type'      => sanitize_text_field($atts['type']),
    'order'          => sanitize_text_field($atts['order']),
    'orderby'        => sanitize_text_field($atts['orderby']),
    'posts_per_page' => is_numeric($atts['posts']) ? (int) $atts['posts'] : -1,
    'category_name'  => sanitize_text_field($atts['category']),
    'post_parent'    => is_numeric($atts['post_parent']) ? (int) $atts['post_parent'] : '',
  );

  $tax = trim(sanitize_text_field($atts['tax']));
  $terms = trim(sanitize_text_field($atts['terms']));
  if ($tax != '' && $terms != '') {
    $terms = array_map('trim', explode(',', $terms));
    $terms = array_filter($terms);
    $terms = array_unique($terms);
    unset($options['category_name']);
    $options['tax_query'] = array(array(
      'taxonomy' => $tax,
      'field'    => 'slug',
      'terms'    => $terms,
    ));
  }

  if (!empty($atts['id'])) {
    $ids = array_map('intval', explode(',', $atts['id']));
    $ids = array_filter($ids);
    $ids = array_unique($ids);
    $options['post__in'] = $ids;
  }

  // Handle cols attribute and map to breakpoints
  $breakpoints = [];
  $bps = [0, 576, 768, 992, 1200, 1400]; // xs, sm, md, lg, xl, 2xl

  if (!empty($atts['cols'])) {
    $cols = array_map('intval', explode(',', $atts['cols']));

    foreach ($cols as $i => $val) {
      if (isset($bps[$i])) {
        $breakpoints[$bps[$i]] = ['slidesPerView' => $val];
      }
    }
  } else {
    // Default: 1 slide on xs/sm, 2 on md, 3 on lg, 4 on xl+, etc.
    $breakpoints = [
      0    => ['slidesPerView' => 1],
      576  => ['slidesPerView' => 1],
      768  => ['slidesPerView' => 2],
      992  => ['slidesPerView' => 3],
      1200 => ['slidesPerView' => 4],
      1400 => ['slidesPerView' => 4],
    ];
  }

  $data_breakpoints = htmlspecialchars(json_encode($breakpoints), ENT_QUOTES, 'UTF-8');

  $query = new WP_Query($options);
  if ($query->have_posts()) : ?>

    <!-- Swiper -->
    <div class="px-5 position-relative">
      <div class="cards swiper-container swiper position-static" data-swiper-breakpoints="<?= $data_breakpoints; ?>">
        <div class="swiper-wrapper">

          <?php while ($query->have_posts()) : $query->the_post(); ?>
            <div class="swiper-slide card h-auto mb-5">

              <?php if (has_post_thumbnail()) : ?>
                <a href="<?php the_permalink(); ?>">
                  <?php the_post_thumbnail('medium', ['class' => 'card-img-top']); ?>
                </a>
              <?php endif; ?>

              <div class="card-body d-flex flex-column">
                <?php if ($atts['categories'] === 'true') : bootscore_category_badge(); endif; ?>

                <a class="text-body text-decoration-none" href="<?php the_permalink(); ?>">
                  <?php the_title('<h2 class="blog-post-title h5">', '</h2>'); ?>
                </a>

                <?php if (get_post_type() === 'post') : ?>
                  <p class="meta small mb-2 text-body-secondary">
                    <?php
                      bootscore_date();
                      bootscore_author();
                      bootscore_comments();
                      bootscore_edit();
                    ?>
                  </p>
                <?php endif; ?>

                <?php if ($atts['excerpt'] === 'true') : ?>
                  <p class="card-text">
                    <a class="text-body text-decoration-none" href="<?php the_permalink(); ?>">
                      <?= strip_tags(get_the_excerpt()); ?>
                    </a>
                  </p>
                <?php endif; ?>

                <p class="card-text mt-auto">
                  <a class="read-more" href="<?php the_permalink(); ?>">
                    <?= __('Read more »', 'bootscore'); ?>
                  </a>
                </p>

                <?php if ($atts['tags'] === 'true') : bootscore_tags(); endif; ?>
              </div>
            </div>
          <?php endwhile; wp_reset_postdata(); ?>
        </div>

        <!-- Add Pagination -->
        <div class="swiper-pagination"></div>
        <!-- Add Arrows -->
        <div class="swiper-button-next end-0"></div>
        <div class="swiper-button-prev start-0"></div>
      </div>
    </div>
    <!-- Swiper End -->

<?php
    return ob_get_clean();
  endif;
}

Init JS

// Cards
document.addEventListener('DOMContentLoaded', function () {
  const bsSwiper = document.querySelectorAll('.cards');

  for (let i = 0; i < bsSwiper.length; i++) {
    const el = bsSwiper[i];
    el.classList.add('cards-' + i);

    let breakpoints = {
      slidesPerView: 1,           // xs
      576: { slidesPerView: 1 },  // sm
      768: { slidesPerView: 2 },  // md
      992: { slidesPerView: 3 },  // lg
      1200: { slidesPerView: 4 }, // xl
      1400: { slidesPerView: 4 }, // 2xl (xxl will be renamed into 2xl in Bootstrap 6)
    };

    const bpData = el.getAttribute('data-swiper-breakpoints');
    if (bpData) {
      try {
        breakpoints = JSON.parse(bpData);
      } catch (e) {
        console.warn('Invalid Swiper breakpoints JSON:', bpData);
      }
    }

    new Swiper('.cards-' + i, {
      slidesPerView: 1,
      spaceBetween: 20,
      loop: true,
      grabCursor: true,
      pagination: {
        el: '.swiper-pagination',
        clickable: true,
      },
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },
      breakpoints: breakpoints
    });
  }
});

This will allow to remove templates which is better for maintenance in the long-term. But this will include breaking changes which leads into bs Swiper v6.

From my experience, it's a massive effort to lift a plugin to the next major version and I'm not sure if I have the energy to do this (alone) yet. Question: @Jonowa Are you willing and able to work on this as well?

Basti

Edit, More options:

  • infinite-loop="true"
  • fade="true"
  • autoplay="true"
  • transition="1500"
  • ...

crftwrk avatar May 20 '25 12:05 crftwrk

Just to add some input from myself to that issue. I do understand that it is tempting to fix the cols in js and work from there. The issue that then occurs is that you get CLS issues as soon as the slider is above the fold. So if a rework of the swiper stuff is done I think CLS should be considered as well in the thought process.

Axos11 avatar Sep 15 '25 09:09 Axos11

I have plenty ideas to rework this plugin into a more flexible and better one. But thinking about turning this from free into a commercial plugin as well.

crftwrk avatar Sep 15 '25 09:09 crftwrk