Merge pull request woocommerce/woocommerce-blocks#89 from woocommerce/update/individual-products
Changes to Individual Products
This commit is contained in:
commit
04ebea5184
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -334,19 +334,36 @@ p.wc-products-display-options__option-description {
|
|||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
|
||||
.wc-products-list-card__search {
|
||||
width: 100%;
|
||||
.wc-products-list-card__input-wrapper {
|
||||
position: relative;
|
||||
background: #ffffff;
|
||||
margin: 0 0 1em;
|
||||
padding: .75em 1.25em;
|
||||
|
||||
.dashicon {
|
||||
position: absolute;
|
||||
top: calc( 1em - 1px );
|
||||
left: 1em;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-products-list-card__search {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 1em 1.25em 1em 3em;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
border-color: $color__border;
|
||||
box-shadow: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.wc-products-list-card__results {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0 1em;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
|
@ -571,20 +588,25 @@ p.wc-products-display-options__option-description {
|
|||
.wc-products-list-card--specific {
|
||||
overflow: visible;
|
||||
|
||||
.wc-products-list-card__content {
|
||||
padding: .75em;
|
||||
border-bottom-width: 0;
|
||||
&:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.wc-products-list-card__item {
|
||||
position: relative;
|
||||
border: none;
|
||||
|
||||
img {
|
||||
max-width: 2.5em;
|
||||
margin: 0 1em 0 0;
|
||||
margin: 0;
|
||||
outline: 4px solid $color__link--hover;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #f1f1f1;
|
||||
border-radius: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: $color__link--hover;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
|
@ -594,54 +616,138 @@ p.wc-products-display-options__option-description {
|
|||
}
|
||||
|
||||
.dashicon {
|
||||
color: #555D66;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-products-list-card__input-wrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.wc-products-list-card__results-wrapper {
|
||||
@for $i from 1 through 6 {
|
||||
$width: percentage( 1 / $i );
|
||||
|
||||
&--cols-#{$i} {
|
||||
.wc-products-list-card__item {
|
||||
width: $width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wc-products-list-card__results {
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 1em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -.5em -1em;
|
||||
|
||||
li {
|
||||
border: none;
|
||||
padding: 0 .5em;
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-products-list-card__content {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wc-products-list-card__search-wrapper {
|
||||
position: relative;
|
||||
margin: 0 0 1.5em;
|
||||
}
|
||||
|
||||
.wc-products-list-card__search-results {
|
||||
position: absolute;
|
||||
top: calc(100% - 1em - 1px);
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
list-style: none;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.1);
|
||||
margin: -1px 0 0;
|
||||
border: 1px solid $color__border;
|
||||
box-shadow: 0 1px 3px $color__border;
|
||||
|
||||
> div {
|
||||
max-height: 200px;
|
||||
max-height: 175px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.wc-products-list-card__content {
|
||||
position: relative;
|
||||
border-width: 1px 0 0;
|
||||
border-style: solid;
|
||||
border-color: $color__border;
|
||||
transition: opacity .7s;
|
||||
cursor: pointer;
|
||||
color: $color__link--hover;
|
||||
|
||||
&--added {
|
||||
background-color: lighten( $color__link, 65% );
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: lighten( $color__link, 65% );
|
||||
}
|
||||
|
||||
&--transition-exit-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-width: 1px;
|
||||
&:first-child {
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 2.5em;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
margin: 0 1em 0 0;
|
||||
}
|
||||
|
||||
button {
|
||||
.dashicon {
|
||||
color: $color__link;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wc-products-list-card__search-wrapper--with-results +
|
||||
.wc-products-list-card__results-wrapper {
|
||||
.wc-products-list-card__item {
|
||||
img {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wc-products-list-card__search-no-results {
|
||||
display: block;
|
||||
margin: 1em 0 0;
|
||||
}
|
||||
|
||||
.wc-products-list-card__search-no-selected {
|
||||
display: block;
|
||||
margin: -.75em 0 0;
|
||||
}
|
||||
|
||||
.wc-products-list-card__results-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -569,7 +569,7 @@ var ProductsBlockPreview = withAPIData(function (_ref) {
|
|||
|
||||
if ('specific' === display) {
|
||||
query.include = display_setting.join(',');
|
||||
query.orderby = 'include';
|
||||
query.per_page = display_setting.length;
|
||||
} else if ('category' === display) {
|
||||
query.category = display_setting.join(',');
|
||||
} else if ('attribute' === display && display_setting.length) {
|
||||
|
@ -585,7 +585,7 @@ var ProductsBlockPreview = withAPIData(function (_ref) {
|
|||
}
|
||||
|
||||
// @todo Add support for orderby by sales, rating, and price to the API.
|
||||
if ('specific' !== display && ('title' === orderby || 'date' === orderby)) {
|
||||
if ('title' === orderby || 'date' === orderby) {
|
||||
query.orderby = orderby;
|
||||
|
||||
if ('title' === orderby) {
|
||||
|
@ -822,35 +822,45 @@ var ProductsBlock = function (_React$Component5) {
|
|||
max: wc_product_block_data.max_columns
|
||||
});
|
||||
|
||||
// Orderby settings don't make sense for specific-selected products display.
|
||||
var orderControl = null;
|
||||
var orderControl = wp.element.createElement(SelectControl, {
|
||||
key: 'query-panel-select',
|
||||
label: __('Order Products By'),
|
||||
value: orderby,
|
||||
options: [{
|
||||
label: __('Newness - newest first'),
|
||||
value: 'date'
|
||||
}, {
|
||||
label: __('Price - low to high'),
|
||||
value: 'price_asc'
|
||||
}, {
|
||||
label: __('Price - high to low'),
|
||||
value: 'price_desc'
|
||||
}, {
|
||||
label: __('Rating - highest first'),
|
||||
value: 'rating'
|
||||
}, {
|
||||
label: __('Sales - most first'),
|
||||
value: 'popularity'
|
||||
}, {
|
||||
label: __('Title - alphabetical'),
|
||||
value: 'title'
|
||||
}],
|
||||
onChange: function onChange(value) {
|
||||
return setAttributes({ orderby: value });
|
||||
}
|
||||
});
|
||||
|
||||
// Row settings don't make sense for specific-selected products display.
|
||||
var rowControl = null;
|
||||
if ('specific' !== display) {
|
||||
orderControl = wp.element.createElement(SelectControl, {
|
||||
key: 'query-panel-select',
|
||||
label: __('Order Products By'),
|
||||
value: orderby,
|
||||
options: [{
|
||||
label: __('Newness - newest first'),
|
||||
value: 'date'
|
||||
}, {
|
||||
label: __('Price - low to high'),
|
||||
value: 'price_asc'
|
||||
}, {
|
||||
label: __('Price - high to low'),
|
||||
value: 'price_desc'
|
||||
}, {
|
||||
label: __('Rating - highest first'),
|
||||
value: 'rating'
|
||||
}, {
|
||||
label: __('Sales - most first'),
|
||||
value: 'popularity'
|
||||
}, {
|
||||
label: __('Title - alphabetical'),
|
||||
value: 'title'
|
||||
}],
|
||||
rowControl = wp.element.createElement(RangeControl, {
|
||||
label: __('Rows'),
|
||||
value: rows,
|
||||
onChange: function onChange(value) {
|
||||
return setAttributes({ orderby: value });
|
||||
}
|
||||
return setAttributes({ rows: value });
|
||||
},
|
||||
min: wc_product_block_data.min_rows,
|
||||
max: wc_product_block_data.max_rows
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -864,15 +874,7 @@ var ProductsBlock = function (_React$Component5) {
|
|||
__('Layout')
|
||||
),
|
||||
columnControl,
|
||||
wp.element.createElement(RangeControl, {
|
||||
label: __('Rows'),
|
||||
value: rows,
|
||||
onChange: function onChange(value) {
|
||||
return setAttributes({ rows: value });
|
||||
},
|
||||
min: wc_product_block_data.min_rows,
|
||||
max: wc_product_block_data.max_rows
|
||||
}),
|
||||
rowControl,
|
||||
orderControl
|
||||
);
|
||||
}
|
||||
|
@ -1015,6 +1017,7 @@ var ProductsBlock = function (_React$Component5) {
|
|||
};
|
||||
|
||||
return wp.element.createElement(ProductsBlockSettingsEditor, {
|
||||
attributes: attributes,
|
||||
selected_display: display,
|
||||
selected_display_setting: display_setting,
|
||||
update_display_callback: update_display_callback,
|
||||
|
@ -1127,7 +1130,9 @@ registerBlockType('woocommerce/products', {
|
|||
|
||||
|
||||
var shortcode_atts = new Map();
|
||||
shortcode_atts.set('limit', rows * columns);
|
||||
if ('specific' !== display) {
|
||||
shortcode_atts.set('limit', rows * columns);
|
||||
}
|
||||
shortcode_atts.set('columns', columns);
|
||||
|
||||
if ('specific' === display) {
|
||||
|
@ -1227,9 +1232,6 @@ var _wp$components = wp.components,
|
|||
withAPIData = _wp$components.withAPIData,
|
||||
Dropdown = _wp$components.Dropdown,
|
||||
Dashicon = _wp$components.Dashicon;
|
||||
var _ReactTransitionGroup = ReactTransitionGroup,
|
||||
TransitionGroup = _ReactTransitionGroup.TransitionGroup,
|
||||
CSSTransition = _ReactTransitionGroup.CSSTransition;
|
||||
|
||||
/**
|
||||
* Product data cache.
|
||||
|
@ -1269,11 +1271,17 @@ var ProductsSpecificSelect = exports.ProductsSpecificSelect = function (_React$C
|
|||
|
||||
|
||||
_createClass(ProductsSpecificSelect, [{
|
||||
key: 'addProduct',
|
||||
value: function addProduct(id) {
|
||||
|
||||
key: 'addOrRemoveProduct',
|
||||
value: function addOrRemoveProduct(id) {
|
||||
var selectedProducts = this.state.selectedProducts;
|
||||
selectedProducts.push(id);
|
||||
|
||||
if (!selectedProducts.includes(id)) {
|
||||
selectedProducts.push(id);
|
||||
} else {
|
||||
selectedProducts = selectedProducts.filter(function (product) {
|
||||
return product !== id;
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedProducts: selectedProducts
|
||||
|
@ -1288,52 +1296,6 @@ var ProductsSpecificSelect = exports.ProductsSpecificSelect = function (_React$C
|
|||
this.props.update_display_setting_callback(selectedProducts.slice());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a product from the list of selected products.
|
||||
*
|
||||
* @param id int Product ID.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'removeProduct',
|
||||
value: function removeProduct(id) {
|
||||
var oldProducts = this.state.selectedProducts;
|
||||
var newProducts = [];
|
||||
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = oldProducts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var productId = _step.value;
|
||||
|
||||
if (productId !== id) {
|
||||
newProducts.push(productId);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedProducts: newProducts
|
||||
});
|
||||
|
||||
this.props.update_display_setting_callback(newProducts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the product specific select screen.
|
||||
*/
|
||||
|
@ -1345,12 +1307,13 @@ var ProductsSpecificSelect = exports.ProductsSpecificSelect = function (_React$C
|
|||
'div',
|
||||
{ className: 'wc-products-list-card wc-products-list-card--specific' },
|
||||
wp.element.createElement(ProductsSpecificSearchField, {
|
||||
addProductCallback: this.addProduct.bind(this),
|
||||
addOrRemoveProductCallback: this.addOrRemoveProduct.bind(this),
|
||||
selectedProducts: this.state.selectedProducts
|
||||
}),
|
||||
wp.element.createElement(ProductSpecificSelectedProducts, {
|
||||
columns: this.props.attributes.columns,
|
||||
productIds: this.state.selectedProducts,
|
||||
removeProductCallback: this.removeProduct.bind(this)
|
||||
addOrRemoveProduct: this.addOrRemoveProduct.bind(this)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -1376,12 +1339,14 @@ var ProductsSpecificSearchField = function (_React$Component2) {
|
|||
var _this2 = _possibleConstructorReturn(this, (ProductsSpecificSearchField.__proto__ || Object.getPrototypeOf(ProductsSpecificSearchField)).call(this, props));
|
||||
|
||||
_this2.state = {
|
||||
searchText: ''
|
||||
searchText: '',
|
||||
dropdownOpen: false
|
||||
};
|
||||
|
||||
_this2.updateSearchResults = _this2.updateSearchResults.bind(_this2);
|
||||
_this2.setWrapperRef = _this2.setWrapperRef.bind(_this2);
|
||||
_this2.handleClickOutside = _this2.handleClickOutside.bind(_this2);
|
||||
_this2.isDropdownOpen = _this2.isDropdownOpen.bind(_this2);
|
||||
return _this2;
|
||||
}
|
||||
|
||||
|
@ -1431,6 +1396,13 @@ var ProductsSpecificSearchField = function (_React$Component2) {
|
|||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'isDropdownOpen',
|
||||
value: function isDropdownOpen(isOpen) {
|
||||
this.setState({
|
||||
dropdownOpen: !!isOpen
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for updating results when text is typed into the input.
|
||||
|
@ -1453,19 +1425,27 @@ var ProductsSpecificSearchField = function (_React$Component2) {
|
|||
}, {
|
||||
key: 'render',
|
||||
value: function render() {
|
||||
var divClass = 'wc-products-list-card__search-wrapper';
|
||||
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-list-card__search-wrapper', ref: this.setWrapperRef },
|
||||
wp.element.createElement('input', { type: 'search',
|
||||
className: 'wc-products-list-card__search',
|
||||
value: this.state.searchText,
|
||||
placeholder: __('Search for products to display'),
|
||||
onChange: this.updateSearchResults
|
||||
}),
|
||||
{ className: divClass + (this.state.dropdownOpen ? ' ' + divClass + '--with-results' : ''), ref: this.setWrapperRef },
|
||||
wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-list-card__input-wrapper' },
|
||||
wp.element.createElement(Dashicon, { icon: 'search' }),
|
||||
wp.element.createElement('input', { type: 'search',
|
||||
className: 'wc-products-list-card__search',
|
||||
value: this.state.searchText,
|
||||
placeholder: __('Search for products to display'),
|
||||
onChange: this.updateSearchResults
|
||||
})
|
||||
),
|
||||
wp.element.createElement(ProductSpecificSearchResults, {
|
||||
searchString: this.state.searchText,
|
||||
addProductCallback: this.props.addProductCallback,
|
||||
selectedProducts: this.props.selectedProducts
|
||||
addOrRemoveProductCallback: this.props.addOrRemoveProductCallback,
|
||||
selectedProducts: this.props.selectedProducts,
|
||||
isDropdownOpenCallback: this.isDropdownOpen
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -1492,47 +1472,55 @@ var ProductSpecificSearchResults = withAPIData(function (props) {
|
|||
};
|
||||
})(function (_ref) {
|
||||
var products = _ref.products,
|
||||
addProductCallback = _ref.addProductCallback,
|
||||
selectedProducts = _ref.selectedProducts;
|
||||
addOrRemoveProductCallback = _ref.addOrRemoveProductCallback,
|
||||
selectedProducts = _ref.selectedProducts,
|
||||
isDropdownOpenCallback = _ref.isDropdownOpenCallback;
|
||||
|
||||
if (!products.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (0 === products.data.length) {
|
||||
return __('No products found');
|
||||
return wp.element.createElement(
|
||||
'span',
|
||||
{ className: 'wc-products-list-card__search-no-results' },
|
||||
' ',
|
||||
__('No products found'),
|
||||
' '
|
||||
);
|
||||
}
|
||||
|
||||
// Populate the cache.
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = products.data[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var product = _step2.value;
|
||||
for (var _iterator = products.data[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var product = _step.value;
|
||||
|
||||
PRODUCT_DATA[product.id] = product;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
||||
_iterator2.return();
|
||||
if (!_iteratorNormalCompletion && _iterator.return) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wp.element.createElement(ProductSpecificSearchResultsDropdown, {
|
||||
products: products.data,
|
||||
addProductCallback: addProductCallback,
|
||||
selectedProducts: selectedProducts
|
||||
addOrRemoveProductCallback: addOrRemoveProductCallback,
|
||||
selectedProducts: selectedProducts,
|
||||
isDropdownOpenCallback: isDropdownOpenCallback
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1550,66 +1538,75 @@ var ProductSpecificSearchResultsDropdown = function (_React$Component3) {
|
|||
}
|
||||
|
||||
_createClass(ProductSpecificSearchResultsDropdown, [{
|
||||
key: 'render',
|
||||
key: 'componentDidMount',
|
||||
|
||||
|
||||
/**
|
||||
* Set the state of the dropdown to open.
|
||||
*/
|
||||
value: function componentDidMount() {
|
||||
this.props.isDropdownOpenCallback(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of the dropdown to closed.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'componentWillUnmount',
|
||||
value: function componentWillUnmount() {
|
||||
this.props.isDropdownOpenCallback(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render dropdown.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'render',
|
||||
value: function render() {
|
||||
var _props = this.props,
|
||||
products = _props.products,
|
||||
addProductCallback = _props.addProductCallback,
|
||||
addOrRemoveProductCallback = _props.addOrRemoveProductCallback,
|
||||
selectedProducts = _props.selectedProducts;
|
||||
|
||||
|
||||
var productElements = [];
|
||||
|
||||
var _iteratorNormalCompletion3 = true;
|
||||
var _didIteratorError3 = false;
|
||||
var _iteratorError3 = undefined;
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator3 = products[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||
var product = _step3.value;
|
||||
for (var _iterator2 = products[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var product = _step2.value;
|
||||
|
||||
if (selectedProducts.includes(product.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
productElements.push(wp.element.createElement(
|
||||
CSSTransition,
|
||||
{
|
||||
key: product.slug,
|
||||
classNames: 'wc-products-list-card__content--transition',
|
||||
timeout: { exit: 700 }
|
||||
},
|
||||
wp.element.createElement(ProductSpecificSearchResultsDropdownElement, {
|
||||
product: product,
|
||||
addProductCallback: addProductCallback
|
||||
})
|
||||
));
|
||||
productElements.push(wp.element.createElement(ProductSpecificSearchResultsDropdownElement, {
|
||||
product: product,
|
||||
addOrRemoveProductCallback: addOrRemoveProductCallback,
|
||||
selected: selectedProducts.includes(product.id)
|
||||
}));
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError3 = true;
|
||||
_iteratorError3 = err;
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
||||
_iterator3.return();
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError3) {
|
||||
throw _iteratorError3;
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
{ role: 'menu', className: 'wc-products-list-card__search-results', 'aria-orientation': 'vertical', 'aria-label': '{ __( \'Products list\' ) }' },
|
||||
{ role: 'menu', className: 'wc-products-list-card__search-results', 'aria-orientation': 'vertical', 'aria-label': __('Products list') },
|
||||
wp.element.createElement(
|
||||
TransitionGroup,
|
||||
'div',
|
||||
null,
|
||||
productElements
|
||||
)
|
||||
|
@ -1636,10 +1633,6 @@ var ProductSpecificSearchResultsDropdownElement = function (_React$Component4) {
|
|||
|
||||
var _this4 = _possibleConstructorReturn(this, (ProductSpecificSearchResultsDropdownElement.__proto__ || Object.getPrototypeOf(ProductSpecificSearchResultsDropdownElement)).call(this, props));
|
||||
|
||||
_this4.state = {
|
||||
clicked: false
|
||||
};
|
||||
|
||||
_this4.handleClick = _this4.handleClick.bind(_this4);
|
||||
return _this4;
|
||||
}
|
||||
|
@ -1652,8 +1645,7 @@ var ProductSpecificSearchResultsDropdownElement = function (_React$Component4) {
|
|||
_createClass(ProductSpecificSearchResultsDropdownElement, [{
|
||||
key: 'handleClick',
|
||||
value: function handleClick() {
|
||||
this.setState({ clicked: true });
|
||||
this.props.addProductCallback(this.props.product.id);
|
||||
this.props.addOrRemoveProductCallback(this.props.product.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1664,24 +1656,18 @@ var ProductSpecificSearchResultsDropdownElement = function (_React$Component4) {
|
|||
key: 'render',
|
||||
value: function render() {
|
||||
var product = this.props.product;
|
||||
var icon = this.props.selected ? wp.element.createElement(Dashicon, { icon: 'yes' }) : null;
|
||||
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-list-card__content' },
|
||||
{ className: 'wc-products-list-card__content' + (this.props.selected ? ' wc-products-list-card__content--added' : ''), onClick: this.handleClick },
|
||||
wp.element.createElement('img', { src: product.images[0].src }),
|
||||
wp.element.createElement(
|
||||
'span',
|
||||
{ className: 'wc-products-list-card__content-item-name' },
|
||||
this.state.clicked ? __('Added') : product.name
|
||||
product.name
|
||||
),
|
||||
wp.element.createElement(
|
||||
'button',
|
||||
{ type: 'button',
|
||||
className: 'button-link',
|
||||
id: 'product-' + product.id,
|
||||
onClick: this.handleClick },
|
||||
__('Add')
|
||||
)
|
||||
icon
|
||||
);
|
||||
}
|
||||
}]);
|
||||
|
@ -1695,7 +1681,6 @@ var ProductSpecificSearchResultsDropdownElement = function (_React$Component4) {
|
|||
|
||||
|
||||
var ProductSpecificSelectedProducts = withAPIData(function (props) {
|
||||
|
||||
if (!props.productIds.length) {
|
||||
return {
|
||||
products: []
|
||||
|
@ -1704,29 +1689,29 @@ var ProductSpecificSelectedProducts = withAPIData(function (props) {
|
|||
|
||||
// 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;
|
||||
var _iteratorNormalCompletion3 = true;
|
||||
var _didIteratorError3 = false;
|
||||
var _iteratorError3 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator4 = props.productIds[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
||||
var productId = _step4.value;
|
||||
for (var _iterator3 = props.productIds[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||
var productId = _step3.value;
|
||||
|
||||
if (!PRODUCT_DATA.hasOwnProperty(productId)) {
|
||||
uncachedProducts.push(productId);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError4 = true;
|
||||
_iteratorError4 = err;
|
||||
_didIteratorError3 = true;
|
||||
_iteratorError3 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
||||
_iterator4.return();
|
||||
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
||||
_iterator3.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError4) {
|
||||
throw _iteratorError4;
|
||||
if (_didIteratorError3) {
|
||||
throw _iteratorError3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1737,41 +1722,38 @@ var ProductSpecificSelectedProducts = withAPIData(function (props) {
|
|||
})(function (_ref2) {
|
||||
var productIds = _ref2.productIds,
|
||||
products = _ref2.products,
|
||||
removeProductCallback = _ref2.removeProductCallback;
|
||||
columns = _ref2.columns,
|
||||
addOrRemoveProduct = _ref2.addOrRemoveProduct;
|
||||
|
||||
|
||||
// Add new products to cache.
|
||||
if (products.data) {
|
||||
var _iteratorNormalCompletion5 = true;
|
||||
var _didIteratorError5 = false;
|
||||
var _iteratorError5 = undefined;
|
||||
var _iteratorNormalCompletion4 = true;
|
||||
var _didIteratorError4 = false;
|
||||
var _iteratorError4 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator5 = products.data[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
||||
var product = _step5.value;
|
||||
for (var _iterator4 = products.data[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
||||
var product = _step4.value;
|
||||
|
||||
PRODUCT_DATA[product.id] = product;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError5 = true;
|
||||
_iteratorError5 = err;
|
||||
_didIteratorError4 = true;
|
||||
_iteratorError4 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
||||
_iterator5.return();
|
||||
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
||||
_iterator4.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError5) {
|
||||
throw _iteratorError5;
|
||||
if (_didIteratorError4) {
|
||||
throw _iteratorError4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === productIds.length) {
|
||||
return __('No products selected');
|
||||
}
|
||||
|
||||
var productElements = [];
|
||||
|
||||
var _loop = function _loop(productId) {
|
||||
|
@ -1801,7 +1783,7 @@ var ProductSpecificSelectedProducts = withAPIData(function (props) {
|
|||
type: 'button',
|
||||
id: 'product-' + productData.id,
|
||||
onClick: function onClick() {
|
||||
removeProductCallback(productData.id);
|
||||
addOrRemoveProduct(productData.id);
|
||||
} },
|
||||
wp.element.createElement(Dashicon, { icon: 'no-alt' })
|
||||
)
|
||||
|
@ -1809,39 +1791,44 @@ var ProductSpecificSelectedProducts = withAPIData(function (props) {
|
|||
));
|
||||
};
|
||||
|
||||
var _iteratorNormalCompletion6 = true;
|
||||
var _didIteratorError6 = false;
|
||||
var _iteratorError6 = undefined;
|
||||
var _iteratorNormalCompletion5 = true;
|
||||
var _didIteratorError5 = false;
|
||||
var _iteratorError5 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator6 = productIds[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
|
||||
var productId = _step6.value;
|
||||
for (var _iterator5 = productIds[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
||||
var productId = _step5.value;
|
||||
|
||||
var _ret = _loop(productId);
|
||||
|
||||
if (_ret === 'continue') continue;
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError6 = true;
|
||||
_iteratorError6 = err;
|
||||
_didIteratorError5 = true;
|
||||
_iteratorError5 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion6 && _iterator6.return) {
|
||||
_iterator6.return();
|
||||
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
||||
_iterator5.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError6) {
|
||||
throw _iteratorError6;
|
||||
if (_didIteratorError5) {
|
||||
throw _iteratorError5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'wc-products-list-card__results-wrapper' },
|
||||
{ className: 'wc-products-list-card__results-wrapper wc-products-list-card__results-wrapper--cols-' + columns },
|
||||
wp.element.createElement(
|
||||
'div',
|
||||
{ role: 'menu', className: 'wc-products-list-card__results', 'aria-orientation': 'vertical', 'aria-label': '{ __( \'Products list\' ) }' },
|
||||
{ role: 'menu', className: 'wc-products-list-card__results', 'aria-orientation': 'vertical', 'aria-label': __('Selected products') },
|
||||
productElements.length > 0 && wp.element.createElement(
|
||||
'h3',
|
||||
null,
|
||||
__('Selected products')
|
||||
),
|
||||
wp.element.createElement(
|
||||
'ul',
|
||||
null,
|
||||
|
@ -2027,7 +2014,8 @@ var ProductCategoryFilter = function ProductCategoryFilter(_ref) {
|
|||
|
||||
return wp.element.createElement(
|
||||
"div",
|
||||
null,
|
||||
{ className: "wc-products-list-card__input-wrapper" },
|
||||
wp.element.createElement(Dashicon, { icon: "search" }),
|
||||
wp.element.createElement("input", { className: "wc-products-list-card__search", type: "search", placeholder: __('Search for categories'), onChange: filterResults })
|
||||
);
|
||||
};
|
||||
|
@ -2277,7 +2265,8 @@ var __ = wp.i18n.__;
|
|||
var _wp$components = wp.components,
|
||||
Toolbar = _wp$components.Toolbar,
|
||||
withAPIData = _wp$components.withAPIData,
|
||||
Dropdown = _wp$components.Dropdown;
|
||||
Dropdown = _wp$components.Dropdown,
|
||||
Dashicon = _wp$components.Dashicon;
|
||||
|
||||
/**
|
||||
* Attribute data cache.
|
||||
|
@ -2481,7 +2470,8 @@ var ProductsAttributeSelect = exports.ProductsAttributeSelect = function (_React
|
|||
var ProductAttributeFilter = function ProductAttributeFilter(props) {
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
null,
|
||||
{ className: 'wc-products-list-card__input-wrapper' },
|
||||
wp.element.createElement(Dashicon, { icon: 'search' }),
|
||||
wp.element.createElement('input', { className: 'wc-products-list-card__search', type: 'search', placeholder: __('Search for attributes'), onChange: props.updateFilter })
|
||||
);
|
||||
};
|
||||
|
|
|
@ -337,7 +337,7 @@ const ProductsBlockPreview = withAPIData( ( { attributes } ) => {
|
|||
|
||||
if ( 'specific' === display ) {
|
||||
query.include = display_setting.join( ',' );
|
||||
query.orderby = 'include';
|
||||
query.per_page = display_setting.length;
|
||||
} else if ( 'category' === display ) {
|
||||
query.category = display_setting.join( ',' );
|
||||
} else if ( 'attribute' === display && display_setting.length ) {
|
||||
|
@ -353,7 +353,7 @@ const ProductsBlockPreview = withAPIData( ( { attributes } ) => {
|
|||
}
|
||||
|
||||
// @todo Add support for orderby by sales, rating, and price to the API.
|
||||
if ( 'specific' !== display && ( 'title' === orderby || 'date' === orderby ) ) {
|
||||
if ( 'title' === orderby || 'date' === orderby ) {
|
||||
query.orderby = orderby;
|
||||
|
||||
if ( 'title' === orderby ) {
|
||||
|
@ -502,41 +502,51 @@ class ProductsBlock extends React.Component {
|
|||
/>
|
||||
);
|
||||
|
||||
// Orderby settings don't make sense for specific-selected products display.
|
||||
let orderControl = null;
|
||||
let orderControl = (
|
||||
<SelectControl
|
||||
key="query-panel-select"
|
||||
label={ __( 'Order Products By' ) }
|
||||
value={ orderby }
|
||||
options={ [
|
||||
{
|
||||
label: __( 'Newness - newest first' ),
|
||||
value: 'date',
|
||||
},
|
||||
{
|
||||
label: __( 'Price - low to high' ),
|
||||
value: 'price_asc',
|
||||
},
|
||||
{
|
||||
label: __( 'Price - high to low' ),
|
||||
value: 'price_desc',
|
||||
},
|
||||
{
|
||||
label: __( 'Rating - highest first' ),
|
||||
value: 'rating',
|
||||
},
|
||||
{
|
||||
label: __( 'Sales - most first' ),
|
||||
value: 'popularity',
|
||||
},
|
||||
{
|
||||
label: __( 'Title - alphabetical' ),
|
||||
value: 'title',
|
||||
},
|
||||
] }
|
||||
onChange={ ( value ) => setAttributes( { orderby: value } ) }
|
||||
/>
|
||||
);
|
||||
|
||||
// Row settings don't make sense for specific-selected products display.
|
||||
let rowControl = null;
|
||||
if ( 'specific' !== display ) {
|
||||
orderControl = (
|
||||
<SelectControl
|
||||
key="query-panel-select"
|
||||
label={ __( 'Order Products By' ) }
|
||||
value={ orderby }
|
||||
options={ [
|
||||
{
|
||||
label: __( 'Newness - newest first' ),
|
||||
value: 'date',
|
||||
},
|
||||
{
|
||||
label: __( 'Price - low to high' ),
|
||||
value: 'price_asc',
|
||||
},
|
||||
{
|
||||
label: __( 'Price - high to low' ),
|
||||
value: 'price_desc',
|
||||
},
|
||||
{
|
||||
label: __( 'Rating - highest first' ),
|
||||
value: 'rating',
|
||||
},
|
||||
{
|
||||
label: __( 'Sales - most first' ),
|
||||
value: 'popularity',
|
||||
},
|
||||
{
|
||||
label: __( 'Title - alphabetical' ),
|
||||
value: 'title',
|
||||
},
|
||||
] }
|
||||
onChange={ ( value ) => setAttributes( { orderby: value } ) }
|
||||
rowControl = (
|
||||
<RangeControl
|
||||
label={ __( 'Rows' ) }
|
||||
value={ rows }
|
||||
onChange={ ( value ) => setAttributes( { rows: value } ) }
|
||||
min={ wc_product_block_data.min_rows }
|
||||
max={ wc_product_block_data.max_rows }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -546,13 +556,7 @@ class ProductsBlock extends React.Component {
|
|||
{ this.getBlockDescription() }
|
||||
<h3>{ __( 'Layout' ) }</h3>
|
||||
{ columnControl }
|
||||
<RangeControl
|
||||
label={ __( 'Rows' ) }
|
||||
value={ rows }
|
||||
onChange={ ( value ) => setAttributes( { rows: value } ) }
|
||||
min={ wc_product_block_data.min_rows }
|
||||
max={ wc_product_block_data.max_rows }
|
||||
/>
|
||||
{ rowControl }
|
||||
{ orderControl }
|
||||
</InspectorControls>
|
||||
);
|
||||
|
@ -662,6 +666,7 @@ class ProductsBlock extends React.Component {
|
|||
|
||||
return (
|
||||
<ProductsBlockSettingsEditor
|
||||
attributes={ attributes }
|
||||
selected_display={ display }
|
||||
selected_display_setting={ display_setting }
|
||||
update_display_callback={ update_display_callback }
|
||||
|
@ -759,7 +764,9 @@ registerBlockType( 'woocommerce/products', {
|
|||
const { rows, columns, display, display_setting, orderby } = props.attributes;
|
||||
|
||||
let shortcode_atts = new Map();
|
||||
shortcode_atts.set( 'limit', rows * columns );
|
||||
if ( 'specific' !== display ) {
|
||||
shortcode_atts.set( 'limit', rows * columns );
|
||||
}
|
||||
shortcode_atts.set( 'columns', columns );
|
||||
|
||||
if ( 'specific' === display ) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const { __ } = wp.i18n;
|
||||
const { Toolbar, withAPIData, Dropdown } = wp.components;
|
||||
const { Toolbar, withAPIData, Dropdown, Dashicon } = wp.components;
|
||||
|
||||
/**
|
||||
* Attribute data cache.
|
||||
|
@ -154,7 +154,8 @@ export class ProductsAttributeSelect extends React.Component {
|
|||
*/
|
||||
const ProductAttributeFilter = ( props ) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="wc-products-list-card__input-wrapper">
|
||||
<Dashicon icon="search" />
|
||||
<input className="wc-products-list-card__search" type="search" placeholder={ __( 'Search for attributes' ) } onChange={ props.updateFilter } />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -114,7 +114,8 @@ export class ProductsCategorySelect extends React.Component {
|
|||
*/
|
||||
const ProductCategoryFilter = ( { filterResults } ) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="wc-products-list-card__input-wrapper">
|
||||
<Dashicon icon="search" />
|
||||
<input className="wc-products-list-card__search" type="search" placeholder={ __( 'Search for categories' ) } onChange={ filterResults } />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const { __ } = wp.i18n;
|
||||
const { Toolbar, withAPIData, Dropdown, Dashicon } = wp.components;
|
||||
const { TransitionGroup, CSSTransition } = ReactTransitionGroup;
|
||||
|
||||
/**
|
||||
* Product data cache.
|
||||
|
@ -31,10 +30,14 @@ export class ProductsSpecificSelect extends React.Component {
|
|||
*
|
||||
* @param id int Product ID.
|
||||
*/
|
||||
addProduct( id ) {
|
||||
|
||||
addOrRemoveProduct( id ) {
|
||||
let selectedProducts = this.state.selectedProducts;
|
||||
selectedProducts.push( id );
|
||||
|
||||
if ( ! selectedProducts.includes( id ) ) {
|
||||
selectedProducts.push( id );
|
||||
} else {
|
||||
selectedProducts = selectedProducts.filter( product => product !== id );
|
||||
}
|
||||
|
||||
this.setState( {
|
||||
selectedProducts: selectedProducts
|
||||
|
@ -49,28 +52,6 @@ export class ProductsSpecificSelect extends React.Component {
|
|||
this.props.update_display_setting_callback( selectedProducts.slice() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a product from the list of selected products.
|
||||
*
|
||||
* @param id int Product ID.
|
||||
*/
|
||||
removeProduct( id ) {
|
||||
let oldProducts = this.state.selectedProducts;
|
||||
let newProducts = [];
|
||||
|
||||
for ( let productId of oldProducts ) {
|
||||
if ( productId !== id ) {
|
||||
newProducts.push( productId );
|
||||
}
|
||||
}
|
||||
|
||||
this.setState( {
|
||||
selectedProducts: newProducts
|
||||
} );
|
||||
|
||||
this.props.update_display_setting_callback( newProducts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the product specific select screen.
|
||||
*/
|
||||
|
@ -78,12 +59,13 @@ export class ProductsSpecificSelect extends React.Component {
|
|||
return (
|
||||
<div className="wc-products-list-card wc-products-list-card--specific">
|
||||
<ProductsSpecificSearchField
|
||||
addProductCallback={ this.addProduct.bind( this ) }
|
||||
addOrRemoveProductCallback={ this.addOrRemoveProduct.bind( this ) }
|
||||
selectedProducts={ this.state.selectedProducts }
|
||||
/>
|
||||
<ProductSpecificSelectedProducts
|
||||
columns={ this.props.attributes.columns }
|
||||
productIds={ this.state.selectedProducts }
|
||||
removeProductCallback={ this.removeProduct.bind( this ) }
|
||||
addOrRemoveProduct={ this.addOrRemoveProduct.bind( this ) }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -103,11 +85,13 @@ class ProductsSpecificSearchField extends React.Component {
|
|||
|
||||
this.state = {
|
||||
searchText: '',
|
||||
dropdownOpen: false,
|
||||
}
|
||||
|
||||
this.updateSearchResults = this.updateSearchResults.bind( this );
|
||||
this.setWrapperRef = this.setWrapperRef.bind( this );
|
||||
this.handleClickOutside = this.handleClickOutside.bind( this );
|
||||
this.isDropdownOpen = this.isDropdownOpen.bind( this );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,6 +128,12 @@ class ProductsSpecificSearchField extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
isDropdownOpen( isOpen ) {
|
||||
this.setState( {
|
||||
dropdownOpen: !! isOpen,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for updating results when text is typed into the input.
|
||||
*
|
||||
|
@ -159,18 +149,24 @@ class ProductsSpecificSearchField extends React.Component {
|
|||
* Render the product search UI.
|
||||
*/
|
||||
render() {
|
||||
const divClass = 'wc-products-list-card__search-wrapper';
|
||||
|
||||
return (
|
||||
<div className="wc-products-list-card__search-wrapper" ref={ this.setWrapperRef }>
|
||||
<input type="search"
|
||||
className="wc-products-list-card__search"
|
||||
value={ this.state.searchText }
|
||||
placeholder={ __( 'Search for products to display' ) }
|
||||
onChange={ this.updateSearchResults }
|
||||
/>
|
||||
<div className={ divClass + ( this.state.dropdownOpen ? ' ' + divClass + '--with-results' : '' ) } ref={ this.setWrapperRef }>
|
||||
<div className="wc-products-list-card__input-wrapper">
|
||||
<Dashicon icon="search" />
|
||||
<input type="search"
|
||||
className="wc-products-list-card__search"
|
||||
value={ this.state.searchText }
|
||||
placeholder={ __( 'Search for products to display' ) }
|
||||
onChange={ this.updateSearchResults }
|
||||
/>
|
||||
</div>
|
||||
<ProductSpecificSearchResults
|
||||
searchString={ this.state.searchText }
|
||||
addProductCallback={ this.props.addProductCallback }
|
||||
addOrRemoveProductCallback={ this.props.addOrRemoveProductCallback }
|
||||
selectedProducts={ this.props.selectedProducts }
|
||||
isDropdownOpenCallback={ this.isDropdownOpen }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -191,13 +187,13 @@ const ProductSpecificSearchResults = withAPIData( ( props ) => {
|
|||
return {
|
||||
products: '/wc/v2/products?per_page=10&search=' + props.searchString,
|
||||
};
|
||||
} )( ( { products, addProductCallback, selectedProducts } ) => {
|
||||
} )( ( { products, addOrRemoveProductCallback, selectedProducts, isDropdownOpenCallback } ) => {
|
||||
if ( ! products.data ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( 0 === products.data.length ) {
|
||||
return __( 'No products found' );
|
||||
return <span className="wc-products-list-card__search-no-results"> { __( 'No products found' ) } </span>;
|
||||
}
|
||||
|
||||
// Populate the cache.
|
||||
|
@ -207,8 +203,9 @@ const ProductSpecificSearchResults = withAPIData( ( props ) => {
|
|||
|
||||
return <ProductSpecificSearchResultsDropdown
|
||||
products={ products.data }
|
||||
addProductCallback={ addProductCallback }
|
||||
addOrRemoveProductCallback={ addOrRemoveProductCallback }
|
||||
selectedProducts={ selectedProducts }
|
||||
isDropdownOpenCallback={ isDropdownOpenCallback }
|
||||
/>
|
||||
}
|
||||
);
|
||||
|
@ -218,39 +215,43 @@ const ProductSpecificSearchResults = withAPIData( ( props ) => {
|
|||
*/
|
||||
class ProductSpecificSearchResultsDropdown extends React.Component {
|
||||
|
||||
/**
|
||||
* Set the state of the dropdown to open.
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.isDropdownOpenCallback( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of the dropdown to closed.
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
this.props.isDropdownOpenCallback( false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render dropdown.
|
||||
*/
|
||||
render() {
|
||||
const { products, addProductCallback, selectedProducts } = this.props;
|
||||
const { products, addOrRemoveProductCallback, selectedProducts } = this.props;
|
||||
|
||||
let productElements = [];
|
||||
|
||||
for ( let product of products ) {
|
||||
if ( selectedProducts.includes( product.id ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
productElements.push(
|
||||
<CSSTransition
|
||||
key={ product.slug }
|
||||
classNames="wc-products-list-card__content--transition"
|
||||
timeout={ { exit: 700 } }
|
||||
>
|
||||
<ProductSpecificSearchResultsDropdownElement
|
||||
product={product}
|
||||
addProductCallback={ addProductCallback }
|
||||
/>
|
||||
</CSSTransition>
|
||||
<ProductSpecificSearchResultsDropdownElement
|
||||
product={product}
|
||||
addOrRemoveProductCallback={ addOrRemoveProductCallback }
|
||||
selected={ selectedProducts.includes( product.id ) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div role="menu" className="wc-products-list-card__search-results" aria-orientation="vertical" aria-label="{ __( 'Products list' ) }">
|
||||
<TransitionGroup>
|
||||
{ productElements }
|
||||
</TransitionGroup>
|
||||
|
||||
<div role="menu" className="wc-products-list-card__search-results" aria-orientation="vertical" aria-label={ __( 'Products list' ) }>
|
||||
<div>
|
||||
{ productElements }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -267,10 +268,6 @@ class ProductSpecificSearchResultsDropdownElement extends React.Component {
|
|||
constructor( props ) {
|
||||
super( props );
|
||||
|
||||
this.state = {
|
||||
clicked: false,
|
||||
}
|
||||
|
||||
this.handleClick = this.handleClick.bind( this );
|
||||
}
|
||||
|
||||
|
@ -278,8 +275,7 @@ class ProductSpecificSearchResultsDropdownElement extends React.Component {
|
|||
* Add product to main list and change UI to show it was added.
|
||||
*/
|
||||
handleClick() {
|
||||
this.setState( { clicked: true } );
|
||||
this.props.addProductCallback( this.props.product.id );
|
||||
this.props.addOrRemoveProductCallback( this.props.product.id );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -287,17 +283,13 @@ class ProductSpecificSearchResultsDropdownElement extends React.Component {
|
|||
*/
|
||||
render() {
|
||||
const product = this.props.product;
|
||||
let icon = this.props.selected ? <Dashicon icon="yes" /> : null;
|
||||
|
||||
return (
|
||||
<div className="wc-products-list-card__content">
|
||||
<div className={ 'wc-products-list-card__content' + ( this.props.selected ? ' wc-products-list-card__content--added' : '' ) } onClick={ this.handleClick }>
|
||||
<img src={ product.images[0].src } />
|
||||
<span className="wc-products-list-card__content-item-name">{ this.state.clicked ? __( 'Added' ) : product.name }</span>
|
||||
<button type="button"
|
||||
className="button-link"
|
||||
id={ 'product-' + product.id }
|
||||
onClick={ this.handleClick } >
|
||||
{ __( 'Add' ) }
|
||||
</button>
|
||||
<span className="wc-products-list-card__content-item-name">{ product.name }</span>
|
||||
{ icon }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -307,7 +299,6 @@ class ProductSpecificSearchResultsDropdownElement extends React.Component {
|
|||
* List preview of selected products.
|
||||
*/
|
||||
const ProductSpecificSelectedProducts = withAPIData( ( props ) => {
|
||||
|
||||
if ( ! props.productIds.length ) {
|
||||
return {
|
||||
products: []
|
||||
|
@ -325,7 +316,7 @@ const ProductSpecificSelectedProducts = withAPIData( ( props ) => {
|
|||
return {
|
||||
products: uncachedProducts.length ? '/wc/v2/products?include=' + uncachedProducts.join( ',' ) : []
|
||||
};
|
||||
} )( ( { productIds, products, removeProductCallback } ) => {
|
||||
} )( ( { productIds, products, columns, addOrRemoveProduct } ) => {
|
||||
|
||||
// Add new products to cache.
|
||||
if ( products.data ) {
|
||||
|
@ -334,10 +325,6 @@ const ProductSpecificSelectedProducts = withAPIData( ( props ) => {
|
|||
}
|
||||
}
|
||||
|
||||
if ( 0 === productIds.length ) {
|
||||
return __( 'No products selected' );
|
||||
}
|
||||
|
||||
const productElements = [];
|
||||
|
||||
for ( const productId of productIds ) {
|
||||
|
@ -357,8 +344,8 @@ const ProductSpecificSelectedProducts = withAPIData( ( props ) => {
|
|||
<button
|
||||
type="button"
|
||||
id={ 'product-' + productData.id }
|
||||
onClick={ function() { removeProductCallback( productData.id ) } } >
|
||||
<Dashicon icon={ 'no-alt' } />
|
||||
onClick={ function() { addOrRemoveProduct( productData.id ) } } >
|
||||
<Dashicon icon="no-alt" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -366,8 +353,11 @@ const ProductSpecificSelectedProducts = withAPIData( ( props ) => {
|
|||
}
|
||||
|
||||
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' ) }">
|
||||
<div className={ 'wc-products-list-card__results-wrapper wc-products-list-card__results-wrapper--cols-' + columns }>
|
||||
<div role="menu" className="wc-products-list-card__results" aria-orientation="vertical" aria-label={ __( 'Selected products' ) }>
|
||||
|
||||
{ productElements.length > 0 && <h3>{ __( 'Selected products' ) }</h3> }
|
||||
|
||||
<ul>
|
||||
{ productElements }
|
||||
</ul>
|
||||
|
|
Loading…
Reference in New Issue