From 13eab2dbfe3df85c584600eed65e9f321c20358b Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Thu, 13 Jan 2022 10:39:52 +0800 Subject: [PATCH 01/31] chore: updated changelog to note premium update --- readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.txt b/readme.txt index 3f71e114a..f63bf141c 100644 --- a/readme.txt +++ b/readme.txt @@ -239,9 +239,9 @@ Nope. Stackable only works with Gutenberg, the new WordPress editor. == Changelog == = 3.1.1 = -* New: Added back ability to change the column order when columns collapse in mobile -* New: Added Post Taxonomies in dynamic content * New: Added link option to the Image Block +* New: Added back ability to change the column order when columns collapse in mobile (premium) +* New: Added Post Taxonomies in dynamic content (premium) * Fixed: Trigger when all display conditions are met is not triggering properly * Fixed: Entrance animation speeds and delay do not take effect correctly * Fixed: Column gap and fit columns options now work in Feature Grid block From d3e9e45a69492eed02d70cda7b4f5479d26ac187 Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Thu, 13 Jan 2022 12:33:27 +0800 Subject: [PATCH 02/31] tooling: ensure LF is used for end of lines when checking out with git --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..dff012a44 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Set all end of lines to be LF (unix) for consistency when checking out the code. +* text eol=lf From 41aa69676e6f7e18f1c0bd27eb1686bd9487a1f7 Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Fri, 14 Jan 2022 01:14:29 +0800 Subject: [PATCH 03/31] fix: block errors when unselecting block in FSE. fixes #2103 (#2110) --- src/components/panel-tabs/editor.scss | 3 +-- src/components/panel-tabs/index.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/panel-tabs/editor.scss b/src/components/panel-tabs/editor.scss index 996c74280..d059d8b6c 100644 --- a/src/components/panel-tabs/editor.scss +++ b/src/components/panel-tabs/editor.scss @@ -49,8 +49,7 @@ .ugb-panel-tabs { border-bottom: 1px solid #e2e4e7 !important; } -.edit-post-sidebar.ugb--has-panel-tabs, -.edit-widgets-sidebar.ugb--has-panel-tabs { +.interface-complementary-area.ugb--has-panel-tabs { background-color: #f0f3f5; .edit-post-settings-sidebar__panel-block .components-panel__body:last-child { margin-bottom: 0; diff --git a/src/components/panel-tabs/index.js b/src/components/panel-tabs/index.js index 813cb5c5d..f166414d6 100644 --- a/src/components/panel-tabs/index.js +++ b/src/components/panel-tabs/index.js @@ -80,7 +80,7 @@ class PanelTabs extends Component { setTimeout( () => { if ( sidebarPanel ) { sidebarPanel.setAttribute( 'data-ugb-tab', tab ) - sidebarPanel.closest( '.edit-post-sidebar, .edit-widgets-sidebar' )?.classList.add( 'ugb--has-panel-tabs' ) + sidebarPanel.closest( '.edit-post-sidebar, .edit-widgets-sidebar, .interface-complementary-area' )?.classList.add( 'ugb--has-panel-tabs' ) } }, 1 ) } @@ -99,7 +99,7 @@ class PanelTabs extends Component { const sidebarPanel = document.querySelector( '[data-ugb-tab]' ) if ( sidebarPanel ) { sidebarPanel.removeAttribute( 'data-ugb-tab' ) - sidebarPanel.closest( '.edit-post-sidebar, .edit-widgets-sidebar' ).classList.remove( 'ugb--has-panel-tabs' ) + sidebarPanel.closest( '.edit-post-sidebar, .edit-widgets-sidebar, .interface-complementary-area' ).classList.remove( 'ugb--has-panel-tabs' ) } // Remove listener to panel closes From 470091458029f509bdf51a6f20408ca6470c9368 Mon Sep 17 00:00:00 2001 From: Marco Enrico Date: Sat, 15 Jan 2022 00:27:54 +0800 Subject: [PATCH 04/31] Add preventScroll to focus. (#2106) --- src/block/expand/frontend-expand.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/block/expand/frontend-expand.js b/src/block/expand/frontend-expand.js index 5856c79c1..80cabd208 100644 --- a/src/block/expand/frontend-expand.js +++ b/src/block/expand/frontend-expand.js @@ -18,7 +18,9 @@ class StackableExpand { visibles.forEach( el => el.setAttribute( 'aria-hidden', 'true' ) ) // Refocus on the hide/show button. - el.querySelector( `.stk-button[aria-hidden="false"]` ).focus() + el.querySelector( `.stk-button[aria-hidden="false"]` ).focus( { + preventScroll: true, + } ) event.preventDefault() } From a8257f6f5de286336f4dd5d3f7c291ac2c930562 Mon Sep 17 00:00:00 2001 From: Benjamin Intal Date: Sat, 15 Jan 2022 00:28:38 +0800 Subject: [PATCH 05/31] Removed unwanted bottom space in columns block in tablet & mobile preview in the editor. Fixes #2097 (#2107) --- src/components/resizable-column/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/resizable-column/index.js b/src/components/resizable-column/index.js index 887a28822..483c0e633 100644 --- a/src/components/resizable-column/index.js +++ b/src/components/resizable-column/index.js @@ -391,7 +391,7 @@ const ResizableColumn = props => { Date: Sat, 15 Jan 2022 00:30:33 +0800 Subject: [PATCH 06/31] fix: column resizing now works in tablet and mobile. fixes #2095 (#2109) --- src/components/resizable-column/index.js | 12 ++-- .../use-device-editor-classes.js | 10 +-- src/hooks/index.js | 1 + src/hooks/use-block-context.js | 11 ++-- src/hooks/use-block-hover-state.js | 4 +- src/hooks/use-editor-dom.js | 61 +++++++++++++++++++ src/plugins/block-hover-state/index.js | 5 +- .../design-library-button.js | 32 ++++++---- 8 files changed, 106 insertions(+), 30 deletions(-) create mode 100644 src/hooks/use-editor-dom.js diff --git a/src/components/resizable-column/index.js b/src/components/resizable-column/index.js index 483c0e633..c586290d9 100644 --- a/src/components/resizable-column/index.js +++ b/src/components/resizable-column/index.js @@ -12,6 +12,7 @@ import { ColumnShowTooltipContext } from '../column-inner-blocks' import { useBlockContext, useDeviceType, + useEditorDom, useWithShift, } from '~stackable/hooks' import { clamp, isEqual } from 'lodash' @@ -40,6 +41,7 @@ const MIN_COLUMN_WIDTH_PERCENTAGE = { const ResizableColumn = props => { const { clientId } = useBlockEditContext() const blockContext = useBlockContext() + const editorDom = useEditorDom() const { isFirstBlock, isLastBlock, isOnlyBlock, adjacentBlocks, blockIndex, parentBlock, @@ -134,7 +136,7 @@ const ResizableColumn = props => { const totalColumnGap = parentColumnGaps.desktop * ( adjacentBlocks.length - 1 ) // Get the current pixel width of the columns. - const parentEl = document.querySelector( `.editor-styles-wrapper [data-block="${ parentBlock.clientId }"]` ) + const parentEl = editorDom.querySelector( `[data-block="${ parentBlock.clientId }"]` ) const parentWidth = parentEl.clientWidth - totalColumnGap const isFirstResize = adjacentBlocks.every( ( { attributes } ) => ! attributes.columnWidth ) const columnWidths = adjacentBlocks.map( ( { clientId, attributes } ) => { @@ -146,7 +148,7 @@ const ResizableColumn = props => { if ( attributes.columnWidth ) { return parentWidth * attributes.columnWidth / 100 } - const blockEl = document.querySelector( `.editor-styles-wrapper [data-block="${ clientId }"]` ) + const blockEl = editorDom.querySelector( `[data-block="${ clientId }"]` ) return blockEl?.clientWidth || 0 } ) setCurrentWidths( columnWidths ) @@ -167,18 +169,18 @@ const ResizableColumn = props => { setCurrentWidths( columnWidths ) // Get the current pixel width of the columns. - const blockEl = document.querySelector( `.editor-styles-wrapper [data-block="${ clientId }"]` ) + const blockEl = editorDom.querySelector( `[data-block="${ clientId }"]` ) const columnWidth = blockEl?.clientWidth || 0 setCurrentWidth( columnWidth ) // The maximum width is the total width of the row. - const parentEl = document.querySelector( `.editor-styles-wrapper [data-block="${ parentBlock.clientId }"]` ) + const parentEl = editorDom.querySelector( `[data-block="${ parentBlock.clientId }"]` ) const maxWidth = parentEl?.clientWidth || 0 setMaxWidth( maxWidth ) } setIsTooltipOver( true ) - }, [ isDesktop, parentBlock?.clientId, adjacentBlocks, blockIndex, clientId, columnGap, columnGapTablet, columnGapMobile ] ) + }, [ isDesktop, parentBlock?.clientId, adjacentBlocks, blockIndex, clientId, columnGap, columnGapTablet, columnGapMobile, editorDom ] ) const onResize = useCallback( ( _event, _direction, elt, delta ) => { let columnPercentages = [] diff --git a/src/components/resizable-column/use-device-editor-classes.js b/src/components/resizable-column/use-device-editor-classes.js index 5189e29cc..9e4e4d2ad 100644 --- a/src/components/resizable-column/use-device-editor-classes.js +++ b/src/components/resizable-column/use-device-editor-classes.js @@ -1,9 +1,8 @@ import { useEffect } from '@wordpress/element' import { throttle } from 'lodash' -import { useDeviceType } from '~stackable/hooks' +import { useDeviceType, useEditorDom } from '~stackable/hooks' -const addDeviceTypeClass = throttle( ( previewDeviceType = '' ) => { - const editorEl = document.querySelector( '.editor-styles-wrapper' ) +const addDeviceTypeClass = throttle( ( previewDeviceType = '', editorEl ) => { if ( editorEl && ! editorEl.classList.contains( `stk-preview-device-${ previewDeviceType.toLowerCase() }` ) ) { editorEl.classList.remove( 'stk-preview-device-desktop', 'stk-preview-device-tablet', 'stk-preview-device-mobile' ) editorEl.classList.add( `stk-preview-device-${ previewDeviceType.toLowerCase() }` ) @@ -12,7 +11,8 @@ const addDeviceTypeClass = throttle( ( previewDeviceType = '' ) => { export const useDeviceEditorClasses = () => { const deviceType = useDeviceType() + const editorDom = useEditorDom() useEffect( () => { - addDeviceTypeClass( deviceType ) - }, [ deviceType ] ) + addDeviceTypeClass( deviceType, editorDom ) + }, [ deviceType, editorDom ] ) } diff --git a/src/hooks/index.js b/src/hooks/index.js index 85dd1f988..cc4fa7d46 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -4,6 +4,7 @@ export * from './use-did-attributes-change' export * from './use-device-type' export * from './use-block-attributes' export * from './use-block-style' +export * from './use-editor-dom' export * from './use-font-loader' export * from './use-attribute-edit-handlers' export * from './use-attribute-name' diff --git a/src/hooks/use-block-context.js b/src/hooks/use-block-context.js index ce3fbabd6..28a8b0a4c 100644 --- a/src/hooks/use-block-context.js +++ b/src/hooks/use-block-context.js @@ -1,13 +1,15 @@ -import { useSelect } from '@wordpress/data' -import { useBlockEditContext } from '@wordpress/block-editor' +import { useEditorDom } from '.' import { first, last, indexOf, nth, } from 'lodash' +import { useSelect } from '@wordpress/data' +import { useBlockEditContext } from '@wordpress/block-editor' const useBlockContext = ( blockClientId = null ) => { // If nothing is provided, use the current block. const blockProps = useBlockEditContext() const clientId = blockClientId || blockProps.clientId + const editorDom = useEditorDom() const blockInfo = useSelect( select => { @@ -31,7 +33,8 @@ const useBlockContext = ( blockClientId = null ) => { // Check if a column block isn't used as a row. If not, then don't // use row-like properties (column resizers, etc). if ( hasParent && block.name === 'stackable/column' ) { - const isRow = document.querySelector( `.editor-styles-wrapper [data-block="${ parent.clientId }"] .stk-block` )?.classList.contains( 'stk-row' ) + const isRow = editorDom?.querySelector( `[data-block="${ parent.clientId }"] .stk-block` )?.classList.contains( 'stk-row' ) + if ( ! isRow ) { return { blockIndex: index, @@ -63,7 +66,7 @@ const useBlockContext = ( blockClientId = null ) => { innerBlocks: block?.innerBlocks, } }, - [ clientId ] + [ clientId, editorDom ] ) return blockInfo diff --git a/src/hooks/use-block-hover-state.js b/src/hooks/use-block-hover-state.js index fb8d08e4e..8f980e516 100644 --- a/src/hooks/use-block-hover-state.js +++ b/src/hooks/use-block-hover-state.js @@ -20,9 +20,9 @@ const DEFAULT_STATE = { } const STORE_ACTIONS = { - updateSelectedBlock: clientId => { + updateSelectedBlock: ( clientId, editorDom ) => { // We need to specify `.editor-styles-wrapper` to avoid targeting the navigation list view. - const blockEl = document.querySelector( `.editor-styles-wrapper [data-block="${ clientId }"]` ) + const blockEl = editorDom?.querySelector( `[data-block="${ clientId }"]` ) // Get the currently parent-hovered block if there is one. const parentHoverEl = blockEl?.closest( '.stk-hover-parent' )?.closest( '[data-block]' ) diff --git a/src/hooks/use-editor-dom.js b/src/hooks/use-editor-dom.js new file mode 100644 index 000000000..45090e2b2 --- /dev/null +++ b/src/hooks/use-editor-dom.js @@ -0,0 +1,61 @@ +/** + * Internal dependencies + */ +import { useDeviceType } from '.' + +/** + * WordPress dependencies + */ +import { + useState, useEffect, useMemo, +} from '@wordpress/element' + +/** + * Gets the editor wrapper DOM element. This should be the way if you need to do + * a querySelector and to get the raw DOM element of a block. This handles + * iframe Tablet and Mobile previews introduced in WP 5.9. + * + * Take note that this can produce a `null` value if the editor is still + * transitioning from Desktop to Tablet or Mobile previews. + * + * @return {DomElement} This can be null when the editor is transitioning from + * one device size to another. + */ +export const useEditorDom = () => { + const deviceType = useDeviceType() + const [ iframeForceUpdate, setIframeForceUpdate ] = useState( 0 ) + + useEffect( () => { + if ( deviceType === 'Desktop' ) { + // Need to force an update because it takes some time for the iframe to disappear. + setTimeout( () => { + setIframeForceUpdate( iframeForceUpdate + 1 ) + }, 200 ) + } else { // Tablet or Mobile. + const iframeEl = document.querySelector( `iframe[name="editor-canvas"]` ) + if ( iframeEl ) { + if ( iframeEl.contentDocument.body ) { + setIframeForceUpdate( iframeForceUpdate + 1 ) + } else { + const timeout = setTimeout( () => { + if ( iframeEl.contentDocument.body ) { + setIframeForceUpdate( iframeForceUpdate + 1 ) + } + }, 200 ) + iframeEl.onload = () => { + clearTimeout( timeout ) + setIframeForceUpdate( iframeForceUpdate + 1 ) + } + } + } + } + }, [ deviceType ] ) + + return useMemo( () => { + const iframeEl = document.querySelector( `iframe[name="editor-canvas"]` ) + if ( iframeEl ) { + return iframeEl.contentDocument.body + } + return document.querySelector( `.editor-styles-wrapper` ) + }, [ iframeForceUpdate, deviceType ] ) +} diff --git a/src/plugins/block-hover-state/index.js b/src/plugins/block-hover-state/index.js index fa509ae26..41d6eaf6e 100644 --- a/src/plugins/block-hover-state/index.js +++ b/src/plugins/block-hover-state/index.js @@ -1,3 +1,4 @@ +import { useEditorDom } from '~stackable/hooks' import { dispatch, useSelect } from '@wordpress/data' import { useEffect } from '@wordpress/element' @@ -12,6 +13,8 @@ import { useEffect } from '@wordpress/element' * @return {Object} Null, nothing is rendered */ export const BlockHoverState = () => { + const editorDom = useEditorDom() + const { selectedClientId, hoverStateClientId, @@ -26,7 +29,7 @@ export const BlockHoverState = () => { useEffect( () => { if ( hoverStateClientId !== selectedClientId ) { if ( selectedClientId ) { - dispatch( 'stackable/hover-state' ).updateSelectedBlock( selectedClientId ) + dispatch( 'stackable/hover-state' ).updateSelectedBlock( selectedClientId, editorDom ) } else { // If there's no selected block, clear the hover states. dispatch( 'stackable/hover-state' ).clearSelectedBlock() diff --git a/src/plugins/design-library-button/design-library-button.js b/src/plugins/design-library-button/design-library-button.js index 4527d0a8d..4317bc922 100644 --- a/src/plugins/design-library-button/design-library-button.js +++ b/src/plugins/design-library-button/design-library-button.js @@ -4,6 +4,7 @@ import { i18n, settings } from 'stackable' import { SVGStackableIcon } from '~stackable/icons' import { Button } from '~stackable/components' +import { useEditorDom } from '~stackable/hooks' /** * WordPress dependencies @@ -11,23 +12,28 @@ import { Button } from '~stackable/components' import { createBlock } from '@wordpress/blocks' import { dispatch } from '@wordpress/data' import { __ } from '@wordpress/i18n' +import { useCallback } from '@wordpress/element' const DesignLibraryButton = () => { + const editorDom = useEditorDom() + + const onClick = useCallback( () => { + // Insert a design library block. + const block = createBlock( 'stackable/design-library' ) + + dispatch( 'core/block-editor' ).insertBlocks( block ) + .then( () => { + const button = editorDom.querySelector( `[data-block="${ block.clientId }"] button` ) + // Open the design library. + if ( button ) { + button.click() + } + } ) + }, [ editorDom ] ) + return ( settings.stackable_enable_design_library &&