Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions src/block/design-library/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/components/pro-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ const LABELS = {
<li>{ __( 'Override styles while keeping them synced', i18n ) }</li>
</ul>,
},
'design-library-saved-patterns': {
title: __( 'Design Library Saved Patterns', i18n ),
description: <ul>
<li>{ __( 'Save entire block layouts in a click', i18n ) }</li>
<li>{ __( 'Apply styling options to each saved pattern', i18n ) }</li>
<li>{ __( 'Import and export your custom patterns across sites', i18n ) }</li>
</ul>,
},
}

const ProControl = props => {
Expand Down
5 changes: 3 additions & 2 deletions src/design-library/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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 )
}

Expand Down
38 changes: 38 additions & 0 deletions src/design-library/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) );
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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 ) );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
<button
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events, jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
className={ mainClasses }
ref={ ref }
onClick={ onClickHost }
onMouseOut={ onMouseOut }
onMouseOver={ onMouseOver }
{ ...( selectedTab === 'patterns' ? buttonAttributes : {} ) }
>
{ ! isPro && plan !== 'free' && <span className="stk-pulsating-circle" role="presentation" /> }
<div style={ { position: 'relative' } } className={ `stk-block-design__design-container ${ designPreviewSize > 100 ? 'stk--design-preview-large' : 'stk--design-preview-small' }` }>
Expand All @@ -110,6 +121,7 @@ const DesignLibraryListItem = memo( props => {
showHideNote={ false }
/>
) }
{ isPro && applyFilters( 'stackable.design-library.pattern-actions', Fragment, previewProps ) }
<div className={ `stk-spinner-container ${ isLoading || ! shouldRender ? '' : 'stk-hide-spinner' }` }><Spinner /></div>
<div
className="stk-block-design__host-container"
Expand All @@ -133,9 +145,11 @@ const DesignLibraryListItem = memo( props => {
</div>
</div>

{ /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */ }
<footer
// Add the number if isToggle is a number, signifying an order instead of just an on/off.
data-selected-num={ selectedNum }
{ ...( selectedTab === 'saved' ? buttonAttributes : {} ) }
>
<div>
<h4> { label } </h4>
Expand All @@ -151,7 +165,7 @@ const DesignLibraryListItem = memo( props => {
<Dashicon icon="editor-help" size={ 16 } />
</Tooltip>
}
{ selectedTab === 'patterns' ? <span className="stk-block-design__selected-num">{ selectedNum === 0 ? '' : selectedNum }</span>
{ selectedTab !== 'pages' ? <span className="stk-block-design__selected-num">{ selectedNum === 0 ? '' : selectedNum }</span>
: <div>
<Button
label={ __( 'Insert', i18n ) }
Expand All @@ -169,7 +183,7 @@ const DesignLibraryListItem = memo( props => {
}
</div>
</footer>
</button>
</div>
)
} )

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const DesignPreview = ( {

useEffect( () => {
const container = ref.current
if ( ! container || selectedTab === 'patterns' ) {
if ( ! container || selectedTab !== 'pages' ) {
return
}

Expand Down Expand Up @@ -90,6 +90,9 @@ export const DesignPreview = ( {

setIsLoading( true )

// Prevent interaction and focus within the preview content
wrapper.setAttribute( 'inert', '' )

const ric = window.requestIdleCallback ? ( cb => window.requestIdleCallback( cb, { timeout: 5000 } ) )
: ( cb => setTimeout( cb, designIndex * 20 ) )
const sanitizedHTML = safeHTML( blocks )
Expand Down Expand Up @@ -121,6 +124,7 @@ export const DesignPreview = ( {
>
<div
ref={ wrapperRef }
className="is-layout-constrained"
style={ { pointerEvents: 'none' } } // prevent blocks from being clicked
/>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,51 @@
opacity: 1;
}
}
.stk-block-design__edit-btn-container {
opacity: 0;
z-index: 2;
transition: opacity 0.4s cubic-bezier(0.2, 0.6, 0.4, 1), visibility 0.4s cubic-bezier(0.2, 0.6, 0.4, 1);
position: absolute;
padding: 60px 30px 30px;
background: linear-gradient(180deg, transparent, #fffe 40px, #fff) !important;
top: auto;
bottom: -1px;
left: 0;
right: 0;
display: flex;
gap: 24px;
visibility: hidden;
@media (hover: none) {
opacity: 1;
}

.ugb-button-component {
width: 100%;
justify-content: center;
pointer-events: none;
@media (hover: none) {
pointer-events: auto;
}
}
}
&:hover {
// box-shadow: rgba(0, 0, 0, 0.25) 0px 25px 50px -12px;
box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
.ugb-button-component {
opacity: 1;
}

.stk-block-design__edit-btn-container {
visibility: visible;
opacity: 1;
transition-delay: 1s;

.ugb-button-component {
pointer-events: initial;
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

&.ugb--is-hidden {
opacity: 0;
pointer-events: none;
Expand Down Expand Up @@ -236,7 +274,6 @@
}
}


.ugb-design-library-items {
.stk-spinner-container {
height: 100%;
Expand All @@ -257,3 +294,10 @@
}
}
}

.stk-design-library__item-saved .ugb-design-library-item {
cursor: default;
footer {
cursor: pointer;
}
}
Loading
Loading