diff --git a/src/block/design-library/edit.js b/src/block/design-library/edit.js
index 2f4643b07c..71a300db50 100644
--- a/src/block/design-library/edit.js
+++ b/src/block/design-library/edit.js
@@ -116,7 +116,7 @@ const Edit = props => {
const spacingSize = ! presetMarks || ! Array.isArray( presetMarks ) ? 120 : presetMarks[ presetMarks.length - 2 ].value
// Replaces the current block with a block made out of attributes.
- const createBlockWithAttributes = async ( category, blockName, attributes, innerBlocks, substituteBlocks, parentClientId ) => {
+ const createBlockWithAttributes = async ( category, blockName, attributes, innerBlocks, substituteBlocks, parentClientId, type ) => {
const disabledBlocks = settings.stackable_block_states || {} // eslint-disable-line camelcase
// Recursively substitute core blocks to disabled Stackable blocks
@@ -207,7 +207,7 @@ const Edit = props => {
innerBlocks = block[ 0 ].innerBlocks
const isDesignLibraryDevMode = devMode && localStorage.getItem( 'stk__design_library__dev_mode' ) === '1'
- if ( ! isDesignLibraryDevMode ) {
+ if ( ! isDesignLibraryDevMode && type !== 'saved' ) {
if ( category !== 'Header' ) {
if ( ! parentClientId && attributes.hasBackground ) {
attributes.blockMargin = {
@@ -255,14 +255,16 @@ const Edit = props => {
const blocks = []
for ( const blockDesign of designs ) {
- const { designData, category } = blockDesign
+ const {
+ designData, category, type,
+ } = blockDesign
for ( const patterns of designData ) {
const {
name, attributes, innerBlocks,
} = patterns
if ( name && attributes ) {
- const block = await createBlockWithAttributes( category, name, applyFilters( 'stackable.design-library.attributes', attributes ), innerBlocks || [], substituteBlocks, parentClientId )
+ const block = await createBlockWithAttributes( category, name, applyFilters( 'stackable.design-library.attributes', attributes ), innerBlocks || [], substituteBlocks, parentClientId, type )
blocks.push( block )
} else {
console.error( 'Design library selection failed: No block data found' ) // eslint-disable-line no-console
@@ -336,14 +338,16 @@ const Edit = props => {
_designs.forEach( design => {
const {
- designData, blocksForSubstitution, category,
+ designData, blocksForSubstitution, category, type: designType,
} = design
if ( blocksForSubstitution.size ) {
disabledBlocks = disabledBlocks.union( blocksForSubstitution )
}
- designs.push( { designData, category } )
+ designs.push( {
+ designData, category, type: designType,
+ } )
} )
designsRef.current = designs
diff --git a/src/components/pro-control/index.js b/src/components/pro-control/index.js
index fce309e547..0d5278cb3b 100644
--- a/src/components/pro-control/index.js
+++ b/src/components/pro-control/index.js
@@ -150,6 +150,14 @@ const LABELS = {
{ __( 'Override styles while keeping them synced', i18n ) }
,
},
+ 'design-library-saved-patterns': {
+ title: __( 'Design Library Saved Patterns', i18n ),
+ description:
+ - { __( 'Save entire block layouts in a click', i18n ) }
+ - { __( 'Apply styling options to each saved pattern', i18n ) }
+ - { __( 'Import and export your custom patterns across sites', i18n ) }
+
,
+ },
}
const ProControl = props => {
diff --git a/src/design-library/index.js b/src/design-library/index.js
index 72c87d36cb..3c5a9adca5 100644
--- a/src/design-library/index.js
+++ b/src/design-library/index.js
@@ -35,7 +35,7 @@ export const fetchDesignLibrary = async ( forceReset = false, version = '', type
}
}
- return designLibrary[ type ]?.[ version || LATEST_API_VERSION ] ?? designLibrary[ type ]
+ return designLibrary[ type ]?.[ version || LATEST_API_VERSION ] ?? designLibrary[ type ] ?? {}
}
export const fetchDesign = async designId => {
@@ -82,8 +82,9 @@ export const filterDesigns = async ( {
library = [],
plan: isPlan = '',
category: isCategory = '',
+ type = 'patterns',
} ) => {
- if ( isPlan ) {
+ if ( isPlan && type !== 'saved' ) {
library = library.filter( ( { plan } ) => plan === isPlan )
}
diff --git a/src/design-library/init.php b/src/design-library/init.php
index e48bb1a6d3..627863ea68 100644
--- a/src/design-library/init.php
+++ b/src/design-library/init.php
@@ -29,6 +29,8 @@ class Stackable_Design_Library {
public function __construct() {
add_action( 'rest_api_init', array( $this, 'register_route' ) );
+ add_action( 'register_stackable_global_settings', array( $this, 'register_saved_patterns' ) );
+
add_action( 'stackable_delete_design_library_cache', array( $this, 'delete_cache_v3' ) );
add_filter( 'stackable_localize_script', array( $this, 'add_wp_theme_global_styles' ) );
@@ -90,6 +92,37 @@ public function register_route() {
) );
}
+ public function register_saved_patterns() {
+ register_setting(
+ 'stackable_design_library',
+ 'stackable_design_library_saved_patterns',
+ array(
+ 'type' => 'array',
+ 'description' => __( 'Stackable Design Library User-Saved Patterns', STACKABLE_I18N ),
+ 'sanitize_callback' => array( $this, 'sanitize_array_setting' ),
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'items' => array(
+ 'type'=>'object',
+ 'properties'=> array(
+ 'id' => array( 'type' => 'string' ),
+ 'label' => array( 'type' => 'string' ),
+ 'description' => array( 'type' => 'string' ),
+ 'category' => array( 'type' => 'string' ),
+ 'template' => array( 'type' => 'string' )
+ )
+ )
+ )
+ ),
+ 'default' => array(),
+ )
+ );
+ }
+
+ public function sanitize_array_setting( $input ) {
+ return is_array( $input ) ? $input : array();
+ }
+
/**
* Deletes all design library v3 caches.
*/
@@ -289,6 +322,11 @@ public function get_design_library( $request ) {
$this->delete_cache();
}
+ if ( $type === 'saved' ) {
+ $saved_patterns = get_option( 'stackable_design_library_saved_patterns', [] );
+ return rest_ensure_response( $saved_patterns );
+ }
+
return rest_ensure_response( $this->get_design_library_from_cloud( $type ) );
}
diff --git a/src/lazy-components/design-library/design-library-list/design-library-list-item.js b/src/lazy-components/design-library/design-library-list/design-library-list-item.js
index d4983380e8..bd9e96c310 100644
--- a/src/lazy-components/design-library/design-library-list/design-library-list-item.js
+++ b/src/lazy-components/design-library/design-library-list/design-library-list-item.js
@@ -21,9 +21,11 @@ import {
import {
useState, useRef, memo,
useMemo,
+ Fragment,
} from '@wordpress/element'
import { Dashicon, Spinner } from '@wordpress/components'
import { __ } from '@wordpress/i18n'
+import { applyFilters } from '@wordpress/hooks'
const DesignLibraryListItem = memo( props => {
const {
@@ -86,20 +88,29 @@ const DesignLibraryListItem = memo( props => {
const onClickHost = e => {
e.stopPropagation()
- if ( selectedTab === 'pages' ) {
- return
- }
onClickDesign()
}
+ const buttonAttributes = {
+ tabIndex: 0,
+ role: 'button',
+ onClick: onClickHost,
+ onKeyDown: e => {
+ if ( e.key === 'Enter' || e.key === ' ' ) {
+ e.preventDefault()
+ onClickHost( e )
+ }
+ },
+ }
+
return (
- // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
-