diff --git a/src/components/design-library-list/design-library-list-item.js b/src/components/design-library-list/design-library-list-item.js
index 82db0950a..b0b77dbcd 100644
--- a/src/components/design-library-list/design-library-list-item.js
+++ b/src/components/design-library-list/design-library-list-item.js
@@ -26,15 +26,16 @@ import { __ } from '@wordpress/i18n'
const DesignLibraryListItem = memo( props => {
const {
- selectedTab,
- plan, label,
- selectedNum,
- selectedData,
- isMultiSelectBusy,
shouldRender,
presetMarks,
+ previewProps,
+ isMultiSelectBusy,
} = props
+ const {
+ selectedTab, selectedNum, selectedData, plan, label,
+ } = previewProps
+
const spacingSize = Array.isArray( presetMarks ) && presetMarks.length >= 2
? presetMarks[ presetMarks.length - 2 ].value
: 120
@@ -50,7 +51,7 @@ const DesignLibraryListItem = memo( props => {
shadowBodySizeRef, blocksForSubstitutionRef,
previewSize, onClickDesign,
updateShadowBodySize,
- } = usePreviewRenderer( props, shouldRender, spacingSize,
+ } = usePreviewRenderer( previewProps, shouldRender, spacingSize,
ref, hostRef, shadowRoot, setIsLoading )
const {
@@ -76,6 +77,7 @@ const DesignLibraryListItem = memo( props => {
], {
[ `ugb--is-${ plan }` ]: ! isPro && plan !== 'free',
'ugb--is-toggled': selectedNum,
+ 'ugb--is-hidden': ! shouldRender,
} )
const onClickHost = e => {
diff --git a/src/components/design-library-list/editor.scss b/src/components/design-library-list/editor.scss
index e68b87e50..8f19e2843 100644
--- a/src/components/design-library-list/editor.scss
+++ b/src/components/design-library-list/editor.scss
@@ -42,6 +42,8 @@
// box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
border: 1px solid #0000001a;
box-shadow: none;
+ opacity: 1;
+ will-change: opacity;
.ugb-button-component {
transition: opacity 0.4s cubic-bezier(0.2, 0.6, 0.4, 1);
opacity: 0;
@@ -56,6 +58,11 @@
opacity: 1;
}
}
+ &.ugb--is-hidden {
+ opacity: 0;
+ pointer-events: none;
+ visibility: hidden;
+ }
footer {
line-height: 18px;
background-color: #fff;
diff --git a/src/components/design-library-list/index.js b/src/components/design-library-list/index.js
index fc1b5f89e..adefe36c4 100644
--- a/src/components/design-library-list/index.js
+++ b/src/components/design-library-list/index.js
@@ -18,16 +18,13 @@ import {
useState, useEffect, useRef, memo, useMemo,
} from '@wordpress/element'
import { usePresetControls } from '~stackable/hooks'
+import { useDesignLibraryContext } from '../modal-design-library/modal'
const DesignLibraryList = memo( props => {
const {
className = '',
designs,
isBusy,
- onSelectMulti,
- selectedDesigns = [],
- selectedDesignData = [],
- selectedTab,
} = props
const containerRef = useRef( null )
@@ -48,29 +45,11 @@ const DesignLibraryList = memo( props => {
{ ! isBusy && <>
{ ( designs || [] ).map( ( design, i ) => {
- const selectedNum = selectedDesigns.indexOf( design.id || design.designId ) + 1
- const selectedData = selectedNum ? selectedDesignData[ selectedNum - 1 ] : null
-
- const previewProps = {
- designId: design.id || design.designId,
- template: design.template || design.content,
- category: design.category,
- containerScheme: props.containerScheme,
- backgroundScheme: props.backgroundScheme,
- enableBackground: props.enableBackground,
- onClick: onSelectMulti,
- }
-
return (
)
} ) }
@@ -92,10 +71,53 @@ DesignLibraryList.defaultProps = {
export default DesignLibraryList
-const DesignLibraryItem = props => {
- const { selectedTab, designIndex } = props
+const DesignLibraryItem = memo( props => {
+ const { design, designIndex } = props
const wrapperRef = useRef( null )
const [ shouldRender, setShouldRender ] = useState( designIndex < 9 )
+
+ const [ selectedTab,
+ selectedDesignIds,
+ selectedDesignData,
+ onSelectDesign,
+ isMultiSelectBusy,
+ containerScheme,
+ backgroundScheme,
+ enableBackground,
+ ] = useDesignLibraryContext()
+
+ const { selectedNum, selectedData } = useMemo( () => {
+ const selectedNum = selectedDesignIds.indexOf( design.id || design.designId ) + 1
+ const selectedData = selectedNum ? selectedDesignData[ selectedNum - 1 ] : null
+
+ return { selectedNum, selectedData }
+ }, [ selectedDesignIds, selectedDesignData ] )
+
+ const previewProps = useMemo( () => ( {
+ designId: design.id || design.designId,
+ template: design.template || design.content,
+ category: design.category,
+ plan: design.plan,
+ label: design.label,
+ containerScheme,
+ backgroundScheme,
+ enableBackground: selectedTab !== 'pages' ? enableBackground : true,
+ selectedTab,
+ selectedNum,
+ selectedData,
+ onClick: onSelectDesign,
+ } ), [
+ // Only track designId for memoization; other design properties will update when designId changes
+ design.id || design.designId,
+ containerScheme,
+ backgroundScheme,
+ enableBackground,
+ selectedTab,
+ // selectedNum and selectedData are always in sync; updating selectedNum also updates selectedData
+ selectedNum,
+ onSelectDesign,
+ ] )
+
const { getPresetMarks } = usePresetControls( 'spacingSizes' )
// Intentionally no dependencies: presetMarks won't change while the design library is open
@@ -135,7 +157,7 @@ const DesignLibraryItem = props => {
const observer = new IntersectionObserver( ( [ entry ] ) => {
// reduce flicker during rapid scrolls
requestAnimationFrame( () => {
- requestAnimationFrame( () => setShouldRender( entry.isIntersecting ) )
+ requestAnimationFrame( () => setShouldRender( entry.isIntersecting || entry.intersectionRatio > 0 ) )
} )
}, {
root: rootEl,
@@ -151,7 +173,11 @@ const DesignLibraryItem = props => {
return (
-
+
)
-}
+} )
diff --git a/src/components/modal-design-library/header-actions.js b/src/components/modal-design-library/header-actions.js
new file mode 100644
index 000000000..1ba1798c8
--- /dev/null
+++ b/src/components/modal-design-library/header-actions.js
@@ -0,0 +1,109 @@
+
+import AdvancedToolbarControl from '../advanced-toolbar-control'
+import Button from '../button'
+/**
+ * External deprendencies
+ */
+import {
+ i18n, isPro, devMode,
+} from 'stackable'
+
+/**
+ * WordPress deprendencies
+ */
+import {
+ Dashicon,
+ Dropdown,
+ ToggleControl,
+} from '@wordpress/components'
+
+import { __ } from '@wordpress/i18n'
+
+export const PLAN_OPTIONS = [ { key: '', label: __( 'All', i18n ) }, { key: 'free', label: __( 'Free', i18n ) }, { key: 'premium', label: __( 'Premium', i18n ) } ]
+
+export const HeaderActions = props => {
+ const {
+ selectedTab,
+ setSelectedTab,
+ selectedPlan,
+ setSelectedPlan,
+ setDoReset,
+ onClose,
+ } = props
+ return <>
+ { /* DEV NOTE: hide for now */ }
+
+
+
+ { devMode && (
+
{
+ localStorage.setItem( 'stk__design_library__dev_mode', value ? '1' : '' )
+ setTimeout( () => {
+ document?.querySelector( '.ugb-insert-library-button__wrapper .ugb-insert-library-button' )?.click()
+ }, 100 )
+ onClose()
+ } }
+ __nextHasNoMarginBottom
+ />
+ ) }
+
+ >
+}
diff --git a/src/components/modal-design-library/modal.js b/src/components/modal-design-library/modal.js
index b500afc0e..1531c1214 100644
--- a/src/components/modal-design-library/modal.js
+++ b/src/components/modal-design-library/modal.js
@@ -4,7 +4,6 @@
import HelpSVG from './images/help.svg'
import BlockList from './block-list'
import Button from '../button'
-import AdvancedToolbarControl from '../advanced-toolbar-control'
import DesignLibraryList from '~stackable/components/design-library-list'
import { GuidedModalTour } from '~stackable/components'
import { getDesigns, filterDesigns } from '~stackable/design-library'
@@ -12,9 +11,7 @@ import { getDesigns, filterDesigns } from '~stackable/design-library'
/**
* External deprendencies
*/
-import {
- i18n, isPro, devMode,
-} from 'stackable'
+import { i18n } from 'stackable'
import classnames from 'classnames'
import { useLocalStorage } from '~stackable/util'
@@ -23,22 +20,22 @@ import { useLocalStorage } from '~stackable/util'
*/
import {
BaseControl,
- Dashicon,
Dropdown,
Modal,
Spinner,
ToggleControl,
} from '@wordpress/components'
import {
- useEffect, useState, useCallback,
+ useEffect, useState, createContext, useContext, useCallback,
+ useMemo,
} from '@wordpress/element'
import { sprintf, __ } from '@wordpress/i18n'
import { useBlockColorSchemes } from '~stackable/hooks'
import ColorSchemePreview from '../color-scheme-preview'
import { ColorSchemesHelp } from '../color-schemes-help'
import Tooltip from '../tooltip'
+import { HeaderActions, PLAN_OPTIONS } from './header-actions'
-const PLAN_OPTIONS = [ { key: '', label: __( 'All', i18n ) }, { key: 'free', label: __( 'Free', i18n ) }, { key: 'premium', label: __( 'Premium', i18n ) } ]
const popoverProps = {
className: 'ugb-design-library__color-scheme-popover',
placement: 'right-start',
@@ -49,6 +46,12 @@ const popoverProps = {
// This is to make sure that the design library shows "all" at the start.
localStorage?.setItem( 'stk__design_library__block-list__selected', '' )
+export const DesignLibraryContext = createContext( null )
+
+export const useDesignLibraryContext = () => {
+ return useContext( DesignLibraryContext )
+}
+
export const ModalDesignLibrary = props => {
const {
backgroundModeColorScheme, containerModeColorScheme, colorSchemesCollection,
@@ -141,310 +144,268 @@ export const ModalDesignLibrary = props => {
return
}
- const newSelectedDesigns = [ ...selectedDesignIds ]
- // We also get the design data from displayDesigns
- // already instead of after clicking the "Add
- // Designs" button since displayDesigns can change
- // when the user is switching tabs (block/ui
- // kits/wireframes) and the data can be lost.
- const newSelectedDesignData = [ ...selectedDesignData ]
-
- if ( newSelectedDesigns.includes( designId ) ) {
- const i = newSelectedDesigns.indexOf( designId )
- newSelectedDesigns.splice( i, 1 )
- setSelectedDesignIds( newSelectedDesigns )
- newSelectedDesignData.splice( i, 1 )
- setSelectedDesignData( newSelectedDesignData )
- } else {
- newSelectedDesigns.push( designId )
- setSelectedDesignIds( newSelectedDesigns )
- newSelectedDesignData.push( {
- designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize,
- } )
- setSelectedDesignData( newSelectedDesignData )
- }
- }, [ selectedTab, selectedDesignIds, selectedDesignData ] )
+ // Use functional updates to avoid depending on current state values
+ setSelectedDesignIds( currentSelectedDesigns => {
+ const newSelectedDesigns = [ ...currentSelectedDesigns ]
+
+ if ( newSelectedDesigns.includes( designId ) ) {
+ const i = newSelectedDesigns.indexOf( designId )
+ newSelectedDesigns.splice( i, 1 )
+ } else {
+ newSelectedDesigns.push( designId )
+ }
+
+ return newSelectedDesigns
+ } )
+
+ setSelectedDesignData( currentSelectedDesignData => {
+ const newSelectedDesignData = [ ...currentSelectedDesignData ]
+
+ if ( currentSelectedDesignData.some( design => design.designId === designId ) ) {
+ const i = newSelectedDesignData.findIndex( design => design.designId === designId )
+ newSelectedDesignData.splice( i, 1 )
+ } else {
+ newSelectedDesignData.push( {
+ designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize,
+ } )
+ }
+
+ return newSelectedDesignData
+ } )
+ }, [ selectedTab ] )
+
+ const headerActions = useMemo( () => {
+ return
+ }, [ selectedTab, selectedPlan, setSelectedTab, setSelectedPlan, setDoReset, props.onClose ] )
+
+ // Memoize the context value to prevent unnecessary rerenders
+ const contextValue = useMemo(
+ () => [
+ selectedTab,
+ selectedDesignIds,
+ selectedDesignData,
+ onSelectDesign,
+ isMultiSelectBusy,
+ selectedContainerScheme,
+ selectedBackgroundScheme,
+ enableBackground,
+ ],
+ [
+ selectedTab,
+ selectedDesignIds,
+ selectedDesignData,
+ onSelectDesign,
+ isMultiSelectBusy,
+ selectedContainerScheme,
+ selectedBackgroundScheme,
+ enableBackground,
+ ]
+ )
return (
- { /* DEV NOTE: hide for now */ }
-
+ headerActions={ headerActions }
+ className={ classnames( 'ugb-modal-design-library', 'ugb-modal-design-library--is-multiselect' ) }
+ onRequestClose={ props.onClose }
+ >
+
+
-
- { devMode && (
-
+
+