From de756b5e831068d736203558831e019b5433e165 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 30 Jul 2019 10:47:09 +0100 Subject: [PATCH 01/51] Basic component setup --- assets/js/blocks/price-filter/block.js | 29 ++++++++ assets/js/blocks/price-filter/edit.js | 18 +++++ assets/js/blocks/price-filter/editor.scss | 0 assets/js/blocks/price-filter/frontend.js | 21 ++++++ assets/js/blocks/price-filter/index.js | 46 ++++++++++++ assets/js/blocks/price-filter/style.scss | 72 +++++++++++++++++++ .../components/frontend/price-slider/index.js | 35 +++++++++ src/Assets.php | 1 + src/BlockTypes/PriceFilter.php | 51 +++++++++++++ src/BlockTypes/ProductCategories.php | 4 +- src/Library.php | 1 + webpack.config.js | 12 +++- 12 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 assets/js/blocks/price-filter/block.js create mode 100644 assets/js/blocks/price-filter/edit.js create mode 100644 assets/js/blocks/price-filter/editor.scss create mode 100644 assets/js/blocks/price-filter/frontend.js create mode 100644 assets/js/blocks/price-filter/index.js create mode 100644 assets/js/blocks/price-filter/style.scss create mode 100644 assets/js/components/frontend/price-slider/index.js create mode 100644 src/BlockTypes/PriceFilter.php diff --git a/assets/js/blocks/price-filter/block.js b/assets/js/blocks/price-filter/block.js new file mode 100644 index 00000000000..01dbfcb99a0 --- /dev/null +++ b/assets/js/blocks/price-filter/block.js @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { Component } from 'react'; +import classnames from 'classnames'; + +/** + * Internal dependencies + */ +import PriceSlider from '../../components/frontend/price-slider'; + +/** + * Component displaying a price filter. + */ +class PriceFilterBlock extends Component { + render() { + const classes = classnames( + 'wc-block-price-slider', + ); + + return ( +
+ +
+ ); + } +} + +export default PriceFilterBlock; diff --git a/assets/js/blocks/price-filter/edit.js b/assets/js/blocks/price-filter/edit.js new file mode 100644 index 00000000000..f10f01a7eb4 --- /dev/null +++ b/assets/js/blocks/price-filter/edit.js @@ -0,0 +1,18 @@ +/** + * External dependencies + */ +import { Fragment } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import './editor.scss'; +import Block from './block.js'; + +export default function( { attributes } ) { + return ( + + + + ); +} diff --git a/assets/js/blocks/price-filter/editor.scss b/assets/js/blocks/price-filter/editor.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/assets/js/blocks/price-filter/frontend.js b/assets/js/blocks/price-filter/frontend.js new file mode 100644 index 00000000000..b78360c51a3 --- /dev/null +++ b/assets/js/blocks/price-filter/frontend.js @@ -0,0 +1,21 @@ +/** + * External dependencies + */ +import { render } from 'react-dom'; + +/** + * Internal dependencies + */ +import Block from './block.js'; + +const containers = document.querySelectorAll( + '.wp-block-woocommerce-price-filter' +); + +if ( containers.length ) { + Array.prototype.forEach.call( containers, ( el ) => { + el.classList.remove( 'is-loading' ); + + render( , el ); + } ); +} diff --git a/assets/js/blocks/price-filter/index.js b/assets/js/blocks/price-filter/index.js new file mode 100644 index 00000000000..70709ac6cc6 --- /dev/null +++ b/assets/js/blocks/price-filter/index.js @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import './editor.scss'; +import './style.scss'; +import edit from './edit.js'; +import { IconFolder } from '../../components/icons'; + +registerBlockType( 'woocommerce/price-filter', { + title: __( 'Filter Products by Price', 'woo-gutenberg-products-block' ), + icon: { + src: , + foreground: '#96588a', + }, + category: 'woocommerce', + keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ], + description: __( + 'Display a slider to filter products in your store by price.', + 'woo-gutenberg-products-block' + ), + supports: { + align: [ 'wide', 'full' ], + }, + + attributes: {}, + + edit, + + /** + * Save the props to post content. + */ + save( { attributes } ) { + const data = {}; + return ( +
+ +
+ ); + }, +} ); diff --git a/assets/js/blocks/price-filter/style.scss b/assets/js/blocks/price-filter/style.scss new file mode 100644 index 00000000000..933e27a9601 --- /dev/null +++ b/assets/js/blocks/price-filter/style.scss @@ -0,0 +1,72 @@ +.wc-block-price-filter__slider_group { + width: 300px; + + .wc-block-price-filter__amount { + display: inline-block; + margin-bottom: 20px; + border-radius: 4px; + } + + .wc-block-price-filter__amount--min { + float: left; + } + + .wc-block-price-filter__amount--max { + float: right; + } + + .wc-block-price-filter__slider_range { + background: #eee; + border: 1px solid #b8b8b8; + border-radius: 2px; + height: 10px; + clear: both; + } + + .wc-block-price-filter__slider { + width: 100%; + height: 0; + border: 0; + margin: 0; + padding: 0; + background: transparent; + -webkit-appearance: none; + display: block; + outline: 0; + + &::-webkit-slider-runnable-track { + -webkit-appearance: none; + height: 0; + outline: 0; + } + + &:focus { + outline: none; + } + } + .wc-block-price-filter__slider--min { + + } + .wc-block-price-filter__slider::-webkit-slider-thumb { + -webkit-appearance: none; + background: #fff; + border: 1px solid #95588a; + width: 20px; + height: 20px; + border-radius: 9px; + cursor: pointer; + } + .wc-block-price-filter__slider::-webkit-slider-thumb:active, + .wc-block-price-filter__slider::-webkit-slider-thumb:hover { + background: #f8f3f7; + box-shadow: 0 0 0 2px rgba(255,255,255,0.75); + } + .wc-block-price-filter__slider--min::-webkit-slider-thumb { + border-bottom-right-radius: 3px; + transform: translateY(-6px) rotate(-45deg); + } + .wc-block-price-filter__slider--max::-webkit-slider-thumb { + border-bottom-left-radius: 3px; + transform: translateY(-6px) rotate(45deg); + } +} diff --git a/assets/js/components/frontend/price-slider/index.js b/assets/js/components/frontend/price-slider/index.js new file mode 100644 index 00000000000..013dbdcd858 --- /dev/null +++ b/assets/js/components/frontend/price-slider/index.js @@ -0,0 +1,35 @@ +/** + * External dependencies + */ +// import { __ } from '@wordpress/i18n'; +import { Component, createRef } from 'react'; + +class PriceSlider extends Component { + constructor() { + super( ...arguments ); + this.minInput = createRef(); + this.maxInput = createRef(); + this.minRange = createRef(); + this.maxRange = createRef(); + this.onRangeChange = this.onRangeChange.bind( this ); + } + + onRangeChange() { + console.log( 1 ); + } + + render() { + return ( +
+ + +
+ + +
+
+ ); + } +} + +export default PriceSlider; diff --git a/src/Assets.php b/src/Assets.php index 5f5747f7e8d..23f21f52360 100644 --- a/src/Assets.php +++ b/src/Assets.php @@ -48,6 +48,7 @@ public static function register_assets() { self::register_script( 'wc-featured-category', plugins_url( 'build/featured-category.js', __DIR__ ), array( 'wc-vendors', 'wc-blocks' ) ); self::register_script( 'wc-product-categories', plugins_url( 'build/product-categories.js', __DIR__ ), array( 'wc-vendors', 'wc-blocks' ) ); self::register_script( 'wc-product-tag', plugins_url( 'build/product-tag.js', __DIR__ ), array( 'wc-vendors', 'wc-blocks' ) ); + self::register_script( 'wc-price-filter', plugins_url( 'build/price-filter.js', __DIR__ ), array( 'wc-vendors', 'wc-blocks' ) ); } /** diff --git a/src/BlockTypes/PriceFilter.php b/src/BlockTypes/PriceFilter.php new file mode 100644 index 00000000000..a0656ba5f00 --- /dev/null +++ b/src/BlockTypes/PriceFilter.php @@ -0,0 +1,51 @@ +namespace . '/' . $this->block_name, + array( + 'render_callback' => array( $this, 'render' ), + 'editor_script' => 'wc-' . $this->block_name, + 'editor_style' => 'wc-block-editor', + 'style' => 'wc-block-style', + 'script' => 'wc-' . $this->block_name . '-frontend', + ) + ); + } + + /** + * Append frontend scripts when rendering the Product Categories List block. + * + * @param array $attributes Block attributes. Default empty array. + * @param string $content Block content. Default empty string. + * @return string Rendered block type output. + */ + public function render( $attributes = array(), $content = '' ) { + \Automattic\WooCommerce\Blocks\Assets::register_block_script( $this->block_name . '-frontend' ); + return $content; + } +} diff --git a/src/BlockTypes/ProductCategories.php b/src/BlockTypes/ProductCategories.php index da87585c360..09db31c3035 100644 --- a/src/BlockTypes/ProductCategories.php +++ b/src/BlockTypes/ProductCategories.php @@ -32,7 +32,7 @@ public function register_block_type() { 'editor_script' => 'wc-' . $this->block_name, 'editor_style' => 'wc-block-editor', 'style' => 'wc-block-style', - 'script' => 'wc-frontend', + 'script' => 'wc-' . $this->block_name . '-frontend', ) ); } @@ -45,7 +45,7 @@ public function register_block_type() { * @return string Rendered block type output. */ public function render( $attributes = array(), $content = '' ) { - \Automattic\WooCommerce\Blocks\Assets::register_block_script( 'frontend' ); + \Automattic\WooCommerce\Blocks\Assets::register_block_script( $this->block_name . '-frontend' ); return $content; } diff --git a/src/Library.php b/src/Library.php index 08e061fc3ed..864be73b7a7 100644 --- a/src/Library.php +++ b/src/Library.php @@ -37,6 +37,7 @@ public static function register_blocks() { 'ProductsByAttribute', 'ProductTopRated', 'ProductTag', + 'PriceFilter', ]; foreach ( $blocks as $class ) { $class = __NAMESPACE__ . '\\BlockTypes\\' . $class; diff --git a/webpack.config.js b/webpack.config.js index 81b1cf4c73e..d40fc032e5e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -55,6 +55,7 @@ const GutenbergBlocksConfig = { 'featured-product': './assets/js/blocks/featured-product/index.js', 'product-tag': './assets/js/blocks/product-tag/index.js', 'featured-category': './assets/js/blocks/featured-category/index.js', + 'price-filter': './assets/js/blocks/price-filter/index.js', }, output: { path: path.resolve( __dirname, './build/' ), @@ -147,10 +148,17 @@ const GutenbergBlocksConfig = { const BlocksFrontendConfig = { ...baseConfig, - entry: './assets/js/blocks/product-categories/frontend.js', + entry: { + 'product-categories': './assets/js/blocks/product-categories/frontend.js', + 'price-filter': './assets/js/blocks/price-filter/frontend.js', + }, output: { path: path.resolve( __dirname, './build/' ), - filename: 'frontend.js', + filename: '[name]-frontend.js', + // This fixes an issue with multiple webpack projects using chunking + // overwriting each other's chunk loader function. + // See https://webpack.js.org/configuration/output/#outputjsonpfunction + jsonpFunction: 'webpackWcBlocksJsonp', }, module: { rules: [ From 1e84811010b96fe67fe7108bdebfbc4e6e27c5c2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 30 Jul 2019 16:04:41 +0100 Subject: [PATCH 02/51] Working slider --- assets/js/blocks/price-filter/block.js | 2 +- assets/js/blocks/price-filter/edit.js | 1 - assets/js/blocks/price-filter/editor.scss | 0 assets/js/blocks/price-filter/index.js | 4 +- assets/js/blocks/price-filter/style.scss | 72 ----------- .../components/frontend/price-slider/index.js | 35 ------ .../frontend-components/price-slider/index.js | 81 ++++++++++++ .../price-slider/style.scss | 98 +++++++++++++++ package-lock.json | 119 +++++++++--------- package.json | 14 ++- webpack.config.js | 4 + 11 files changed, 252 insertions(+), 178 deletions(-) delete mode 100644 assets/js/blocks/price-filter/editor.scss delete mode 100644 assets/js/blocks/price-filter/style.scss delete mode 100644 assets/js/components/frontend/price-slider/index.js create mode 100644 assets/js/frontend-components/price-slider/index.js create mode 100644 assets/js/frontend-components/price-slider/style.scss diff --git a/assets/js/blocks/price-filter/block.js b/assets/js/blocks/price-filter/block.js index 01dbfcb99a0..eee1d18d864 100644 --- a/assets/js/blocks/price-filter/block.js +++ b/assets/js/blocks/price-filter/block.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; /** * Internal dependencies */ -import PriceSlider from '../../components/frontend/price-slider'; +import PriceSlider from '../../frontend-components/price-slider'; /** * Component displaying a price filter. diff --git a/assets/js/blocks/price-filter/edit.js b/assets/js/blocks/price-filter/edit.js index f10f01a7eb4..af07c2e4e41 100644 --- a/assets/js/blocks/price-filter/edit.js +++ b/assets/js/blocks/price-filter/edit.js @@ -6,7 +6,6 @@ import { Fragment } from '@wordpress/element'; /** * Internal dependencies */ -import './editor.scss'; import Block from './block.js'; export default function( { attributes } ) { diff --git a/assets/js/blocks/price-filter/editor.scss b/assets/js/blocks/price-filter/editor.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/assets/js/blocks/price-filter/index.js b/assets/js/blocks/price-filter/index.js index 70709ac6cc6..7ffdd4b94db 100644 --- a/assets/js/blocks/price-filter/index.js +++ b/assets/js/blocks/price-filter/index.js @@ -7,8 +7,6 @@ import { registerBlockType } from '@wordpress/blocks'; /** * Internal dependencies */ -import './editor.scss'; -import './style.scss'; import edit from './edit.js'; import { IconFolder } from '../../components/icons'; @@ -35,7 +33,7 @@ registerBlockType( 'woocommerce/price-filter', { /** * Save the props to post content. */ - save( { attributes } ) { + save() { const data = {}; return (
diff --git a/assets/js/blocks/price-filter/style.scss b/assets/js/blocks/price-filter/style.scss deleted file mode 100644 index 933e27a9601..00000000000 --- a/assets/js/blocks/price-filter/style.scss +++ /dev/null @@ -1,72 +0,0 @@ -.wc-block-price-filter__slider_group { - width: 300px; - - .wc-block-price-filter__amount { - display: inline-block; - margin-bottom: 20px; - border-radius: 4px; - } - - .wc-block-price-filter__amount--min { - float: left; - } - - .wc-block-price-filter__amount--max { - float: right; - } - - .wc-block-price-filter__slider_range { - background: #eee; - border: 1px solid #b8b8b8; - border-radius: 2px; - height: 10px; - clear: both; - } - - .wc-block-price-filter__slider { - width: 100%; - height: 0; - border: 0; - margin: 0; - padding: 0; - background: transparent; - -webkit-appearance: none; - display: block; - outline: 0; - - &::-webkit-slider-runnable-track { - -webkit-appearance: none; - height: 0; - outline: 0; - } - - &:focus { - outline: none; - } - } - .wc-block-price-filter__slider--min { - - } - .wc-block-price-filter__slider::-webkit-slider-thumb { - -webkit-appearance: none; - background: #fff; - border: 1px solid #95588a; - width: 20px; - height: 20px; - border-radius: 9px; - cursor: pointer; - } - .wc-block-price-filter__slider::-webkit-slider-thumb:active, - .wc-block-price-filter__slider::-webkit-slider-thumb:hover { - background: #f8f3f7; - box-shadow: 0 0 0 2px rgba(255,255,255,0.75); - } - .wc-block-price-filter__slider--min::-webkit-slider-thumb { - border-bottom-right-radius: 3px; - transform: translateY(-6px) rotate(-45deg); - } - .wc-block-price-filter__slider--max::-webkit-slider-thumb { - border-bottom-left-radius: 3px; - transform: translateY(-6px) rotate(45deg); - } -} diff --git a/assets/js/components/frontend/price-slider/index.js b/assets/js/components/frontend/price-slider/index.js deleted file mode 100644 index 013dbdcd858..00000000000 --- a/assets/js/components/frontend/price-slider/index.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * External dependencies - */ -// import { __ } from '@wordpress/i18n'; -import { Component, createRef } from 'react'; - -class PriceSlider extends Component { - constructor() { - super( ...arguments ); - this.minInput = createRef(); - this.maxInput = createRef(); - this.minRange = createRef(); - this.maxRange = createRef(); - this.onRangeChange = this.onRangeChange.bind( this ); - } - - onRangeChange() { - console.log( 1 ); - } - - render() { - return ( -
- - -
- - -
-
- ); - } -} - -export default PriceSlider; diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js new file mode 100644 index 00000000000..8ab54c9ddf8 --- /dev/null +++ b/assets/js/frontend-components/price-slider/index.js @@ -0,0 +1,81 @@ +/** + * External dependencies + */ +// import { __ } from '@wordpress/i18n'; +import { Component, createRef } from 'react'; + +/** + * Internal dependencies + */ +import './style.scss'; + +class PriceSlider extends Component { + constructor() { + super( ...arguments ); + this.state = { + currentMin: 0, + currentMax: 100, + step: 10, + min: 0, + max: 100, + }; + this.minInput = createRef(); + this.maxInput = createRef(); + this.minRange = createRef(); + this.maxRange = createRef(); + this.onChangeMin = this.onChangeMin.bind( this ); + this.onChangeMax = this.onChangeMax.bind( this ); + this.onInputChange = this.onInputChange.bind( this ); + } + + onChangeMin() { + const { step, max } = this.state; + + this.setState( { + currentMin: this.minRange.current.value, + currentMax: Math.min( max, Math.max( parseInt( this.minRange.current.value, 10 ) + step, this.maxRange.current.value ) ), + } ); + } + + onChangeMax() { + const { step, min } = this.state; + + this.setState( { + currentMin: Math.max( min, Math.min( this.minRange.current.value, parseInt( this.maxRange.current.value, 10 ) - step ) ), + currentMax: this.maxRange.current.value, + } ); + } + + onInputChange() { + this.setState( { + currentMin: this.minInput.current.value, + currentMax: this.maxInput.current.value, + } ); + } + + render() { + const { min, max, step, currentMin, currentMax } = this.state; + + const minProgress = ( ( currentMin / max ) * 100 ); + // eslint-disable-next-line no-mixed-operators + const maxProgress = ( max - ( currentMax / max ) * 100 ); + return ( +
+ + +
+ + +
+
+
+
+ ); + } +} + +export default PriceSlider; diff --git a/assets/js/frontend-components/price-slider/style.scss b/assets/js/frontend-components/price-slider/style.scss new file mode 100644 index 00000000000..373aa2839dc --- /dev/null +++ b/assets/js/frontend-components/price-slider/style.scss @@ -0,0 +1,98 @@ +.wc-block-price-filter__slider_group { + + .wc-block-price-filter__amount { + display: inline-block; + margin-bottom: 20px; + border-radius: 4px; + } + + .wc-block-price-filter__amount--min { + float: left; + } + + .wc-block-price-filter__amount--max { + float: right; + } + + .wc-block-price-filter__slider_range { + background: #a8739d; + box-shadow: 0 0 0 1px inset #95588a; + height: 9px; + padding: 0; + clear: both; + position: relative; + } + + .wc-block-price-filter__slider_range_progress { + background: #e1e1e1; + box-shadow: 0 0 0 1px inset #b8b8b8; + border-radius: 2px; + height: 9px; + padding: 0; + position: absolute; + z-index: 5; + } + + .wc-block-price-filter__slider_range_progress--start { + left: 0; + } + + .wc-block-price-filter__slider_range_progress--end { + right: 0; + } + + .wc-block-price-filter__slider { + width: 100%; + height: 0; + border: 0; + margin: 0; + padding: 0; + background: transparent; + -webkit-appearance: none; + display: block; + outline: 0; + z-index: 10; + position: relative; + + &::-webkit-slider-runnable-track { + -webkit-appearance: none; + height: 0; + outline: 0; + } + + &:focus { + outline: none; + } + } + .wc-block-price-filter__slider::-webkit-slider-thumb { + -webkit-appearance: none; + /* stylelint-disable */ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='52' height='42'%3E%3Cdefs%3E%3Cpath id='a' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath id='b' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M24.3176 8.9423l-8.4163-6.1432c-1.706-1.2285-3.6395-1.8988-5.6867-1.787h-.1137c-4.8906.335-8.985 4.356-9.0987 9.2706C.8885 15.644 5.2102 20 10.6696 20h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M9 6v9m3-9v9'/%3E%3Cg fill-rule='nonzero' transform='translate(1 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23a'/%3E%3Cuse stroke='%2395588A' xlink:href='%23a'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M9 27v9m3-9v9'/%3E%3Cg%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M27.6824 8.9423l8.4163-6.1432c1.706-1.2285 3.6395-1.8988 5.6867-1.787h.1137c4.8906.335 8.985 4.356 9.0987 9.2706C51.1115 15.644 46.7898 20 41.3304 20h-.1137c-1.8197 0-3.6395-.6702-5.118-1.787l-8.4163-6.255c-.9099-.8935-.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M43 6v9m-3-9v9'/%3E%3C/g%3E%3Cg%3E%3Cg fill-rule='nonzero' transform='matrix(-1 0 0 1 51 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23b'/%3E%3Cuse stroke='%2395588A' xlink:href='%23b'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M43 27v9m-3-9v9'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + /* stylelint-enable */ + background-position: 0 0; + width: 26px; + height: 21px; + border: 0; + padding: 0; + margin: -6px 0 0 0; + cursor: pointer; + z-index: 20; + } + .wc-block-price-filter__slider::-webkit-slider-thumb:active, + .wc-block-price-filter__slider::-webkit-slider-thumb:hover { + background-position-y: -21px; + } + .wc-block-price-filter__slider--min { + z-index: 25; + } + .wc-block-price-filter__slider--max { + z-index: 20; + } + .wc-block-price-filter__slider--min::-webkit-slider-thumb { + margin-left: -7px; + } + .wc-block-price-filter__slider--max::-webkit-slider-thumb { + background-position-x: 26px; + margin-left: 7px; + } +} diff --git a/package-lock.json b/package-lock.json index 1f04b7e2ee0..8fec74dac0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7515,8 +7515,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -7537,14 +7536,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7559,20 +7556,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -7689,8 +7683,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -7702,7 +7695,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7717,7 +7709,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7725,14 +7716,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -7751,7 +7740,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -7832,8 +7820,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -7845,7 +7832,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -7931,8 +7917,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -7968,7 +7953,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7988,7 +7972,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8032,14 +8015,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -11350,8 +11331,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -11372,14 +11352,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11394,20 +11372,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -11524,8 +11499,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -11537,7 +11511,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -11552,7 +11525,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11560,14 +11532,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -11586,7 +11556,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -11674,8 +11643,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -11687,7 +11655,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -11773,8 +11740,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -11810,7 +11776,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -11830,7 +11795,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -11874,14 +11838,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -12778,6 +12740,12 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, + "ignore-loader": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz", + "integrity": "sha1-2B8kA3bQuk8Nd4lyw60lh0EXpGM=", + "dev": true + }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -20582,6 +20550,37 @@ "has-flag": "^3.0.0" } }, + "svg-inline-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/svg-inline-loader/-/svg-inline-loader-0.8.0.tgz", + "integrity": "sha512-rynplY2eXFrdNomL1FvyTFQlP+dx0WqbzHglmNtA9M4IHRC3no2aPAl3ny9lUpJzFzFMZfWRK5YIclNU+FRePA==", + "dev": true, + "requires": { + "loader-utils": "^0.2.11", + "object-assign": "^4.0.1", + "simple-html-tokenizer": "^0.1.1" + }, + "dependencies": { + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "simple-html-tokenizer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.1.1.tgz", + "integrity": "sha1-BcLuxXn//+FFoDCsJs/qYbmA+r4=", + "dev": true + } + } + }, "svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", diff --git a/package.json b/package.json index 48925cb554a..afc092b0c85 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,8 @@ "devDependencies": { "@babel/core": "7.5.5", "@wordpress/babel-preset-default": "4.3.0", - "@wordpress/browserslist-config": "2.5.0", - "babel-plugin-transform-async-generator-functions": "6.24.1", - "babel-plugin-transform-object-rest-spread": "6.26.0", - "babel-plugin-transform-react-jsx": "6.24.1", - "babel-plugin-transform-runtime": "6.23.0", - "babel-preset-env": "1.7.0", "@wordpress/blocks": "6.4.0", + "@wordpress/browserslist-config": "2.5.0", "@wordpress/components": "8.0.0", "@wordpress/date": "3.3.0", "@wordpress/dependency-extraction-webpack-plugin": "1.0.1", @@ -56,6 +51,11 @@ "babel-core": "7.0.0-bridge.0", "babel-eslint": "10.0.2", "babel-loader": "8.0.6", + "babel-plugin-transform-async-generator-functions": "6.24.1", + "babel-plugin-transform-object-rest-spread": "6.26.0", + "babel-plugin-transform-react-jsx": "6.24.1", + "babel-plugin-transform-runtime": "6.23.0", + "babel-preset-env": "1.7.0", "chalk": "2.4.2", "classnames": "2.2.6", "clean-webpack-plugin": "3.0.0", @@ -71,6 +71,7 @@ "eslint-plugin-wordpress": "git://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress.git#1774343f6226052a46b081e01db3fca8793cc9f1", "har-validator": "5.1.3", "husky": "2.4.1", + "ignore-loader": "^0.1.2", "interpolate-components": "1.1.1", "js-md5": "0.7.3", "lint-staged": "9.2.1", @@ -85,6 +86,7 @@ "style-loader": "0.23.1", "stylelint": "10.1.0", "stylelint-config-wordpress": "14.0.0", + "svg-inline-loader": "^0.8.0", "webpack": "4.38.0", "webpack-cli": "3.3.6", "yargs": "13.3.0" diff --git a/webpack.config.js b/webpack.config.js index d40fc032e5e..d1db451abcb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -178,6 +178,10 @@ const BlocksFrontendConfig = { }, }, }, + { + test: /\.s[c|a]ss$/, + loader: 'ignore-loader', + }, ], }, plugins: [ From 6e8a6c119d66208f645ef02980434e16261a759d Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 30 Jul 2019 17:31:07 +0100 Subject: [PATCH 03/51] Validation --- .eslintrc.js | 1 + .../frontend-components/price-slider/index.js | 78 ++++++++++++++++--- src/Assets.php | 1 + 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index f3d48bd0b40..eea9740219f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,6 +22,7 @@ module.exports = { window: true, document: true, wc_product_block_data: true, + wcSettings: true, }, plugins: [ 'wordpress', diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 8ab54c9ddf8..ca09845a0c1 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -// import { __ } from '@wordpress/i18n'; +import { sprintf } from '@wordpress/i18n'; import { Component, createRef } from 'react'; /** @@ -9,13 +9,20 @@ import { Component, createRef } from 'react'; */ import './style.scss'; +const get = ( obj, path, defaultValue ) => { + const result = String.prototype.split.call( path, /[,[\].]+?/ ) + .filter( Boolean ) + .reduce( ( res, key ) => ( res !== null && res !== undefined ) ? res[ key ] : res, obj ); + return ( result === undefined || result === obj ) ? defaultValue : result; +}; + class PriceSlider extends Component { constructor() { super( ...arguments ); this.state = { currentMin: 0, currentMax: 100, - step: 10, + step: 1, min: 0, max: 100, }; @@ -25,7 +32,9 @@ class PriceSlider extends Component { this.maxRange = createRef(); this.onChangeMin = this.onChangeMin.bind( this ); this.onChangeMax = this.onChangeMax.bind( this ); - this.onInputChange = this.onInputChange.bind( this ); + this.onInputMin = this.onInputMin.bind( this ); + this.onInputMax = this.onInputMax.bind( this ); + this.formatCurrencyForInput = this.formatCurrencyForInput.bind( this ); } onChangeMin() { @@ -46,13 +55,62 @@ class PriceSlider extends Component { } ); } - onInputChange() { + onInputMin() { + const { min, max } = this.state; + + let value = this.minInput.current.value.replace( /[^0-9.-]+/g, '' ); + + if ( min > value ) { + value = min; + } + + if ( max < value ) { + value = max; + } + this.setState( { - currentMin: this.minInput.current.value, - currentMax: this.maxInput.current.value, + currentMin: value ? parseInt( value, 10 ) : '', + currentMax: Math.min( max, Math.max( parseInt( value, 10 ), this.maxRange.current.value ) ), } ); } + onInputMax() { + const { min, max } = this.state; + + let value = this.maxInput.current.value.replace( /[^0-9.-]+/g, '' ); + + if ( min > value ) { + value = min; + } + + if ( max < value ) { + value = max; + } + + this.setState( { + currentMin: Math.max( min, Math.min( this.minRange.current.value, parseInt( value, 10 ) ) ), + currentMax: value ? parseInt( value, 10 ) : '', + } ); + } + + formatCurrencyForInput( value ) { + if ( '' === value ) { + return ''; + } + const formattedNumber = parseInt( value, 10 ); + const currencySymbol = get( wcSettings, [ 'currency', 'symbol' ], '$' ); + const priceFormat = get( wcSettings, [ 'currency', 'price_format' ], '%1$s%2$s' ); + + if ( '' === formattedNumber ) { + return formattedNumber; + } + + const formattedValue = sprintf( priceFormat, currencySymbol, formattedNumber ); + const txt = document.createElement( 'textarea' ); + txt.innerHTML = formattedValue; + return txt.value; + } + render() { const { min, max, step, currentMin, currentMax } = this.state; @@ -61,11 +119,11 @@ class PriceSlider extends Component { const maxProgress = ( max - ( currentMax / max ) * 100 ); return (
- - + +
- - + +
diff --git a/src/Assets.php b/src/Assets.php index 23f21f52360..0c72236b426 100644 --- a/src/Assets.php +++ b/src/Assets.php @@ -20,6 +20,7 @@ class Assets { public static function init() { add_action( 'init', array( __CLASS__, 'register_assets' ) ); add_action( 'body_class', array( __CLASS__, 'add_theme_body_class' ), 1 ); + add_action( 'wp_enqueue_scripts', array( __CLASS__, 'print_script_wc_settings' ), 1 ); add_action( 'admin_print_footer_scripts', array( __CLASS__, 'print_script_wc_settings' ), 1 ); add_action( 'admin_print_footer_scripts', array( __CLASS__, 'print_script_block_data' ), 1 ); add_action( 'wp_print_footer_scripts', array( __CLASS__, 'print_script_block_data' ), 1 ); From 448f9bde0dff0cd116b39010cc7e1298c6eaaa92 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 31 Jul 2019 13:28:10 +0100 Subject: [PATCH 04/51] styling --- .../frontend-components/price-slider/index.js | 4 +- .../price-slider/style.scss | 153 ++++++++++++------ 2 files changed, 104 insertions(+), 53 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index ca09845a0c1..348ca34cf3a 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -70,7 +70,7 @@ class PriceSlider extends Component { this.setState( { currentMin: value ? parseInt( value, 10 ) : '', - currentMax: Math.min( max, Math.max( parseInt( value, 10 ), this.maxRange.current.value ) ), + currentMax: Math.min( max, Math.max( parseInt( value, 10 ) || 0, this.maxRange.current.value ) ), } ); } @@ -88,7 +88,7 @@ class PriceSlider extends Component { } this.setState( { - currentMin: Math.max( min, Math.min( this.minRange.current.value, parseInt( value, 10 ) ) ), + currentMin: Math.max( min, Math.min( this.minRange.current.value, parseInt( value, 10 ) || 0 ) ), currentMax: value ? parseInt( value, 10 ) : '', } ); } diff --git a/assets/js/frontend-components/price-slider/style.scss b/assets/js/frontend-components/price-slider/style.scss index 373aa2839dc..134bcef3f5e 100644 --- a/assets/js/frontend-components/price-slider/style.scss +++ b/assets/js/frontend-components/price-slider/style.scss @@ -1,17 +1,45 @@ +@mixin thumb { + background-color: transparent; + /* stylelint-disable */ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='52' height='42'%3E%3Cdefs%3E%3Cpath id='a' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath id='b' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M24.3176 8.9423l-8.4163-6.1432c-1.706-1.2285-3.6395-1.8988-5.6867-1.787h-.1137c-4.8906.335-8.985 4.356-9.0987 9.2706C.8885 15.644 5.2102 20 10.6696 20h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M9 6v9m3-9v9'/%3E%3Cg fill-rule='nonzero' transform='translate(1 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23a'/%3E%3Cuse stroke='%2395588A' xlink:href='%23a'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M9 27v9m3-9v9'/%3E%3Cg%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M27.6824 8.9423l8.4163-6.1432c1.706-1.2285 3.6395-1.8988 5.6867-1.787h.1137c4.8906.335 8.985 4.356 9.0987 9.2706C51.1115 15.644 46.7898 20 41.3304 20h-.1137c-1.8197 0-3.6395-.6702-5.118-1.787l-8.4163-6.255c-.9099-.8935-.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M43 6v9m-3-9v9'/%3E%3C/g%3E%3Cg%3E%3Cg fill-rule='nonzero' transform='matrix(-1 0 0 1 51 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23b'/%3E%3Cuse stroke='%2395588A' xlink:href='%23b'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M43 27v9m-3-9v9'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + /* stylelint-enable */ + background-position: 0 0; + width: 26px; + height: 21px; + border: 0; + padding: 0; + margin: 0; + vertical-align: top; + cursor: pointer; + z-index: 20; + pointer-events: auto; + + &:hover, + &:active { + background-position-y: -21px; + filter: drop-shadow( 3px 0 0 rgba( 255, 255, 255, .75 ) ) drop-shadow( -3px 0 0 rgba( 255, 255, 255, .75 ) ); + } +} + +@mixin track { + cursor: default; + height: 0; + outline: 0; +} + .wc-block-price-filter__slider_group { .wc-block-price-filter__amount { display: inline-block; margin-bottom: 20px; border-radius: 4px; - } - - .wc-block-price-filter__amount--min { - float: left; - } - .wc-block-price-filter__amount--max { - float: right; + &.wc-block-price-filter__amount--min { + float: left; + } + &.wc-block-price-filter__amount--max { + float: right; + } } .wc-block-price-filter__slider_range { @@ -31,14 +59,13 @@ padding: 0; position: absolute; z-index: 5; - } - - .wc-block-price-filter__slider_range_progress--start { - left: 0; - } - .wc-block-price-filter__slider_range_progress--end { - right: 0; + &.wc-block-price-filter__slider_range_progress--start { + left: 0; + } + &.wc-block-price-filter__slider_range_progress--end { + right: 0; + } } .wc-block-price-filter__slider { @@ -49,50 +76,74 @@ padding: 0; background: transparent; -webkit-appearance: none; + -moz-appearance: none; display: block; - outline: 0; - z-index: 10; + outline: none; position: relative; + pointer-events: none; + + &:focus { + outline: none; + } + + &::-moz-focus-outer { + border: 0; + } &::-webkit-slider-runnable-track { -webkit-appearance: none; - height: 0; - outline: 0; + @include track; + } + &::-moz-range-track { + -moz-appearance: none; + @include track; + } + &::-ms-track { + @include track; } - &:focus { - outline: none; + &::-webkit-slider-thumb { + -webkit-appearance: none; + @include thumb; + margin: -6px 0 0 0; + } + &::-moz-range-thumb { + -moz-appearance: none; + @include thumb; + } + &::-ms-thumb { + @include thumb; + } + + &.wc-block-price-filter__slider--min { + z-index: 15; + + &::-webkit-slider-thumb { + margin-left: -7px; + } + &::-moz-range-thumb { + transform: translate( -7px, 4px ); + } + &::-ms-thumb { + margin-left: -7px; + } + } + + &.wc-block-price-filter__slider--max { + z-index: 10; + + &::-webkit-slider-thumb { + background-position-x: 26px; + margin-left: 7px; + } + &::-moz-range-thumb { + background-position-x: 26px; + transform: translate( 7px, 4px ); + } + &::-ms-thumb { + background-position-x: 26px; + margin-left: 7px; + } } - } - .wc-block-price-filter__slider::-webkit-slider-thumb { - -webkit-appearance: none; - /* stylelint-disable */ - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='52' height='42'%3E%3Cdefs%3E%3Cpath id='a' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath id='b' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M24.3176 8.9423l-8.4163-6.1432c-1.706-1.2285-3.6395-1.8988-5.6867-1.787h-.1137c-4.8906.335-8.985 4.356-9.0987 9.2706C.8885 15.644 5.2102 20 10.6696 20h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M9 6v9m3-9v9'/%3E%3Cg fill-rule='nonzero' transform='translate(1 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23a'/%3E%3Cuse stroke='%2395588A' xlink:href='%23a'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M9 27v9m3-9v9'/%3E%3Cg%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M27.6824 8.9423l8.4163-6.1432c1.706-1.2285 3.6395-1.8988 5.6867-1.787h.1137c4.8906.335 8.985 4.356 9.0987 9.2706C51.1115 15.644 46.7898 20 41.3304 20h-.1137c-1.8197 0-3.6395-.6702-5.118-1.787l-8.4163-6.255c-.9099-.8935-.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M43 6v9m-3-9v9'/%3E%3C/g%3E%3Cg%3E%3Cg fill-rule='nonzero' transform='matrix(-1 0 0 1 51 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23b'/%3E%3Cuse stroke='%2395588A' xlink:href='%23b'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M43 27v9m-3-9v9'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); - /* stylelint-enable */ - background-position: 0 0; - width: 26px; - height: 21px; - border: 0; - padding: 0; - margin: -6px 0 0 0; - cursor: pointer; - z-index: 20; - } - .wc-block-price-filter__slider::-webkit-slider-thumb:active, - .wc-block-price-filter__slider::-webkit-slider-thumb:hover { - background-position-y: -21px; - } - .wc-block-price-filter__slider--min { - z-index: 25; - } - .wc-block-price-filter__slider--max { - z-index: 20; - } - .wc-block-price-filter__slider--min::-webkit-slider-thumb { - margin-left: -7px; - } - .wc-block-price-filter__slider--max::-webkit-slider-thumb { - background-position-x: 26px; - margin-left: 7px; } } From e83b7644d0d36b029d91ba440fc5c286c172a0c7 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 31 Jul 2019 14:59:07 +0100 Subject: [PATCH 05/51] Update webpack config to fix ie11 --- webpack.config.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index d1db451abcb..1216c1577d9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -168,13 +168,20 @@ const BlocksFrontendConfig = { use: { loader: 'babel-loader?cacheDirectory', options: { - ...require( '@wordpress/babel-preset-default' ), - plugins: [ - '@babel/plugin-transform-react-jsx', - '@babel/plugin-transform-runtime', - 'transform-object-rest-spread', - 'transform-async-generator-functions', + presets: [ + [ '@babel/preset-env', { + modules: false, + targets: { + browsers: [ 'extends @wordpress/browserslist-config' ], + }, + } ], ], + plugins: [ + require.resolve( '@babel/plugin-proposal-object-rest-spread' ), + require.resolve( '@babel/plugin-transform-react-jsx' ), + require.resolve( '@babel/plugin-proposal-async-generator-functions' ), + require.resolve( '@babel/plugin-transform-runtime' ), + ].filter( Boolean ), }, }, }, From accaa3af8cbb29363eab20af934ceeca967be3a3 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 31 Jul 2019 17:04:04 +0100 Subject: [PATCH 06/51] ie progress --- .../frontend-components/price-slider/index.js | 12 +- .../price-slider/style.scss | 116 ++++++++++++------ 2 files changed, 86 insertions(+), 42 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 348ca34cf3a..015bb3b4ae5 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -118,16 +118,16 @@ class PriceSlider extends Component { // eslint-disable-next-line no-mixed-operators const maxProgress = ( max - ( currentMax / max ) * 100 ); return ( -
+
-
- - -
+ + +
-
diff --git a/assets/js/frontend-components/price-slider/style.scss b/assets/js/frontend-components/price-slider/style.scss index 134bcef3f5e..5d7a889394e 100644 --- a/assets/js/frontend-components/price-slider/style.scss +++ b/assets/js/frontend-components/price-slider/style.scss @@ -1,7 +1,7 @@ @mixin thumb { background-color: transparent; /* stylelint-disable */ - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='52' height='42'%3E%3Cdefs%3E%3Cpath id='a' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath id='b' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M24.3176 8.9423l-8.4163-6.1432c-1.706-1.2285-3.6395-1.8988-5.6867-1.787h-.1137c-4.8906.335-8.985 4.356-9.0987 9.2706C.8885 15.644 5.2102 20 10.6696 20h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M9 6v9m3-9v9'/%3E%3Cg fill-rule='nonzero' transform='translate(1 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23a'/%3E%3Cuse stroke='%2395588A' xlink:href='%23a'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M9 27v9m3-9v9'/%3E%3Cg%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M27.6824 8.9423l8.4163-6.1432c1.706-1.2285 3.6395-1.8988 5.6867-1.787h.1137c4.8906.335 8.985 4.356 9.0987 9.2706C51.1115 15.644 46.7898 20 41.3304 20h-.1137c-1.8197 0-3.6395-.6702-5.118-1.787l-8.4163-6.255c-.9099-.8935-.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M43 6v9m-3-9v9'/%3E%3C/g%3E%3Cg%3E%3Cg fill-rule='nonzero' transform='matrix(-1 0 0 1 51 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23b'/%3E%3Cuse stroke='%2395588A' xlink:href='%23b'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M43 27v9m-3-9v9'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='52' height='42'%3E%3Cdefs%3E%3Cpath id='a' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath id='b' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M24.3176 8.9423l-8.4163-6.1432c-1.706-1.2285-3.6395-1.8988-5.6867-1.787h-.1137c-4.8906.335-8.985 4.356-9.0987 9.2706C.8885 15.644 5.2102 20 10.6696 20h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M9 6v9m3-9v9'/%3E%3Cg fill-rule='nonzero' transform='translate(1 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23a'/%3E%3Cuse stroke='%2395588A' xlink:href='%23a'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M9 27v9m3-9v9'/%3E%3Cg%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M27.6824 8.9423l8.4163-6.1432c1.706-1.2285 3.6395-1.8988 5.6867-1.787h.1137c4.8906.335 8.985 4.356 9.0987 9.2706C51.1115 15.644 46.7898 20 41.3304 20h-.1137c-1.8197 0-3.6395-.6702-5.118-1.787l-8.4163-6.255c-.9099-.8935-.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M43 6v9m-3-9v9'/%3E%3C/g%3E%3Cg%3E%3Cg fill-rule='nonzero' transform='matrix(-1 0 0 1 51 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23b'/%3E%3Cuse stroke='%2395588A' xlink:href='%23b'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M43 27v9m-3-9v9'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); /* stylelint-enable */ background-position: 0 0; width: 26px; @@ -27,8 +27,7 @@ outline: 0; } -.wc-block-price-filter__slider_group { - +.wc-block-price-filter { .wc-block-price-filter__amount { display: inline-block; margin-bottom: 20px; @@ -41,8 +40,7 @@ float: right; } } - - .wc-block-price-filter__slider_range { + .wc-block-price-filter__range-input-wrapper { background: #a8739d; box-shadow: 0 0 0 1px inset #95588a; height: 9px; @@ -50,8 +48,7 @@ clear: both; position: relative; } - - .wc-block-price-filter__slider_range_progress { + .wc-block-price-filter__progress { background: #e1e1e1; box-shadow: 0 0 0 1px inset #b8b8b8; border-radius: 2px; @@ -60,15 +57,14 @@ position: absolute; z-index: 5; - &.wc-block-price-filter__slider_range_progress--start { + &.wc-block-price-filter__progress--lower { left: 0; } - &.wc-block-price-filter__slider_range_progress--end { + &.wc-block-price-filter__progress--upper { right: 0; } } - - .wc-block-price-filter__slider { + .wc-block-price-filter__range-input { width: 100%; height: 0; border: 0; @@ -78,44 +74,41 @@ -webkit-appearance: none; -moz-appearance: none; display: block; - outline: none; + outline: none !important; position: relative; pointer-events: none; - &:focus { - outline: none; + &::-webkit-slider-runnable-track { + -webkit-appearance: none; + @include track; + } + &::-webkit-slider-thumb { + -webkit-appearance: none; + @include thumb; + margin: -6px 0 0 0; + } + &::-webkit-slider-progress { + background: #a8739d; + box-shadow: 0 0 0 1px inset #95588a; } &::-moz-focus-outer { border: 0; } - - &::-webkit-slider-runnable-track { - -webkit-appearance: none; - @include track; - } &::-moz-range-track { -moz-appearance: none; @include track; } - &::-ms-track { - @include track; - } - - &::-webkit-slider-thumb { - -webkit-appearance: none; - @include thumb; - margin: -6px 0 0 0; + &::-moz-range-progress { + -moz-appearance: none; + background: transparent; } &::-moz-range-thumb { -moz-appearance: none; @include thumb; } - &::-ms-thumb { - @include thumb; - } - &.wc-block-price-filter__slider--min { + &.wc-block-price-filter__range-input--min { z-index: 15; &::-webkit-slider-thumb { @@ -124,12 +117,9 @@ &::-moz-range-thumb { transform: translate( -7px, 4px ); } - &::-ms-thumb { - margin-left: -7px; - } } - &.wc-block-price-filter__slider--max { + &.wc-block-price-filter__range-input--max { z-index: 10; &::-webkit-slider-thumb { @@ -140,9 +130,63 @@ background-position-x: 26px; transform: translate( 7px, 4px ); } + } + } +} + +/* IE 11 will not support multi-range slider due to poor pointer-events support on the thumb. Reverts to 2 sliders. */ +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + .wc-block-price-filter { + .wc-block-price-filter__progress { + display: none; + } + .wc-block-price-filter__range-input-wrapper { + background: transparent; + box-shadow: none; + height: 48px; + } + .wc-block-price-filter__range-input { + height: 24px; + pointer-events: auto; + + &::-ms-track { + /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */ + background: transparent; + + /*leave room for the larger thumb to overflow with a transparent border */ + border-color: transparent; + border-width: 7px 0; + + /*remove default tick marks*/ + color: transparent; + } + &::-ms-fill-lower { + background: #e1e1e1; + box-shadow: 0 0 0 1px inset #b8b8b8; + } + &::-ms-fill-upper { + background: #a8739d; + box-shadow: 0 0 0 1px inset #95588a; + } + &::-ms-thumb { + @include thumb; + pointer-events: auto; + } + &::-ms-tooltip { + display: none; + } + } + .wc-block-price-filter__range-input--max { + &::-ms-fill-upper { + background: #e1e1e1; + box-shadow: 0 0 0 1px inset #b8b8b8; + } + &::-ms-fill-lower { + background: #a8739d; + box-shadow: 0 0 0 1px inset #95588a; + } &::-ms-thumb { background-position-x: 26px; - margin-left: 7px; } } } From 88beed1b143087ff5a3b21cce2437841cf793352 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 1 Aug 2019 07:10:25 +0100 Subject: [PATCH 07/51] styling improvements --- .../frontend-components/price-slider/index.js | 77 +++++++--- .../price-slider/style.scss | 137 +++++++++++------- 2 files changed, 135 insertions(+), 79 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 015bb3b4ae5..73eaac0bd08 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -40,23 +40,35 @@ class PriceSlider extends Component { onChangeMin() { const { step, max } = this.state; + let value = this.minRange.current.value; + + if ( max <= value ) { + value = max - step; + } + this.setState( { - currentMin: this.minRange.current.value, - currentMax: Math.min( max, Math.max( parseInt( this.minRange.current.value, 10 ) + step, this.maxRange.current.value ) ), + currentMin: value, + currentMax: Math.min( max, Math.max( this.maxRange.current.value, parseInt( value, 10 ) + step ) ), } ); } onChangeMax() { const { step, min } = this.state; + let value = this.maxRange.current.value; + + if ( min >= value ) { + value = min + step; + } + this.setState( { - currentMin: Math.max( min, Math.min( this.minRange.current.value, parseInt( this.maxRange.current.value, 10 ) - step ) ), - currentMax: this.maxRange.current.value, + currentMin: Math.max( min, Math.min( this.minRange.current.value, parseInt( value, 10 ) - step ) ), + currentMax: value, } ); } onInputMin() { - const { min, max } = this.state; + const { min, max, step } = this.state; let value = this.minInput.current.value.replace( /[^0-9.-]+/g, '' ); @@ -64,23 +76,23 @@ class PriceSlider extends Component { value = min; } - if ( max < value ) { - value = max; + if ( max <= value ) { + value = max - step; } this.setState( { currentMin: value ? parseInt( value, 10 ) : '', - currentMax: Math.min( max, Math.max( parseInt( value, 10 ) || 0, this.maxRange.current.value ) ), + currentMax: Math.min( max, Math.max( ( parseInt( value, 10 ) || 0 ) + step, this.maxRange.current.value ) ), } ); } onInputMax() { - const { min, max } = this.state; + const { min, max, step } = this.state; let value = this.maxInput.current.value.replace( /[^0-9.-]+/g, '' ); - if ( min > value ) { - value = min; + if ( min >= value ) { + value = min + step; } if ( max < value ) { @@ -88,7 +100,7 @@ class PriceSlider extends Component { } this.setState( { - currentMin: Math.max( min, Math.min( this.minRange.current.value, parseInt( value, 10 ) || 0 ) ), + currentMin: Math.max( min, Math.min( ( parseInt( value, 10 ) || 0 ) - step ), this.minRange.current.value ), currentMax: value ? parseInt( value, 10 ) : '', } ); } @@ -114,22 +126,41 @@ class PriceSlider extends Component { render() { const { min, max, step, currentMin, currentMax } = this.state; - const minProgress = ( ( currentMin / max ) * 100 ); - // eslint-disable-next-line no-mixed-operators - const maxProgress = ( max - ( currentMax / max ) * 100 ); + const low = Math.round( 100 * ( ( currentMin - min ) / ( max - min ) ) ) + 0.5; + const high = Math.round( 100 * ( ( currentMax - min ) / ( max - min ) ) ) + 0.5; + return (
- - -
-
+
+ +
); diff --git a/assets/js/frontend-components/price-slider/style.scss b/assets/js/frontend-components/price-slider/style.scss index 5d7a889394e..af162dadebb 100644 --- a/assets/js/frontend-components/price-slider/style.scss +++ b/assets/js/frontend-components/price-slider/style.scss @@ -1,8 +1,6 @@ +/* stylelint-disable */ @mixin thumb { background-color: transparent; - /* stylelint-disable */ - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='52' height='42'%3E%3Cdefs%3E%3Cpath id='a' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath id='b' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M24.3176 8.9423l-8.4163-6.1432c-1.706-1.2285-3.6395-1.8988-5.6867-1.787h-.1137c-4.8906.335-8.985 4.356-9.0987 9.2706C.8885 15.644 5.2102 20 10.6696 20h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M9 6v9m3-9v9'/%3E%3Cg fill-rule='nonzero' transform='translate(1 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23a'/%3E%3Cuse stroke='%2395588A' xlink:href='%23a'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M9 27v9m3-9v9'/%3E%3Cg%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M27.6824 8.9423l8.4163-6.1432c1.706-1.2285 3.6395-1.8988 5.6867-1.787h.1137c4.8906.335 8.985 4.356 9.0987 9.2706C51.1115 15.644 46.7898 20 41.3304 20h-.1137c-1.8197 0-3.6395-.6702-5.118-1.787l-8.4163-6.255c-.9099-.8935-.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M43 6v9m-3-9v9'/%3E%3C/g%3E%3Cg%3E%3Cg fill-rule='nonzero' transform='matrix(-1 0 0 1 51 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23b'/%3E%3Cuse stroke='%2395588A' xlink:href='%23b'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M43 27v9m-3-9v9'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); - /* stylelint-enable */ background-position: 0 0; width: 26px; height: 21px; @@ -13,18 +11,36 @@ cursor: pointer; z-index: 20; pointer-events: auto; + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='52' height='42'%3E%3Cdefs%3E%3Cpath id='a' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath id='b' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M24.3176 8.9423l-8.4163-6.1432c-1.706-1.2285-3.6395-1.8988-5.6867-1.787h-.1137c-4.8906.335-8.985 4.356-9.0987 9.2706C.8885 15.644 5.2102 20 10.6696 20h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M9 6v9m3-9v9'/%3E%3Cg fill-rule='nonzero' transform='translate(1 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23a'/%3E%3Cuse stroke='%2395588A' xlink:href='%23a'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M9 27v9m3-9v9'/%3E%3Cg%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M27.6824 8.9423l8.4163-6.1432c1.706-1.2285 3.6395-1.8988 5.6867-1.787h.1137c4.8906.335 8.985 4.356 9.0987 9.2706C51.1115 15.644 46.7898 20 41.3304 20h-.1137c-1.8197 0-3.6395-.6702-5.118-1.787l-8.4163-6.255c-.9099-.8935-.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M43 6v9m-3-9v9'/%3E%3C/g%3E%3Cg%3E%3Cg fill-rule='nonzero' transform='matrix(-1 0 0 1 51 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23b'/%3E%3Cuse stroke='%2395588A' xlink:href='%23b'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M43 27v9m-3-9v9'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + transition: transform .2s ease-in-out; - &:hover, - &:active { - background-position-y: -21px; - filter: drop-shadow( 3px 0 0 rgba( 255, 255, 255, .75 ) ) drop-shadow( -3px 0 0 rgba( 255, 255, 255, .75 ) ); + &:hover { + @include thumbFocus; + transform: scale(1.1); } } - +@mixin thumbFocus { + background-position-y: -21px; + filter: drop-shadow(3px 0 0 rgba(255, 255, 255, .75)) drop-shadow(-3px 0 0 rgba(255, 255, 255, .75)); +} +/* stylelint-enable */ @mixin track { cursor: default; height: 0; outline: 0; + -webkit-appearance: none; + -moz-appearance: none; +} +@mixin reset { + margin: 0; + padding: 0; + border: 0; + outline: none; + background: transparent; + -webkit-appearance: none; + -moz-appearance: none; } .wc-block-price-filter { @@ -41,49 +57,36 @@ } } .wc-block-price-filter__range-input-wrapper { - background: #a8739d; - box-shadow: 0 0 0 1px inset #95588a; + @include reset; height: 9px; - padding: 0; clear: both; position: relative; - } - .wc-block-price-filter__progress { + box-shadow: 0 0 0 1px inset rgba(0, 0, 0, 0.1); background: #e1e1e1; - box-shadow: 0 0 0 1px inset #b8b8b8; - border-radius: 2px; - height: 9px; - padding: 0; - position: absolute; - z-index: 5; - - &.wc-block-price-filter__progress--lower { + .wc-block-price-filter__range-input-progress { + height: 9px; + width: 100%; + position: absolute; left: 0; - } - &.wc-block-price-filter__progress--upper { - right: 0; + top: 0; + --track-background: linear-gradient(to right, transparent var(--low), var(--range-color) 0, var(--range-color) var(--high), transparent 0) no-repeat 0 100% / 100% 100%; + --range-color: #a8739d; + background: var(--track-background); } } .wc-block-price-filter__range-input { + @include reset; width: 100%; height: 0; - border: 0; - margin: 0; - padding: 0; - background: transparent; - -webkit-appearance: none; - -moz-appearance: none; display: block; - outline: none !important; position: relative; pointer-events: none; + outline: none !important; &::-webkit-slider-runnable-track { - -webkit-appearance: none; @include track; } &::-webkit-slider-thumb { - -webkit-appearance: none; @include thumb; margin: -6px 0 0 0; } @@ -96,26 +99,39 @@ border: 0; } &::-moz-range-track { - -moz-appearance: none; @include track; } &::-moz-range-progress { - -moz-appearance: none; - background: transparent; + @include reset; } &::-moz-range-thumb { - -moz-appearance: none; @include thumb; } + &::-ms-thumb { + @include thumb; + } + + &:focus { + &::-webkit-slider-thumb { + @include thumbFocus; + } + &::-moz-range-thumb { + @include thumbFocus; + } + &::-ms-thumb { + @include thumbFocus; + } + } + &.wc-block-price-filter__range-input--min { z-index: 15; &::-webkit-slider-thumb { - margin-left: -7px; + margin-left: -2px; } &::-moz-range-thumb { - transform: translate( -7px, 4px ); + transform: translate(-2px, 4px); } } @@ -124,30 +140,35 @@ &::-webkit-slider-thumb { background-position-x: 26px; - margin-left: 7px; + margin-left: 2px; } &::-moz-range-thumb { background-position-x: 26px; - transform: translate( 7px, 4px ); + transform: translate(2px, 4px); + } + &::-ms-thumb { + background-position-x: 26px; } } } } -/* IE 11 will not support multi-range slider due to poor pointer-events support on the thumb. Reverts to 2 sliders. */ -@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { +@mixin ie { .wc-block-price-filter { - .wc-block-price-filter__progress { - display: none; - } .wc-block-price-filter__range-input-wrapper { background: transparent; box-shadow: none; - height: 48px; + height: 24px; + .wc-block-price-filter__range-input-progress { + display: none; + } } .wc-block-price-filter__range-input { height: 24px; pointer-events: auto; + position: absolute; + left: 0; + top: 0; &::-ms-track { /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */ @@ -165,16 +186,15 @@ box-shadow: 0 0 0 1px inset #b8b8b8; } &::-ms-fill-upper { - background: #a8739d; - box-shadow: 0 0 0 1px inset #95588a; - } - &::-ms-thumb { - @include thumb; - pointer-events: auto; + background: transparent; } &::-ms-tooltip { display: none; } + &::-ms-thumb { + transform: translate(1px, 0); + pointer-events: auto; + } } .wc-block-price-filter__range-input--max { &::-ms-fill-upper { @@ -185,9 +205,14 @@ background: #a8739d; box-shadow: 0 0 0 1px inset #95588a; } - &::-ms-thumb { - background-position-x: 26px; - } } } } + +/* IE 11 will not support multi-range slider due to poor pointer-events support on the thumb. Reverts to 2 sliders. */ +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + @include ie; +} +@supports (-ms-ime-align:auto) { + @include ie; +} From 59fc79b561fae7a363076bb3dc86a47a37f47a7c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 1 Aug 2019 08:14:11 +0100 Subject: [PATCH 08/51] improve events --- .../frontend-components/price-slider/index.js | 139 ++++++++++++------ 1 file changed, 95 insertions(+), 44 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 73eaac0bd08..73d228306ac 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -22,6 +22,8 @@ class PriceSlider extends Component { this.state = { currentMin: 0, currentMax: 100, + inputMin: this.formatCurrencyForInput( 0 ), + inputMax: this.formatCurrencyForInput( 100 ), step: 1, min: 0, max: 100, @@ -30,13 +32,32 @@ class PriceSlider extends Component { this.maxInput = createRef(); this.minRange = createRef(); this.maxRange = createRef(); - this.onChangeMin = this.onChangeMin.bind( this ); - this.onChangeMax = this.onChangeMax.bind( this ); - this.onInputMin = this.onInputMin.bind( this ); - this.onInputMax = this.onInputMax.bind( this ); + this.onDrag = this.onDrag.bind( this ); + this.onInputChange = this.onInputChange.bind( this ); + this.onInputBlur = this.onInputBlur.bind( this ); + this.findClosestRange = this.findClosestRange.bind( this ); + this.validateValues = this.validateValues.bind( this ); this.formatCurrencyForInput = this.formatCurrencyForInput.bind( this ); } + formatCurrencyForInput( value ) { + if ( '' === value ) { + return ''; + } + const formattedNumber = parseInt( value, 10 ); + const currencySymbol = get( wcSettings, [ 'currency', 'symbol' ], '$' ); + const priceFormat = get( wcSettings, [ 'currency', 'price_format' ], '%1$s%2$s' ); + + if ( '' === formattedNumber ) { + return formattedNumber; + } + + const formattedValue = sprintf( priceFormat, currencySymbol, formattedNumber ); + const txt = document.createElement( 'textarea' ); + txt.innerHTML = formattedValue; + return txt.value; + } + onChangeMin() { const { step, max } = this.state; @@ -67,60 +88,87 @@ class PriceSlider extends Component { } ); } - onInputMin() { + validateValues( values, isMin ) { const { min, max, step } = this.state; - let value = this.minInput.current.value.replace( /[^0-9.-]+/g, '' ); + let minValue = parseInt( values[ 0 ], 10 ) || min; + let maxValue = parseInt( values[ 1 ], 10 ) || step; - if ( min > value ) { - value = min; + if ( min > minValue ) { + minValue = min; } - if ( max <= value ) { - value = max - step; + if ( max <= minValue ) { + minValue = max - step; } - this.setState( { - currentMin: value ? parseInt( value, 10 ) : '', - currentMax: Math.min( max, Math.max( ( parseInt( value, 10 ) || 0 ) + step, this.maxRange.current.value ) ), - } ); - } - - onInputMax() { - const { min, max, step } = this.state; + if ( min >= maxValue ) { + maxValue = min + step; + } - let value = this.maxInput.current.value.replace( /[^0-9.-]+/g, '' ); + if ( max < maxValue ) { + maxValue = max; + } - if ( min >= value ) { - value = min + step; + if ( ! isMin && minValue >= maxValue ) { + minValue = maxValue - step; } - if ( max < value ) { - value = max; + if ( isMin && maxValue <= minValue ) { + maxValue = minValue + step; } + return [ minValue, maxValue ]; + } + + onInputChange( event ) { + const newValue = event.target.value.replace( /[^0-9.-]+/g, '' ); + const isMin = event.target.classList.contains( 'wc-block-price-filter__amount--min' ); + const editing = isMin ? 'inputMin' : 'inputMax'; + const newState = {}; + newState[ editing ] = newValue; + + this.setState( newState ); + } + + onInputBlur( event ) { + const isMin = event.target.classList.contains( 'wc-block-price-filter__amount--min' ); + const values = this.validateValues( + [ + this.minInput.current.value.replace( /[^0-9.-]+/g, '' ), + this.maxInput.current.value.replace( /[^0-9.-]+/g, '' ), + ], + isMin + ); + this.setState( { - currentMin: Math.max( min, Math.min( ( parseInt( value, 10 ) || 0 ) - step ), this.minRange.current.value ), - currentMax: value ? parseInt( value, 10 ) : '', + currentMin: values[ 0 ], + currentMax: values[ 1 ], + inputMin: this.formatCurrencyForInput( values[ 0 ] ), + inputMax: this.formatCurrencyForInput( values[ 1 ] ), } ); } - formatCurrencyForInput( value ) { - if ( '' === value ) { - return ''; - } - const formattedNumber = parseInt( value, 10 ); - const currencySymbol = get( wcSettings, [ 'currency', 'symbol' ], '$' ); - const priceFormat = get( wcSettings, [ 'currency', 'price_format' ], '%1$s%2$s' ); + onDrag( event ) { + const isMin = event.target.classList.contains( 'wc-block-price-filter__range-input--min' ); + const values = this.validateValues( + [ + this.minRange.current.value, + this.maxRange.current.value, + ], + isMin + ); - if ( '' === formattedNumber ) { - return formattedNumber; - } + this.setState( { + currentMin: values[ 0 ], + currentMax: values[ 1 ], + inputMin: this.formatCurrencyForInput( values[ 0 ] ), + inputMax: this.formatCurrencyForInput( values[ 1 ] ), + } ); + } + + findClosestRange() { - const formattedValue = sprintf( priceFormat, currencySymbol, formattedNumber ); - const txt = document.createElement( 'textarea' ); - txt.innerHTML = formattedValue; - return txt.value; } render() { @@ -131,8 +179,8 @@ class PriceSlider extends Component { return (
- - + +
Date: Thu, 1 Aug 2019 08:37:18 +0100 Subject: [PATCH 09/51] IE shim --- .../frontend-components/price-slider/index.js | 40 +++++++++++++++---- .../price-slider/style.scss | 8 ++-- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 73d228306ac..bf61583861b 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -167,8 +167,34 @@ class PriceSlider extends Component { } ); } - findClosestRange() { - + /** + * Works around an IE issue where only one range selector is visible by changing the display order + * based on the mouse position. + * + * @param {obj} event event data. + */ + findClosestRange( event ) { + const { max } = this.state; + const bounds = event.target.getBoundingClientRect(); + const x = event.clientX - bounds.left; + const minWidth = this.minRange.current.offsetWidth; + const minValue = this.minRange.current.value; + const maxWidth = this.maxRange.current.offsetWidth; + const maxValue = this.maxRange.current.value; + + const minX = minWidth * ( minValue / max ); + const maxX = maxWidth * ( maxValue / max ); + + const minXDiff = Math.abs( x - minX ); + const maxXDiff = Math.abs( x - maxX ); + + if ( minXDiff > maxXDiff ) { + this.minRange.current.style.zIndex = 10; + this.maxRange.current.style.zIndex = 20; + } else { + this.minRange.current.style.zIndex = 20; + this.maxRange.current.style.zIndex = 10; + } } render() { @@ -181,7 +207,11 @@ class PriceSlider extends Component {
-
+
Date: Thu, 1 Aug 2019 10:42:03 +0100 Subject: [PATCH 10/51] Fix samsung internet styles --- assets/js/frontend-components/price-slider/index.js | 8 ++++---- .../js/frontend-components/price-slider/style.scss | 13 ++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index bf61583861b..ea51af1adbb 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -189,11 +189,11 @@ class PriceSlider extends Component { const maxXDiff = Math.abs( x - maxX ); if ( minXDiff > maxXDiff ) { - this.minRange.current.style.zIndex = 10; - this.maxRange.current.style.zIndex = 20; - } else { this.minRange.current.style.zIndex = 20; - this.maxRange.current.style.zIndex = 10; + this.maxRange.current.style.zIndex = 21; + } else { + this.minRange.current.style.zIndex = 21; + this.maxRange.current.style.zIndex = 20; } } diff --git a/assets/js/frontend-components/price-slider/style.scss b/assets/js/frontend-components/price-slider/style.scss index c7bb25fb575..182b12006a3 100644 --- a/assets/js/frontend-components/price-slider/style.scss +++ b/assets/js/frontend-components/price-slider/style.scss @@ -11,10 +11,11 @@ cursor: pointer; z-index: 20; pointer-events: auto; - -webkit-appearance: none; - -moz-appearance: none; background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='52' height='42'%3E%3Cdefs%3E%3Cpath id='a' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath id='b' d='M23.3176 7.9423l-8.4163-6.1432C13.1953.5706 11.2618-.0997 9.2146.0121h-.1137C4.2103.347.1159 4.368.0022 9.2827-.1115 14.644 4.2102 19 9.6696 19h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M24.3176 8.9423l-8.4163-6.1432c-1.706-1.2285-3.6395-1.8988-5.6867-1.787h-.1137c-4.8906.335-8.985 4.356-9.0987 9.2706C.8885 15.644 5.2102 20 10.6696 20h.1137c1.8197 0 3.6395-.6702 5.118-1.787l8.4163-6.255c.9099-.8935.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M9 6v9m3-9v9'/%3E%3Cg fill-rule='nonzero' transform='translate(1 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23a'/%3E%3Cuse stroke='%2395588A' xlink:href='%23a'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M9 27v9m3-9v9'/%3E%3Cg%3E%3Cpath fill='%23FFF' fill-rule='nonzero' stroke='%2395588A' d='M27.6824 8.9423l8.4163-6.1432c1.706-1.2285 3.6395-1.8988 5.6867-1.787h.1137c4.8906.335 8.985 4.356 9.0987 9.2706C51.1115 15.644 46.7898 20 41.3304 20h-.1137c-1.8197 0-3.6395-.6702-5.118-1.787l-8.4163-6.255c-.9099-.8935-.9099-2.2338 0-3.0157z'/%3E%3Cpath stroke='%23B8B8B8' d='M43 6v9m-3-9v9'/%3E%3C/g%3E%3Cg%3E%3Cg fill-rule='nonzero' transform='matrix(-1 0 0 1 51 22)'%3E%3Cuse fill='%23F8F3F7' stroke='%23FFF' stroke-opacity='.75' stroke-width='3' xlink:href='%23b'/%3E%3Cuse stroke='%2395588A' xlink:href='%23b'/%3E%3C/g%3E%3Cpath stroke='%2395588A' d='M43 27v9m-3-9v9'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); transition: transform .2s ease-in-out; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; &:hover { @include thumbFocus; @@ -28,10 +29,11 @@ /* stylelint-enable */ @mixin track { cursor: default; - height: 0; + height: 1px; /* Required for Samsung internet based browsers */ outline: 0; -webkit-appearance: none; -moz-appearance: none; + appearance: none; } @mixin reset { margin: 0; @@ -41,6 +43,7 @@ background: transparent; -webkit-appearance: none; -moz-appearance: none; + appearance: none; } .wc-block-price-filter { @@ -125,7 +128,7 @@ } &.wc-block-price-filter__range-input--min { - z-index: 15; + z-index: 21; &::-webkit-slider-thumb { margin-left: -2px; @@ -136,7 +139,7 @@ } &.wc-block-price-filter__range-input--max { - z-index: 10; + z-index: 20; &::-webkit-slider-thumb { background-position-x: 26px; From a384d7cdf41954273f96a60c212716b88462a8e9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 1 Aug 2019 11:04:44 +0100 Subject: [PATCH 11/51] Add aria --- .../frontend-components/price-slider/index.js | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index ea51af1adbb..f5dbc0d9cc3 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { sprintf } from '@wordpress/i18n'; +import { sprintf, __ } from '@wordpress/i18n'; import { Component, createRef } from 'react'; /** @@ -37,6 +37,7 @@ class PriceSlider extends Component { this.onInputBlur = this.onInputBlur.bind( this ); this.findClosestRange = this.findClosestRange.bind( this ); this.validateValues = this.validateValues.bind( this ); + this.getProgressStyle = this.getProgressStyle.bind( this ); this.formatCurrencyForInput = this.formatCurrencyForInput.bind( this ); } @@ -197,30 +198,52 @@ class PriceSlider extends Component { } } - render() { - const { min, max, step, currentMin, currentMax } = this.state; + getProgressStyle() { + const { min, max, currentMin, currentMax } = this.state; const low = Math.round( 100 * ( ( currentMin - min ) / ( max - min ) ) ) + 0.5; const high = Math.round( 100 * ( ( currentMax - min ) / ( max - min ) ) ) + 0.5; + return { + '--low': low + '%', + '--high': high + '%', + }; + } + + render() { + const { min, max, step, currentMin, currentMax } = this.state; return (
- - + +
-
+
Date: Thu, 1 Aug 2019 11:05:26 +0100 Subject: [PATCH 12/51] remove old methods --- .../frontend-components/price-slider/index.js | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index f5dbc0d9cc3..8a747ac1abc 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -59,36 +59,6 @@ class PriceSlider extends Component { return txt.value; } - onChangeMin() { - const { step, max } = this.state; - - let value = this.minRange.current.value; - - if ( max <= value ) { - value = max - step; - } - - this.setState( { - currentMin: value, - currentMax: Math.min( max, Math.max( this.maxRange.current.value, parseInt( value, 10 ) + step ) ), - } ); - } - - onChangeMax() { - const { step, min } = this.state; - - let value = this.maxRange.current.value; - - if ( min >= value ) { - value = min + step; - } - - this.setState( { - currentMin: Math.max( min, Math.min( this.minRange.current.value, parseInt( value, 10 ) - step ) ), - currentMax: value, - } ); - } - validateValues( values, isMin ) { const { min, max, step } = this.state; From 226985faa786ac0fb9e18d2c521a5c4622193cec Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 1 Aug 2019 11:27:12 +0100 Subject: [PATCH 13/51] event handling --- assets/js/blocks/price-filter/block.js | 22 ++++++- .../frontend-components/price-slider/index.js | 60 +++++++++++++++---- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/assets/js/blocks/price-filter/block.js b/assets/js/blocks/price-filter/block.js index eee1d18d864..18d3f9507b7 100644 --- a/assets/js/blocks/price-filter/block.js +++ b/assets/js/blocks/price-filter/block.js @@ -13,14 +13,34 @@ import PriceSlider from '../../frontend-components/price-slider'; * Component displaying a price filter. */ class PriceFilterBlock extends Component { + constructor() { + super( ...arguments ); + this.state = { + min: 0, + max: 200, + }; + this.onChange = this.onChange.bind( this ); + } + + onChange( values ) { + // This is probably temporary - to test the values can be pulled from the PriceSlider component. + this.setState( { + min: values.min, + max: values.max, + } ); + } + render() { const classes = classnames( 'wc-block-price-slider', ); + const { min, max } = this.state; return (
- +

Current Min: { min }

+

Current Max: { max }

+
); } diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 8a747ac1abc..1fad1780f10 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -3,6 +3,7 @@ */ import { sprintf, __ } from '@wordpress/i18n'; import { Component, createRef } from 'react'; +import PropTypes from 'prop-types'; /** * Internal dependencies @@ -17,16 +18,14 @@ const get = ( obj, path, defaultValue ) => { }; class PriceSlider extends Component { - constructor() { + constructor( props ) { + const { min, max } = props; super( ...arguments ); this.state = { - currentMin: 0, - currentMax: 100, - inputMin: this.formatCurrencyForInput( 0 ), - inputMax: this.formatCurrencyForInput( 100 ), - step: 1, - min: 0, - max: 100, + currentMin: min, + currentMax: max, + inputMin: this.formatCurrencyForInput( min ), + inputMax: this.formatCurrencyForInput( max ), }; this.minInput = createRef(); this.maxInput = createRef(); @@ -41,6 +40,17 @@ class PriceSlider extends Component { this.formatCurrencyForInput = this.formatCurrencyForInput.bind( this ); } + componentDidUpdate( prevProps, prevState ) { + if ( prevState.currentMin !== this.state.currentMin || prevState.currentMax !== this.state.currentMax ) { + const { onChange } = this.props; + + onChange( { + min: this.state.currentMin, + max: this.state.currentMax, + } ); + } + } + formatCurrencyForInput( value ) { if ( '' === value ) { return ''; @@ -60,7 +70,7 @@ class PriceSlider extends Component { } validateValues( values, isMin ) { - const { min, max, step } = this.state; + const { min, max, step } = this.props; let minValue = parseInt( values[ 0 ], 10 ) || min; let maxValue = parseInt( values[ 1 ], 10 ) || step; @@ -145,7 +155,7 @@ class PriceSlider extends Component { * @param {obj} event event data. */ findClosestRange( event ) { - const { max } = this.state; + const { max } = this.props; const bounds = event.target.getBoundingClientRect(); const x = event.clientX - bounds.left; const minWidth = this.minRange.current.offsetWidth; @@ -169,7 +179,8 @@ class PriceSlider extends Component { } getProgressStyle() { - const { min, max, currentMin, currentMax } = this.state; + const { min, max } = this.props; + const { currentMin, currentMax } = this.state; const low = Math.round( 100 * ( ( currentMin - min ) / ( max - min ) ) ) + 0.5; const high = Math.round( 100 * ( ( currentMax - min ) / ( max - min ) ) ) + 0.5; @@ -181,7 +192,8 @@ class PriceSlider extends Component { } render() { - const { min, max, step, currentMin, currentMax } = this.state; + const { min, max, step } = this.props; + const { currentMin, currentMax } = this.state; return (
Date: Fri, 2 Aug 2019 15:54:34 +0100 Subject: [PATCH 14/51] Tweak size and width of inputs --- assets/js/frontend-components/price-slider/index.js | 2 +- assets/js/frontend-components/price-slider/style.scss | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 1fad1780f10..61444f3df92 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -182,7 +182,7 @@ class PriceSlider extends Component { const { min, max } = this.props; const { currentMin, currentMax } = this.state; - const low = Math.round( 100 * ( ( currentMin - min ) / ( max - min ) ) ) + 0.5; + const low = Math.round( 100 * ( ( currentMin - min ) / ( max - min ) ) ) - 0.5; const high = Math.round( 100 * ( ( currentMax - min ) / ( max - min ) ) ) + 0.5; return { diff --git a/assets/js/frontend-components/price-slider/style.scss b/assets/js/frontend-components/price-slider/style.scss index 182b12006a3..bde2bb7cd53 100644 --- a/assets/js/frontend-components/price-slider/style.scss +++ b/assets/js/frontend-components/price-slider/style.scss @@ -51,6 +51,7 @@ display: inline-block; margin-bottom: 20px; border-radius: 4px; + width: auto; &.wc-block-price-filter__amount--min { float: left; From 17524205347b40e2dfb1447f54910e6c71469c56 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 2 Aug 2019 17:02:02 +0100 Subject: [PATCH 15/51] reset progress --- assets/js/frontend-components/price-slider/style.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assets/js/frontend-components/price-slider/style.scss b/assets/js/frontend-components/price-slider/style.scss index bde2bb7cd53..126de12aa6e 100644 --- a/assets/js/frontend-components/price-slider/style.scss +++ b/assets/js/frontend-components/price-slider/style.scss @@ -95,8 +95,7 @@ margin: -6px 0 0 0; } &::-webkit-slider-progress { - background: #a8739d; - box-shadow: 0 0 0 1px inset #95588a; + @include reset; } &::-moz-focus-outer { From decef1213913b0eef36610022ec2640d43527c80 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 8 Aug 2019 15:01:48 +0100 Subject: [PATCH 16/51] shorthand notation for setstate --- assets/js/frontend-components/price-slider/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 61444f3df92..67242387219 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -106,10 +106,10 @@ class PriceSlider extends Component { const newValue = event.target.value.replace( /[^0-9.-]+/g, '' ); const isMin = event.target.classList.contains( 'wc-block-price-filter__amount--min' ); const editing = isMin ? 'inputMin' : 'inputMax'; - const newState = {}; - newState[ editing ] = newValue; - this.setState( newState ); + this.setState( { + [ editing ]: newValue, + } ); } onInputBlur( event ) { From 3465b45e44aa338ce0057ba8cf08514e48645bff Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 8 Aug 2019 15:05:12 +0100 Subject: [PATCH 17/51] Inline comment for textare usage --- assets/js/frontend-components/price-slider/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 67242387219..a51730e574b 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -63,6 +63,7 @@ class PriceSlider extends Component { return formattedNumber; } + // This uses a textarea to magically decode HTML currency symbols. const formattedValue = sprintf( priceFormat, currencySymbol, formattedNumber ); const txt = document.createElement( 'textarea' ); txt.innerHTML = formattedValue; From dec7ac8b929f80bd97a752c7f3794a1db1a06d48 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 8 Aug 2019 15:07:02 +0100 Subject: [PATCH 18/51] Remove pointless comparison --- assets/js/frontend-components/price-slider/index.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index a51730e574b..fec163a74a1 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -59,10 +59,6 @@ class PriceSlider extends Component { const currencySymbol = get( wcSettings, [ 'currency', 'symbol' ], '$' ); const priceFormat = get( wcSettings, [ 'currency', 'price_format' ], '%1$s%2$s' ); - if ( '' === formattedNumber ) { - return formattedNumber; - } - // This uses a textarea to magically decode HTML currency symbols. const formattedValue = sprintf( priceFormat, currencySymbol, formattedNumber ); const txt = document.createElement( 'textarea' ); From 80b7159b27fedf13e13033c841f171a7aa6f24c4 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 8 Aug 2019 15:16:38 +0100 Subject: [PATCH 19/51] destruct from state --- .../js/frontend-components/price-slider/index.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index fec163a74a1..78ea5c53ca0 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -41,12 +41,14 @@ class PriceSlider extends Component { } componentDidUpdate( prevProps, prevState ) { - if ( prevState.currentMin !== this.state.currentMin || prevState.currentMax !== this.state.currentMax ) { + const { currentMin, currentMax } = this.state; + + if ( prevState.currentMin !== currentMin || prevState.currentMax !== currentMax ) { const { onChange } = this.props; onChange( { - min: this.state.currentMin, - max: this.state.currentMax, + min: currentMin, + max: currentMax, } ); } } @@ -190,7 +192,7 @@ class PriceSlider extends Component { render() { const { min, max, step } = this.props; - const { currentMin, currentMax } = this.state; + const { inputMin, inputMax, currentMin, currentMax } = this.state; return (
@@ -209,7 +211,7 @@ class PriceSlider extends Component { aria-label={ __( 'Filter products by maximum price', 'woo-gutenberg-products-block' ) } size="5" ref={ this.maxInput } - value={ this.state.inputMax } + value={ inputMax } onChange={ this.onInputChange } onBlur={ this.onInputBlur } /> From 5cc89074b8e77e236b58064bfa9e66c59edfee0d Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 8 Aug 2019 15:22:03 +0100 Subject: [PATCH 20/51] zindex comment --- assets/js/frontend-components/price-slider/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 78ea5c53ca0..1459d667f43 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -168,6 +168,10 @@ class PriceSlider extends Component { const minXDiff = Math.abs( x - minX ); const maxXDiff = Math.abs( x - maxX ); + /** + * The default z-index in the stylesheet as 20. 20 vs 21 is just for determining which range + * slider should be at the front and has no meaning beyond this. + */ if ( minXDiff > maxXDiff ) { this.minRange.current.style.zIndex = 20; this.maxRange.current.style.zIndex = 21; From 85cb4c2b12baf8624429539305f3d7d5f03fed9e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 8 Aug 2019 16:33:39 +0100 Subject: [PATCH 21/51] Move out currency settings and validation --- assets/js/blocks/price-filter/block.js | 10 ++- assets/js/data/index.js | 14 ++++ .../frontend-components/price-slider/index.js | 70 +++++++------------ .../frontend-components/price-slider/utils.js | 40 +++++++++++ src/Assets.php | 54 +++++++------- 5 files changed, 112 insertions(+), 76 deletions(-) create mode 100644 assets/js/data/index.js create mode 100644 assets/js/frontend-components/price-slider/utils.js diff --git a/assets/js/blocks/price-filter/block.js b/assets/js/blocks/price-filter/block.js index 18d3f9507b7..156a6ee9e41 100644 --- a/assets/js/blocks/price-filter/block.js +++ b/assets/js/blocks/price-filter/block.js @@ -8,6 +8,7 @@ import classnames from 'classnames'; * Internal dependencies */ import PriceSlider from '../../frontend-components/price-slider'; +import { currency } from '../../data'; /** * Component displaying a price filter. @@ -40,7 +41,14 @@ class PriceFilterBlock extends Component {

Current Min: { min }

Current Max: { max }

- +
); } diff --git a/assets/js/data/index.js b/assets/js/data/index.js new file mode 100644 index 00000000000..6d0d9856ab8 --- /dev/null +++ b/assets/js/data/index.js @@ -0,0 +1,14 @@ +/** + * Wrapper for the wcSettings global, which sets defaults if data is missing. + * + * Only settings used by blocks are defined here. Component settings are left out. + */ +export const currency = wcSettings.currency || { + code: 'USD', + precision: 2, + symbol: '$', + position: 'left', + decimal_separator: '.', + thousand_separator: ',', + price_format: '%1$s%2$s', +}; diff --git a/assets/js/frontend-components/price-slider/index.js b/assets/js/frontend-components/price-slider/index.js index 1459d667f43..721b766f43c 100644 --- a/assets/js/frontend-components/price-slider/index.js +++ b/assets/js/frontend-components/price-slider/index.js @@ -9,13 +9,7 @@ import PropTypes from 'prop-types'; * Internal dependencies */ import './style.scss'; - -const get = ( obj, path, defaultValue ) => { - const result = String.prototype.split.call( path, /[,[\].]+?/ ) - .filter( Boolean ) - .reduce( ( res, key ) => ( res !== null && res !== undefined ) ? res[ key ] : res, obj ); - return ( result === undefined || result === obj ) ? defaultValue : result; -}; +import { constrainRangeSliderValues } from './utils'; class PriceSlider extends Component { constructor( props ) { @@ -35,7 +29,6 @@ class PriceSlider extends Component { this.onInputChange = this.onInputChange.bind( this ); this.onInputBlur = this.onInputBlur.bind( this ); this.findClosestRange = this.findClosestRange.bind( this ); - this.validateValues = this.validateValues.bind( this ); this.getProgressStyle = this.getProgressStyle.bind( this ); this.formatCurrencyForInput = this.formatCurrencyForInput.bind( this ); } @@ -57,50 +50,17 @@ class PriceSlider extends Component { if ( '' === value ) { return ''; } + const { currencySymbol, priceFormat } = this.props; + const formattedNumber = parseInt( value, 10 ); - const currencySymbol = get( wcSettings, [ 'currency', 'symbol' ], '$' ); - const priceFormat = get( wcSettings, [ 'currency', 'price_format' ], '%1$s%2$s' ); + const formattedValue = sprintf( priceFormat, currencySymbol, formattedNumber ); // This uses a textarea to magically decode HTML currency symbols. - const formattedValue = sprintf( priceFormat, currencySymbol, formattedNumber ); const txt = document.createElement( 'textarea' ); txt.innerHTML = formattedValue; return txt.value; } - validateValues( values, isMin ) { - const { min, max, step } = this.props; - - let minValue = parseInt( values[ 0 ], 10 ) || min; - let maxValue = parseInt( values[ 1 ], 10 ) || step; - - if ( min > minValue ) { - minValue = min; - } - - if ( max <= minValue ) { - minValue = max - step; - } - - if ( min >= maxValue ) { - maxValue = min + step; - } - - if ( max < maxValue ) { - maxValue = max; - } - - if ( ! isMin && minValue >= maxValue ) { - minValue = maxValue - step; - } - - if ( isMin && maxValue <= minValue ) { - maxValue = minValue + step; - } - - return [ minValue, maxValue ]; - } - onInputChange( event ) { const newValue = event.target.value.replace( /[^0-9.-]+/g, '' ); const isMin = event.target.classList.contains( 'wc-block-price-filter__amount--min' ); @@ -112,12 +72,16 @@ class PriceSlider extends Component { } onInputBlur( event ) { + const { min, max, step } = this.props; const isMin = event.target.classList.contains( 'wc-block-price-filter__amount--min' ); - const values = this.validateValues( + const values = constrainRangeSliderValues( [ this.minInput.current.value.replace( /[^0-9.-]+/g, '' ), this.maxInput.current.value.replace( /[^0-9.-]+/g, '' ), ], + min, + max, + step, isMin ); @@ -130,12 +94,16 @@ class PriceSlider extends Component { } onDrag( event ) { + const { min, max, step } = this.props; const isMin = event.target.classList.contains( 'wc-block-price-filter__range-input--min' ); - const values = this.validateValues( + const values = constrainRangeSliderValues( [ this.minRange.current.value, this.maxRange.current.value, ], + min, + max, + step, isMin ); @@ -270,11 +238,21 @@ PriceSlider.propTypes = { * Step for slider inputs. */ step: PropTypes.number, + /** + * Currency symbol to use when formatting prices for display. + */ + currencySymbol: PropTypes.string, + /** + * Price format to use when formatting prices for display. + */ + priceFormat: PropTypes.string, }; PriceSlider.defaultProps = { min: 0, step: 1, + currencySymbol: '$', + priceFormat: '%1$s%2$s', }; export default PriceSlider; diff --git a/assets/js/frontend-components/price-slider/utils.js b/assets/js/frontend-components/price-slider/utils.js new file mode 100644 index 00000000000..f8048bd20b9 --- /dev/null +++ b/assets/js/frontend-components/price-slider/utils.js @@ -0,0 +1,40 @@ +/** + * Validate a min and max value for a range slider againt defined constraints (min, max, step). + * + * @param {array} values Array containing min and max values. + * @param {int} min Min allowed value for the sliders. + * @param {int} max Max allowed value for the sliders. + * @param {step} step Step value for the sliders. + * @param {boolean} isMin Whether we're currently interacting with the min range slider or not, so we update the correct values. + * @returns {array} Validated and updated min/max values that fit within the range slider constraints. + */ +export const constrainRangeSliderValues = ( values, min, max, step, isMin ) => { + let minValue = parseInt( values[ 0 ], 10 ) || min; + let maxValue = parseInt( values[ 1 ], 10 ) || step; // Max should be one step above min if invalid or 0. + + if ( min > minValue ) { + minValue = min; + } + + if ( max <= minValue ) { + minValue = max - step; + } + + if ( min >= maxValue ) { + maxValue = min + step; + } + + if ( max < maxValue ) { + maxValue = max; + } + + if ( ! isMin && minValue >= maxValue ) { + minValue = maxValue - step; + } + + if ( isMin && maxValue <= minValue ) { + maxValue = minValue + step; + } + + return [ minValue, maxValue ]; +}; diff --git a/src/Assets.php b/src/Assets.php index 0c72236b426..4336e5d283c 100644 --- a/src/Assets.php +++ b/src/Assets.php @@ -64,40 +64,36 @@ public static function add_theme_body_class( $classes = array() ) { } /** - * These are used by @woocommerce/components & the block library to set up defaults + * These are used by @woocommerce/components and the block library to set up defaults * based on user-controlled settings from WordPress. Only use this in wp-admin. */ public static function print_script_wc_settings() { global $wp_locale; - $code = get_woocommerce_currency(); - - // NOTE: wcSettings is not used directly, it's only for @woocommerce/components - // - // Settings and variables can be passed here for access in the app. - // Will need `wcAdminAssetUrl` if the ImageAsset component is used. - // Will need `dataEndpoints.countries` if Search component is used with 'country' type. - // Will need `orderStatuses` if the OrderStatus component is used. - // Deliberately excluding: `embedBreadcrumbs`, `trackingEnabled`. - $settings = array( - 'adminUrl' => admin_url(), - 'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ), - 'siteLocale' => esc_attr( get_bloginfo( 'language' ) ), - 'currency' => array( - 'code' => $code, - 'precision' => wc_get_price_decimals(), - 'symbol' => get_woocommerce_currency_symbol( $code ), - 'position' => get_option( 'woocommerce_currency_pos' ), - ), - 'stockStatuses' => wc_get_product_stock_status_options(), - 'siteTitle' => get_bloginfo( 'name' ), - 'dataEndpoints' => array(), - 'l10n' => array( - 'userLocale' => get_user_locale(), - 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ), - ), + $code = get_woocommerce_currency(); + $settings = apply_filters( + 'woocommerce_components_settings', + array( + 'adminUrl' => admin_url(), + 'wcAssetUrl' => plugins_url( 'assets/', WC_PLUGIN_FILE ), + 'siteLocale' => esc_attr( get_bloginfo( 'language' ) ), + 'currency' => array( + 'code' => $code, + 'precision' => wc_get_price_decimals(), + 'symbol' => html_entity_decode( get_woocommerce_currency_symbol( $code ) ), + 'position' => get_option( 'woocommerce_currency_pos' ), + 'decimal_separator' => wc_get_price_decimal_separator(), + 'thousand_separator' => wc_get_price_thousand_separator(), + 'price_format' => html_entity_decode( get_woocommerce_price_format() ), + ), + 'stockStatuses' => wc_get_product_stock_status_options(), + 'siteTitle' => get_bloginfo( 'name' ), + 'dataEndpoints' => array(), + 'l10n' => array( + 'userLocale' => get_user_locale(), + 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ), + ), + ) ); - // NOTE: wcSettings is not used directly, it's only for @woocommerce/components. - $settings = apply_filters( 'woocommerce_components_settings', $settings ); ?>