Skip to content
Merged
5 changes: 5 additions & 0 deletions packages/block-editor/src/components/block-list/content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@
.is-dark-theme & {
box-shadow: 0 0 0 var(--wp-admin-border-width-focus) $dark-theme-focus;
}

transition:
border-color 0.1s linear,
border-style 0.1s linear,
box-shadow 0.1s linear;
}
}

Expand Down
45 changes: 27 additions & 18 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1759,29 +1759,38 @@ export function lastBlockAttributesChange( state = null, action ) {
}

/**
* Reducer returning current highlighted block.
* Reducer returning map of block client IDs to whether or not they are
* highlighted.
*
* @param {boolean} state Current highlighted block.
* @param {Object} action Dispatched action.
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {string} Updated state.
* @return {Object} Updated state.
*/
export function highlightedBlock( state, action ) {
export function highlightedBlocks( state = {}, action ) {
switch ( action.type ) {
case 'TOGGLE_BLOCK_HIGHLIGHT':
const { clientId, isHighlighted } = action;

if ( isHighlighted ) {
return clientId;
} else if ( state === clientId ) {
return null;
case 'TOGGLE_BLOCK_HIGHLIGHT': {
if ( action.isHighlighted ) {
if ( ! state[ action.clientId ] ) {
return {
...state,
[ action.clientId ]: true,
};
}
} else if ( state[ action.clientId ] ) {
const nextState = { ...state };
delete nextState[ action.clientId ];
return nextState;
}

return state;
case 'SELECT_BLOCK':
if ( action.clientId !== state ) {
return null;
break;
}
case 'SELECT_BLOCK': {
if ( state[ action.clientId ] ) {
const nextState = { ...state };
delete nextState[ action.clientId ];
return nextState;
}
}
}

return state;
Expand Down Expand Up @@ -1876,7 +1885,7 @@ const combinedReducers = combineReducers( {
lastBlockAttributesChange,
editorMode,
hasBlockMovingClientId,
highlightedBlock,
highlightedBlocks,
lastBlockInserted,
temporarilyEditingAsBlocks,
blockVisibility,
Expand Down
4 changes: 2 additions & 2 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2676,7 +2676,7 @@ export function didAutomaticChange( state ) {
* @return {boolean} Whether the block is currently highlighted.
*/
export function isBlockHighlighted( state, clientId ) {
return state.highlightedBlock === clientId;
return !! state.highlightedBlocks[ clientId ];
}

/**
Expand Down Expand Up @@ -2822,7 +2822,7 @@ export function __unstableHasActiveBlockOverlayActive( state, clientId ) {
// Prevent overlay on disabled blocks. It's redundant since disabled blocks
// can't be selected, and prevents non-disabled nested blocks from being
// selected.
if ( getBlockEditingMode( state, clientId ) === 'disabled' ) {
if ( getBlockEditingMode( state, clientId ) !== 'default' ) {
return false;
}

Expand Down
41 changes: 16 additions & 25 deletions packages/edit-site/src/components/block-editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,7 @@ import ResizableEditor from './resizable-editor';
import EditorCanvas from './editor-canvas';
import { unlock } from '../../lock-unlock';
import EditorCanvasContainer from '../editor-canvas-container';
import {
DisableNonPageContentBlocks,
usePageContentFocusNotifications,
} from '../page-content-focus';
import PageContentFocusManager from '../page-content-focus-manager';

const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis );

Expand All @@ -53,25 +50,20 @@ const LAYOUT = {

export default function BlockEditor() {
const { setIsInserterOpened } = useDispatch( editSiteStore );
const { storedSettings, templateType, canvasMode, hasPageContentFocus } =
useSelect(
( select ) => {
const {
getSettings,
getEditedPostType,
getCanvasMode,
hasPageContentFocus: _hasPageContentFocus,
} = unlock( select( editSiteStore ) );

return {
storedSettings: getSettings( setIsInserterOpened ),
templateType: getEditedPostType(),
canvasMode: getCanvasMode(),
hasPageContentFocus: _hasPageContentFocus(),
};
},
[ setIsInserterOpened ]
);
const { storedSettings, templateType, canvasMode } = useSelect(
( select ) => {
const { getSettings, getEditedPostType, getCanvasMode } = unlock(
select( editSiteStore )
);

return {
storedSettings: getSettings( setIsInserterOpened ),
templateType: getEditedPostType(),
canvasMode: getCanvasMode(),
};
},
[ setIsInserterOpened ]
);

const settingsBlockPatterns =
storedSettings.__experimentalAdditionalBlockPatterns ?? // WP 6.0
Expand Down Expand Up @@ -146,7 +138,6 @@ export default function BlockEditor() {
contentRef,
useClipboardHandler(),
useTypingObserver(),
usePageContentFocusNotifications(),
] );
const isMobileViewport = useViewportMatch( 'small', '<' );
const { clearSelectedBlock } = useDispatch( blockEditorStore );
Expand All @@ -172,7 +163,7 @@ export default function BlockEditor() {
onChange={ onChange }
useSubRegistry={ false }
>
{ hasPageContentFocus && <DisableNonPageContentBlocks /> }
<PageContentFocusManager contentRef={ contentRef } />
<TemplatePartConverter />
<SidebarInspectorFill>
<BlockInspector />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../store';

/**
* Component that displays a 'You are editing a template' notification when the
* user switches from focusing on editing page content to editing a template.
*/
export default function BackToPageNotification() {
useBackToPageNotification();
return null;
}

/**
* Hook that displays a 'You are editing a template' notification when the user
* switches from focusing on editing page content to editing a template.
*/
export function useBackToPageNotification() {
const hasPageContentFocus = useSelect(
( select ) => select( editSiteStore ).hasPageContentFocus(),
[]
);

const alreadySeen = useRef( false );
const prevHasPageContentFocus = useRef( false );

const { createInfoNotice } = useDispatch( noticesStore );
const { setHasPageContentFocus } = useDispatch( editSiteStore );

useEffect( () => {
if (
! alreadySeen.current &&
prevHasPageContentFocus.current &&
! hasPageContentFocus
) {
createInfoNotice( __( 'You are editing a template' ), {
isDismissible: true,
type: 'snackbar',
actions: [
{
label: __( 'Back to page' ),
onClick: () => setHasPageContentFocus( true ),
},
],
} );
alreadySeen.current = true;
}
prevHasPageContentFocus.current = hasPageContentFocus;
}, [
alreadySeen,
prevHasPageContentFocus,
Comment on lines +59 to +60
Copy link
Member

@Mamaduka Mamaduka Jun 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: There's no need to add mutable values like refs as dependencies. When you provide them, alreadySeen.current ESLint shows a warning.

hasPageContentFocus,
createInfoNotice,
setHasPageContentFocus,
] );
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const { useBlockEditingMode } = unlock( blockEditorPrivateApis );
* Component that when rendered, makes it so that the site editor allows only
* page content to be edited.
*/
export function DisableNonPageContentBlocks() {
export default function DisableNonPageContentBlocks() {
useDisableNonPageContentBlocks();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* WordPress dependencies
*/
import { useDispatch } from '@wordpress/data';
import { useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { __experimentalConfirmDialog as ConfirmDialog } from '@wordpress/components';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../store';

/**
* Component that displays a 'Edit your template to edit this block'
* notification when the user is focusing on editing page content and clicks on
* a disabled template block.
*
* @param {Object} props
* @param {import('react').RefObject<HTMLElement>} props.contentRef Ref to the block
* editor iframe canvas.
*/
export default function EditTemplateDialog( { contentRef } ) {
const [ isOpen, setIsOpen ] = useState( false );

useEffect( () => {
const handleDblClick = ( event ) => {
if ( event.target.classList.contains( 'is-root-container' ) ) {
setIsOpen( true );
}
};
const canvas = contentRef.current;
canvas?.addEventListener( 'dblclick', handleDblClick );
return () => canvas?.removeEventListener( 'dblclick', handleDblClick );
}, [ contentRef.current ] );

const { setHasPageContentFocus } = useDispatch( editSiteStore );

return (
<ConfirmDialog
isOpen={ isOpen }
confirmButtonText={ __( 'Edit template' ) }
onConfirm={ () => {
setIsOpen( false );
setHasPageContentFocus( false );
} }
onCancel={ () => setIsOpen( false ) }
>
{ __( 'Edit your template to edit this block' ) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a biggie for this PR, but this text can be misleading since you can click anywhere outside a content block to trigger the notice + modal, even on the latters' padding or margin.

2023-06-16 14 11 54

Maybe "Edit your template to edit this area" ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny nit: for text within the dialog, should we end the sentence in a full-stop? Also, not a blocker (and wording can be tweaked in follow-ups), but since we have a modal, I was wondering if it's worth including more explanatory text in this one. E.g. something along the lines of, "You are currently editing ${ pageTitle }, do you wish to switch to template editing?"... though now that I write that out, I'm not sure how much more helpful that is. Writing this sort of micro copy is hard! 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will add the periods! 👍

Not 100% sure about changing the copy. Wdyt @SaxonF?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this sort of copy is indeed difficult. I'd be fine shipping as is and follow up with a copy pass.

</ConfirmDialog>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../store';

/**
* Component that displays a 'Edit your template to edit this block'
* notification when the user is focusing on editing page content and clicks on
* a disabled template block.
*
* @param {Object} props
* @param {import('react').RefObject<HTMLElement>} props.contentRef Ref to the block
* editor iframe canvas.
*/
export default function EditTemplateNotification( { contentRef } ) {
useEditTemplateNotification( contentRef );
return null;
}

/**
* Hook that displays a 'Edit your template to edit this block' notification
* when the user is focusing on editing page content and clicks on a disabled
* template block.
*
* @param {import('react').RefObject<HTMLElement>} contentRef Ref to the block
* editor iframe canvas.
*/
function useEditTemplateNotification( contentRef ) {
const hasPageContentFocus = useSelect(
( select ) => select( editSiteStore ).hasPageContentFocus(),
[]
);

const alreadySeen = useRef( false );

const { createInfoNotice } = useDispatch( noticesStore );
const { setHasPageContentFocus } = useDispatch( editSiteStore );

useEffect( () => {
const handleClick = ( event ) => {
if (
! alreadySeen.current &&
hasPageContentFocus &&
event.target.classList.contains( 'is-root-container' )
) {
createInfoNotice(
__( 'Edit your template to edit this block' ),
{
isDismissible: true,
type: 'snackbar',
actions: [
{
label: __( 'Edit template' ),
onClick: () => setHasPageContentFocus( false ),
},
],
}
);
alreadySeen.current = true;
}
};
const canvas = contentRef.current;
canvas?.addEventListener( 'click', handleClick );
return () => canvas?.removeEventListener( 'click', handleClick );
}, [ alreadySeen, hasPageContentFocus, contentRef.current ] );
}
Loading