Files
f1den 29d10ce4cf Тема магазина по макету Figma
Каталог, карточка товара, корзина, оформление заказа и блог на WooCommerce.

Стили разложены по слоям: токены из Figma, база, компоненты, страницы,
брейкпоинты. Цвета, типографика, радиусы и отступы взяты из Variables и
текстовых стилей макета, ниже по слоям хардкода нет.

Иконки — Phosphor, вставляются инлайном как SVG, символами ничего не рисуется.
Стили WooCommerce отключены, вёрстка своя, !important не используется.
2026-07-30 22:38:49 +03:00

180 lines
9.8 KiB
PHP

<?php
/**
* Product archive and category page.
*
* @package VyatkaShop
*/
defined( 'ABSPATH' ) || exit;
get_header();
$title = woocommerce_page_title( false );
$categories = get_terms(
array(
'taxonomy' => 'product_cat',
'hide_empty' => true,
'parent' => 0,
)
);
$price_min = isset( $_GET['filter_min_price'] ) ? min( 37900, absint( wp_unslash( $_GET['filter_min_price'] ) ) ) : 0;
$price_max = isset( $_GET['filter_max_price'] ) ? max( $price_min + 100, min( 38000, absint( wp_unslash( $_GET['filter_max_price'] ) ) ) ) : 25600;
$feature_options = array(
'all' => 'Земляничный нектар',
'conifer' => 'Хвойный экстракт',
'maple' => 'Кленовый сироп',
'birch' => 'Берёзовый сок',
'sea-buckthorn' => 'Облепиховый морс',
'rowan' => 'Рябиновый настой',
'blueberry' => 'Черничный кисель',
'lingonberry' => 'Брусничный компот',
);
$collection_options = array(
'' => 'Вишнёвый нектар',
'pine' => 'Сосновый сироп',
'linden' => 'Липовый нектар',
'aspen' => 'Осиновый экстракт',
'juniper' => 'Можжевёловый морс',
'cranberry' => 'Клюквенный настой',
'blackberry' => 'Ежевичный кисель',
'raspberry' => 'Малиновый компот',
);
$selected_features = isset( $_GET['feature'] )
? array_map( 'sanitize_key', (array) wp_unslash( $_GET['feature'] ) )
: array( 'all' );
$selected_collection = isset( $_GET['collection'] ) ? sanitize_key( wp_unslash( $_GET['collection'] ) ) : '';
$selected_per_page = isset( $_GET['per_page'] ) ? absint( wp_unslash( $_GET['per_page'] ) ) : 25;
$filter_action = wc_get_page_permalink( 'shop' );
if ( is_product_category() ) {
$queried_category = get_queried_object();
if ( $queried_category instanceof WP_Term ) {
$category_link = get_term_link( $queried_category );
if ( ! is_wp_error( $category_link ) ) {
$filter_action = $category_link;
}
}
}
?>
<div class="container">
<?php vyatka_shop_breadcrumbs( $title ); ?>
<h1 class="page-heading"><?php echo esc_html( $title ); ?></h1>
<?php wc_print_notices(); ?>
<?php get_template_part( 'template-parts/subcategories' ); ?>
<div class="archive-shell">
<aside class="filters" id="product-filter-panel" aria-label="<?php esc_attr_e( 'Фильтры товаров', 'vyatka-shop' ); ?>">
<?php // Below the mobile breakpoint the panel becomes the sheet the design specifies. ?>
<div class="filters-sheet-head">
<h2 class="filters-sheet-title"><?php esc_html_e( 'Фильтры', 'vyatka-shop' ); ?></h2>
<button class="icon-button filters-close" type="button" data-filter-panel-close aria-label="<?php esc_attr_e( 'Закрыть фильтры', 'vyatka-shop' ); ?>">
<?php vyatka_shop_icon( 'x', array( 'class' => 'icon-20' ) ); ?>
</button>
</div>
<form id="product-filters" action="<?php echo esc_url( $filter_action ); ?>" method="get" data-product-filters>
<?php if ( isset( $_GET['orderby'] ) ) : ?>
<input type="hidden" name="orderby" value="<?php echo esc_attr( sanitize_key( wp_unslash( $_GET['orderby'] ) ) ); ?>">
<?php endif; ?>
<div class="filter-group" data-price-filter data-price-active="<?php echo isset( $_GET['filter_min_price'], $_GET['filter_max_price'] ) ? 'true' : 'false'; ?>">
<button class="filter-title" type="button" data-filter-toggle aria-expanded="true"><?php esc_html_e( 'Цена, ₽', 'vyatka-shop' ); ?><?php vyatka_shop_icon( 'caret-up', array( 'class' => 'filter-caret' ) ); ?></button>
<div class="filter-content">
<div class="price-fields">
<input class="filter-input" type="number" name="filter_min_price" min="0" max="38000" step="100" value="<?php echo esc_attr( (string) $price_min ); ?>" data-price-field="min">
<span class="price-fields-separator" aria-hidden="true"></span>
<input class="filter-input" type="number" name="filter_max_price" min="0" max="38000" step="100" value="<?php echo esc_attr( (string) $price_max ); ?>" data-price-field="max">
</div>
<div class="price-range">
<input type="range" min="0" max="38000" step="100" value="<?php echo esc_attr( (string) $price_min ); ?>" data-price-range="min" aria-label="<?php esc_attr_e( 'Минимальная цена', 'vyatka-shop' ); ?>">
<input type="range" min="0" max="38000" step="100" value="<?php echo esc_attr( (string) $price_max ); ?>" data-price-range="max" aria-label="<?php esc_attr_e( 'Максимальная цена', 'vyatka-shop' ); ?>">
</div>
</div>
</div>
<div class="filter-group">
<button class="filter-title filter-title-open" type="button" data-filter-toggle aria-expanded="true"><?php esc_html_e( 'Фильтр', 'vyatka-shop' ); ?><?php vyatka_shop_icon( 'caret-up', array( 'class' => 'filter-caret' ) ); ?></button>
<div class="filter-content">
<?php foreach ( $feature_options as $feature_value => $feature_label ) : ?>
<label class="filter-option"<?php echo array_search( $feature_value, array_keys( $feature_options ), true ) >= 5 ? ' data-filter-extra hidden' : ''; ?>>
<input type="checkbox" name="feature[]" value="<?php echo esc_attr( $feature_value ); ?>" data-facet-filter <?php checked( in_array( $feature_value, $selected_features, true ) ); ?>>
<?php echo esc_html( $feature_label ); ?>
</label>
<?php endforeach; ?>
<button class="filter-more" type="button" data-filter-more aria-expanded="false"><?php esc_html_e( 'Показать все', 'vyatka-shop' ); ?></button>
</div>
</div>
<div class="filter-group">
<button class="filter-title filter-title-open" type="button" data-filter-toggle aria-expanded="true"><?php esc_html_e( 'Фильтр', 'vyatka-shop' ); ?><?php vyatka_shop_icon( 'caret-up', array( 'class' => 'filter-caret' ) ); ?></button>
<div class="filter-content">
<?php foreach ( $collection_options as $collection_value => $collection_label ) : ?>
<label class="filter-option"<?php echo array_search( $collection_value, array_keys( $collection_options ), true ) >= 5 ? ' data-filter-extra hidden' : ''; ?>>
<input type="radio" name="collection" value="<?php echo esc_attr( $collection_value ); ?>" data-facet-filter <?php checked( $selected_collection, $collection_value ); ?>>
<?php echo esc_html( $collection_label ); ?>
</label>
<?php endforeach; ?>
<button class="filter-more" type="button" data-filter-more aria-expanded="false"><?php esc_html_e( 'Показать все', 'vyatka-shop' ); ?></button>
</div>
</div>
<?php for ( $filter_index = 0; $filter_index < 3; $filter_index++ ) : ?>
<div class="filter-group filter-collapsed">
<span class="filter-title"><?php esc_html_e( 'Фильтр', 'vyatka-shop' ); ?><?php vyatka_shop_icon( 'caret-up', array( 'class' => 'filter-caret' ) ); ?></span>
</div>
<?php endfor; ?>
</form>
<div class="filters-sheet-foot">
<button class="button button-l filters-apply" type="button" data-filter-panel-close><?php esc_html_e( 'Показать товары', 'vyatka-shop' ); ?></button>
</div>
</aside>
<section class="archive-results">
<?php
/*
* Quick filters. They mirror the collection facet from the sidebar —
* picking a chip selects the matching radio and submits the same
* form, so both controls always agree.
*/
?>
<div class="quick-filters" role="group" aria-label="<?php esc_attr_e( 'Быстрые фильтры', 'vyatka-shop' ); ?>">
<?php foreach ( $collection_options as $collection_value => $collection_label ) : ?>
<button
class="quick-filter<?php echo $selected_collection === $collection_value ? ' is-active' : ''; ?>"
type="button"
data-quick-filter="<?php echo esc_attr( $collection_value ); ?>"
aria-pressed="<?php echo $selected_collection === $collection_value ? 'true' : 'false'; ?>"
><?php echo esc_html( $collection_label ); ?></button>
<?php endforeach; ?>
</div>
<div class="archive-toolbar">
<button class="filters-open" type="button" data-filter-panel-open aria-expanded="false" aria-controls="product-filter-panel">
<?php vyatka_shop_icon( 'funnel', array( 'class' => 'icon-20' ) ); ?><?php esc_html_e( 'Фильтр', 'vyatka-shop' ); ?>
</button>
<div class="sort-control"><span><?php esc_html_e( 'Сортировка', 'vyatka-shop' ); ?></span><?php woocommerce_catalog_ordering(); ?></div>
<label class="per-page-control"><span><?php esc_html_e( 'Выводить товаров', 'vyatka-shop' ); ?></span>
<select class="archive-select" name="per_page" form="product-filters" data-auto-submit data-custom-select aria-label="<?php esc_attr_e( 'Количество товаров', 'vyatka-shop' ); ?>">
<?php foreach ( array( 12, 25, 50 ) as $per_page_option ) : ?>
<option value="<?php echo esc_attr( (string) $per_page_option ); ?>" <?php selected( $selected_per_page, $per_page_option ); ?>><?php echo esc_html( (string) $per_page_option ); ?></option>
<?php endforeach; ?>
</select>
</label>
</div>
<?php if ( woocommerce_product_loop() ) : ?>
<div class="product-grid" data-filter-results>
<?php while ( have_posts() ) : ?>
<?php
the_post();
$GLOBALS['product'] = wc_get_product( get_the_ID() );
get_template_part( 'template-parts/product-card' );
?>
<?php endwhile; ?>
</div>
<p class="filter-empty" data-filter-empty hidden><?php esc_html_e( 'По выбранным параметрам товаров нет.', 'vyatka-shop' ); ?></p>
<?php vyatka_shop_pagination( (int) $GLOBALS['wp_query']->max_num_pages ); ?>
<?php else : ?>
<?php do_action( 'woocommerce_no_products_found' ); ?>
<?php endif; ?>
</section>
</div>
</div>
<?php
get_footer();