Working attributes select UI
This commit is contained in:
parent
af5749397a
commit
9148e9801b
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -166,12 +166,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.product-category-select {
|
||||
.product-category-select,
|
||||
.product-attribute-select {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
#product-category-search {
|
||||
#product-category-search,
|
||||
#product-attribute-search {
|
||||
width: 100%;
|
||||
margin: 0 0 10px;
|
||||
padding: 10px 15px;
|
||||
|
@ -179,7 +181,8 @@
|
|||
border-color: #E6EAEE;
|
||||
}
|
||||
|
||||
.product-categories-list {
|
||||
.product-categories-list,
|
||||
.product-attributes-list {
|
||||
height: 200px;
|
||||
overflow-y: scroll;
|
||||
|
||||
|
@ -255,7 +258,8 @@
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.product-category-count {
|
||||
.product-category-count,
|
||||
.product-attribute-count {
|
||||
padding: 0 10px;
|
||||
border: 1px solid #e9e9e9;
|
||||
border-radius: 15px;
|
||||
|
@ -263,6 +267,24 @@
|
|||
font-size: .8em;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.product-attribute {
|
||||
border: 1px solid #e9e9e9;
|
||||
|
||||
.product-attribute-name {
|
||||
padding: .5em;
|
||||
background: #FFFFFF;
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
|
||||
.product-attribute-count {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
|
|
|
@ -327,7 +327,7 @@ var ProductsBlockSettingsEditor = function (_React$Component3) {
|
|||
} else if ('category' === this.state.display) {
|
||||
extra_settings = wp.element.createElement(_categorySelect.ProductsCategorySelect, this.props);
|
||||
} else if ('attribute' === this.state.display) {
|
||||
extra_settings = wp.element.createElement(_attributeSelect.ProductsAttributeSelect, null);
|
||||
extra_settings = wp.element.createElement(_attributeSelect.ProductsAttributeSelect, this.props);
|
||||
}
|
||||
|
||||
var menu = this.state.menu_visible ? wp.element.createElement(ProductsBlockSettingsEditorDisplayOptions, { existing: this.state.display ? true : false, update_display_callback: this.updateDisplay }) : null;
|
||||
|
@ -1493,6 +1493,12 @@ var _wp$components = wp.components,
|
|||
withAPIData = _wp$components.withAPIData,
|
||||
Dropdown = _wp$components.Dropdown;
|
||||
|
||||
/**
|
||||
* Attribute data cache. Needed because it takes a lot of API calls to generate attribute info.
|
||||
*/
|
||||
|
||||
var PRODUCT_ATTRIBUTE_DATA = {};
|
||||
|
||||
/**
|
||||
* When the display mode is 'Attribute' search for and select product attributes to pull products from.
|
||||
*/
|
||||
|
@ -1500,19 +1506,123 @@ var _wp$components = wp.components,
|
|||
var ProductsAttributeSelect = exports.ProductsAttributeSelect = function (_React$Component) {
|
||||
_inherits(ProductsAttributeSelect, _React$Component);
|
||||
|
||||
function ProductsAttributeSelect() {
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
function ProductsAttributeSelect(props) {
|
||||
_classCallCheck(this, ProductsAttributeSelect);
|
||||
|
||||
return _possibleConstructorReturn(this, (ProductsAttributeSelect.__proto__ || Object.getPrototypeOf(ProductsAttributeSelect)).apply(this, arguments));
|
||||
var _this = _possibleConstructorReturn(this, (ProductsAttributeSelect.__proto__ || Object.getPrototypeOf(ProductsAttributeSelect)).call(this, props));
|
||||
|
||||
_this.state = {
|
||||
selectedAttribute: '',
|
||||
selectedTerms: [],
|
||||
filterQuery: ''
|
||||
};
|
||||
|
||||
_this.setSelectedAttribute = _this.setSelectedAttribute.bind(_this);
|
||||
_this.addTerm = _this.addTerm.bind(_this);
|
||||
_this.removeTerm = _this.removeTerm.bind(_this);
|
||||
return _this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected attribute.
|
||||
*
|
||||
* @param slug string Attribute slug.
|
||||
*/
|
||||
|
||||
|
||||
_createClass(ProductsAttributeSelect, [{
|
||||
key: "render",
|
||||
key: 'setSelectedAttribute',
|
||||
value: function setSelectedAttribute(slug) {
|
||||
this.setState({
|
||||
selectedAttribute: slug,
|
||||
selectedTerms: []
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a term to the selected attribute's terms.
|
||||
*
|
||||
* @param slug string Term slug.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'addTerm',
|
||||
value: function addTerm(slug) {
|
||||
var terms = this.state.selectedTerms;
|
||||
terms.push(slug);
|
||||
this.setState({
|
||||
selectedTerms: terms
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a term from the selected attribute's terms.
|
||||
*
|
||||
* @param slug string Term slug.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'removeTerm',
|
||||
value: function removeTerm(slug) {
|
||||
var newTerms = [];
|
||||
var _iteratorNormalCompletion = true;
|
||||
var _didIteratorError = false;
|
||||
var _iteratorError = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator = this.state.selectedTerms[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||
var termSlug = _step.value;
|
||||
|
||||
if (termSlug !== slug) {
|
||||
newTerms.push(termSlug);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError = true;
|
||||
_iteratorError = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion && _iterator.return) {
|
||||
_iterator.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError) {
|
||||
throw _iteratorError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedTerms: newTerms
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the whole section.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'render',
|
||||
value: function render() {
|
||||
|
||||
// @todo Remove this once data is moving around properly.
|
||||
console.log("STATE UPDATED");
|
||||
console.log(this.state);
|
||||
|
||||
return wp.element.createElement(
|
||||
"div",
|
||||
{ className: "product-attribute-select" },
|
||||
"TODO: Attribute select screen"
|
||||
'div',
|
||||
{ className: 'product-attribute-select' },
|
||||
wp.element.createElement(ProductAttributeFilter, null),
|
||||
wp.element.createElement(ProductAttributeList, {
|
||||
selectedAttribute: this.state.selectedAttribute,
|
||||
selectedTerms: this.state.selectedTerms,
|
||||
setSelectedAttribute: this.setSelectedAttribute.bind(this),
|
||||
addTerm: this.addTerm.bind(this),
|
||||
removeTerm: this.removeTerm.bind(this)
|
||||
})
|
||||
);
|
||||
}
|
||||
}]);
|
||||
|
@ -1520,5 +1630,284 @@ var ProductsAttributeSelect = exports.ProductsAttributeSelect = function (_React
|
|||
return ProductsAttributeSelect;
|
||||
}(React.Component);
|
||||
|
||||
/**
|
||||
* Search area for filtering through the attributes list.
|
||||
*/
|
||||
|
||||
|
||||
var ProductAttributeFilter = function ProductAttributeFilter() {
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
null,
|
||||
wp.element.createElement('input', { id: 'product-attribute-search', type: 'search', placeholder: __('Search for attributes') })
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* List of attributes.
|
||||
*/
|
||||
var ProductAttributeList = withAPIData(function (props) {
|
||||
return {
|
||||
attributes: '/wc/v2/products/attributes'
|
||||
};
|
||||
})(function (_ref) {
|
||||
var attributes = _ref.attributes,
|
||||
selectedAttribute = _ref.selectedAttribute,
|
||||
selectedTerms = _ref.selectedTerms,
|
||||
setSelectedAttribute = _ref.setSelectedAttribute,
|
||||
addTerm = _ref.addTerm,
|
||||
removeTerm = _ref.removeTerm;
|
||||
|
||||
if (!attributes.data) {
|
||||
return __('Loading');
|
||||
}
|
||||
|
||||
if (0 === attributes.data.length) {
|
||||
return __('No attributes found');
|
||||
}
|
||||
|
||||
var attributeElements = [];
|
||||
var _iteratorNormalCompletion2 = true;
|
||||
var _didIteratorError2 = false;
|
||||
var _iteratorError2 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator2 = attributes.data[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||
var attribute = _step2.value;
|
||||
|
||||
if (PRODUCT_ATTRIBUTE_DATA.hasOwnProperty(attribute.slug)) {
|
||||
attributeElements.push(wp.element.createElement(ProductAttributeElement, {
|
||||
selectedAttribute: selectedAttribute,
|
||||
selectedTerms: selectedTerms,
|
||||
attribute: attribute,
|
||||
setSelectedAttribute: setSelectedAttribute,
|
||||
addTerm: addTerm,
|
||||
removeTerm: removeTerm
|
||||
}));
|
||||
} else {
|
||||
attributeElements.push(wp.element.createElement(UncachedProductAttributeElement, {
|
||||
selectedAttribute: selectedAttribute,
|
||||
selectedTerms: selectedTerms,
|
||||
attribute: attribute,
|
||||
setSelectedAttribute: setSelectedAttribute,
|
||||
addTerm: addTerm,
|
||||
removeTerm: removeTerm
|
||||
}));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError2 = true;
|
||||
_iteratorError2 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
||||
_iterator2.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError2) {
|
||||
throw _iteratorError2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'product-attributes-list' },
|
||||
attributeElements
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Caches then renders a product attribute term element.
|
||||
*/
|
||||
var UncachedProductAttributeElement = withAPIData(function (props) {
|
||||
return {
|
||||
terms: '/wc/v2/products/attributes/' + props.attribute.id + '/terms'
|
||||
};
|
||||
})(function (_ref2) {
|
||||
var terms = _ref2.terms,
|
||||
selectedAttribute = _ref2.selectedAttribute,
|
||||
selectedTerms = _ref2.selectedTerms,
|
||||
attribute = _ref2.attribute,
|
||||
setSelectedAttribute = _ref2.setSelectedAttribute,
|
||||
addTerm = _ref2.addTerm,
|
||||
removeTerm = _ref2.removeTerm;
|
||||
|
||||
if (!terms.data) {
|
||||
return __('Loading');
|
||||
}
|
||||
|
||||
if (0 === terms.data.length) {
|
||||
return __('No attribute options found');
|
||||
}
|
||||
|
||||
// Populate cache.
|
||||
PRODUCT_ATTRIBUTE_DATA[attribute.slug] = { terms: [] };
|
||||
|
||||
var totalCount = 0;
|
||||
var _iteratorNormalCompletion3 = true;
|
||||
var _didIteratorError3 = false;
|
||||
var _iteratorError3 = undefined;
|
||||
|
||||
try {
|
||||
for (var _iterator3 = terms.data[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||
var term = _step3.value;
|
||||
|
||||
totalCount += term.count;
|
||||
PRODUCT_ATTRIBUTE_DATA[attribute.slug].terms.push(term);
|
||||
}
|
||||
} catch (err) {
|
||||
_didIteratorError3 = true;
|
||||
_iteratorError3 = err;
|
||||
} finally {
|
||||
try {
|
||||
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
||||
_iterator3.return();
|
||||
}
|
||||
} finally {
|
||||
if (_didIteratorError3) {
|
||||
throw _iteratorError3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRODUCT_ATTRIBUTE_DATA[attribute.slug].count = totalCount;
|
||||
|
||||
return wp.element.createElement(ProductAttributeElement, {
|
||||
selectedAttribute: selectedAttribute,
|
||||
selectedTerms: selectedTerms,
|
||||
attribute: attribute,
|
||||
setSelectedAttribute: setSelectedAttribute,
|
||||
addTerm: addTerm,
|
||||
removeTerm: removeTerm
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* A product attribute term element.
|
||||
*/
|
||||
|
||||
var ProductAttributeElement = function (_React$Component2) {
|
||||
_inherits(ProductAttributeElement, _React$Component2);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
function ProductAttributeElement(props) {
|
||||
_classCallCheck(this, ProductAttributeElement);
|
||||
|
||||
var _this2 = _possibleConstructorReturn(this, (ProductAttributeElement.__proto__ || Object.getPrototypeOf(ProductAttributeElement)).call(this, props));
|
||||
|
||||
_this2.handleAttributeChange = _this2.handleAttributeChange.bind(_this2);
|
||||
_this2.handleTermChange = _this2.handleTermChange.bind(_this2);
|
||||
return _this2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate and reset values when the selected attribute is changed.
|
||||
*
|
||||
* @param evt Event object
|
||||
*/
|
||||
|
||||
|
||||
_createClass(ProductAttributeElement, [{
|
||||
key: 'handleAttributeChange',
|
||||
value: function handleAttributeChange(evt) {
|
||||
var slug = evt.target.value;
|
||||
|
||||
if (this.props.selectedAttribute === slug) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.setSelectedAttribute(slug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or remove selected terms.
|
||||
*
|
||||
* @param evt Event object
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'handleTermChange',
|
||||
value: function handleTermChange(evt) {
|
||||
if (evt.target.checked) {
|
||||
this.props.addTerm(evt.target.value);
|
||||
} else {
|
||||
this.props.removeTerm(evt.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the details for one attribute.
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'render',
|
||||
value: function render() {
|
||||
var _this3 = this;
|
||||
|
||||
var attribute = PRODUCT_ATTRIBUTE_DATA[this.props.attribute.slug];
|
||||
var isSelected = this.props.selectedAttribute === this.props.attribute.slug;
|
||||
|
||||
var attributeTerms = null;
|
||||
if (isSelected) {
|
||||
attributeTerms = wp.element.createElement(
|
||||
'ul',
|
||||
{ className: 'product-attribute-terms' },
|
||||
attribute.terms.map(function (term) {
|
||||
return wp.element.createElement(
|
||||
'li',
|
||||
{ className: 'product-attribute-term' },
|
||||
wp.element.createElement(
|
||||
'label',
|
||||
null,
|
||||
wp.element.createElement('input', { type: 'checkbox',
|
||||
value: term.slug,
|
||||
onChange: _this3.handleTermChange,
|
||||
checked: _this3.props.selectedTerms.includes(term.slug)
|
||||
}),
|
||||
term.name,
|
||||
wp.element.createElement(
|
||||
'span',
|
||||
{ className: 'product-attribute-count' },
|
||||
term.count
|
||||
)
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'product-attribute' },
|
||||
wp.element.createElement(
|
||||
'div',
|
||||
{ className: 'product-attribute-name' },
|
||||
wp.element.createElement(
|
||||
'label',
|
||||
null,
|
||||
wp.element.createElement('input', { type: 'radio',
|
||||
value: this.props.attribute.slug,
|
||||
onClick: this.handleAttributeChange,
|
||||
checked: isSelected
|
||||
}),
|
||||
this.props.attribute.name,
|
||||
wp.element.createElement(
|
||||
'span',
|
||||
{ className: 'product-attribute-count' },
|
||||
attribute.count
|
||||
)
|
||||
)
|
||||
),
|
||||
attributeTerms
|
||||
);
|
||||
}
|
||||
}]);
|
||||
|
||||
return ProductAttributeElement;
|
||||
}(React.Component);
|
||||
|
||||
/***/ })
|
||||
/******/ ]);
|
|
@ -173,7 +173,7 @@ class ProductsBlockSettingsEditor extends React.Component {
|
|||
} else if ( 'category' === this.state.display ) {
|
||||
extra_settings = <ProductsCategorySelect { ...this.props } />;
|
||||
} else if ( 'attribute' === this.state.display ) {
|
||||
extra_settings = <ProductsAttributeSelect />
|
||||
extra_settings = <ProductsAttributeSelect { ...this.props } />
|
||||
}
|
||||
|
||||
const menu = this.state.menu_visible ? <ProductsBlockSettingsEditorDisplayOptions existing={ this.state.display ? true : false } update_display_callback={ this.updateDisplay } /> : null;
|
||||
|
|
|
@ -1,17 +1,282 @@
|
|||
const { __ } = wp.i18n;
|
||||
const { Toolbar, withAPIData, Dropdown } = wp.components;
|
||||
|
||||
/**
|
||||
* Attribute data cache. Needed because it takes a lot of API calls to generate attribute info.
|
||||
*/
|
||||
const PRODUCT_ATTRIBUTE_DATA = {};
|
||||
|
||||
/**
|
||||
* When the display mode is 'Attribute' search for and select product attributes to pull products from.
|
||||
*/
|
||||
export class ProductsAttributeSelect extends React.Component {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
|
||||
this.state = {
|
||||
selectedAttribute: '',
|
||||
selectedTerms: [],
|
||||
filterQuery: '',
|
||||
}
|
||||
|
||||
this.setSelectedAttribute = this.setSelectedAttribute.bind( this );
|
||||
this.addTerm = this.addTerm.bind( this );
|
||||
this.removeTerm = this.removeTerm.bind( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected attribute.
|
||||
*
|
||||
* @param slug string Attribute slug.
|
||||
*/
|
||||
setSelectedAttribute( slug ) {
|
||||
this.setState( {
|
||||
selectedAttribute: slug,
|
||||
selectedTerms: [],
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a term to the selected attribute's terms.
|
||||
*
|
||||
* @param slug string Term slug.
|
||||
*/
|
||||
addTerm( slug ) {
|
||||
let terms = this.state.selectedTerms;
|
||||
terms.push( slug );
|
||||
this.setState( {
|
||||
selectedTerms: terms,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a term from the selected attribute's terms.
|
||||
*
|
||||
* @param slug string Term slug.
|
||||
*/
|
||||
removeTerm( slug ) {
|
||||
let newTerms = [];
|
||||
for ( let termSlug of this.state.selectedTerms ) {
|
||||
if ( termSlug !== slug ) {
|
||||
newTerms.push( termSlug );
|
||||
}
|
||||
}
|
||||
|
||||
this.setState( {
|
||||
selectedTerms: newTerms,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the whole section.
|
||||
*/
|
||||
render() {
|
||||
|
||||
// @todo Remove this once data is moving around properly.
|
||||
console.log( "STATE UPDATED" );
|
||||
console.log( this.state );
|
||||
|
||||
return (
|
||||
<div className="product-attribute-select">
|
||||
TODO: Attribute select screen
|
||||
<ProductAttributeFilter />
|
||||
<ProductAttributeList
|
||||
selectedAttribute={ this.state.selectedAttribute }
|
||||
selectedTerms={ this.state.selectedTerms }
|
||||
setSelectedAttribute={ this.setSelectedAttribute.bind( this ) }
|
||||
addTerm={ this.addTerm.bind( this ) }
|
||||
removeTerm={ this.removeTerm.bind( this ) }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search area for filtering through the attributes list.
|
||||
*/
|
||||
const ProductAttributeFilter = () => {
|
||||
return (
|
||||
<div>
|
||||
<input id="product-attribute-search" type="search" placeholder={ __( 'Search for attributes' ) } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of attributes.
|
||||
*/
|
||||
const ProductAttributeList = withAPIData( ( props ) => {
|
||||
return {
|
||||
attributes: '/wc/v2/products/attributes'
|
||||
};
|
||||
} )( ( { attributes, selectedAttribute, selectedTerms, setSelectedAttribute, addTerm, removeTerm } ) => {
|
||||
if ( ! attributes.data ) {
|
||||
return __( 'Loading' );
|
||||
}
|
||||
|
||||
if ( 0 === attributes.data.length ) {
|
||||
return __( 'No attributes found' );
|
||||
}
|
||||
|
||||
let attributeElements = [];
|
||||
for ( let attribute of attributes.data ) {
|
||||
if ( PRODUCT_ATTRIBUTE_DATA.hasOwnProperty( attribute.slug ) ) {
|
||||
attributeElements.push( <ProductAttributeElement
|
||||
selectedAttribute={ selectedAttribute }
|
||||
selectedTerms={ selectedTerms }
|
||||
attribute={attribute}
|
||||
setSelectedAttribute={ setSelectedAttribute }
|
||||
addTerm={ addTerm }
|
||||
removeTerm={ removeTerm }
|
||||
/> );
|
||||
} else {
|
||||
attributeElements.push( <UncachedProductAttributeElement
|
||||
selectedAttribute={ selectedAttribute }
|
||||
selectedTerms={ selectedTerms }
|
||||
attribute={ attribute }
|
||||
setSelectedAttribute={ setSelectedAttribute }
|
||||
addTerm={ addTerm }
|
||||
removeTerm={ removeTerm }
|
||||
/> );
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="product-attributes-list">
|
||||
{ attributeElements }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Caches then renders a product attribute term element.
|
||||
*/
|
||||
const UncachedProductAttributeElement = withAPIData( ( props ) => {
|
||||
return {
|
||||
terms: '/wc/v2/products/attributes/' + props.attribute.id + '/terms'
|
||||
};
|
||||
} )( ( { terms, selectedAttribute, selectedTerms, attribute, setSelectedAttribute, addTerm, removeTerm } ) => {
|
||||
if ( ! terms.data ) {
|
||||
return __( 'Loading' );
|
||||
}
|
||||
|
||||
if ( 0 === terms.data.length ) {
|
||||
return __( 'No attribute options found' );
|
||||
}
|
||||
|
||||
// Populate cache.
|
||||
PRODUCT_ATTRIBUTE_DATA[ attribute.slug ] = { terms: [] };
|
||||
|
||||
let totalCount = 0;
|
||||
for ( let term of terms.data ) {
|
||||
totalCount += term.count;
|
||||
PRODUCT_ATTRIBUTE_DATA[ attribute.slug ].terms.push( term );
|
||||
}
|
||||
|
||||
PRODUCT_ATTRIBUTE_DATA[ attribute.slug ].count = totalCount;
|
||||
|
||||
return <ProductAttributeElement
|
||||
selectedAttribute={ selectedAttribute }
|
||||
selectedTerms={ selectedTerms }
|
||||
attribute={ attribute }
|
||||
setSelectedAttribute={ setSelectedAttribute }
|
||||
addTerm={ addTerm }
|
||||
removeTerm={ removeTerm }
|
||||
/>
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* A product attribute term element.
|
||||
*/
|
||||
class ProductAttributeElement extends React.Component {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
|
||||
this.handleAttributeChange = this.handleAttributeChange.bind( this );
|
||||
this.handleTermChange = this.handleTermChange.bind( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate and reset values when the selected attribute is changed.
|
||||
*
|
||||
* @param evt Event object
|
||||
*/
|
||||
handleAttributeChange( evt ) {
|
||||
const slug = evt.target.value;
|
||||
|
||||
if ( this.props.selectedAttribute === slug ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.setSelectedAttribute( slug );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or remove selected terms.
|
||||
*
|
||||
* @param evt Event object
|
||||
*/
|
||||
handleTermChange( evt ) {
|
||||
if ( evt.target.checked ) {
|
||||
this.props.addTerm( evt.target.value );
|
||||
} else {
|
||||
this.props.removeTerm( evt.target.value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the details for one attribute.
|
||||
*/
|
||||
render() {
|
||||
const attribute = PRODUCT_ATTRIBUTE_DATA[ this.props.attribute.slug ];
|
||||
const isSelected = this.props.selectedAttribute === this.props.attribute.slug;
|
||||
|
||||
let attributeTerms = null;
|
||||
if ( isSelected ) {
|
||||
attributeTerms = (
|
||||
<ul className="product-attribute-terms">
|
||||
{ attribute.terms.map( ( term ) => (
|
||||
<li className="product-attribute-term">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
value={ term.slug }
|
||||
onChange={ this.handleTermChange }
|
||||
checked={ this.props.selectedTerms.includes( term.slug ) }
|
||||
/>
|
||||
{ term.name }
|
||||
<span className="product-attribute-count">{ term.count }</span>
|
||||
</label>
|
||||
</li>
|
||||
) ) }
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="product-attribute">
|
||||
<div className="product-attribute-name">
|
||||
<label>
|
||||
<input type="radio"
|
||||
value={ this.props.attribute.slug }
|
||||
onClick={ this.handleAttributeChange }
|
||||
checked={ isSelected }
|
||||
/>
|
||||
{ this.props.attribute.name }
|
||||
<span className="product-attribute-count">{ attribute.count }</span>
|
||||
</label>
|
||||
</div>
|
||||
{ attributeTerms }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue