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

Commit 98c6bf4

Browse files
committed
Add Simple Price Filter block
1 parent b27eedf commit 98c6bf4

File tree

8 files changed

+269
-0
lines changed

8 files changed

+269
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"$schema": "https://schemas.wp.org/trunk/block.json",
3+
"name": "woocommerce/simple-price-filter",
4+
"version": "1.0.0",
5+
"title": "Simple Price filter",
6+
"description": "Enable customers to filter the product grid by choosing a price range.",
7+
"category": "woocommerce",
8+
"keywords": [ "WooCommerce" ],
9+
"textdomain": "woo-gutenberg-products-block",
10+
"apiVersion": 2,
11+
"viewScript": [
12+
"wc-simple-price-filter-block-frontend",
13+
"wc-interactivity"
14+
],
15+
"supports": {
16+
"interactivity": true
17+
}
18+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { store, navigate } from '@woocommerce/interactivity';
5+
6+
const getHrefWithFilters = ( { state } ) => {
7+
const { minPrice, maxPrice } = state.filters;
8+
const url = new URL( window.location.href );
9+
const { searchParams } = url;
10+
11+
if ( minPrice > 0 ) {
12+
searchParams.set( 'min_price', minPrice );
13+
} else {
14+
searchParams.delete( 'min_price' );
15+
}
16+
17+
if ( maxPrice < state.filters.maxRange ) {
18+
searchParams.set( 'max_price', maxPrice );
19+
} else {
20+
searchParams.delete( 'max_price' );
21+
}
22+
23+
return url.href;
24+
};
25+
26+
store( {
27+
state: {
28+
filters: {
29+
rangeStyle: ( { state } ) => {
30+
const { minPrice, maxPrice, maxRange } = state.filters;
31+
return [
32+
`--low: ${ ( 100 * minPrice ) / maxRange }%`,
33+
`--high: ${ ( 100 * maxPrice ) / maxRange }%`,
34+
].join( ';' );
35+
},
36+
},
37+
},
38+
actions: {
39+
filters: {
40+
setMinPrice: ( { state, event } ) => {
41+
const value = parseFloat( event.target.value ) || 0;
42+
state.filters.minPrice = value;
43+
},
44+
setMaxPrice: ( { state, event } ) => {
45+
const value =
46+
parseFloat( event.target.value ) || state.filters.maxRange;
47+
state.filters.maxPrice = value;
48+
},
49+
updateProducts: ( { state } ) => {
50+
navigate( getHrefWithFilters( { state } ) );
51+
},
52+
reset: ( { state } ) => {
53+
state.filters.minPrice = 0;
54+
state.filters.maxPrice = state.filters.maxRange;
55+
navigate( getHrefWithFilters( { state } ) );
56+
},
57+
updateActiveHandle: ( { state, event } ) => {
58+
const { minPrice, maxPrice, maxRange } = state.filters;
59+
const { target, offsetX } = event;
60+
const xPos = offsetX / target.offsetWidth;
61+
const minPos = minPrice / maxRange;
62+
const maxPos = maxPrice / maxRange;
63+
64+
state.filters.isMinActive =
65+
Math.abs( xPos - minPos ) < Math.abs( xPos - maxPos );
66+
67+
state.filters.isMaxActive = ! state.filters.isMinActive;
68+
},
69+
},
70+
},
71+
} );
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { registerBlockType } from '@wordpress/blocks';
5+
6+
registerBlockType( 'woocommerce/simple-price-filter', {
7+
edit: () => <div>Simple price filter</div>,
8+
save: () => null,
9+
} );
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.wp-block-woocommerce-simple-price-filter {
2+
--low: 0%;
3+
--high: 100%;
4+
--range-color: currentColor;
5+
6+
.range {
7+
position: relative;
8+
margin: 15px 0;
9+
10+
.range-bar {
11+
position: relative;
12+
height: 4px;
13+
background: linear-gradient(90deg, transparent var(--low), var(--range-color) 0, var(--range-color) var(--high), transparent 0) no-repeat 0 100% / 100% 100%;
14+
15+
&::before {
16+
content: "";
17+
position: absolute;
18+
top: 0;
19+
left: 0;
20+
width: 100%;
21+
height: 100%;
22+
background: currentColor;
23+
opacity: 0.2;
24+
}
25+
}
26+
27+
input[type="range"] {
28+
position: absolute;
29+
top: 50%;
30+
left: 0;
31+
width: 100%;
32+
height: 0;
33+
margin: 0;
34+
padding: 0;
35+
36+
&.active {
37+
z-index: 10;
38+
}
39+
}
40+
41+
input[type="range" i] {
42+
color: -internal-light-dark(rgb(16, 16, 16), rgb(255, 255, 255));
43+
padding: initial;
44+
}
45+
}
46+
47+
.text {
48+
display: flex;
49+
align-items: center;
50+
justify-content: space-between;
51+
margin: 16px 0;
52+
gap: 8px;
53+
54+
input[type="text"] {
55+
padding: 8px;
56+
margin: 0;
57+
width: auto;
58+
max-width: 60px;
59+
min-width: 0;
60+
font-size: 0.875em;
61+
border-width: 1px;
62+
border-style: solid;
63+
border-color: currentColor;
64+
border-radius: 4px;
65+
}
66+
}
67+
}

bin/webpack-entries.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ const blocks = {
6464
'reviews-by-product': {
6565
customDir: 'reviews/reviews-by-product',
6666
},
67+
'simple-price-filter': {},
6768
'single-product': {},
6869
'stock-filter': {},
6970
'product-collection': {

src/BlockTypes/SimplePriceFilter.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
namespace Automattic\WooCommerce\Blocks\BlockTypes;
3+
4+
/**
5+
* SimplePriceFilter class.
6+
*/
7+
class SimplePriceFilter extends AbstractBlock {
8+
9+
/**
10+
* Block name.
11+
*
12+
* @var string
13+
*/
14+
protected $block_name = 'simple-price-filter';
15+
const MIN_PRICE_QUERY_VAR = 'min_price';
16+
const MAX_PRICE_QUERY_VAR = 'max_price';
17+
18+
/**
19+
* Render the block.
20+
*
21+
* @param array $attributes Block attributes.
22+
* @param string $content Block content.
23+
* @param WP_Block $block Block instance.
24+
*
25+
* @return string Rendered block output.
26+
*/
27+
public function render( $attributes = [], $content = '', $block = null ) {
28+
$wrapper_attributes = get_block_wrapper_attributes();
29+
$max_range = 90; // TODO: get this value from DB.
30+
$min_price = get_query_var( self::MIN_PRICE_QUERY_VAR, 0 );
31+
$max_price = get_query_var( self::MAX_PRICE_QUERY_VAR, $max_range );
32+
33+
// CSS variables for the range bar style.
34+
$__low = 100 * $min_price / $max_range;
35+
$__high = 100 * $max_price / $max_range;
36+
$range_style = "--low: $__low%; --high: $__high%";
37+
38+
wc_store(
39+
array(
40+
'state' => array(
41+
'filters' => array(
42+
'minPrice' => $min_price,
43+
'maxPrice' => $max_price,
44+
'maxRange' => $max_range,
45+
'rangeStyle' => $range_style,
46+
'isMinActive' => true,
47+
'isMaxActive' => false,
48+
),
49+
),
50+
)
51+
);
52+
53+
return <<<HTML
54+
<div $wrapper_attributes>
55+
<h3>Filter by price</h3>
56+
<div
57+
class='range'
58+
data-wc-bind--style='state.filters.rangeStyle'
59+
data-wc-on--mousemove='actions.filters.updateActiveHandle'
60+
>
61+
<div class='range-bar'></div>
62+
<input
63+
type='range'
64+
min='0'
65+
data-wc-bind--max='state.filters.maxRange'
66+
data-wc-bind--value='state.filters.minPrice'
67+
data-wc-class--active='state.filters.isMinActive'
68+
data-wc-on--input='actions.filters.setMinPrice'
69+
data-wc-on--change='actions.filters.updateProducts'
70+
>
71+
<input
72+
type='range'
73+
min='0'
74+
data-wc-bind--max='state.filters.maxRange'
75+
data-wc-bind--value='state.filters.maxPrice'
76+
data-wc-class--active='state.filters.isMaxActive'
77+
data-wc-on--input='actions.filters.setMaxPrice'
78+
data-wc-on--change='actions.filters.updateProducts'
79+
>
80+
</div>
81+
<div class='text'>
82+
<input
83+
type='text'
84+
data-wc-bind--value='state.filters.minPrice'
85+
data-wc-on--input='actions.filters.setMinPrice'
86+
data-wc-on--change='actions.filters.updateProducts'
87+
>
88+
<input
89+
type='text'
90+
data-wc-bind--value='state.filters.maxPrice'
91+
data-wc-on--input='actions.filters.setMaxPrice'
92+
data-wc-on--change='actions.filters.updateProducts'
93+
>
94+
</div>
95+
<button data-wc-on--click='actions.filters.reset'>Reset</button>
96+
</div>
97+
HTML;
98+
}
99+
}

src/BlockTypesController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ protected function get_block_types() {
213213
'ReviewsByProduct',
214214
'RelatedProducts',
215215
'ProductDetails',
216+
'SimplePriceFilter',
216217
'SingleProduct',
217218
'StockFilter',
218219
];

woocommerce-gutenberg-products-block.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,6 @@ function woocommerce_blocks_interactivity_setup() {
318318
}
319319
}
320320
add_action( 'plugins_loaded', 'woocommerce_blocks_interactivity_setup' );
321+
322+
// Enable the interactivity API.
323+
add_filter( 'woocommerce_blocks_enable_interactivity_api', '__return_true' );

0 commit comments

Comments
 (0)