feat: implement WooCommerce AJAX product filtering
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Shop / category archive — uses Woo hooks, XShop toolbar/grid/empty states.
|
||||
* Shop / category archive — XShop filters + AJAX grid + no-JS fallback.
|
||||
*
|
||||
* Override is minimal: we keep Woo's before/after hooks but ensure XShop
|
||||
* container, breadcrumbs, title, sorting, result count, pagination, and empty state
|
||||
* are rendered with XShop components/tokens. For full logic see
|
||||
* inc/woocommerce/setup.php hooks (xshop_wc_loop_start, toolbar).
|
||||
* Integrates product-filters sidebar (desktop) + drawer (mobile) + toolbar.
|
||||
* Server render uses Woo hooks + ProductQuery via pre_get_posts (inc/woocommerce/setup.php) for no-JS.
|
||||
* JS enhances via REST /xshop/v1/products.
|
||||
*
|
||||
* WC version tested: 11.1.0
|
||||
* Template version: 3.4.0
|
||||
@@ -33,36 +32,72 @@ if (is_product_category() || is_product_tag() || is_tax('product_brand')) {
|
||||
$title = $shopId ? get_the_title($shopId) : esc_html__('Shop', 'xshop');
|
||||
echo '<header class="xshop-page-header"><h1 class="xshop-page-header__title">' . esc_html($title) . '</h1></header>';
|
||||
}
|
||||
?>
|
||||
|
||||
do_action('woocommerce_before_shop_loop');
|
||||
<div class="xshop-shop" data-xshop-shop>
|
||||
<!-- Mobile filter toggle -->
|
||||
<button type="button" class="xshop-btn xshop-btn--secondary xshop-shop__filter-toggle" data-xshop-drawer-toggle aria-expanded="false" aria-controls="xshop-filter-drawer">
|
||||
<?php esc_html_e('Filters', 'xshop'); ?>
|
||||
</button>
|
||||
|
||||
if (woocommerce_product_loop()) {
|
||||
woocommerce_product_loop_start();
|
||||
if (wc_get_loop_prop('is_shortcode')) {
|
||||
$columns = absint(wc_get_loop_prop('columns'));
|
||||
} else {
|
||||
$columns = absint(get_option('woocommerce_catalog_columns', 4));
|
||||
}
|
||||
<div class="xshop-shop__layout">
|
||||
<!-- Desktop sidebar -->
|
||||
<aside class="xshop-shop__sidebar" aria-label="<?php esc_attr_e('Filters', 'xshop'); ?>">
|
||||
<?php get_template_part('template-parts/components/product-filters'); ?>
|
||||
</aside>
|
||||
|
||||
while (have_posts()) {
|
||||
the_post();
|
||||
do_action('woocommerce_shop_loop');
|
||||
wc_get_template_part('content', 'product');
|
||||
}
|
||||
<!-- Mobile drawer -->
|
||||
<div class="xshop-drawer" id="xshop-filter-drawer" hidden data-xshop-drawer role="dialog" aria-modal="true" aria-label="<?php esc_attr_e('Filters', 'xshop'); ?>">
|
||||
<div class="xshop-drawer__header">
|
||||
<h2 class="xshop-drawer__title"><?php esc_html_e('Filters', 'xshop'); ?></h2>
|
||||
<button type="button" class="xshop-btn xshop-btn--ghost" data-xshop-drawer-close aria-label="<?php esc_attr_e('Close filters', 'xshop'); ?>">×</button>
|
||||
</div>
|
||||
<div class="xshop-drawer__body">
|
||||
<?php get_template_part('template-parts/components/product-filters'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xshop-drawer__overlay" hidden data-xshop-drawer-overlay></div>
|
||||
|
||||
woocommerce_product_loop_end();
|
||||
do_action('woocommerce_after_shop_loop');
|
||||
} else {
|
||||
do_action('woocommerce_no_products_found');
|
||||
// Enhanced empty state for shop/category
|
||||
get_template_part('template-parts/components/empty-state', null, [
|
||||
'title' => esc_html__('No products found', 'xshop'),
|
||||
'text' => esc_html__('Try adjusting your filters or browse all products.', 'xshop'),
|
||||
'cta_label' => esc_html__('Browse shop', 'xshop'),
|
||||
'cta_url' => wc_get_page_permalink('shop'),
|
||||
]);
|
||||
}
|
||||
<!-- Main -->
|
||||
<div class="xshop-shop__main" data-xshop-shop-main>
|
||||
<div class="xshop-shop__toolbar" data-xshop-toolbar>
|
||||
<?php do_action('woocommerce_before_shop_loop'); ?>
|
||||
</div>
|
||||
|
||||
<div class="xshop-search__loading" hidden data-xshop-loading style="display:none">
|
||||
<span class="xshop-loading__spinner" aria-hidden="true"></span> <?php esc_html_e('Loading…', 'xshop'); ?>
|
||||
</div>
|
||||
<div class="xshop-search__error" hidden data-xshop-error role="alert"></div>
|
||||
|
||||
<div data-xshop-products-container>
|
||||
<?php
|
||||
if (woocommerce_product_loop()) {
|
||||
woocommerce_product_loop_start();
|
||||
while (have_posts()) { the_post(); do_action('woocommerce_shop_loop'); wc_get_template_part('content', 'product'); }
|
||||
woocommerce_product_loop_end();
|
||||
do_action('woocommerce_after_shop_loop');
|
||||
} else {
|
||||
do_action('woocommerce_no_products_found');
|
||||
get_template_part('template-parts/components/empty-state', null, [
|
||||
'title' => esc_html__('No products found', 'xshop'),
|
||||
'text' => esc_html__('Try adjusting your filters or browse all products.', 'xshop'),
|
||||
'cta_label' => esc_html__('Browse shop', 'xshop'),
|
||||
'cta_url' => wc_get_page_permalink('shop'),
|
||||
]);
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div data-xshop-pagination>
|
||||
<?php
|
||||
// Woo pagination rendered via woocommerce_after_shop_loop already inside loop; for AJAX we render custom via JS
|
||||
// Ensure result count is accessible
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
do_action('woocommerce_after_main_content');
|
||||
|
||||
get_footer('shop');
|
||||
|
||||
Reference in New Issue
Block a user