Merge pull request woocommerce/woocommerce-blocks#57 from woocommerce/improve/specific
Specific Select improvements
This commit is contained in:
commit
4c3470ce18
|
@ -99,7 +99,8 @@ var _wp$components = wp.components,
|
|||
withAPIData = _wp$components.withAPIData,
|
||||
Dropdown = _wp$components.Dropdown,
|
||||
Dashicon = _wp$components.Dashicon,
|
||||
RangeControl = _wp$components.RangeControl;
|
||||
RangeControl = _wp$components.RangeControl,
|
||||
Tooltip = _wp$components.Tooltip;
|
||||
var ToggleControl = InspectorControls.ToggleControl,
|
||||
SelectControl = InspectorControls.SelectControl;
|
||||
|
||||
|
@ -452,6 +453,30 @@ var ProductsBlockSettingsEditor = function (_React$Component3) {
|
|||
);
|
||||
}
|
||||
|
||||
var done_button = wp.element.createElement(
|
||||
'button',
|
||||
{ type: 'button', className: 'button wc-products-settings__footer-button', onClick: this.props.done_callback },
|
||||
__('Done')
|
||||
);
|
||||
if (['', 'specific', 'category', 'attribute'].includes(this.state.display) && !this.props.selected_display_setting.length) {
|
||||
var done_tooltips = {
|
||||
'': __('Please select which products you\'d like to display'),
|
||||
specific: __('Please search for and select products to display'),
|
||||
category: __('Please select at least one category to display'),
|
||||
attribute: __('Please select an attribute')
|
||||
};
|
||||
|
||||
done_button = wp.element.createElement(
|
||||
Tooltip,
|
||||
{ text: done_tooltips[this.state.display] },
|
||||
wp.element.createElement(
|
||||
'button',
|
||||
{ type: 'button', className: 'button wc-products-settings__footer-button disabled' },
|
||||
__('Done')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-settings ' + (this.state.expanded_group ? 'expanded-group-' + this.state.expanded_group : '') },
|
||||
|
@ -468,11 +493,7 @@ var ProductsBlockSettingsEditor = function (_React$Component3) {
|
|||
wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-settings__footer' },
|
||||
wp.element.createElement(
|
||||
'button',
|
||||
{ type: 'button', className: 'button wc-products-settings__footer-button', onClick: this.props.done_callback },
|
||||
__('Done')
|
||||
)
|
||||
done_button
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -798,12 +819,20 @@ registerBlockType('woocommerce/products', {
|
|||
* @return Component
|
||||
*/
|
||||
function getSettingsEditor() {
|
||||
|
||||
var update_display_callback = function update_display_callback(value) {
|
||||
if (display !== value) {
|
||||
setAttributes({
|
||||
display: value,
|
||||
display_setting: []
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return wp.element.createElement(ProductsBlockSettingsEditor, {
|
||||
selected_display: display,
|
||||
selected_display_setting: display_setting,
|
||||
update_display_callback: function update_display_callback(value) {
|
||||
return setAttributes({ display: value });
|
||||
},
|
||||
update_display_callback: update_display_callback,
|
||||
update_display_setting_callback: function update_display_setting_callback(value) {
|
||||
return setAttributes({ display_setting: value });
|
||||
},
|
||||
|
@ -843,7 +872,7 @@ registerBlockType('woocommerce/products', {
|
|||
}
|
||||
|
||||
if ('specific' === display) {
|
||||
shortcode_atts.set('include', display_setting.join(','));
|
||||
shortcode_atts.set('ids', display_setting.join(','));
|
||||
} else if ('category' === display) {
|
||||
shortcode_atts.set('category', display_setting.join(','));
|
||||
} else if ('featured' === display) {
|
||||
|
@ -935,6 +964,13 @@ var _ReactTransitionGroup = ReactTransitionGroup,
|
|||
TransitionGroup = _ReactTransitionGroup.TransitionGroup,
|
||||
CSSTransition = _ReactTransitionGroup.CSSTransition;
|
||||
|
||||
/**
|
||||
* Product data cache.
|
||||
* Reduces the number of API calls and makes UI smoother and faster.
|
||||
*/
|
||||
|
||||
var PRODUCT_DATA = {};
|
||||
|
||||
/**
|
||||
* When the display mode is 'Specific products' search for and add products to the block.
|
||||
*
|
||||
|
@ -1046,7 +1082,7 @@ var ProductsSpecificSelect = exports.ProductsSpecificSelect = function (_React$C
|
|||
selectedProducts: this.state.selectedProducts
|
||||
}),
|
||||
wp.element.createElement(ProductSpecificSelectedProducts, {
|
||||
products: this.state.selectedProducts,
|
||||
productIds: this.state.selectedProducts,
|
||||
removeProductCallback: this.removeProduct.bind(this)
|
||||
})
|
||||
);
|
||||
|
@ -1200,6 +1236,32 @@ var ProductSpecificSearchResults = withAPIData(function (props) {
|
|||
return __('No products found');
|
||||
}
|
||||
|
||||
// Populate the cache.
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = products.data[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var product = _step2.value;
|
||||
|
||||
PRODUCT_DATA[product.id] = product;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wp.element.createElement(ProductSpecificSearchResultsDropdown, {
|
||||
products: products.data,
|
||||
addProductCallback: addProductCallback,
|
||||
|
@ -1236,13 +1298,13 @@ var ProductSpecificSearchResultsDropdown = function (_React$Component3) {
|
|||
|
||||
var productElements = [];
|
||||
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
var _iteratorNormalCompletion3 = true;
|
||||
var _didIteratorError3 = false;
|
||||
var _iteratorError3 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = products[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var product = _step2.value;
|
||||
for (var _iterator3 = products[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||
var product = _step3.value;
|
||||
|
||||
if (selectedProducts.includes(product.id)) {
|
||||
continue;
|
||||
|
@ -1262,16 +1324,16 @@ var ProductSpecificSearchResultsDropdown = function (_React$Component3) {
|
|||
));
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
_didIteratorError3 = true;
|
||||
_iteratorError3 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
||||
_iterator2.return();
|
||||
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
||||
_iterator3.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
if (_didIteratorError3) {
|
||||
throw _iteratorError3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1367,27 +1429,146 @@ var ProductSpecificSearchResultsDropdownElement = function (_React$Component4) {
|
|||
|
||||
var ProductSpecificSelectedProducts = withAPIData(function (props) {
|
||||
|
||||
if (!props.products.length) {
|
||||
if (!props.productIds.length) {
|
||||
return {
|
||||
products: []
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
products: '/wc/v2/products?include=' + props.products.join(',')
|
||||
};
|
||||
})(function (_ref2) {
|
||||
var products = _ref2.products,
|
||||
removeProductCallback = _ref2.removeProductCallback;
|
||||
// Determine which products are not already in the cache and only fetch uncached products.
|
||||
var uncachedProducts = [];
|
||||
var _iteratorNormalCompletion4 = true;
|
||||
var _didIteratorError4 = false;
|
||||
var _iteratorError4 = undefined;
|
||||
|
||||
if (!products.data) {
|
||||
return null;
|
||||
try {
|
||||
for (var _iterator4 = props.productIds[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
||||
var productId = _step4.value;
|
||||
|
||||
if (!PRODUCT_DATA.hasOwnProperty(productId)) {
|
||||
uncachedProducts.push(productId);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError4 = true;
|
||||
_iteratorError4 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
||||
_iterator4.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError4) {
|
||||
throw _iteratorError4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === products.data.length) {
|
||||
return {
|
||||
products: uncachedProducts.length ? '/wc/v2/products?include=' + uncachedProducts.join(',') : []
|
||||
};
|
||||
})(function (_ref2) {
|
||||
var productIds = _ref2.productIds,
|
||||
products = _ref2.products,
|
||||
removeProductCallback = _ref2.removeProductCallback;
|
||||
|
||||
|
||||
// Add new products to cache.
|
||||
if (products.data) {
|
||||
var _iteratorNormalCompletion5 = true;
|
||||
var _didIteratorError5 = false;
|
||||
var _iteratorError5 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator5 = products.data[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
||||
var product = _step5.value;
|
||||
|
||||
PRODUCT_DATA[product.id] = product;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError5 = true;
|
||||
_iteratorError5 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
||||
_iterator5.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError5) {
|
||||
throw _iteratorError5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === productIds.length) {
|
||||
return __('No products selected');
|
||||
}
|
||||
|
||||
var productElements = [];
|
||||
|
||||
var _loop = function _loop(productId) {
|
||||
|
||||
// Skip products that aren't in the cache yet or failed to fetch.
|
||||
if (!PRODUCT_DATA.hasOwnProperty(productId)) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
var productData = PRODUCT_DATA[productId];
|
||||
|
||||
productElements.push(wp.element.createElement(
|
||||
'li',
|
||||
{ className: 'wc-products-list-card__item' },
|
||||
wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-list-card__content' },
|
||||
wp.element.createElement('img', { src: productData.images[0].src }),
|
||||
wp.element.createElement(
|
||||
'span',
|
||||
{ className: 'wc-products-list-card__content-item-name' },
|
||||
productData.name
|
||||
),
|
||||
wp.element.createElement(
|
||||
'button',
|
||||
{
|
||||
type: 'button',
|
||||
id: 'product-' + productData.id,
|
||||
onClick: function onClick() {
|
||||
removeProductCallback(productData.id);
|
||||
} },
|
||||
wp.element.createElement(Dashicon, { icon: 'no-alt' })
|
||||
)
|
||||
)
|
||||
));
|
||||
};
|
||||
|
||||
var _iteratorNormalCompletion6 = true;
|
||||
var _didIteratorError6 = false;
|
||||
var _iteratorError6 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator6 = productIds[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
|
||||
var productId = _step6.value;
|
||||
|
||||
var _ret = _loop(productId);
|
||||
|
||||
if (_ret === 'continue') continue;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError6 = true;
|
||||
_iteratorError6 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion6 && _iterator6.return) {
|
||||
_iterator6.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError6) {
|
||||
throw _iteratorError6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-list-card__results-wrapper' },
|
||||
|
@ -1397,32 +1578,7 @@ var ProductSpecificSelectedProducts = withAPIData(function (props) {
|
|||
wp.element.createElement(
|
||||
'ul',
|
||||
null,
|
||||
products.data.map(function (product) {
|
||||
return wp.element.createElement(
|
||||
'li',
|
||||
{ className: 'wc-products-list-card__item' },
|
||||
wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-list-card__content' },
|
||||
wp.element.createElement('img', { src: product.images[0].src }),
|
||||
wp.element.createElement(
|
||||
'span',
|
||||
{ className: 'wc-products-list-card__content-item-name' },
|
||||
product.name
|
||||
),
|
||||
wp.element.createElement(
|
||||
'button',
|
||||
{
|
||||
type: 'button',
|
||||
id: 'product-' + product.id,
|
||||
onClick: function onClick() {
|
||||
removeProductCallback(product.id);
|
||||
} },
|
||||
wp.element.createElement(Dashicon, { icon: 'no-alt' })
|
||||
)
|
||||
)
|
||||
);
|
||||
})
|
||||
productElements
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const { __ } = wp.i18n;
|
||||
const { RawHTML } = wp.element;
|
||||
const { registerBlockType, InspectorControls, BlockControls } = wp.blocks;
|
||||
const { Toolbar, withAPIData, Dropdown, Dashicon, RangeControl } = wp.components;
|
||||
const { Toolbar, withAPIData, Dropdown, Dashicon, RangeControl, Tooltip } = wp.components;
|
||||
const { ToggleControl, SelectControl } = InspectorControls;
|
||||
|
||||
import { ProductsSpecificSelect } from './views/specific-select.jsx';
|
||||
|
@ -267,6 +267,23 @@ class ProductsBlockSettingsEditor extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
let done_button = <button type="button" className="button wc-products-settings__footer-button" onClick={ this.props.done_callback }>{ __( 'Done' ) }</button>;
|
||||
if ( ['', 'specific', 'category', 'attribute'].includes( this.state.display ) && ! this.props.selected_display_setting.length ) {
|
||||
const done_tooltips = {
|
||||
'': __( 'Please select which products you\'d like to display' ),
|
||||
specific: __( 'Please search for and select products to display' ),
|
||||
category: __( 'Please select at least one category to display' ),
|
||||
attribute: __( 'Please select an attribute' ),
|
||||
}
|
||||
|
||||
done_button = (
|
||||
<Tooltip text={ done_tooltips[ this.state.display ] } >
|
||||
<button type="button" className="button wc-products-settings__footer-button disabled">{ __( 'Done' ) }</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={ 'wc-products-settings ' + ( this.state.expanded_group ? 'expanded-group-' + this.state.expanded_group : '' ) }>
|
||||
<h4 className="wc-products-settings__title"><Dashicon icon={ 'universal-access-alt' } /> { __( 'Products' ) }</h4>
|
||||
|
@ -278,7 +295,7 @@ class ProductsBlockSettingsEditor extends React.Component {
|
|||
{ extra_settings }
|
||||
|
||||
<div className="wc-products-settings__footer">
|
||||
<button type="button" className="button wc-products-settings__footer-button" onClick={ this.props.done_callback }>{ __( 'Done' ) }</button>
|
||||
{ done_button }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -526,11 +543,21 @@ registerBlockType( 'woocommerce/products', {
|
|||
* @return Component
|
||||
*/
|
||||
function getSettingsEditor() {
|
||||
|
||||
const update_display_callback = ( value ) => {
|
||||
if ( display !== value ) {
|
||||
setAttributes( {
|
||||
display: value,
|
||||
display_setting: [],
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ProductsBlockSettingsEditor
|
||||
selected_display={ display }
|
||||
selected_display_setting={ display_setting }
|
||||
update_display_callback={ ( value ) => setAttributes( { display: value } ) }
|
||||
update_display_callback={ update_display_callback }
|
||||
update_display_setting_callback={ ( value ) => setAttributes( { display_setting: value } ) }
|
||||
done_callback={ () => setAttributes( { edit_mode: false } ) }
|
||||
/>
|
||||
|
@ -564,7 +591,7 @@ registerBlockType( 'woocommerce/products', {
|
|||
}
|
||||
|
||||
if ( 'specific' === display ) {
|
||||
shortcode_atts.set( 'include', display_setting.join( ',' ) );
|
||||
shortcode_atts.set( 'ids', display_setting.join( ',' ) );
|
||||
} else if ( 'category' === display ) {
|
||||
shortcode_atts.set( 'category', display_setting.join( ',' ) );
|
||||
} else if ( 'featured' === display ) {
|
||||
|
|
|
@ -2,6 +2,12 @@ const { __ } = wp.i18n;
|
|||
const { Toolbar, withAPIData, Dropdown, Dashicon } = wp.components;
|
||||
const { TransitionGroup, CSSTransition } = ReactTransitionGroup;
|
||||
|
||||
/**
|
||||
* Product data cache.
|
||||
* Reduces the number of API calls and makes UI smoother and faster.
|
||||
*/
|
||||
const PRODUCT_DATA = {};
|
||||
|
||||
/**
|
||||
* When the display mode is 'Specific products' search for and add products to the block.
|
||||
*
|
||||
|
@ -76,7 +82,7 @@ export class ProductsSpecificSelect extends React.Component {
|
|||
selectedProducts={ this.state.selectedProducts }
|
||||
/>
|
||||
<ProductSpecificSelectedProducts
|
||||
products={ this.state.selectedProducts }
|
||||
productIds={ this.state.selectedProducts }
|
||||
removeProductCallback={ this.removeProduct.bind( this ) }
|
||||
/>
|
||||
</div>
|
||||
|
@ -194,6 +200,11 @@ const ProductSpecificSearchResults = withAPIData( ( props ) => {
|
|||
return __( 'No products found' );
|
||||
}
|
||||
|
||||
// Populate the cache.
|
||||
for ( let product of products.data ) {
|
||||
PRODUCT_DATA[ product.id ] = product;
|
||||
}
|
||||
|
||||
return <ProductSpecificSearchResultsDropdown
|
||||
products={ products.data }
|
||||
addProductCallback={ addProductCallback }
|
||||
|
@ -297,42 +308,68 @@ class ProductSpecificSearchResultsDropdownElement extends React.Component {
|
|||
*/
|
||||
const ProductSpecificSelectedProducts = withAPIData( ( props ) => {
|
||||
|
||||
if ( ! props.products.length ) {
|
||||
if ( ! props.productIds.length ) {
|
||||
return {
|
||||
products: []
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
products: '/wc/v2/products?include=' + props.products.join( ',' )
|
||||
};
|
||||
} )( ( { products, removeProductCallback } ) => {
|
||||
if ( ! products.data ) {
|
||||
return null;
|
||||
// Determine which products are not already in the cache and only fetch uncached products.
|
||||
let uncachedProducts = [];
|
||||
for( const productId of props.productIds ) {
|
||||
if ( ! PRODUCT_DATA.hasOwnProperty( productId ) ) {
|
||||
uncachedProducts.push( productId );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 0 === products.data.length ) {
|
||||
return {
|
||||
products: uncachedProducts.length ? '/wc/v2/products?include=' + uncachedProducts.join( ',' ) : []
|
||||
};
|
||||
} )( ( { productIds, products, removeProductCallback } ) => {
|
||||
|
||||
// Add new products to cache.
|
||||
if ( products.data ) {
|
||||
for ( const product of products.data ) {
|
||||
PRODUCT_DATA[ product.id ] = product;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 0 === productIds.length ) {
|
||||
return __( 'No products selected' );
|
||||
}
|
||||
|
||||
const productElements = [];
|
||||
|
||||
for ( const productId of productIds ) {
|
||||
|
||||
// Skip products that aren't in the cache yet or failed to fetch.
|
||||
if ( ! PRODUCT_DATA.hasOwnProperty( productId ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const productData = PRODUCT_DATA[ productId ];
|
||||
|
||||
productElements.push(
|
||||
<li className="wc-products-list-card__item">
|
||||
<div className="wc-products-list-card__content">
|
||||
<img src={ productData.images[0].src } />
|
||||
<span className="wc-products-list-card__content-item-name">{ productData.name }</span>
|
||||
<button
|
||||
type="button"
|
||||
id={ 'product-' + productData.id }
|
||||
onClick={ function() { removeProductCallback( productData.id ) } } >
|
||||
<Dashicon icon={ 'no-alt' } />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="wc-products-list-card__results-wrapper">
|
||||
<div role="menu" className="wc-products-list-card__results" aria-orientation="vertical" aria-label="{ __( 'Products list' ) }">
|
||||
<ul>
|
||||
{ products.data.map( ( product ) => (
|
||||
<li className="wc-products-list-card__item">
|
||||
<div className="wc-products-list-card__content">
|
||||
<img src={ product.images[0].src } />
|
||||
<span className="wc-products-list-card__content-item-name">{ product.name }</span>
|
||||
<button
|
||||
type="button"
|
||||
id={ 'product-' + product.id }
|
||||
onClick={ function() { removeProductCallback( product.id ) } } >
|
||||
<Dashicon icon={ 'no-alt' } />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
) ) }
|
||||
{ productElements }
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue