2018-02-13 19:03:53 +00:00
const { _ _ } = wp . i18n ;
2018-02-26 20:13:08 +00:00
const { RawHTML } = wp . element ;
2018-06-01 16:19:25 +00:00
const { registerBlockType } = wp . blocks ;
const { InspectorControls , BlockControls } = wp . editor ;
2018-03-15 17:20:43 +00:00
const { Toolbar , withAPIData , Dropdown , Dashicon , RangeControl , Tooltip , SelectControl } = wp . components ;
2018-02-13 19:03:53 +00:00
2018-02-15 18:16:14 +00:00
import { ProductsSpecificSelect } from './views/specific-select.jsx' ;
import { ProductsCategorySelect } from './views/category-select.jsx' ;
2018-04-06 20:12:26 +00:00
import { ProductsAttributeSelect , getAttributeSlug , getAttributeID } from './views/attribute-select.jsx' ;
2018-02-15 17:42:24 +00:00
/ * *
* A setting has the following properties :
* title - Display title of the setting .
* description - Display description of the setting .
* value - Display setting slug to set when selected .
* group _container - ( optional ) If set the setting is a parent container .
2018-04-25 19:03:08 +00:00
* no _orderby - ( optional ) If set the setting does not allow orderby settings .
2018-02-15 17:42:24 +00:00
* /
const PRODUCTS _BLOCK _DISPLAY _SETTINGS = {
'specific' : {
title : _ _ ( 'Individual products' ) ,
description : _ _ ( 'Hand-pick which products to display' ) ,
value : 'specific' ,
} ,
'category' : {
title : _ _ ( 'Product category' ) ,
description : _ _ ( 'Display products from a specific category or multiple categories' ) ,
value : 'category' ,
} ,
'filter' : {
title : _ _ ( 'Filter products' ) ,
description : _ _ ( 'E.g. featured products, or products with a specific attribute like size or color' ) ,
value : 'filter' ,
group _container : 'filter'
} ,
'featured' : {
title : _ _ ( 'Featured products' ) ,
description : '' ,
value : 'featured' ,
} ,
'on_sale' : {
title : _ _ ( 'On sale' ) ,
description : '' ,
value : 'on_sale' ,
} ,
2018-04-25 19:03:08 +00:00
'best_selling' : {
title : _ _ ( 'Best sellers' ) ,
description : '' ,
value : 'best_selling' ,
no _orderby : true ,
} ,
'top_rated' : {
title : _ _ ( 'Top rated' ) ,
description : '' ,
value : 'top_rated' ,
no _orderby : true ,
} ,
2018-02-15 17:42:24 +00:00
'attribute' : {
title : _ _ ( 'Attribute' ) ,
description : '' ,
value : 'attribute' ,
} ,
'all' : {
title : _ _ ( 'All products' ) ,
2018-04-03 17:33:20 +00:00
description : _ _ ( 'Display all products ordered chronologically, alphabetically, by price, by rating or by sales' ) ,
2018-02-15 17:42:24 +00:00
value : 'all' ,
}
} ;
2018-04-25 19:03:08 +00:00
/ * *
* Returns whether or not a display scope supports orderby options .
*
* @ param string display The display scope slug .
* @ return bool
* /
function supportsOrderby ( display ) {
return ! ( PRODUCTS _BLOCK _DISPLAY _SETTINGS . hasOwnProperty ( display )
&& PRODUCTS _BLOCK _DISPLAY _SETTINGS [ display ] . hasOwnProperty ( 'no_orderby' )
&& PRODUCTS _BLOCK _DISPLAY _SETTINGS [ display ] . no _orderby ) ;
}
2018-02-13 19:03:53 +00:00
/ * *
* One option from the list of all available ways to display products .
* /
class ProductsBlockSettingsEditorDisplayOption extends React . Component {
render ( ) {
2018-02-23 14:57:27 +00:00
let icon = 'arrow-right-alt2' ;
if ( 'filter' === this . props . value && this . props . extended ) {
icon = 'arrow-down-alt2' ;
}
2018-03-14 15:11:36 +00:00
let classes = 'wc-products-display-options__option wc-products-display-options__option--' + this . props . value ;
if ( this . props . current === this . props . value ) {
icon = 'yes' ;
classes += ' wc-products-display-options__option--current' ;
}
2018-02-13 19:03:53 +00:00
return (
2018-03-15 15:07:09 +00:00
< div className = { classes } onClick = { ( ) => { this . props . current !== this . props . value && this . props . update _display _callback ( this . props . value ) } } >
2018-02-26 18:26:08 +00:00
< div className = "wc-products-display-options__option-content" >
2018-02-23 14:57:27 +00:00
< span className = "wc-products-display-options__option-title" > { this . props . title } < / span >
< p className = "wc-products-display-options__option-description" > { this . props . description } < / p >
< / div >
< div className = "wc-products-display-options__icon" >
< Dashicon icon = { icon } / >
< / div >
2018-02-13 19:03:53 +00:00
< / div >
) ;
}
}
/ * *
* A list of all available ways to display products .
* /
class ProductsBlockSettingsEditorDisplayOptions extends React . Component {
2018-02-23 18:22:05 +00:00
/ * *
* Constructor .
* /
constructor ( props ) {
super ( props ) ;
this . setWrapperRef = this . setWrapperRef . bind ( this ) ;
this . handleClickOutside = this . handleClickOutside . bind ( this ) ;
}
/ * *
* Hook in the listener for closing menu when clicked outside .
* /
componentDidMount ( ) {
if ( this . props . existing ) {
document . addEventListener ( 'mousedown' , this . handleClickOutside ) ;
}
}
/ * *
* Remove the listener for closing menu when clicked outside .
* /
componentWillUnmount ( ) {
if ( this . props . existing ) {
document . removeEventListener ( 'mousedown' , this . handleClickOutside ) ;
}
}
/ * *
* Set the wrapper reference .
*
* @ param node DOMNode
* /
setWrapperRef ( node ) {
this . wrapperRef = node ;
}
/ * *
* Close the menu when user clicks outside the search area .
* /
handleClickOutside ( evt ) {
2018-02-26 18:26:08 +00:00
if ( this . wrapperRef && ! this . wrapperRef . contains ( event . target ) && 'wc-products-settings-heading__change-button button-link' !== event . target . getAttribute ( 'class' ) ) {
2018-02-23 18:22:05 +00:00
this . props . closeMenu ( ) ;
}
}
/ * *
* Render the list of options .
* /
2018-02-15 17:42:24 +00:00
render ( ) {
2018-02-23 14:57:27 +00:00
let classes = 'wc-products-display-options' ;
if ( this . props . extended ) {
classes += ' wc-products-display-options--extended' ;
}
2018-02-13 19:03:53 +00:00
if ( this . props . existing ) {
2018-02-23 14:57:27 +00:00
classes += ' wc-products-display-options--popover' ;
2018-02-13 19:03:53 +00:00
}
2018-02-15 17:42:24 +00:00
let display _settings = [ ] ;
for ( var setting _key in PRODUCTS _BLOCK _DISPLAY _SETTINGS ) {
2018-03-14 15:11:36 +00:00
display _settings . push ( < ProductsBlockSettingsEditorDisplayOption { ...PRODUCTS_BLOCK_DISPLAY_SETTINGS [ setting_key ] } update_display_callback = { this . props . update _display _callback } extended = { this . props . extended } current = { this . props . current } / > ) ;
2018-02-15 17:42:24 +00:00
}
2018-02-23 14:57:27 +00:00
let arrow = < span className = "wc-products-display-options--popover__arrow" > < / span > ;
let description = < p className = "wc-products-block-description" > { _ _ ( 'Choose which products you\'d like to display:' ) } < / p > ;
2018-02-15 17:42:24 +00:00
2018-02-13 19:03:53 +00:00
return (
2018-02-23 18:22:05 +00:00
< div className = { classes } ref = { this . setWrapperRef } >
2018-02-23 14:57:27 +00:00
{ this . props . existing && arrow }
{ ! this . props . existing && description }
2018-02-15 17:42:24 +00:00
{ display _settings }
2018-02-13 19:03:53 +00:00
< / div >
) ;
}
}
/ * *
* The products block when in Edit mode .
* /
class ProductsBlockSettingsEditor extends React . Component {
/ * *
* Constructor .
* /
constructor ( props ) {
super ( props ) ;
this . state = {
display : props . selected _display ,
menu _visible : props . selected _display ? false : true ,
2018-02-15 17:42:24 +00:00
expanded _group : '' ,
2018-02-13 19:03:53 +00:00
}
this . updateDisplay = this . updateDisplay . bind ( this ) ;
2018-02-23 18:22:05 +00:00
this . closeMenu = this . closeMenu . bind ( this ) ;
2018-02-13 19:03:53 +00:00
}
/ * *
* Update the display settings for the block .
*
2018-02-15 17:42:24 +00:00
* @ param value String
2018-02-13 19:03:53 +00:00
* /
updateDisplay ( value ) {
2018-02-15 17:42:24 +00:00
// If not a group update display.
let new _state = {
2018-02-13 19:03:53 +00:00
display : value ,
menu _visible : false ,
2018-02-15 17:42:24 +00:00
expanded _group : '' ,
} ;
2018-02-15 19:54:03 +00:00
const is _group = 'undefined' !== PRODUCTS _BLOCK _DISPLAY _SETTINGS [ value ] . group _container && PRODUCTS _BLOCK _DISPLAY _SETTINGS [ value ] . group _container ;
if ( is _group ) {
// If the group has not been expanded, expand it.
2018-02-15 17:42:24 +00:00
new _state = {
menu _visible : true ,
expanded _group : value ,
}
2018-02-15 19:54:03 +00:00
// If the group has already been expanded, collapse it.
if ( this . state . expanded _group === PRODUCTS _BLOCK _DISPLAY _SETTINGS [ value ] . group _container ) {
new _state . expanded _group = '' ;
}
}
2018-02-13 19:03:53 +00:00
2018-02-15 17:42:24 +00:00
this . setState ( new _state ) ;
// Only update the display setting if a non-group setting was selected.
2018-02-15 19:54:03 +00:00
if ( ! is _group ) {
2018-02-15 17:42:24 +00:00
this . props . update _display _callback ( value ) ;
}
2018-02-13 19:03:53 +00:00
}
2018-02-23 18:22:05 +00:00
closeMenu ( ) {
this . setState ( {
2018-04-04 19:26:23 +00:00
menu _visible : false ,
2018-02-23 18:22:05 +00:00
} ) ;
}
2018-02-13 19:03:53 +00:00
/ * *
* Render the display settings dropdown and any extra contextual settings .
* /
render ( ) {
let extra _settings = null ;
if ( 'specific' === this . state . display ) {
2018-02-16 19:40:19 +00:00
extra _settings = < ProductsSpecificSelect { ...this.props } / > ;
2018-02-13 19:03:53 +00:00
} else if ( 'category' === this . state . display ) {
extra _settings = < ProductsCategorySelect { ...this.props } / > ;
2018-02-15 18:16:14 +00:00
} else if ( 'attribute' === this . state . display ) {
2018-02-20 19:47:50 +00:00
extra _settings = < ProductsAttributeSelect { ...this.props } / >
2018-02-13 19:03:53 +00:00
}
2018-03-14 15:11:36 +00:00
const menu = this . state . menu _visible ? < ProductsBlockSettingsEditorDisplayOptions extended = { this . state . expanded _group ? true : false } existing = { this . state . display ? true : false } current = { this . state . display } closeMenu = { this . closeMenu } update_display_callback = { this . updateDisplay } / > : null ;
2018-02-13 19:03:53 +00:00
2018-02-15 17:42:24 +00:00
let heading = null ;
if ( this . state . display ) {
2018-04-25 19:03:08 +00:00
const group _options = [ 'featured' , 'on_sale' , 'attribute' , 'best_selling' , 'top_rated' ] ;
2018-03-14 15:15:34 +00:00
let should _group _expand = group _options . includes ( this . state . display ) ? this . state . display : '' ;
2018-03-14 15:11:36 +00:00
let menu _link = < button type = "button" className = "wc-products-settings-heading__change-button button-link" onClick = { ( ) => { this . setState ( { menu _visible : ! this . state . menu _visible , expanded _group : should _group _expand } ) } } > { _ _ ( 'Display different products' ) } < / button > ;
2018-02-15 17:42:24 +00:00
heading = (
2018-02-23 14:57:27 +00:00
< div className = "wc-products-settings-heading" >
< div className = "wc-products-settings-heading__current" >
2018-02-15 17:42:24 +00:00
{ _ _ ( 'Displaying ' ) }
< strong > { _ _ ( PRODUCTS _BLOCK _DISPLAY _SETTINGS [ this . state . display ] . title ) } < / strong >
< / div >
2018-02-23 14:57:27 +00:00
< div className = "wc-products-settings-heading__change" >
2018-02-15 17:42:24 +00:00
{ menu _link }
< / div >
< / div >
) ;
2018-02-13 19:03:53 +00:00
}
2018-03-02 18:05:13 +00:00
let done _button = < button type = "button" className = "button wc-products-settings__footer-button" onClick = { this . props . done _callback } > { _ _ ( 'Done' ) } < / button > ;
2018-03-09 17:57:08 +00:00
if ( [ '' , 'specific' , 'category' , 'attribute' ] . includes ( this . state . display ) && ! this . props . selected _display _setting . length ) {
2018-03-02 18:05:13 +00:00
const done _tooltips = {
2018-03-12 17:03:51 +00:00
'' : _ _ ( 'Please select which products you\'d like to display' ) ,
2018-03-02 18:05:13 +00:00
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 >
) ;
}
2018-02-13 19:03:53 +00:00
return (
2018-02-26 17:10:45 +00:00
< div className = { 'wc-products-settings ' + ( this . state . expanded _group ? 'expanded-group-' + this . state . expanded _group : '' ) } >
2018-04-03 16:22:01 +00:00
< h4 className = "wc-products-settings__title" > < Dashicon icon = { 'screenoptions' } / > { _ _ ( 'Products' ) } < / h4 >
2018-02-13 19:03:53 +00:00
{ heading }
{ menu }
{ extra _settings }
2018-02-26 17:10:45 +00:00
< div className = "wc-products-settings__footer" >
2018-03-02 18:05:13 +00:00
{ done _button }
2018-02-13 19:03:53 +00:00
< / div >
< / div >
) ;
}
}
/ * *
* One product in the product block preview .
* /
class ProductPreview extends React . Component {
render ( ) {
const { attributes , product } = this . props ;
let image = null ;
if ( product . images . length ) {
image = < img src = { product . images [ 0 ] . src } / >
}
return (
< div className = "product-preview" >
{ image }
2018-02-22 20:11:53 +00:00
< div className = "product-title" > { product . name } < / div >
2018-03-09 19:45:53 +00:00
< div className = "product-price" dangerouslySetInnerHTML = { { _ _html : product . price _html } } / >
2018-02-22 20:11:53 +00:00
< span className = "product-add-to-cart" > { _ _ ( 'Add to cart' ) } < / span >
2018-02-13 19:03:53 +00:00
< / div >
) ;
}
}
/ * *
* Renders a preview of what the block will look like with current settings .
* /
const ProductsBlockPreview = withAPIData ( ( { attributes } ) => {
2018-04-02 18:46:57 +00:00
const { columns , rows , display , display _setting , orderby } = attributes ;
2018-02-13 19:03:53 +00:00
let query = {
2018-04-02 18:46:57 +00:00
per _page : rows * columns ,
2018-02-13 19:03:53 +00:00
} ;
if ( 'specific' === display ) {
2018-02-23 20:05:44 +00:00
query . include = display _setting . join ( ',' ) ;
2018-04-10 14:14:39 +00:00
query . per _page = display _setting . length ;
2018-02-13 19:03:53 +00:00
} else if ( 'category' === display ) {
query . category = display _setting . join ( ',' ) ;
2018-02-21 19:53:36 +00:00
} else if ( 'attribute' === display && display _setting . length ) {
2018-04-06 20:12:26 +00:00
query . attribute = getAttributeSlug ( display _setting [ 0 ] ) ;
2018-02-21 19:53:36 +00:00
if ( display _setting . length > 1 ) {
query . attribute _term = display _setting . slice ( 1 ) . join ( ',' ) ;
}
2018-02-26 19:58:19 +00:00
} else if ( 'featured' === display ) {
query . featured = 1 ;
} else if ( 'on_sale' === display ) {
query . on _sale = 1 ;
2018-02-13 19:03:53 +00:00
}
2018-04-25 19:03:08 +00:00
// @todo Add support for orderby by sales, rating, and price here when we switch to V3 API.
if ( supportsOrderby ( display ) && ( 'title' === orderby || 'date' === orderby ) ) {
2018-03-15 17:20:43 +00:00
query . orderby = orderby ;
if ( 'title' === orderby ) {
query . order = 'asc' ;
}
}
2018-02-13 19:03:53 +00:00
let query _string = '?' ;
for ( const key of Object . keys ( query ) ) {
query _string += key + '=' + query [ key ] + '&' ;
}
return {
products : '/wc/v2/products' + query _string
} ;
} ) ( ( { products , attributes } ) => {
if ( ! products . data ) {
return _ _ ( 'Loading' ) ;
}
if ( 0 === products . data . length ) {
return _ _ ( 'No products found' ) ;
}
2018-04-02 18:46:57 +00:00
const classes = "wc-products-block-preview cols-" + attributes . columns ;
2018-02-13 19:03:53 +00:00
return (
< div className = { classes } >
{ products . data . map ( ( product ) => (
< ProductPreview key = { product . id } product = { product } attributes = { attributes } / >
) ) }
< / div >
) ;
} ) ;
2018-04-05 19:25:59 +00:00
/ * *
* Information about current block settings rendered in the sidebar .
* /
const ProductsBlockSidebarInfo = withAPIData ( ( { attributes } ) => {
const { display , display _setting } = attributes ;
if ( 'attribute' === display && display _setting . length ) {
2018-04-06 20:12:26 +00:00
const ID = getAttributeID ( display _setting [ 0 ] ) ;
const terms = display _setting . slice ( 1 ) . join ( ', ' ) ;
const endpoints = {
attributeInfo : '/wc/v2/products/attributes/' + ID ,
}
2018-04-05 19:25:59 +00:00
2018-04-06 20:12:26 +00:00
if ( terms . length ) {
endpoints . termInfo = '/wc/v2/products/attributes/' + ID + '/terms?include=' + terms ;
}
return endpoints ;
2018-04-05 19:25:59 +00:00
} else if ( 'category' === display && display _setting . length ) {
return {
2018-04-06 20:12:26 +00:00
categoriesInfo : '/wc/v2/products/categories?include=' + display _setting . join ( ',' ) ,
2018-04-05 19:25:59 +00:00
} ;
}
return { } ;
2018-04-06 20:12:26 +00:00
} ) ( ( { attributes , categoriesInfo , attributeInfo , termInfo } ) => {
2018-04-05 19:25:59 +00:00
2018-04-06 20:12:26 +00:00
let descriptions = [
// Standard description of selected scope.
PRODUCTS _BLOCK _DISPLAY _SETTINGS [ attributes . display ] . title
] ;
2018-04-05 19:25:59 +00:00
2018-04-06 20:12:26 +00:00
// Description of categories selected scope.
if ( categoriesInfo && categoriesInfo . data && categoriesInfo . data . length ) {
let descriptionText = _ _ ( 'Product categories: ' ) ;
const categories = [ ] ;
for ( let category of categoriesInfo . data ) {
categories . push ( category . name ) ;
2018-04-05 19:25:59 +00:00
}
2018-04-06 20:12:26 +00:00
descriptionText += categories . join ( ', ' ) ;
2018-04-05 19:25:59 +00:00
2018-04-06 20:12:26 +00:00
descriptions = [
descriptionText
] ;
2018-04-05 19:25:59 +00:00
2018-04-06 20:12:26 +00:00
// Description of attributes selected scope.
} else if ( attributeInfo && attributeInfo . data ) {
descriptions = [
_ _ ( 'Attribute: ' ) + attributeInfo . data . name
] ;
2018-04-05 19:25:59 +00:00
2018-04-06 20:12:26 +00:00
if ( termInfo && termInfo . data && termInfo . data . length ) {
let termDescriptionText = _ _ ( "Terms: " ) ;
const terms = [ ]
for ( const term of termInfo . data ) {
terms . push ( term . name ) ;
}
termDescriptionText += terms . join ( ', ' ) ;
descriptions . push ( termDescriptionText ) ;
}
2018-04-05 19:25:59 +00:00
}
return (
2018-04-06 21:03:06 +00:00
< div >
2018-04-06 20:12:26 +00:00
{ descriptions . map ( ( description ) => (
< div className = "scope-description" > { description } < / div >
) ) }
2018-04-05 19:25:59 +00:00
< / div >
) ;
} ) ;
2018-04-04 19:26:23 +00:00
/ * *
* The main products block UI .
* /
class ProductsBlock extends React . Component {
/ * *
* Constructor .
* /
constructor ( props ) {
super ( props ) ;
this . getInspectorControls = this . getInspectorControls . bind ( this ) ;
this . getToolbarControls = this . getToolbarControls . bind ( this ) ;
this . getBlockDescription = this . getBlockDescription . bind ( this ) ;
this . getPreview = this . getPreview . bind ( this ) ;
this . getSettingsEditor = this . getSettingsEditor . bind ( this ) ;
}
/ * *
* Get the components for the sidebar settings area that is rendered while focused on a Products block .
*
* @ return Component
* /
getInspectorControls ( ) {
const { attributes , setAttributes } = this . props ;
const { rows , columns , display , display _setting , orderby , edit _mode } = attributes ;
let columnControl = (
< RangeControl
label = { _ _ ( 'Columns' ) }
value = { columns }
onChange = { ( value ) => setAttributes ( { columns : value } ) }
min = { wc _product _block _data . min _columns }
max = { wc _product _block _data . max _columns }
/ >
) ;
2018-04-09 12:53:38 +00:00
2018-04-25 19:03:08 +00:00
let orderControl = null ;
if ( supportsOrderby ( 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 } ) }
/ >
) ;
}
2018-04-11 15:44:56 +00:00
// Row settings don't make sense for specific-selected products display.
let rowControl = null ;
2018-04-04 19:26:23 +00:00
if ( 'specific' !== display ) {
2018-04-11 15:44:56 +00:00
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 }
2018-04-04 19:26:23 +00:00
/ >
) ;
}
return (
< InspectorControls key = "inspector" >
2018-04-06 21:03:06 +00:00
{ this . getBlockDescription ( ) }
2018-04-04 19:26:23 +00:00
< h3 > { _ _ ( 'Layout' ) } < / h3 >
{ columnControl }
2018-04-11 15:44:56 +00:00
{ rowControl }
2018-04-04 19:26:23 +00:00
{ orderControl }
< / InspectorControls >
) ;
}
/ * *
* Get the components for the toolbar area that appears on top of the block when focused .
*
* @ return Component
* /
getToolbarControls ( ) {
let props = this . props ;
const { attributes , setAttributes } = props ;
const { display , display _setting , edit _mode } = attributes ;
// Edit button should not do anything if valid product selection has not been made.
const shouldDisableEditButton = [ '' , 'specific' , 'category' , 'attribute' ] . includes ( display ) && ! display _setting . length ;
const editButton = [
{
icon : 'edit' ,
title : _ _ ( 'Edit' ) ,
onClick : shouldDisableEditButton ? function ( ) { } : ( ) => setAttributes ( { edit _mode : ! edit _mode } ) ,
isActive : edit _mode ,
} ,
] ;
return (
< BlockControls key = "controls" >
< Toolbar controls = { editButton } / >
< / BlockControls >
) ;
}
/ * *
* Get a description of the current block settings .
*
* @ return Component
* /
getBlockDescription ( ) {
const { attributes , setAttributes } = this . props ;
const { display , display _setting , edit _mode } = attributes ;
if ( ! display . length ) {
return null ;
}
2018-04-06 20:12:26 +00:00
function editQuicklinkHandler ( ) {
setAttributes ( {
edit _mode : true ,
} ) ;
// @todo center in view
2018-04-04 19:26:23 +00:00
}
2018-04-06 20:12:26 +00:00
let editQuickLink = null ;
if ( ! attributes . edit _mode ) {
editQuickLink = (
2018-04-06 21:03:06 +00:00
< div className = "wc-products-scope-description--edit-quicklink" >
2018-04-06 20:12:26 +00:00
< a onClick = { editQuicklinkHandler } > { _ _ ( 'Edit' ) } < / a >
< / div >
) ;
}
2018-04-05 19:25:59 +00:00
2018-04-04 19:26:23 +00:00
return (
2018-04-06 21:03:06 +00:00
< div className = "wc-products-scope-descriptions" >
2018-04-09 12:53:38 +00:00
< div className = "wc-products-scope-details" >
< h3 > { _ _ ( 'Current Source' ) } < / h3 >
< ProductsBlockSidebarInfo attributes = { attributes } / >
< / div >
2018-04-06 20:12:26 +00:00
{ editQuickLink }
2018-04-06 21:03:06 +00:00
< / div >
2018-04-04 19:26:23 +00:00
) ;
}
/ * *
* Get the block preview component for preview mode .
*
* @ return Component
* /
getPreview ( ) {
return < ProductsBlockPreview attributes = { this . props . attributes } / > ;
}
/ * *
* Get the block edit component for edit mode .
*
* @ return Component
* /
getSettingsEditor ( ) {
const { attributes , setAttributes } = this . props ;
const { display , display _setting } = attributes ;
const update _display _callback = ( value ) => {
// These options have setting screens that need further input from the user, so keep edit mode open.
const needsFurtherSettings = [ 'specific' , 'attribute' , 'category' ] ;
if ( display !== value ) {
setAttributes ( {
display : value ,
display _setting : [ ] ,
edit _mode : needsFurtherSettings . includes ( value ) ,
} ) ;
}
} ;
return (
< ProductsBlockSettingsEditor
2018-04-11 15:44:56 +00:00
attributes = { attributes }
2018-04-04 19:26:23 +00:00
selected _display = { display }
selected _display _setting = { display _setting }
update _display _callback = { update _display _callback }
update _display _setting _callback = { ( value ) => setAttributes ( { display _setting : value } ) }
done _callback = { ( ) => setAttributes ( { edit _mode : false } ) }
/ >
) ;
}
render ( ) {
2018-06-01 16:19:25 +00:00
const { attributes } = this . props ;
2018-04-04 19:26:23 +00:00
const { edit _mode } = attributes ;
return [
2018-06-01 16:19:25 +00:00
this . getInspectorControls ( ) ,
this . getToolbarControls ( ) ,
2018-04-04 19:26:23 +00:00
edit _mode ? this . getSettingsEditor ( ) : this . getPreview ( ) ,
] ;
}
}
2018-02-13 19:03:53 +00:00
/ * *
* Register and run the products block .
* /
registerBlockType ( 'woocommerce/products' , {
title : _ _ ( 'Products' ) ,
2018-04-03 16:22:01 +00:00
icon : 'screenoptions' ,
2018-02-13 19:03:53 +00:00
category : 'widgets' ,
2018-04-04 19:26:23 +00:00
description : _ _ ( 'Display a grid of products from a variety of sources.' ) ,
2018-02-13 19:03:53 +00:00
attributes : {
/ * *
* Number of columns .
* /
columns : {
type : 'number' ,
2018-03-09 19:45:53 +00:00
default : wc _product _block _data . default _columns ,
2018-02-13 19:03:53 +00:00
} ,
/ * *
* Number of rows .
* /
rows : {
type : 'number' ,
2018-04-03 17:26:56 +00:00
default : wc _product _block _data . default _rows ,
2018-02-13 19:03:53 +00:00
} ,
/ * *
* What types of products to display . 'all' , 'specific' , or 'category' .
* /
display : {
type : 'string' ,
default : '' ,
} ,
/ * *
* Which products to display if 'display' is 'specific' or 'category' . Array of product ids or category slugs depending on setting .
* /
display _setting : {
type : 'array' ,
default : [ ] ,
} ,
2018-03-15 17:20:43 +00:00
/ * *
2018-03-16 16:26:42 +00:00
* How to order the products : 'date' , 'popularity' , 'price_asc' , 'price_desc' 'rating' , 'title' .
2018-03-15 17:20:43 +00:00
* /
orderby : {
type : 'string' ,
default : 'date' ,
} ,
2018-02-13 19:03:53 +00:00
/ * *
* Whether the block is in edit or preview mode .
* /
edit _mode : {
type : 'boolean' ,
default : true ,
} ,
} ,
/ * *
* Renders and manages the block .
* /
edit ( props ) {
2018-04-04 19:26:23 +00:00
return < ProductsBlock { ...props } / >
2018-02-13 19:03:53 +00:00
} ,
/ * *
* Save the block content in the post content . Block content is saved as a products shortcode .
*
* @ return string
* /
save ( props ) {
2018-04-02 18:46:57 +00:00
const { rows , columns , display , display _setting , orderby } = props . attributes ;
2018-02-13 19:03:53 +00:00
let shortcode _atts = new Map ( ) ;
2018-04-11 15:44:56 +00:00
if ( 'specific' !== display ) {
shortcode _atts . set ( 'limit' , rows * columns ) ;
2018-02-13 19:03:53 +00:00
}
2018-04-02 18:46:57 +00:00
shortcode _atts . set ( 'columns' , columns ) ;
2018-02-13 19:03:53 +00:00
if ( 'specific' === display ) {
2018-03-02 19:18:42 +00:00
shortcode _atts . set ( 'ids' , display _setting . join ( ',' ) ) ;
2018-02-26 19:12:21 +00:00
} else if ( 'category' === display ) {
2018-02-13 19:03:53 +00:00
shortcode _atts . set ( 'category' , display _setting . join ( ',' ) ) ;
2018-02-26 19:12:21 +00:00
} else if ( 'featured' === display ) {
shortcode _atts . set ( 'visibility' , 'featured' ) ;
} else if ( 'on_sale' === display ) {
shortcode _atts . set ( 'on_sale' , '1' ) ;
2018-04-25 19:03:08 +00:00
} else if ( 'best_selling' === display ) {
shortcode _atts . set ( 'best_selling' , '1' ) ;
} else if ( 'top_rated' === display ) {
shortcode _atts . set ( 'top_rated' , '1' ) ;
2018-02-26 19:12:21 +00:00
} else if ( 'attribute' === display ) {
2018-04-06 20:12:26 +00:00
const attribute = display _setting . length ? getAttributeSlug ( display _setting [ 0 ] ) : '' ;
2018-02-26 19:12:21 +00:00
const terms = display _setting . length > 1 ? display _setting . slice ( 1 ) . join ( ',' ) : '' ;
shortcode _atts . set ( 'attribute' , attribute ) ;
if ( terms . length ) {
shortcode _atts . set ( 'terms' , terms ) ;
}
2018-02-13 19:03:53 +00:00
}
2018-04-25 19:03:08 +00:00
if ( supportsOrderby ( display ) ) {
2018-03-16 16:43:25 +00:00
if ( 'price_desc' === orderby ) {
shortcode _atts . set ( 'orderby' , 'price' ) ;
shortcode _atts . set ( 'order' , 'DESC' )
} else if ( 'price_asc' === orderby ) {
shortcode _atts . set ( 'orderby' , 'price' ) ;
shortcode _atts . set ( 'order' , 'ASC' )
} else {
shortcode _atts . set ( 'orderby' , orderby ) ;
}
2018-03-15 17:20:43 +00:00
}
2018-02-13 19:03:53 +00:00
// Build the shortcode string out of the set shortcode attributes.
let shortcode = '[products' ;
for ( let [ key , value ] of shortcode _atts ) {
shortcode += ' ' + key + '="' + value + '"' ;
}
shortcode += ']' ;
2018-02-26 20:13:08 +00:00
return < RawHTML > { shortcode } < / RawHTML > ;
2018-02-13 19:03:53 +00:00
} ,
} ) ;