Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Add Simple Price Filter block
Browse files Browse the repository at this point in the history
  • Loading branch information
DAreRodz committed Jun 20, 2023
1 parent b27eedf commit 98c6bf4
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 0 deletions.
18 changes: 18 additions & 0 deletions assets/js/blocks/simple-price-filter/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"name": "woocommerce/simple-price-filter",
"version": "1.0.0",
"title": "Simple Price filter",
"description": "Enable customers to filter the product grid by choosing a price range.",
"category": "woocommerce",
"keywords": [ "WooCommerce" ],
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2,
"viewScript": [
"wc-simple-price-filter-block-frontend",
"wc-interactivity"
],
"supports": {
"interactivity": true
}
}
71 changes: 71 additions & 0 deletions assets/js/blocks/simple-price-filter/frontend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* External dependencies
*/
import { store, navigate } from '@woocommerce/interactivity';

const getHrefWithFilters = ( { state } ) => {
const { minPrice, maxPrice } = state.filters;
const url = new URL( window.location.href );
const { searchParams } = url;

if ( minPrice > 0 ) {
searchParams.set( 'min_price', minPrice );
} else {
searchParams.delete( 'min_price' );
}

if ( maxPrice < state.filters.maxRange ) {
searchParams.set( 'max_price', maxPrice );
} else {
searchParams.delete( 'max_price' );
}

return url.href;
};

store( {
state: {
filters: {
rangeStyle: ( { state } ) => {
const { minPrice, maxPrice, maxRange } = state.filters;
return [
`--low: ${ ( 100 * minPrice ) / maxRange }%`,
`--high: ${ ( 100 * maxPrice ) / maxRange }%`,
].join( ';' );
},
},
},
actions: {
filters: {
setMinPrice: ( { state, event } ) => {
const value = parseFloat( event.target.value ) || 0;
state.filters.minPrice = value;
},
setMaxPrice: ( { state, event } ) => {
const value =
parseFloat( event.target.value ) || state.filters.maxRange;
state.filters.maxPrice = value;
},
updateProducts: ( { state } ) => {
navigate( getHrefWithFilters( { state } ) );
},
reset: ( { state } ) => {
state.filters.minPrice = 0;
state.filters.maxPrice = state.filters.maxRange;
navigate( getHrefWithFilters( { state } ) );
},
updateActiveHandle: ( { state, event } ) => {
const { minPrice, maxPrice, maxRange } = state.filters;
const { target, offsetX } = event;
const xPos = offsetX / target.offsetWidth;
const minPos = minPrice / maxRange;
const maxPos = maxPrice / maxRange;

state.filters.isMinActive =
Math.abs( xPos - minPos ) < Math.abs( xPos - maxPos );

state.filters.isMaxActive = ! state.filters.isMinActive;
},
},
},
} );
9 changes: 9 additions & 0 deletions assets/js/blocks/simple-price-filter/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* External dependencies
*/
import { registerBlockType } from '@wordpress/blocks';

registerBlockType( 'woocommerce/simple-price-filter', {
edit: () => <div>Simple price filter</div>,
save: () => null,
} );
67 changes: 67 additions & 0 deletions assets/js/blocks/simple-price-filter/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.wp-block-woocommerce-simple-price-filter {
--low: 0%;
--high: 100%;
--range-color: currentColor;

.range {
position: relative;
margin: 15px 0;

.range-bar {
position: relative;
height: 4px;
background: linear-gradient(90deg, transparent var(--low), var(--range-color) 0, var(--range-color) var(--high), transparent 0) no-repeat 0 100% / 100% 100%;

&::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: currentColor;
opacity: 0.2;
}
}

input[type="range"] {
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 0;
margin: 0;
padding: 0;

&.active {
z-index: 10;
}
}

input[type="range" i] {
color: -internal-light-dark(rgb(16, 16, 16), rgb(255, 255, 255));
padding: initial;
}
}

.text {
display: flex;
align-items: center;
justify-content: space-between;
margin: 16px 0;
gap: 8px;

input[type="text"] {
padding: 8px;
margin: 0;
width: auto;
max-width: 60px;
min-width: 0;
font-size: 0.875em;
border-width: 1px;
border-style: solid;
border-color: currentColor;
border-radius: 4px;
}
}
}
1 change: 1 addition & 0 deletions bin/webpack-entries.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const blocks = {
'reviews-by-product': {
customDir: 'reviews/reviews-by-product',
},
'simple-price-filter': {},
'single-product': {},
'stock-filter': {},
'product-collection': {
Expand Down
99 changes: 99 additions & 0 deletions src/BlockTypes/SimplePriceFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
namespace Automattic\WooCommerce\Blocks\BlockTypes;

/**
* SimplePriceFilter class.
*/
class SimplePriceFilter extends AbstractBlock {

/**
* Block name.
*
* @var string
*/
protected $block_name = 'simple-price-filter';
const MIN_PRICE_QUERY_VAR = 'min_price';
const MAX_PRICE_QUERY_VAR = 'max_price';

/**
* Render the block.
*
* @param array $attributes Block attributes.
* @param string $content Block content.
* @param WP_Block $block Block instance.
*
* @return string Rendered block output.
*/
public function render( $attributes = [], $content = '', $block = null ) {
$wrapper_attributes = get_block_wrapper_attributes();
$max_range = 90; // TODO: get this value from DB.
$min_price = get_query_var( self::MIN_PRICE_QUERY_VAR, 0 );
$max_price = get_query_var( self::MAX_PRICE_QUERY_VAR, $max_range );

// CSS variables for the range bar style.
$__low = 100 * $min_price / $max_range;
$__high = 100 * $max_price / $max_range;
$range_style = "--low: $__low%; --high: $__high%";

wc_store(
array(
'state' => array(
'filters' => array(
'minPrice' => $min_price,
'maxPrice' => $max_price,
'maxRange' => $max_range,
'rangeStyle' => $range_style,
'isMinActive' => true,
'isMaxActive' => false,
),
),
)
);

return <<<HTML
<div $wrapper_attributes>
<h3>Filter by price</h3>
<div
class='range'
data-wc-bind--style='state.filters.rangeStyle'
data-wc-on--mousemove='actions.filters.updateActiveHandle'
>
<div class='range-bar'></div>
<input
type='range'
min='0'
data-wc-bind--max='state.filters.maxRange'
data-wc-bind--value='state.filters.minPrice'
data-wc-class--active='state.filters.isMinActive'
data-wc-on--input='actions.filters.setMinPrice'
data-wc-on--change='actions.filters.updateProducts'
>
<input
type='range'
min='0'
data-wc-bind--max='state.filters.maxRange'
data-wc-bind--value='state.filters.maxPrice'
data-wc-class--active='state.filters.isMaxActive'
data-wc-on--input='actions.filters.setMaxPrice'
data-wc-on--change='actions.filters.updateProducts'
>
</div>
<div class='text'>
<input
type='text'
data-wc-bind--value='state.filters.minPrice'
data-wc-on--input='actions.filters.setMinPrice'
data-wc-on--change='actions.filters.updateProducts'
>
<input
type='text'
data-wc-bind--value='state.filters.maxPrice'
data-wc-on--input='actions.filters.setMaxPrice'
data-wc-on--change='actions.filters.updateProducts'
>
</div>
<button data-wc-on--click='actions.filters.reset'>Reset</button>
</div>
HTML;
}
}
1 change: 1 addition & 0 deletions src/BlockTypesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ protected function get_block_types() {
'ReviewsByProduct',
'RelatedProducts',
'ProductDetails',
'SimplePriceFilter',
'SingleProduct',
'StockFilter',
];
Expand Down
3 changes: 3 additions & 0 deletions woocommerce-gutenberg-products-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,6 @@ function woocommerce_blocks_interactivity_setup() {
}
}
add_action( 'plugins_loaded', 'woocommerce_blocks_interactivity_setup' );

// Enable the interactivity API.
add_filter( 'woocommerce_blocks_enable_interactivity_api', '__return_true' );

0 comments on commit 98c6bf4

Please sign in to comment.