-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
First pass at adding simple editing mode #65027
Changes from all commits
6574a25
e1c5b75
35a9b70
ed283a5
7be5cd0
4f29dcd
613c95e
df42492
9dcebcf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,13 +100,24 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => { | |
getBlockName, | ||
getContentLockingParent, | ||
getTemplateLock, | ||
getClosestSectionBlock, | ||
getBlockEditingMode, | ||
} = unlock( select( blockEditorStore ) ); | ||
const _selectedBlockClientId = getSelectedBlockClientId(); | ||
const _selectedBlockName = | ||
_selectedBlockClientId && getBlockName( _selectedBlockClientId ); | ||
const _blockType = | ||
_selectedBlockName && getBlockType( _selectedBlockName ); | ||
|
||
const closestSectionBlock = getClosestSectionBlock( | ||
_selectedBlockClientId | ||
); | ||
|
||
const closestContentOnlySectionBlock = | ||
getBlockEditingMode( closestSectionBlock ) === 'contentOnly' | ||
? closestSectionBlock | ||
: undefined; | ||
|
||
return { | ||
count: getSelectedBlockCount(), | ||
selectedBlockClientId: _selectedBlockClientId, | ||
|
@@ -117,7 +128,8 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => { | |
( getTemplateLock( _selectedBlockClientId ) === 'contentOnly' || | ||
_selectedBlockName === 'core/block' | ||
? _selectedBlockClientId | ||
: undefined ), | ||
: undefined ) || | ||
closestContentOnlySectionBlock, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding of the changes in this file is that we want to show the "inspector control" of the parent "content-only" block when it exists rather than the leaf block that is being selected. Are we doing the same for the block toolbar. This also feels like something we want to do consistently for block inspector and block toolbar? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this change can probably be extracted to a dedicated PR as it can be seen as an improvement to content only blocks. would be good to e2e test as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That change is incorrect. I've been looking at how to amend it so that:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If we want to combine two blocks in the inspector controls, I feel we need some design explorations no? Or do we just dump everything there? It maybe hard for users to understand what's happening no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @richtabor What are your thoughts here? If you select a child block of a section, should we be exposing the currently selected block's inspector controls or just the parent "section" block's controls? If it's the former how can we combine that with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think this is already how It's something that's always felt a little off to me (having a different selected block shown at the top of the inspector compared to the block toolbar), but I think it might be worth breaking it out into a separate issue to discuss given the precedence. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should be consistent with what happens when you have a Wouldn't mind changing it in a separate PR though. Either to use a drill-down UI like we do for the Navigation block or to just get rid of it entirely. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drill down might work |
||
}; | ||
}, [] ); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ import { unlock } from '../../lock-unlock'; | |
import { store as editorStore } from '../../store'; | ||
import EditorHistoryRedo from '../editor-history/redo'; | ||
import EditorHistoryUndo from '../editor-history/undo'; | ||
import SimpleEditingModeSelector from '../simple-editing-mode-selector'; | ||
|
||
function DocumentTools( { className, disableBlockTools = false } ) { | ||
const { setIsInserterOpened, setIsListViewOpened } = | ||
|
@@ -139,7 +140,11 @@ function DocumentTools( { className, disableBlockTools = false } ) { | |
<> | ||
{ isLargeViewport && ! hasFixedToolbar && ( | ||
<ToolbarItem | ||
as={ ToolSelector } | ||
as={ | ||
window.__experimentalSimpleEditingMode | ||
? SimpleEditingModeSelector | ||
: ToolSelector | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why we're using a separate tool selector personally, can't we reuse the same? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this was just for the purposes of getting an experimnet up without editing the other component. We should be able to reuse the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It had to be a new component so that it could live in |
||
} | ||
showTooltip={ ! showIconLabels } | ||
variant={ | ||
showIconLabels ? 'tertiary' : undefined | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useSelect, useDispatch, useRegistry } from '@wordpress/data'; | ||
import { store as blockEditorStore } from '@wordpress/block-editor'; | ||
import { useEffect } from '@wordpress/element'; | ||
import { store as blocksStore } from '@wordpress/blocks'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { unlock } from '../../lock-unlock'; | ||
|
||
/** | ||
* Component that when rendered, makes it so that the editor allows only | ||
* specific sections to be edited in content-only mode for templates. | ||
*/ | ||
export default function ContentOnlyLockSections() { | ||
const registry = useRegistry(); | ||
|
||
const { setBlockEditingMode, unsetBlockEditingMode } = | ||
useDispatch( blockEditorStore ); | ||
|
||
const selected = useSelect( ( select ) => { | ||
const { | ||
getClientIdsOfDescendants, | ||
getClientIdsWithDescendants, | ||
getSectionRootClientId, | ||
getSectionClientIds, | ||
} = unlock( select( blockEditorStore ) ); | ||
|
||
const sectionRootClientId = getSectionRootClientId(); | ||
const sectionClientIds = getSectionClientIds(); | ||
const allClientIds = sectionRootClientId | ||
? getClientIdsOfDescendants( sectionRootClientId ) | ||
: getClientIdsWithDescendants(); | ||
Comment on lines
+34
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is appearing in various PRs. I think we should have a selector which finds the "sections". I don't think it's a premature abstraction because it will make the code easier to reason about. |
||
return { | ||
sectionRootClientId, | ||
sectionClientIds, | ||
allClientIds, | ||
}; | ||
}, [] ); | ||
const { sectionClientIds, allClientIds, sectionRootClientId } = selected; | ||
const { getBlockOrder, getBlockName } = useSelect( blockEditorStore ); | ||
const { __experimentalHasContentRoleAttribute } = useSelect( blocksStore ); | ||
|
||
useEffect( () => { | ||
const sectionChildrenClientIds = sectionClientIds.flatMap( | ||
( clientId ) => getBlockOrder( clientId ) | ||
); | ||
const contentClientIds = allClientIds.filter( ( clientId ) => | ||
__experimentalHasContentRoleAttribute( getBlockName( clientId ) ) | ||
); | ||
|
||
registry.batch( () => { | ||
// 1. The section root should hide non-content controls. | ||
setBlockEditingMode( sectionRootClientId, 'contentOnly' ); | ||
|
||
// 2. Each section should hide non-content controls. | ||
for ( const clientId of sectionClientIds ) { | ||
setBlockEditingMode( clientId, 'contentOnly' ); | ||
} | ||
|
||
// 3. The children of the section should be disabled. | ||
for ( const clientId of sectionChildrenClientIds ) { | ||
setBlockEditingMode( clientId, 'disabled' ); | ||
} | ||
|
||
// 4. ...except for the "content" blocks which should be content-only. | ||
for ( const clientId of contentClientIds ) { | ||
setBlockEditingMode( clientId, 'contentOnly' ); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we start moving away from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. So rather than batching multiple actions, instead we make the selector more complex so that it can infer the correct block editing mode on a per block basis? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, that makes the code more solid IMO. because it's easy for folks to render competing components like the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll take a look at this. I'd also love to solve the similar situation in the pattern block, though I wonder if it'd be ok to build so much into one selector. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea was that folks would use |
||
} ); | ||
|
||
return () => { | ||
registry.batch( () => { | ||
for ( const clientId of [ | ||
sectionRootClientId, | ||
...sectionClientIds, | ||
...sectionChildrenClientIds, | ||
...contentClientIds, | ||
] ) { | ||
unsetBlockEditingMode( clientId ); | ||
} | ||
} ); | ||
}; | ||
}, [ | ||
__experimentalHasContentRoleAttribute, | ||
allClientIds, | ||
getBlockName, | ||
getBlockOrder, | ||
registry, | ||
sectionRootClientId, | ||
sectionClientIds, | ||
setBlockEditingMode, | ||
unsetBlockEditingMode, | ||
] ); | ||
|
||
return null; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { | ||
Button, | ||
SVG, | ||
Path, | ||
privateApis as componentsPrivateApis, | ||
} from '@wordpress/components'; | ||
import { useSelect, useDispatch } from '@wordpress/data'; | ||
import { forwardRef } from '@wordpress/element'; | ||
import { edit } from '@wordpress/icons'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { unlock } from '../../lock-unlock'; | ||
import { store as editorStore } from '../../store'; | ||
import { TEMPLATE_POST_TYPE } from '../../store/constants'; | ||
|
||
const { DropdownMenuV2 } = unlock( componentsPrivateApis ); | ||
|
||
const selectIcon = ( | ||
<SVG | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="24" | ||
height="24" | ||
viewBox="0 0 24 24" | ||
> | ||
<Path d="M9.4 20.5L5.2 3.8l14.6 9-2 .3c-.2 0-.4.1-.7.1-.9.2-1.6.3-2.2.5-.8.3-1.4.5-1.8.8-.4.3-.8.8-1.3 1.5-.4.5-.8 1.2-1.2 2l-.3.6-.9 1.9zM7.6 7.1l2.4 9.3c.2-.4.5-.8.7-1.1.6-.8 1.1-1.4 1.6-1.8.5-.4 1.3-.8 2.2-1.1l1.2-.3-8.1-5z" /> | ||
</SVG> | ||
); | ||
|
||
function SimpleEditingModeSelector( props, ref ) { | ||
const { postType, renderingMode } = useSelect( | ||
( select ) => ( { | ||
postType: select( editorStore ).getCurrentPostType(), | ||
renderingMode: select( editorStore ).getRenderingMode(), | ||
} ), | ||
[] | ||
); | ||
|
||
const { setRenderingMode } = useDispatch( editorStore ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why we're using the "rendering mode" instead of the editor mode (navigation mode) for this. These two feel to me like different things and that we shouldn't be touching the rendering modes for this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I expect it was a quick way to get up and running. There a couple of things that need to be solved with using editor mode:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used rendering mode because it's in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't think we should allow this. When zoomed out, the text is small and not conducive to editing. To me there's three "modes":
So yeah sounds like editor mode is a better fit 👍 |
||
|
||
if ( postType !== TEMPLATE_POST_TYPE ) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<DropdownMenuV2 | ||
align="start" | ||
trigger={ | ||
<Button | ||
{ ...props } | ||
ref={ ref } | ||
icon={ | ||
renderingMode === 'template-locked' ? selectIcon : edit | ||
} | ||
label={ __( 'Editing mode' ) } | ||
size="small" | ||
/> | ||
} | ||
> | ||
<DropdownMenuV2.Group> | ||
<DropdownMenuV2.RadioItem | ||
name="editing-mode" | ||
value="simple" | ||
checked={ renderingMode === 'template-locked' } | ||
onChange={ () => setRenderingMode( 'template-locked' ) } | ||
> | ||
<DropdownMenuV2.ItemLabel> | ||
{ __( 'Edit' ) } | ||
</DropdownMenuV2.ItemLabel> | ||
<DropdownMenuV2.ItemHelpText> | ||
{ __( 'Focus on content.' ) } | ||
</DropdownMenuV2.ItemHelpText> | ||
</DropdownMenuV2.RadioItem> | ||
<DropdownMenuV2.RadioItem | ||
name="editing-mode" | ||
value="advanced" | ||
checked={ renderingMode !== 'template-locked' } | ||
onChange={ () => setRenderingMode( 'post-only' ) } | ||
> | ||
<DropdownMenuV2.ItemLabel> | ||
{ __( 'Design' ) } | ||
</DropdownMenuV2.ItemLabel> | ||
<DropdownMenuV2.ItemHelpText> | ||
{ __( 'Full control over layout and styling.' ) } | ||
</DropdownMenuV2.ItemHelpText> | ||
</DropdownMenuV2.RadioItem> | ||
</DropdownMenuV2.Group> | ||
</DropdownMenuV2> | ||
); | ||
} | ||
|
||
export default forwardRef( SimpleEditingModeSelector ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
.editor-simple-editing-mode-selector__menu { | ||
.components-menu-item__button { | ||
height: auto; | ||
} | ||
|
||
.components-menu-item__info { | ||
width: max-content; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I don't fell like we need an experiment if we take over the "select mode" and improve it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we take over "Select Mode" do we need
blockEditingMode
at all, or can we just conditionalise all the block tools (in both locations) so that theyrole=content
attribute setting in block json)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think
blockEditingMode
probably needs to stay for BC reasons but I think ideally the block adapts to the tool without needing to callsetBlockEditingMode
explicitly.