Plugin Directory

Changeset 3798874


Ignore:
Timestamp:
11/12/2026 05:04:18 PM (less than one hour ago)
Author:
shshakib2001
Message:

Initial commit for version 1.3.2

Location:
shopshelf
Files:
280 added
17 edited

Legend:

Unmodified
Added
Removed
  • shopshelf/trunk/languages/shopshelf.pot

    r3789213 r3798874  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: ShopShelf – Product Grid & Carousel for WooCommerce 1.3.1\n"
     5"Project-Id-Version: ShopShelf – Product Grid & Carousel for WooCommerce 1.3.2\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/shopshelf\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
    … …  
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2026-11-07T08:28:26+00:00\n"
     12"POT-Creation-Date: 2026-11-12T16:58:26+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.12.0\n"
    … …  
    4545#: src/Admin/Elementor/Integration.php:98
    4646#: src/Admin/Views/ShopShelfMetaboxes.php:41
    47 #: src/Admin/Views/ShopShelfSettings.php:54
     47#: src/Admin/Views/ShopShelfSettings.php:55
    4848#: src/Includes/PostType.php:52
    4949#: src/Admin/assets/app/shopshelf-admin-app.js:51
    … …  
    7070#: src/Admin/Admin.php:109
    7171#: src/Admin/Admin.php:297
    72 #: src/Admin/Views/ShopShelfSettings.php:44
     72#: src/Admin/Views/ShopShelfSettings.php:45
    7373#: src/Admin/assets/app/shopshelf-admin-app.js:51
    7474#: src/Admin/assets/app/shopshelf-admin-app.js:56
    … …  
    110110#: src/Admin/Elementor/Controls/ContentControls.php:90
    111111#: src/Admin/Views/Generator/FilterPost.php:54
    112 #: src/Frontend/Helpers/class-shopshelf-html.php:938
     112#: src/Frontend/Helpers/class-shopshelf-html.php:966
    113113msgid "Featured"
    114114msgstr ""
    … …  
    925925#: src/Admin/Views/Generator/Display.php:1121
    926926#: src/Frontend/Helpers/class-shopshelf-html.php:337
    927 #: src/Frontend/Helpers/class-shopshelf-html.php:829
     927#: src/Frontend/Helpers/class-shopshelf-html.php:857
    928928msgid "Read More"
    929929msgstr ""
    … …  
    14101410#: src/Admin/Elementor/Controls/ContentControls.php:1357
    14111411#: src/Admin/Views/Generator/DetailSettings.php:49
     1412#: src/Admin/Views/Settings/LoadingEffects.php:64
    14121413msgid "None"
    14131414msgstr ""
    … …  
    19111912#: src/Admin/Views/Generator/Display.php:1313
    19121913#: src/Admin/Views/Settings/Accessibility.php:46
     1914#: src/Admin/Views/Settings/LoadingEffects.php:51
    19131915msgid "Enabled"
    19141916msgstr ""
    … …  
    19261928#: src/Admin/Views/Generator/Display.php:1314
    19271929#: src/Admin/Views/Settings/Accessibility.php:47
     1930#: src/Admin/Views/Settings/LoadingEffects.php:52
    19281931msgid "Disabled"
    19291932msgstr ""
    … …  
    32993302msgstr ""
    33003303
     3304#: src/Admin/Views/Settings/LoadingEffects.php:42
     3305msgid "Loading Effects"
     3306msgstr ""
     3307
     3308#: src/Admin/Views/Settings/LoadingEffects.php:43
     3309msgid "What shoppers see while products load after a filter, a search, a page change or Load More."
     3310msgstr ""
     3311
     3312#: src/Admin/Views/Settings/LoadingEffects.php:49
     3313msgid "Skeleton Loading"
     3314msgstr ""
     3315
     3316#: src/Admin/Views/Settings/LoadingEffects.php:50
     3317msgid "Show placeholder product cards, shaped like the layout's own cards, while products load. Off keeps the current dimmed-products effect."
     3318msgstr ""
     3319
     3320#: src/Admin/Views/Settings/LoadingEffects.php:59
     3321msgid "Skeleton Animation"
     3322msgstr ""
     3323
     3324#: src/Admin/Views/Settings/LoadingEffects.php:60
     3325msgid "How the placeholders move. Visitors who ask their device for reduced motion always see them still."
     3326msgstr ""
     3327
     3328#: src/Admin/Views/Settings/LoadingEffects.php:62
     3329msgid "Shimmer"
     3330msgstr ""
     3331
     3332#: src/Admin/Views/Settings/LoadingEffects.php:63
     3333msgid "Pulse"
     3334msgstr ""
     3335
     3336#: src/Admin/Views/Settings/LoadingEffects.php:72
     3337msgid "Skeleton Background Color"
     3338msgstr ""
     3339
     3340#: src/Admin/Views/Settings/LoadingEffects.php:73
     3341msgid "The base colour of the placeholder shapes."
     3342msgstr ""
     3343
     3344#: src/Admin/Views/Settings/LoadingEffects.php:80
     3345msgid "Skeleton Highlight Color"
     3346msgstr ""
     3347
     3348#: src/Admin/Views/Settings/LoadingEffects.php:81
     3349msgid "The lighter colour the shimmer and pulse move through."
     3350msgstr ""
     3351
     3352#: src/Admin/Views/Settings/LoadingEffects.php:88
     3353msgid "Number of Skeleton Items"
     3354msgstr ""
     3355
     3356#: src/Admin/Views/Settings/LoadingEffects.php:89
     3357msgid "How many placeholder cards Grid and List layouts show. A carousel shows as many as fit on screen."
     3358msgstr ""
     3359
     3360#: src/Admin/Views/Settings/LoadingEffects.php:98
     3361msgid "Skeleton Border Radius"
     3362msgstr ""
     3363
     3364#: src/Admin/Views/Settings/LoadingEffects.php:99
     3365msgid "Corner rounding of the placeholder cards and images. Text lines use half of it."
     3366msgstr ""
     3367
    33013368#: src/Admin/Views/Settings/ScriptsAndStyles.php:37
    33023369#: src/Admin/assets/app/shopshelf-admin-app.js:51
    … …  
    33573424msgstr ""
    33583425
    3359 #: src/Frontend/class-shopshelf-frontend.php:213
    3360 #: src/Frontend/class-shopshelf-frontend.php:218
     3426#: src/Frontend/class-shopshelf-frontend.php:276
     3427#: src/Frontend/class-shopshelf-frontend.php:281
    33613428msgid "This layout is not available."
    33623429msgstr ""
    … …  
    33923459msgstr ""
    33933460
    3394 #: src/Frontend/Helpers/class-shopshelf-html.php:936
     3461#: src/Frontend/Helpers/class-shopshelf-html.php:964
    33953462msgid "Sale"
    33963463msgstr ""
    33973464
    3398 #: src/Frontend/Helpers/class-shopshelf-html.php:1107
    3399 #: src/Frontend/Helpers/class-shopshelf-html.php:1123
     3465#: src/Frontend/Helpers/class-shopshelf-html.php:1136
     3466#: src/Frontend/Helpers/class-shopshelf-html.php:1152
    34003467#: src/Frontend/templates/pagination.php:48
    34013468msgid "Previous page"
    34023469msgstr ""
    34033470
    3404 #: src/Frontend/Helpers/class-shopshelf-html.php:1108
    3405 #: src/Frontend/Helpers/class-shopshelf-html.php:1139
     3471#: src/Frontend/Helpers/class-shopshelf-html.php:1137
     3472#: src/Frontend/Helpers/class-shopshelf-html.php:1168
    34063473#: src/Frontend/templates/pagination.php:49
    34073474msgid "Next page"
  • shopshelf/trunk/readme.txt

    r3789213 r3798874  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 1.3.1
     7Stable tag: 1.3.2
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    260260
    261261== Changelog ==
     262= 1.3.2 - 12 November 2026 =
     263* New: Skeleton Loading — placeholder product cards, shaped like the layout's own cards, shown while products load. Off by default; turn it on under Settings → Loading Effects.
     264* New: Loading Effects settings — Skeleton Animation (Shimmer, Pulse or None), Skeleton Background and Highlight colours, Number of Skeleton Items and Skeleton Border Radius.
     265* Improved: Loading experience for live filters, keyword search, AJAX pagination, Load More and infinite scroll in Grid, Carousel and List layouts, including the ShopShelf Layout block and the Elementor widgets.
     266* Improved: Placeholders follow each layout's responsive columns, card height, List image side and Stack on Mobile, and a carousel's slides per view, so the page does not jump when products arrive.
     267* Improved: Placeholders are hidden from screen readers, the product area is marked busy while it loads, and the animation stops for visitors who prefer reduced motion. If a request fails, the products that were on screen come back.
     268* New: `shopshelf_skeleton_settings` filter, and `shopShelfFrontend.skeletonCards` for add-ons that bring their own layouts.
     269
    262270= 1.3.1 - 07 November 2026 =
    263271* Changed: Rewrote the plugin description, with a short overview, a "Why ShopShelf" section, a section on the pre-made templates and a quick-start tip in Installation.
    … …  
    316324
    317325== Upgrade Notice ==
     326= 1.3.2 =
     327Adds optional Skeleton Loading under Settings → Loading Effects. It is off until you switch it on, so existing layouts look and behave exactly as before.
     328
    318329= 1.3.1 =
    319330Documentation update only: a clearer description, new screenshots and more FAQs. Plugin code, settings and layouts are unchanged.
  • shopshelf/trunk/shopshelf-admin/package.json

    r3789213 r3798874  
    22  "name": "shopshelf-admin",
    33  "private": true,
    4   "version": "1.3.1",
     4  "version": "1.3.2",
    55  "type": "module",
    66  "scripts": {
  • shopshelf/trunk/shopshelf.php

    r3789213 r3798874  
    1111 * Plugin URI:        https://wordpress.org/plugins/shopshelf/
    1212 * Description:       Showcase WooCommerce products in responsive grids, carousels and lists, with full control over what appears on each product card.
    13  * Version:           1.3.1
     13 * Version:           1.3.2
    1414 * Requires at least: 5.7
    1515 * Requires PHP:      7.4
    … …  
    2828
    2929if ( ! defined( 'SHOPSHELF_VERSION' ) ) {
    30         define( 'SHOPSHELF_VERSION', '1.3.1' );
     30        define( 'SHOPSHELF_VERSION', '1.3.2' );
    3131}
    3232
  • shopshelf/trunk/src/Admin/Block/Block.php

    r3787871 r3798874  
    245245                \wp_enqueue_script( 'shopshelf-swiper' );
    246246                \wp_enqueue_script( 'shopshelf-script' );
     247                \ShopShelf_Frontend::enqueue_skeleton_style();
    247248        }
    248249
  • shopshelf/trunk/src/Admin/Elementor/Integration.php

    r3787871 r3798874  
    135135                if ( self::is_editing() ) {
    136136                        $deps[] = 'shopshelf-swiper';
     137
     138                        // Skeleton Loading's stylesheet, while it is on. On a page the
     139                        // renderer enqueues it; in the editor a widget dropped in later is
     140                        // rendered over AJAX, too late for that. Not declared through the
     141                        // widgets' get_style_depends(): Elementor caches those per page, so
     142                        // it would keep loading after Skeleton Loading is switched off.
     143                        \ShopShelf_Frontend::enqueue_skeleton_style();
    137144                }
    138145
  • shopshelf/trunk/src/Admin/Views/ShopShelfSettings.php

    r3778148 r3798874  
    2222use ShopShelf\Admin\Views\Settings\ColorScheme;
    2323use ShopShelf\Admin\Views\Settings\CustomCode;
     24use ShopShelf\Admin\Views\Settings\LoadingEffects;
    2425use ShopShelf\Admin\Views\Settings\ScriptsAndStyles;
    2526
    … …  
    5758                );
    5859                ColorScheme::section( $prefix );
     60                LoadingEffects::section( $prefix );
    5961                CustomCode::section( $prefix );
    6062                ScriptsAndStyles::section( $prefix );
  • shopshelf/trunk/src/Frontend/Helpers/class-shopshelf-dynamic-css.php

    r3787871 r3798874  
    4444         * A colour: hex, rgb()/rgba()/hsl()/hsla() or a keyword. Anything else is dropped.
    4545         *
     46         * Public since 1.3.2, for the skeleton colours read in ShopShelf_Functions.
     47         *
    4648         * @param mixed $value The stored value.
    4749         * @return string
    4850         */
    49         private static function color( $value ) {
     51        public static function color( $value ) {
    5052                $value = is_string( $value ) ? trim( $value ) : '';
    5153
  • shopshelf/trunk/src/Frontend/Helpers/class-shopshelf-functions.php

    r3787871 r3798874  
    701701
    702702        /**
     703         * The Loading Effects settings, validated and with their defaults.
     704         *
     705         * The option is stored as the admin posts it (strings), and a site that
     706         * has never saved the Settings screen has none of these keys at all — so
     707         * every value is checked here, and the defaults match the screen's.
     708         * Skeleton Loading is off unless it has been switched on.
     709         *
     710         * @since 1.3.2
     711         * @return array{enabled:bool,animation:string,background:string,highlight:string,items:int,radius:int}
     712         */
     713        public static function shopshelf_skeleton_settings() {
     714                static $skeleton = null;
     715
     716                if ( null !== $skeleton ) {
     717                        return $skeleton;
     718                }
     719
     720                $settings = self::shopshelf_global_settings();
     721                $value    = static function ( $key, $fallback ) use ( $settings ) {
     722                        return isset( $settings[ $key ] ) && '' !== $settings[ $key ] ? $settings[ $key ] : $fallback;
     723                };
     724
     725                $enabled   = $value( 'shopshelf_skeleton_loading', false );
     726                $animation = $value( 'shopshelf_skeleton_animation', 'shimmer' );
     727                $bg        = ShopShelf_Dynamic_CSS::color( $value( 'shopshelf_skeleton_bg_color', '' ) );
     728                $highlight = ShopShelf_Dynamic_CSS::color( $value( 'shopshelf_skeleton_highlight_color', '' ) );
     729
     730                $skeleton = array(
     731                        // The switch posts true/false, or "1"/"0" from an import.
     732                        'enabled'    => true === $enabled || 1 === $enabled || in_array( $enabled, array( '1', 'true' ), true ),
     733                        'animation'  => in_array( $animation, array( 'shimmer', 'pulse', 'none' ), true ) ? $animation : 'shimmer',
     734                        'background' => '' !== $bg ? $bg : '#e9e9ec',
     735                        'highlight'  => '' !== $highlight ? $highlight : '#f6f6f8',
     736                        'items'      => min( 24, max( 1, (int) $value( 'shopshelf_skeleton_items', 6 ) ) ),
     737                        'radius'     => min( 50, max( 0, (int) $value( 'shopshelf_skeleton_radius', 6 ) ) ),
     738                );
     739
     740                /**
     741                 * Filters the Loading Effects settings the front end uses.
     742                 *
     743                 * @since 1.3.2
     744                 * @param array $skeleton The validated settings.
     745                 */
     746                $skeleton = array_merge( $skeleton, (array) apply_filters( 'shopshelf_skeleton_settings', $skeleton ) );
     747
     748                return $skeleton;
     749        }
     750
     751        /**
    703752         * Resolve one colour the way the theme resolves its colours.
    704753         *
  • shopshelf/trunk/src/Frontend/Helpers/class-shopshelf-html.php

    r3787871 r3798874  
    571571
    572572        /**
     573         * Print the attribute Skeleton Loading reads its card columns from.
     574         *
     575         * The script normally copies the columns from a card on screen; this is
     576         * what it falls back to when there is none (a filter that matched nothing,
     577         * then another filter). Printed only while Skeleton Loading is on, so the
     578         * markup is unchanged otherwise. A carousel needs none: its columns are in
     579         * its own configuration.
     580         *
     581         * @since 1.3.2
     582         * @param string $layout  The layout preset.
     583         * @param array  $options The layout's options.
     584         * @return void
     585         */
     586        public static function shopshelf_skeleton_attributes( $layout, $options ) {
     587                $skeleton = ShopShelf_Functions::shopshelf_skeleton_settings();
     588
     589                if ( empty( $skeleton['enabled'] ) || 'carousel_layout' === $layout ) {
     590                        return;
     591                }
     592
     593                $columns = 'list_layout' === $layout
     594                        ? ShopShelf_Functions::shopshelf_metabox_value( 'shopshelf_list_columns', $options, self::LIST_COLUMNS )
     595                        : ShopShelf_Functions::shopshelf_metabox_value( 'shopshelf_number_of_columns', $options );
     596
     597                printf( ' data-skeleton-cols="%s"', esc_attr( self::shopshelf_product_responsive_columns( $layout, $columns ) ) );
     598        }
     599
     600        /**
    573601         * Print the cards for a query's products.
    574602         *
    … …  
    10641092
    10651093                wp_enqueue_script( 'shopshelf-script' );
     1094                ShopShelf_Frontend::enqueue_skeleton_style();
    10661095
    10671096                $html = self::capture( self::LAYOUT_TEMPLATES[ $layout_preset ], $vars );
  • shopshelf/trunk/src/Frontend/assets/js/scripts.js

    r3781336 r3798874  
    234234  }
    235235
     236  /* ── Skeleton Loading ──────────────────────────────────────────────── */
     237
     238  /** One placeholder shape. */
     239  function bone(classes) {
     240    return '<span class="shopshelf-skeleton-bone ' + classes + '"></span>';
     241  }
     242
     243  /**
     244   * The placeholder card of each layout, shaped like the layout's own cards.
     245   * Grid and Carousel cards are the same card, so they share one; a List card
     246   * puts the image beside the details.
     247   *
     248   * An add-on with a layout of its own can register its card here, under the
     249   * name it gives its wrapper's `data-skeleton-layout` attribute:
     250   * `shopShelfFrontend.skeletonCards.myLayout = function () { return "…"; };`
     251   */
     252  var SKELETON_CARDS = {
     253    grid: function () {
     254      return (
     255        '<div class="shopshelf-skeleton-card shopshelf-skeleton-card--grid">' +
     256        bone("shopshelf-skeleton-media") +
     257        '<div class="shopshelf-skeleton-body">' +
     258        bone("shopshelf-skeleton-title") +
     259        bone("shopshelf-skeleton-title shopshelf-skeleton-short") +
     260        bone("shopshelf-skeleton-rating") +
     261        bone("shopshelf-skeleton-price") +
     262        bone("shopshelf-skeleton-button") +
     263        "</div></div>"
     264      );
     265    },
     266    list: function () {
     267      return (
     268        '<div class="shopshelf-skeleton-card shopshelf-skeleton-card--list">' +
     269        bone("shopshelf-skeleton-media") +
     270        '<div class="shopshelf-skeleton-body">' +
     271        bone("shopshelf-skeleton-title") +
     272        bone("shopshelf-skeleton-rating") +
     273        bone("shopshelf-skeleton-text") +
     274        bone("shopshelf-skeleton-text shopshelf-skeleton-short") +
     275        bone("shopshelf-skeleton-price") +
     276        bone("shopshelf-skeleton-button") +
     277        "</div></div>"
     278      );
     279    },
     280  };
     281  SKELETON_CARDS.carousel = SKELETON_CARDS.grid;
     282
     283  settings.skeletonCards = $.extend({}, SKELETON_CARDS, settings.skeletonCards);
     284
     285  /** Which placeholder card a layout uses. */
     286  function skeletonLayout($section) {
     287    var declared = $section.data("skeletonLayout");
     288
     289    if (declared) {
     290      return String(declared);
     291    }
     292    if ($section.hasClass("shopshelf-carousel-wrapper")) {
     293      return "carousel";
     294    }
     295    return $section.hasClass("shopshelf-list-wrapper") ? "list" : "grid";
     296  }
     297
     298  /** `count` copies of a string. */
     299  function repeat(html, count) {
     300    return new Array(count + 1).join(html);
     301  }
     302
     303  /**
     304   * A layout's placeholders: shown while a request is in flight, taken away
     305   * when it answers. One set at a time — a second request replaces the first
     306   * request's placeholders rather than adding to them.
     307   *
     308   * Replacing products (a filter, a search, a page link), the cards on screen
     309   * are set aside and placeholders take their place; if the request fails
     310   * they are put back. Load More adds placeholders after the cards. A
     311   * carousel is hidden, not changed, behind a row of placeholders, because
     312   * Swiper owns its slides.
     313   *
     314   * Returns null while Skeleton Loading is off, so the layout keeps its
     315   * existing loading behaviour.
     316   */
     317  function createSkeleton($section, $products) {
     318    var config = settings.skeleton;
     319
     320    if (!config) {
     321      return null;
     322    }
     323
     324    var animation = "shopshelf-skeleton--" + (config.animation || "shimmer");
     325    var items = Math.max(1, parseInt(config.items, 10) || 6);
     326    var $nodes = null; // The placeholders on screen.
     327    var $stash = null; // The cards they stand in for.
     328    var $hidden = null; // The carousel they stand in for.
     329    var columns = $section.data("skeletonCols") || "";
     330
     331    function card() {
     332      var build = settings.skeletonCards[skeletonLayout($section)] || settings.skeletonCards.grid;
     333      return build();
     334    }
     335
     336    /** Measure a product card, so the placeholders take its space. */
     337    function measure($target, $card) {
     338      var target = $target.get(0);
     339      var $item = $card.find(".shopshelf__item, .shopshelf-premade").first();
     340      var $media = $item.find(".shopshelf__item--thumbnail, .shopshelf-premade-media").first();
     341
     342      if (!target || !$item.length) {
     343        return;
     344      }
     345
     346      target.style.setProperty("--shopshelf-skeleton-card-h", Math.round($item.outerHeight()) + "px");
     347
     348      if ($media.length && $media.outerHeight() > 0) {
     349        target.style.setProperty("--shopshelf-skeleton-media-h", Math.round($media.outerHeight()) + "px");
     350      }
     351    }
     352
     353    function unmeasure(target) {
     354      if (target) {
     355        target.style.removeProperty("--shopshelf-skeleton-card-h");
     356        target.style.removeProperty("--shopshelf-skeleton-media-h");
     357      }
     358    }
     359
     360    /** Take the placeholders away. The set-aside cards stay set aside. */
     361    function remove() {
     362      if ($nodes) {
     363        $nodes.remove();
     364        $nodes = null;
     365      }
     366      if ($hidden) {
     367        $hidden.removeClass("shopshelf-skeleton-hidden");
     368        $hidden = null;
     369      }
     370    }
     371
     372    function showCarousel($carousel) {
     373      var carousel = $carousel.data("carousel") || {};
     374      var gap = parseInt(carousel.spaceBetween, 10) || 0;
     375      var perView = 1;
     376      var rows = 1;
     377
     378      if (carousel.mode === "ticker") {
     379        perView = Math.max(1, Math.round($carousel.width() / ((carousel.tickerWidth || 380) + gap)));
     380      } else if (carousel.slidesPerView && carousel.breakpoints) {
     381        var size = currentSize(carousel);
     382        perView = Math.max(1, parseInt(carousel.slidesPerView[size], 10) || 1);
     383        rows = Math.max(1, parseInt((carousel.rows || {})[size], 10) || 1);
     384      }
     385
     386      var $slide = $carousel.find(".swiper-slide").first();
     387      var height = $carousel.outerHeight();
     388      var $track = $('<div class="shopshelf-skeleton-track ' + animation + '" aria-hidden="true"></div>');
     389      var track = $track.get(0);
     390
     391      $track.html(repeat('<div class="shopshelf-skeleton-slide">' + card() + "</div>", perView * rows));
     392      track.style.setProperty("--shopshelf-skeleton-gap", gap + "px");
     393      track.style.setProperty("--shopshelf-skeleton-per-view", String(perView));
     394      if (height > 0) {
     395        track.style.minHeight = Math.round(height) + "px";
     396      }
     397      measure($track, $slide);
     398
     399      $carousel.addClass("shopshelf-skeleton-hidden").before($track);
     400      $hidden = $carousel;
     401      $nodes = $track;
     402    }
     403
     404    function showCards(append) {
     405      var $row = $products();
     406      var $cards = $row.children();
     407
     408      if ($cards.length) {
     409        // The cards' own column classes, so the placeholders follow the
     410        // layout's Columns at every breakpoint.
     411        columns = $.trim(String($cards.first().attr("class") || "").replace(/\bshopshelf-fade-in\b/g, "")) || columns;
     412        measure($row, $cards.first());
     413      }
     414
     415      if (!append && !$stash) {
     416        $stash = $cards.detach();
     417      }
     418
     419      $nodes = $(
     420        $.parseHTML(
     421          repeat(
     422            '<div class="' + (columns || "shopshelf-col-xs-1 shopshelf-col-sm-2 shopshelf-col-md-2 shopshelf-col-lg-3 shopshelf-col-xl-3") +
     423              " shopshelf-skeleton-item " + animation + '" aria-hidden="true">' + card() + "</div>",
     424            items
     425          )
     426        )
     427      );
     428      $row.append($nodes);
     429    }
     430
     431    return {
     432      /** Show placeholders for a request. */
     433      show: function (append) {
     434        var $carousel = $section.find(".shopshelf-carousel").first();
     435
     436        remove();
     437        $section.addClass("shopshelf-skeleton-loading");
     438        $products().attr("aria-busy", "true");
     439
     440        if ($carousel.length) {
     441          showCarousel($carousel);
     442        } else {
     443          showCards(append);
     444        }
     445      },
     446
     447      /**
     448       * Take the placeholders away.
     449       *
     450       * @param {boolean} restore Put back the cards that were set aside (the
     451       *                          request failed), rather than drop them.
     452       */
     453      end: function (restore) {
     454        var $row = $products();
     455
     456        remove();
     457
     458        if (restore && $stash) {
     459          $row.append($stash);
     460        }
     461        $stash = null;
     462
     463        $section.removeClass("shopshelf-skeleton-loading");
     464        $row.removeAttr("aria-busy");
     465        unmeasure($row.get(0));
     466      },
     467    };
     468  }
     469
    236470  /* ── One layout ────────────────────────────────────────────────────── */
    237471
    … …  
    274508        .first();
    275509    }
     510
     511    // Placeholder cards while products load; null when Skeleton Loading is off.
     512    var skeleton = createSkeleton($section, $products);
    276513
    277514    /** Draw the numbered page links for the current state. */
    … …  
    362599    function request(page, append) {
    363600      var id = ++requestId;
     601      var answered = false;
    364602      var termList = Object.keys(state.terms).map(function (taxonomy) {
    365603        return { taxonomy: taxonomy, term_id: state.terms[taxonomy] };
    … …  
    369607      $section.addClass("shopshelf-is-loading");
    370608      $more.find("button").prop("disabled", true);
     609
     610      if (skeleton) {
     611        skeleton.show(append);
     612      }
    371613
    372614      var data = {
    … …  
    398640          state.page = data.page;
    399641          state.pages = data.pages;
     642          answered = true;
     643
     644          // Before the new cards go in: the placeholders leave the row, and
     645          // a hidden carousel is visible again before Swiper measures it.
     646          if (skeleton) {
     647            skeleton.end(false);
     648          }
    400649
    401650          if (swiper) {
    … …  
    422671        .always(function () {
    423672          if (id === requestId) {
     673            // A failed or refused request: the products that were on screen
     674            // come back, as they were.
     675            if (skeleton && !answered) {
     676              skeleton.end(true);
     677            }
    424678            loading = false;
    425679            $section.removeClass("shopshelf-is-loading");
  • shopshelf/trunk/src/Frontend/assets/js/scripts.min.js

    r3781336 r3798874  
    1 (y=>{var x=window.shopShelfFrontend||{},l=["mobile","mobile_landscape","tablet","desktop","lg_desktop"];function d(e,a){var t,s,n,i=a.slidesPerView[t=a,s=window.innerWidth,n="mobile",l.slice(1).forEach(function(e){s>=t.breakpoints[e]&&(n=e)}),n]||1,o=e.children(".swiper-slide");if(!(i<2)){o.removeClass("swiper-slide");for(var r=0;r<o.length;r+=i)o.slice(r,r+i).wrapAll(y('<div class="swiper-slide shopshelf-slide-group"></div>').css("gap",a.spaceBetween+"px"))}}function f(e,t){var a,s="fade"===t.effect||"cube"===t.effect||"flip"===t.effect,n=!s&&(a=t,l.some(function(e){return 1<a.rows[e]})),i=e.find(".shopshelf-button-next").get(0),o=e.find(".shopshelf-button-prev").get(0),e=e.find(".shopshelf-pagination").get(0),r={speed:t.speed,effect:t.effect,spaceBetween:t.spaceBetween,loop:t.loop&&!n,autoHeight:t.autoHeight&&!n,centeredSlides:t.centered,simulateTouch:t.simulateTouch,allowTouchMove:t.allowTouchMove,watchOverflow:!0,observer:!0,observeParents:!0,mousewheel:!!t.mousewheel&&{forceToAxis:!0},keyboard:{enabled:t.keyboard,onlyInViewport:!0},fadeEffect:{crossFade:!0},autoplay:!!t.autoplay&&{delay:t.autoplayDelay,disableOnInteraction:!1,pauseOnMouseEnter:t.pauseOnHover},navigation:!!(t.navigation&&i&&o)&&{nextEl:i,prevEl:o,addIcons:!1},pagination:!(!t.pagination||!e)&&{el:e,clickable:!0,dynamicBullets:t.dynamicBullets,renderBullet:function(e,a){return'<span class="'+a+'">'+("number"===t.bulletType?e+1:"")+"</span>"}},a11y:{enabled:t.a11y.enabled,prevSlideMessage:t.a11y.prevSlideMessage,nextSlideMessage:t.a11y.nextSlideMessage,firstSlideMessage:t.a11y.firstSlideMessage,lastSlideMessage:t.a11y.lastSlideMessage,paginationBulletMessage:t.a11y.paginationBulletMessage}};return s?(r.slidesPerView=1,r.slidesPerGroup=1):(r.slidesPerView=t.slidesPerView.mobile,r.slidesPerGroup=t.slidesPerGroup.mobile,r.grid={rows:t.rows.mobile,fill:"row"},r.breakpoints={},l.slice(1).forEach(function(e){r.breakpoints[t.breakpoints[e]]={slidesPerView:t.slidesPerView[e],slidesPerGroup:t.slidesPerGroup[e],grid:{rows:t.rows[e],fill:"row"}}})),r}function k(e){var e=e.find(".shopshelf-carousel").first(),a=e.data("carousel"),t=window.ShopShelfSwiper||window.Swiper;if(!e.length||!a||"function"!=typeof t)return null;var s=e.children(".swiper-wrapper");if("ticker"!==a.mode)return"fade"!==a.effect&&"cube"!==a.effect&&"flip"!==a.effect||d(s,a),new t(e.get(0),f(e,a));var n=e,i=s,s=a,o=i.children(".swiper-slide"),r=s.tickerWidth+s.spaceBetween,l=Math.ceil(2*n.width()/r)+1,p=0;if(o.css("width",s.tickerWidth+"px"),o.length)for(;i.children(".swiper-slide").length<l&&p<20;)o.clone().attr("aria-hidden","true").find("a, button, input").attr("tabindex","-1").end().appendTo(i),p++;return new t(e.get(0),{loop:!0,slidesPerView:"auto",spaceBetween:a.spaceBetween,speed:a.tickerSpeed,allowTouchMove:!1,freeMode:{enabled:!0,momentum:!1},autoplay:{delay:0,disableOnInteraction:!1,pauseOnMouseEnter:a.pauseOnHover,reverseDirection:a.reverse},a11y:{enabled:!1}})}function a(a){var t,s,n,i,r,o,l,p,d,f,c,h,u=y(a);function g(){return u.find(".shopshelf-carousel > .swiper-wrapper, .shopshelf-row").first()}function w(){var e;r.length&&(e=h.page>=h.pages,r.toggleClass("shopshelf-hide",e||"infinite_scroll"===l),r.find("button").prop("disabled",!1),o.text(e&&1<h.page&&n.data("endingtext")||"").toggleClass("shopshelf-hide",!(e&&1<h.page)))}function m(){if("no_ajax"===l&&(l="ajax_pagination"),i.length&&(i.empty(),!(h.pages<2))){var t=function(e,a,t){return y("<a/>",{href:"#",class:"page-numbers "+(t||""),"data-page":e}).append(a)},e=function(e,a){return t(e,'<i class="icofont-simple-'+("prev"===e?"left":"right")+'" aria-hidden="true"></i>',"shopshelf_"+e+(a?" shopshelf-disabled":"")).append(y("<span/>",{class:"screen-reader-text",text:i.data(e)||""}))};i.append(e("prev",h.page<=1));for(var a=1;a<=h.pages;a++){var s=t(a,document.createTextNode(a),a===h.page?"active":"");a===h.page&&s.attr("aria-current","page"),i.append(s)}i.append(e("next",h.page>=h.pages))}w()}function v(e,i){var o=++d,a=Object.keys(h.terms).map(function(e){return{taxonomy:e,term_id:h.terms[e]}}),e=(f=!0,u.addClass("shopshelf-is-loading"),r.find("button").prop("disabled",!0),{action:"shopshelf_products",nonce:x.nonce,id:t,lang:u.data("lang")||"",keyword:h.keyword,page:e,term_list:a});s&&(e.source=s),y.post(x.ajaxurl,e).done(function(e){var a,t,s,n;o===d&&e&&e.success&&(e=e.data,a=y(y.parseHTML(e.products||"",document,!1)),h.page=e.page,h.pages=e.pages,p?(p.destroy(!0,!0),g().html(a),p=k(u)):i?(a.filter("div").addClass("shopshelf-fade-in"),g().append(a)):g().html(a),i||(e=e.filters||"",(n=u.find(".shopshelf-filter-bar").first()).length&&""!==e&&(t=document.activeElement,s=t&&y.contains(n.get(0),t)?{name:t.name,value:t.value,tag:t.tagName}:null,n.html(y.parseHTML(e,document,!1)),s)&&n.find(s.tag.toLowerCase()).filter(function(){return this.name===s.name&&("SELECT"===s.tag||this.value===s.value)}).first().trigger("focus")),m(),u.trigger("shopshelf:products-loaded",[a,i]))}).always(function(){o===d&&(f=!1,u.removeClass("shopshelf-is-loading"),r.find("button").prop("disabled",!1))})}function b(){v(1,!1)}u.data("shopshelfReady")||(u.data("shopshelfReady",!0),t=u.data("sid"),s=u.closest("[data-shopshelf-source]").data("shopshelfSource")||null,n=u.find(".shopshelf-pagination-area").first(),i=n.find(".shopshelf-post-pagination"),r=n.find(".shopshelf-load-more"),o=n.find(".shopshelf-load-more-end"),l=u.data("pagination")||"",p=k(u),d=0,f=!1,c=null,h={keyword:"",terms:{},page:parseInt(n.data("page"),10)||1,pages:parseInt(n.data("pages"),10)||1},u.addClass("shopshelf-is-ready"),u.trigger("shopshelf:ready",[u]),u.on("change",".shopshelf-filter-by",function(){var e=y(this),a=e.data("taxonomy"),e="checkbox"===e.data("type")?e.find("input:checked").map(function(){return this.value}).get().join(","):("select"===e.data("type")?e.find("select"):e.find("input:checked")).val();a&&(e&&"all"!==e?h.terms[a]=String(e):delete h.terms[a],b())}),u.on("input",".shopshelf-search-field",function(){var e=y.trim(this.value);window.clearTimeout(c),c=window.setTimeout(function(){e!==h.keyword&&(h.keyword=e,b())},350)}),u.on("click",".shopshelf-post-pagination a",function(e){"no_ajax"===l||(e.preventDefault(),e="prev"===(e=y(this).data("page"))?h.page-1:"next"===e?h.page+1:parseInt(e,10),f)||!e||e<1||e>h.pages||e===h.page||(v(e,!1),a.getBoundingClientRect().top<0&&a.scrollIntoView({behavior:"smooth",block:"start"}))}),u.on("click",".shopshelf-load-more button",function(e){e.preventDefault(),!f&&h.page<h.pages&&v(h.page+1,!0)}),"infinite_scroll"===l&&n.length&&(r.addClass("shopshelf-hide"),"IntersectionObserver"in window?new window.IntersectionObserver(function(e){e.forEach(function(e){e.isIntersecting&&!f&&h.page<h.pages&&v(h.page+1,!0)})},{rootMargin:"200px 0px"}).observe(n.get(0)):(l="ajax_load_more",w())))}function e(e){y(e||document).find(".shopshelf-section").each(function(){a(this)})}y(document).on("click",'a[data-shopshelf-share="popup"]',function(e){var a=Math.max(0,(window.screen.availWidth-600)/2),t=Math.max(0,(window.screen.availHeight-500)/2),a=window.open(this.href,"shopshelf-share","width=600,height=500,left="+a+",top="+t);a&&(a.opener=null,e.preventDefault())}),x.init=e,window.shopShelfFrontend=x,y(function(){e(document)})})(jQuery);
     1(y=>{var x=window.shopShelfFrontend||{},r=["mobile","mobile_landscape","tablet","desktop","lg_desktop"];function b(s){var t=window.innerWidth,a="mobile";return r.slice(1).forEach(function(e){t>=s.breakpoints[e]&&(a=e)}),a}function g(e,t){var s,a="fade"===t.effect||"cube"===t.effect||"flip"===t.effect,o=!a&&(s=t,r.some(function(e){return 1<s.rows[e]})),n=e.find(".shopshelf-button-next").get(0),l=e.find(".shopshelf-button-prev").get(0),e=e.find(".shopshelf-pagination").get(0),i={speed:t.speed,effect:t.effect,spaceBetween:t.spaceBetween,loop:t.loop&&!o,autoHeight:t.autoHeight&&!o,centeredSlides:t.centered,simulateTouch:t.simulateTouch,allowTouchMove:t.allowTouchMove,watchOverflow:!0,observer:!0,observeParents:!0,mousewheel:!!t.mousewheel&&{forceToAxis:!0},keyboard:{enabled:t.keyboard,onlyInViewport:!0},fadeEffect:{crossFade:!0},autoplay:!!t.autoplay&&{delay:t.autoplayDelay,disableOnInteraction:!1,pauseOnMouseEnter:t.pauseOnHover},navigation:!!(t.navigation&&n&&l)&&{nextEl:n,prevEl:l,addIcons:!1},pagination:!(!t.pagination||!e)&&{el:e,clickable:!0,dynamicBullets:t.dynamicBullets,renderBullet:function(e,s){return'<span class="'+s+'">'+("number"===t.bulletType?e+1:"")+"</span>"}},a11y:{enabled:t.a11y.enabled,prevSlideMessage:t.a11y.prevSlideMessage,nextSlideMessage:t.a11y.nextSlideMessage,firstSlideMessage:t.a11y.firstSlideMessage,lastSlideMessage:t.a11y.lastSlideMessage,paginationBulletMessage:t.a11y.paginationBulletMessage}};return a?(i.slidesPerView=1,i.slidesPerGroup=1):(i.slidesPerView=t.slidesPerView.mobile,i.slidesPerGroup=t.slidesPerGroup.mobile,i.grid={rows:t.rows.mobile,fill:"row"},i.breakpoints={},r.slice(1).forEach(function(e){i.breakpoints[t.breakpoints[e]]={slidesPerView:t.slidesPerView[e],slidesPerGroup:t.slidesPerGroup[e],grid:{rows:t.rows[e],fill:"row"}}})),i}function M(e){var e=e.find(".shopshelf-carousel").first(),s=e.data("carousel"),t=window.ShopShelfSwiper||window.Swiper;if(!e.length||!s||"function"!=typeof t)return null;var a=e.children(".swiper-wrapper");if("ticker"===s.mode){var o=e,n=a,l=s,i=n.children(".swiper-slide"),r=l.tickerWidth+l.spaceBetween,p=Math.ceil(2*o.width()/r)+1,d=0;if(i.css("width",l.tickerWidth+"px"),i.length)for(;n.children(".swiper-slide").length<p&&d<20;)i.clone().attr("aria-hidden","true").find("a, button, input").attr("tabindex","-1").end().appendTo(n),d++;return new t(e.get(0),{loop:!0,slidesPerView:"auto",spaceBetween:s.spaceBetween,speed:s.tickerSpeed,allowTouchMove:!1,freeMode:{enabled:!0,momentum:!1},autoplay:{delay:0,disableOnInteraction:!1,pauseOnMouseEnter:s.pauseOnHover,reverseDirection:s.reverse},a11y:{enabled:!1}})}if("fade"===s.effect||"cube"===s.effect||"flip"===s.effect){var o=a,h=s,f=h.slidesPerView[b(h)]||1,c=o.children(".swiper-slide");if(!(f<2)){c.removeClass("swiper-slide");for(var u=0;u<c.length;u+=f)c.slice(u,u+f).wrapAll(y('<div class="swiper-slide shopshelf-slide-group"></div>').css("gap",h.spaceBetween+"px"))}}return new t(e.get(0),g(e,s))}function e(e){return'<span class="shopshelf-skeleton-bone '+e+'"></span>'}var s={grid:function(){return'<div class="shopshelf-skeleton-card shopshelf-skeleton-card--grid">'+e("shopshelf-skeleton-media")+'<div class="shopshelf-skeleton-body">'+e("shopshelf-skeleton-title")+e("shopshelf-skeleton-title shopshelf-skeleton-short")+e("shopshelf-skeleton-rating")+e("shopshelf-skeleton-price")+e("shopshelf-skeleton-button")+"</div></div>"},list:function(){return'<div class="shopshelf-skeleton-card shopshelf-skeleton-card--list">'+e("shopshelf-skeleton-media")+'<div class="shopshelf-skeleton-body">'+e("shopshelf-skeleton-title")+e("shopshelf-skeleton-rating")+e("shopshelf-skeleton-text")+e("shopshelf-skeleton-text shopshelf-skeleton-short")+e("shopshelf-skeleton-price")+e("shopshelf-skeleton-button")+"</div></div>"}};function C(e,s){return new Array(s+1).join(e)}function S(p,d){var h,f,c,u,g,m,e=x.skeleton;return e?(h="shopshelf-skeleton--"+(e.animation||"shimmer"),f=Math.max(1,parseInt(e.items,10)||6),g=u=c=null,m=p.data("skeletonCols")||"",{show:function(e){var s,t,a,o,n,l,i,r=p.find(".shopshelf-carousel").first();k(),p.addClass("shopshelf-skeleton-loading"),d().attr("aria-busy","true"),r.length?(s=(r=r).data("carousel")||{},t=parseInt(s.spaceBetween,10)||0,o=a=1,"ticker"===s.mode?a=Math.max(1,Math.round(r.width()/((s.tickerWidth||380)+t))):s.slidesPerView&&s.breakpoints&&(n=b(s),a=Math.max(1,parseInt(s.slidesPerView[n],10)||1),o=Math.max(1,parseInt((s.rows||{})[n],10)||1)),s=r.find(".swiper-slide").first(),n=r.outerHeight(),l=y('<div class="shopshelf-skeleton-track '+h+'" aria-hidden="true"></div>'),i=l.get(0),l.html(C('<div class="shopshelf-skeleton-slide">'+w()+"</div>",a*o)),i.style.setProperty("--shopshelf-skeleton-gap",t+"px"),i.style.setProperty("--shopshelf-skeleton-per-view",String(a)),0<n&&(i.style.minHeight=Math.round(n)+"px"),v(l,s),r.addClass("shopshelf-skeleton-hidden").before(l),g=r,c=l):(o=e,t=d(),(a=t.children()).length&&(m=y.trim(String(a.first().attr("class")||"").replace(/\bshopshelf-fade-in\b/g,""))||m,v(t,a.first())),o||(u=u||a.detach()),c=y(y.parseHTML(C('<div class="'+(m||"shopshelf-col-xs-1 shopshelf-col-sm-2 shopshelf-col-md-2 shopshelf-col-lg-3 shopshelf-col-xl-3")+" shopshelf-skeleton-item "+h+'" aria-hidden="true">'+w()+"</div>",f))),t.append(c))},end:function(e){var s=d();k(),e&&u&&s.append(u),u=null,p.removeClass("shopshelf-skeleton-loading"),s.removeAttr("aria-busy"),(e=s.get(0))&&(e.style.removeProperty("--shopshelf-skeleton-card-h"),e.style.removeProperty("--shopshelf-skeleton-media-h"))}}):null;function w(){var e,s;return(x.skeletonCards[(s=(e=p).data("skeletonLayout"))?String(s):e.hasClass("shopshelf-carousel-wrapper")?"carousel":e.hasClass("shopshelf-list-wrapper")?"list":"grid"]||x.skeletonCards.grid)()}function v(e,s){var e=e.get(0),s=s.find(".shopshelf__item, .shopshelf-premade").first(),t=s.find(".shopshelf__item--thumbnail, .shopshelf-premade-media").first();e&&s.length&&(e.style.setProperty("--shopshelf-skeleton-card-h",Math.round(s.outerHeight())+"px"),t.length)&&0<t.outerHeight()&&e.style.setProperty("--shopshelf-skeleton-media-h",Math.round(t.outerHeight())+"px")}function k(){c&&(c.remove(),c=null),g&&(g.removeClass("shopshelf-skeleton-hidden"),g=null)}}function t(s){var t,a,o,n,r,l,i,p,d,h,f,c,u,g=y(s);function m(){return g.find(".shopshelf-carousel > .swiper-wrapper, .shopshelf-row").first()}function w(){var e;r.length&&(e=c.page>=c.pages,r.toggleClass("shopshelf-hide",e||"infinite_scroll"===i),r.find("button").prop("disabled",!1),l.text(e&&1<c.page&&o.data("endingtext")||"").toggleClass("shopshelf-hide",!(e&&1<c.page)))}function v(){if("no_ajax"===i&&(i="ajax_pagination"),n.length&&(n.empty(),!(c.pages<2))){var t=function(e,s,t){return y("<a/>",{href:"#",class:"page-numbers "+(t||""),"data-page":e}).append(s)},e=function(e,s){return t(e,'<i class="icofont-simple-'+("prev"===e?"left":"right")+'" aria-hidden="true"></i>',"shopshelf_"+e+(s?" shopshelf-disabled":"")).append(y("<span/>",{class:"screen-reader-text",text:n.data(e)||""}))};n.append(e("prev",c.page<=1));for(var s=1;s<=c.pages;s++){var a=t(s,document.createTextNode(s),s===c.page?"active":"");s===c.page&&a.attr("aria-current","page"),n.append(a)}n.append(e("next",c.page>=c.pages))}w()}function k(e,n){var l=++d,i=!1,s=Object.keys(c.terms).map(function(e){return{taxonomy:e,term_id:c.terms[e]}}),e=(h=!0,g.addClass("shopshelf-is-loading"),r.find("button").prop("disabled",!0),u&&u.show(n),{action:"shopshelf_products",nonce:x.nonce,id:t,lang:g.data("lang")||"",keyword:c.keyword,page:e,term_list:s});a&&(e.source=a),y.post(x.ajaxurl,e).done(function(e){var s,t,a,o;l===d&&e&&e.success&&(e=e.data,s=y(y.parseHTML(e.products||"",document,!1)),c.page=e.page,c.pages=e.pages,i=!0,u&&u.end(!1),p?(p.destroy(!0,!0),m().html(s),p=M(g)):n?(s.filter("div").addClass("shopshelf-fade-in"),m().append(s)):m().html(s),n||(e=e.filters||"",(o=g.find(".shopshelf-filter-bar").first()).length&&""!==e&&(t=document.activeElement,a=t&&y.contains(o.get(0),t)?{name:t.name,value:t.value,tag:t.tagName}:null,o.html(y.parseHTML(e,document,!1)),a)&&o.find(a.tag.toLowerCase()).filter(function(){return this.name===a.name&&("SELECT"===a.tag||this.value===a.value)}).first().trigger("focus")),v(),g.trigger("shopshelf:products-loaded",[s,n]))}).always(function(){l===d&&(u&&!i&&u.end(!0),h=!1,g.removeClass("shopshelf-is-loading"),r.find("button").prop("disabled",!1))})}function b(){k(1,!1)}g.data("shopshelfReady")||(g.data("shopshelfReady",!0),t=g.data("sid"),a=g.closest("[data-shopshelf-source]").data("shopshelfSource")||null,o=g.find(".shopshelf-pagination-area").first(),n=o.find(".shopshelf-post-pagination"),r=o.find(".shopshelf-load-more"),l=o.find(".shopshelf-load-more-end"),i=g.data("pagination")||"",p=M(g),d=0,h=!1,f=null,c={keyword:"",terms:{},page:parseInt(o.data("page"),10)||1,pages:parseInt(o.data("pages"),10)||1},g.addClass("shopshelf-is-ready"),g.trigger("shopshelf:ready",[g]),u=S(g,m),g.on("change",".shopshelf-filter-by",function(){var e=y(this),s=e.data("taxonomy"),e="checkbox"===e.data("type")?e.find("input:checked").map(function(){return this.value}).get().join(","):("select"===e.data("type")?e.find("select"):e.find("input:checked")).val();s&&(e&&"all"!==e?c.terms[s]=String(e):delete c.terms[s],b())}),g.on("input",".shopshelf-search-field",function(){var e=y.trim(this.value);window.clearTimeout(f),f=window.setTimeout(function(){e!==c.keyword&&(c.keyword=e,b())},350)}),g.on("click",".shopshelf-post-pagination a",function(e){"no_ajax"===i||(e.preventDefault(),e="prev"===(e=y(this).data("page"))?c.page-1:"next"===e?c.page+1:parseInt(e,10),h)||!e||e<1||e>c.pages||e===c.page||(k(e,!1),s.getBoundingClientRect().top<0&&s.scrollIntoView({behavior:"smooth",block:"start"}))}),g.on("click",".shopshelf-load-more button",function(e){e.preventDefault(),!h&&c.page<c.pages&&k(c.page+1,!0)}),"infinite_scroll"===i&&o.length&&(r.addClass("shopshelf-hide"),"IntersectionObserver"in window?new window.IntersectionObserver(function(e){e.forEach(function(e){e.isIntersecting&&!h&&c.page<c.pages&&k(c.page+1,!0)})},{rootMargin:"200px 0px"}).observe(o.get(0)):(i="ajax_load_more",w())))}function a(e){y(e||document).find(".shopshelf-section").each(function(){t(this)})}s.carousel=s.grid,x.skeletonCards=y.extend({},s,x.skeletonCards),y(document).on("click",'a[data-shopshelf-share="popup"]',function(e){var s=Math.max(0,(window.screen.availWidth-600)/2),t=Math.max(0,(window.screen.availHeight-500)/2),s=window.open(this.href,"shopshelf-share","width=600,height=500,left="+s+",top="+t);s&&(s.opener=null,e.preventDefault())}),x.init=a,window.shopShelfFrontend=x,y(function(){a(document)})})(jQuery);
  • shopshelf/trunk/src/Frontend/class-shopshelf-frontend.php

    r3781336 r3798874  
    7070                wp_enqueue_style( 'shopshelf-style' );
    7171
     72                $this->register_skeleton_style();
     73
    7274                $css = $this->dynamic_css();
    7375
    … …  
    8486                        wp_add_inline_style( 'shopshelf-style', $custom_css );
    8587                }
     88        }
     89
     90        /**
     91         * Register the Skeleton Loading stylesheet, with the colours and radius
     92         * from Settings attached.
     93         *
     94         * Only registered: the renderer enqueues it on pages that show a layout,
     95         * and only while Skeleton Loading is on. See self::enqueue_skeleton_style().
     96         *
     97         * @since 1.3.2
     98         * @return void
     99         */
     100        public function register_skeleton_style() {
     101                if ( wp_style_is( 'shopshelf-skeleton', 'registered' ) ) {
     102                        return;
     103                }
     104
     105                $skeleton = ShopShelf_Functions::shopshelf_skeleton_settings();
     106
     107                wp_register_style( 'shopshelf-skeleton', SHOPSHELF_FRONTEND_URL . 'assets/css/shopshelf-skeleton' . $this->suffix . '.css', array( 'shopshelf-style' ), SHOPSHELF_VERSION );
     108
     109                if ( empty( $skeleton['enabled'] ) ) {
     110                        return;
     111                }
     112
     113                wp_add_inline_style(
     114                        'shopshelf-skeleton',
     115                        sprintf(
     116                                '.shopshelf-section{--shopshelf-skeleton-bg:%1$s;--shopshelf-skeleton-highlight:%2$s;--shopshelf-skeleton-radius:%3$dpx;}',
     117                                ShopShelf_Dynamic_CSS::color( $skeleton['background'] ),
     118                                ShopShelf_Dynamic_CSS::color( $skeleton['highlight'] ),
     119                                absint( $skeleton['radius'] )
     120                        )
     121                );
     122        }
     123
     124        /**
     125         * Enqueue the Skeleton Loading stylesheet, if Skeleton Loading is on.
     126         *
     127         * @since 1.3.2
     128         * @return bool Whether it was enqueued.
     129         */
     130        public static function enqueue_skeleton_style() {
     131                $skeleton = ShopShelf_Functions::shopshelf_skeleton_settings();
     132
     133                if ( empty( $skeleton['enabled'] ) || ! wp_style_is( 'shopshelf-skeleton', 'registered' ) ) {
     134                        return false;
     135                }
     136
     137                wp_enqueue_style( 'shopshelf-skeleton' );
     138
     139                return true;
    86140        }
    87141
    … …  
    154208                wp_register_script( 'shopshelf-script', SHOPSHELF_FRONTEND_URL . 'assets/js/scripts' . $this->suffix . '.js', array( 'jquery' ), SHOPSHELF_VERSION, true );
    155209
    156                 wp_localize_script(
    157                         'shopshelf-script',
    158                         'shopShelfFrontend',
    159                         array(
    160                                 'ajaxurl' => admin_url( 'admin-ajax.php' ),
    161                                 'nonce'   => wp_create_nonce( self::NONCE ),
    162                         )
    163                 );
     210                $frontend = array(
     211                        'ajaxurl' => admin_url( 'admin-ajax.php' ),
     212                        'nonce'   => wp_create_nonce( self::NONCE ),
     213                );
     214
     215                // Present only while Skeleton Loading is on: its absence is what keeps
     216                // the script on the existing loading behaviour.
     217                $skeleton = ShopShelf_Functions::shopshelf_skeleton_settings();
     218
     219                if ( ! empty( $skeleton['enabled'] ) ) {
     220                        $frontend['skeleton'] = array(
     221                                'animation' => $skeleton['animation'],
     222                                'items'     => (int) $skeleton['items'],
     223                        );
     224                }
     225
     226                wp_localize_script( 'shopshelf-script', 'shopShelfFrontend', $frontend );
    164227
    165228                // Custom JavaScript rides on the plugin's own script, which the renderer
  • shopshelf/trunk/src/Frontend/templates/grid.php

    r3777923 r3798874  
    2828        data-pagination="<?php echo esc_attr( $show_pagination ? $pagination_type : '' ); ?>"
    2929        data-lang="<?php echo esc_attr( $lang ); ?>"
     30        <?php ShopShelf_HTML::shopshelf_skeleton_attributes( $layout_preset, $shopshelf_view_options ); ?>
    3031>
    3132        <?php
  • shopshelf/trunk/src/Frontend/templates/list.php

    r3787871 r3798874  
    3737        data-pagination="<?php echo esc_attr( $show_pagination ? $pagination_type : '' ); ?>"
    3838        data-lang="<?php echo esc_attr( $lang ); ?>"
     39        <?php ShopShelf_HTML::shopshelf_skeleton_attributes( $layout_preset, $shopshelf_view_options ); ?>
    3940>
    4041        <?php
  • shopshelf/trunk/vendor/composer/autoload_classmap.php

    r3778148 r3798874  
    1010    'ShopShelf\\Admin\\Admin' => $baseDir . '/src/Admin/Admin.php',
    1111    'ShopShelf\\Admin\\Assets' => $baseDir . '/src/Admin/Assets.php',
     12    'ShopShelf\\Admin\\Block\\Block' => $baseDir . '/src/Admin/Block/Block.php',
     13    'ShopShelf\\Admin\\Elementor\\Choices' => $baseDir . '/src/Admin/Elementor/Choices.php',
     14    'ShopShelf\\Admin\\Elementor\\Controls\\ContentControls' => $baseDir . '/src/Admin/Elementor/Controls/ContentControls.php',
     15    'ShopShelf\\Admin\\Elementor\\Controls\\StyleControls' => $baseDir . '/src/Admin/Elementor/Controls/StyleControls.php',
     16    'ShopShelf\\Admin\\Elementor\\Integration' => $baseDir . '/src/Admin/Elementor/Integration.php',
     17    'ShopShelf\\Admin\\Elementor\\Settings' => $baseDir . '/src/Admin/Elementor/Settings.php',
     18    'ShopShelf\\Admin\\Elementor\\Widgets\\CarouselWidget' => $baseDir . '/src/Admin/Elementor/Widgets/CarouselWidget.php',
     19    'ShopShelf\\Admin\\Elementor\\Widgets\\GridWidget' => $baseDir . '/src/Admin/Elementor/Widgets/GridWidget.php',
     20    'ShopShelf\\Admin\\Elementor\\Widgets\\LayoutWidget' => $baseDir . '/src/Admin/Elementor/Widgets/LayoutWidget.php',
     21    'ShopShelf\\Admin\\Elementor\\Widgets\\ListWidget' => $baseDir . '/src/Admin/Elementor/Widgets/ListWidget.php',
     22    'ShopShelf\\Admin\\Elementor\\Widgets\\ProductsWidget' => $baseDir . '/src/Admin/Elementor/Widgets/ProductsWidget.php',
    1223    'ShopShelf\\Admin\\Preview\\Preview' => $baseDir . '/src/Admin/Preview/Preview.php',
    1324    'ShopShelf\\Admin\\Rest\\AbstractRestController' => $baseDir . '/src/Admin/Rest/AbstractRestController.php',
    … …  
    2637    'ShopShelf\\Admin\\Views\\Settings\\ColorScheme' => $baseDir . '/src/Admin/Views/Settings/ColorScheme.php',
    2738    'ShopShelf\\Admin\\Views\\Settings\\CustomCode' => $baseDir . '/src/Admin/Views/Settings/CustomCode.php',
     39    'ShopShelf\\Admin\\Views\\Settings\\LoadingEffects' => $baseDir . '/src/Admin/Views/Settings/LoadingEffects.php',
    2840    'ShopShelf\\Admin\\Views\\Settings\\ScriptsAndStyles' => $baseDir . '/src/Admin/Views/Settings/ScriptsAndStyles.php',
    2941    'ShopShelf\\Admin\\Views\\ShopShelfMetaboxes' => $baseDir . '/src/Admin/Views/ShopShelfMetaboxes.php',
  • shopshelf/trunk/vendor/composer/autoload_static.php

    r3778148 r3798874  
    2525        'ShopShelf\\Admin\\Admin' => __DIR__ . '/../..' . '/src/Admin/Admin.php',
    2626        'ShopShelf\\Admin\\Assets' => __DIR__ . '/../..' . '/src/Admin/Assets.php',
     27        'ShopShelf\\Admin\\Block\\Block' => __DIR__ . '/../..' . '/src/Admin/Block/Block.php',
     28        'ShopShelf\\Admin\\Elementor\\Choices' => __DIR__ . '/../..' . '/src/Admin/Elementor/Choices.php',
     29        'ShopShelf\\Admin\\Elementor\\Controls\\ContentControls' => __DIR__ . '/../..' . '/src/Admin/Elementor/Controls/ContentControls.php',
     30        'ShopShelf\\Admin\\Elementor\\Controls\\StyleControls' => __DIR__ . '/../..' . '/src/Admin/Elementor/Controls/StyleControls.php',
     31        'ShopShelf\\Admin\\Elementor\\Integration' => __DIR__ . '/../..' . '/src/Admin/Elementor/Integration.php',
     32        'ShopShelf\\Admin\\Elementor\\Settings' => __DIR__ . '/../..' . '/src/Admin/Elementor/Settings.php',
     33        'ShopShelf\\Admin\\Elementor\\Widgets\\CarouselWidget' => __DIR__ . '/../..' . '/src/Admin/Elementor/Widgets/CarouselWidget.php',
     34        'ShopShelf\\Admin\\Elementor\\Widgets\\GridWidget' => __DIR__ . '/../..' . '/src/Admin/Elementor/Widgets/GridWidget.php',
     35        'ShopShelf\\Admin\\Elementor\\Widgets\\LayoutWidget' => __DIR__ . '/../..' . '/src/Admin/Elementor/Widgets/LayoutWidget.php',
     36        'ShopShelf\\Admin\\Elementor\\Widgets\\ListWidget' => __DIR__ . '/../..' . '/src/Admin/Elementor/Widgets/ListWidget.php',
     37        'ShopShelf\\Admin\\Elementor\\Widgets\\ProductsWidget' => __DIR__ . '/../..' . '/src/Admin/Elementor/Widgets/ProductsWidget.php',
    2738        'ShopShelf\\Admin\\Preview\\Preview' => __DIR__ . '/../..' . '/src/Admin/Preview/Preview.php',
    2839        'ShopShelf\\Admin\\Rest\\AbstractRestController' => __DIR__ . '/../..' . '/src/Admin/Rest/AbstractRestController.php',
    … …  
    4152        'ShopShelf\\Admin\\Views\\Settings\\ColorScheme' => __DIR__ . '/../..' . '/src/Admin/Views/Settings/ColorScheme.php',
    4253        'ShopShelf\\Admin\\Views\\Settings\\CustomCode' => __DIR__ . '/../..' . '/src/Admin/Views/Settings/CustomCode.php',
     54        'ShopShelf\\Admin\\Views\\Settings\\LoadingEffects' => __DIR__ . '/../..' . '/src/Admin/Views/Settings/LoadingEffects.php',
    4355        'ShopShelf\\Admin\\Views\\Settings\\ScriptsAndStyles' => __DIR__ . '/../..' . '/src/Admin/Views/Settings/ScriptsAndStyles.php',
    4456        'ShopShelf\\Admin\\Views\\ShopShelfMetaboxes' => __DIR__ . '/../..' . '/src/Admin/Views/ShopShelfMetaboxes.php',
Note: See TracChangeset for help on using the changeset viewer.