@wordpress/editor 14.34.1-next.2f1c7c01b.0 → 14.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +1 -1
  3. package/build/components/collab-sidebar/add-comment.js +4 -6
  4. package/build/components/collab-sidebar/add-comment.js.map +2 -2
  5. package/build/components/collab-sidebar/comment-form.js +14 -5
  6. package/build/components/collab-sidebar/comment-form.js.map +2 -2
  7. package/build/components/collab-sidebar/comments.js +60 -24
  8. package/build/components/collab-sidebar/comments.js.map +2 -2
  9. package/build/components/collab-sidebar/index.js +4 -4
  10. package/build/components/collab-sidebar/index.js.map +2 -2
  11. package/build/components/commands/index.js +16 -4
  12. package/build/components/commands/index.js.map +2 -2
  13. package/build/components/editor/index.js +10 -2
  14. package/build/components/editor/index.js.map +2 -2
  15. package/build/components/entities-saved-states/index.js.map +1 -1
  16. package/build/components/global-styles/hooks.js.map +1 -1
  17. package/build/components/post-template/panel.js +9 -6
  18. package/build/components/post-template/panel.js.map +2 -2
  19. package/build/components/post-title/index.js +18 -9
  20. package/build/components/post-title/index.js.map +2 -2
  21. package/build/components/visual-editor/index.js +10 -2
  22. package/build/components/visual-editor/index.js.map +2 -2
  23. package/build/components/visual-editor/use-edit-content-only-section-exit.js +65 -0
  24. package/build/components/visual-editor/use-edit-content-only-section-exit.js.map +7 -0
  25. package/build/components/visual-editor/use-padding-appender.js +75 -0
  26. package/build/components/visual-editor/use-padding-appender.js.map +7 -0
  27. package/build/dataviews/fields/content-preview/content-preview-view.js +2 -2
  28. package/build/dataviews/fields/content-preview/content-preview-view.js.map +3 -3
  29. package/build/hooks/pattern-overrides.js +1 -3
  30. package/build/hooks/pattern-overrides.js.map +2 -2
  31. package/build/store/actions.js +1 -1
  32. package/build/store/actions.js.map +2 -2
  33. package/build-module/components/collab-sidebar/add-comment.js +4 -6
  34. package/build-module/components/collab-sidebar/add-comment.js.map +2 -2
  35. package/build-module/components/collab-sidebar/comment-form.js +14 -5
  36. package/build-module/components/collab-sidebar/comment-form.js.map +2 -2
  37. package/build-module/components/collab-sidebar/comments.js +60 -24
  38. package/build-module/components/collab-sidebar/comments.js.map +2 -2
  39. package/build-module/components/collab-sidebar/index.js +4 -4
  40. package/build-module/components/collab-sidebar/index.js.map +2 -2
  41. package/build-module/components/commands/index.js +16 -4
  42. package/build-module/components/commands/index.js.map +2 -2
  43. package/build-module/components/editor/index.js +10 -2
  44. package/build-module/components/editor/index.js.map +2 -2
  45. package/build-module/components/entities-saved-states/index.js.map +1 -1
  46. package/build-module/components/global-styles/hooks.js.map +1 -1
  47. package/build-module/components/post-template/panel.js +9 -6
  48. package/build-module/components/post-template/panel.js.map +2 -2
  49. package/build-module/components/post-title/index.js +18 -9
  50. package/build-module/components/post-title/index.js.map +2 -2
  51. package/build-module/components/visual-editor/index.js +10 -2
  52. package/build-module/components/visual-editor/index.js.map +2 -2
  53. package/build-module/components/visual-editor/use-edit-content-only-section-exit.js +40 -0
  54. package/build-module/components/visual-editor/use-edit-content-only-section-exit.js.map +7 -0
  55. package/build-module/components/visual-editor/use-padding-appender.js +50 -0
  56. package/build-module/components/visual-editor/use-padding-appender.js.map +7 -0
  57. package/build-module/dataviews/fields/content-preview/content-preview-view.js +3 -4
  58. package/build-module/dataviews/fields/content-preview/content-preview-view.js.map +2 -2
  59. package/build-module/hooks/pattern-overrides.js +1 -3
  60. package/build-module/hooks/pattern-overrides.js.map +2 -2
  61. package/build-module/store/actions.js +1 -1
  62. package/build-module/store/actions.js.map +2 -2
  63. package/build-style/style-rtl.css +52 -40
  64. package/build-style/style.css +52 -40
  65. package/build-types/components/collab-sidebar/add-comment.d.ts.map +1 -1
  66. package/build-types/components/collab-sidebar/comment-form.d.ts.map +1 -1
  67. package/build-types/components/collab-sidebar/comments.d.ts +1 -1
  68. package/build-types/components/collab-sidebar/comments.d.ts.map +1 -1
  69. package/build-types/components/commands/index.d.ts.map +1 -1
  70. package/build-types/components/editor/index.d.ts.map +1 -1
  71. package/build-types/components/entities-saved-states/index.d.ts +2 -2
  72. package/build-types/components/entities-saved-states/index.d.ts.map +1 -1
  73. package/build-types/components/global-styles/hooks.d.ts +6 -6
  74. package/build-types/components/global-styles/hooks.d.ts.map +1 -1
  75. package/build-types/components/header/back-button.d.ts.map +1 -1
  76. package/build-types/components/local-autosave-monitor/index.d.ts.map +1 -1
  77. package/build-types/components/more-menu/tools-more-menu-group.d.ts.map +1 -1
  78. package/build-types/components/more-menu/view-more-menu-group.d.ts.map +1 -1
  79. package/build-types/components/plugin-document-setting-panel/index.d.ts.map +1 -1
  80. package/build-types/components/plugin-post-publish-panel/index.d.ts.map +1 -1
  81. package/build-types/components/plugin-post-status-info/index.d.ts.map +1 -1
  82. package/build-types/components/plugin-pre-publish-panel/index.d.ts.map +1 -1
  83. package/build-types/components/post-excerpt/plugin.d.ts.map +1 -1
  84. package/build-types/components/post-locked-modal/index.d.ts.map +1 -1
  85. package/build-types/components/post-publish-button/index.d.ts.map +1 -1
  86. package/build-types/components/post-taxonomies/flat-term-selector.d.ts.map +1 -1
  87. package/build-types/components/post-taxonomies/hierarchical-term-selector.d.ts +1 -10
  88. package/build-types/components/post-taxonomies/hierarchical-term-selector.d.ts.map +1 -1
  89. package/build-types/components/post-template/panel.d.ts.map +1 -1
  90. package/build-types/components/preferences-modal/enable-plugin-document-setting-panel.d.ts.map +1 -1
  91. package/build-types/components/save-publish-panels/index.d.ts.map +1 -1
  92. package/build-types/components/style-book/color-examples.d.ts.map +1 -1
  93. package/build-types/components/style-book/duotone-examples.d.ts.map +1 -1
  94. package/build-types/components/visual-editor/index.d.ts.map +1 -1
  95. package/build-types/components/visual-editor/use-edit-content-only-section-exit.d.ts +6 -0
  96. package/build-types/components/visual-editor/use-edit-content-only-section-exit.d.ts.map +1 -0
  97. package/build-types/components/visual-editor/use-padding-appender.d.ts +2 -0
  98. package/build-types/components/visual-editor/use-padding-appender.d.ts.map +1 -0
  99. package/build-types/dataviews/fields/content-preview/content-preview-view.d.ts.map +1 -1
  100. package/build-types/dataviews/store/private-actions.d.ts.map +1 -1
  101. package/build-types/store/actions.d.ts +3 -4
  102. package/build-types/store/actions.d.ts.map +1 -1
  103. package/build-types/store/private-actions.d.ts.map +1 -1
  104. package/build-types/store/private-selectors.d.ts +5 -5
  105. package/build-types/store/selectors.d.ts +68 -68
  106. package/package.json +40 -40
  107. package/src/components/collab-sidebar/add-comment.js +4 -10
  108. package/src/components/collab-sidebar/comment-form.js +16 -4
  109. package/src/components/collab-sidebar/comments.js +100 -41
  110. package/src/components/collab-sidebar/index.js +4 -4
  111. package/src/components/collab-sidebar/style.scss +15 -0
  112. package/src/components/commands/index.js +32 -19
  113. package/src/components/editor/index.js +11 -0
  114. package/src/components/entities-saved-states/index.js +1 -1
  115. package/src/components/global-styles/hooks.js +4 -4
  116. package/src/components/post-template/panel.js +11 -8
  117. package/src/components/post-title/index.js +22 -9
  118. package/src/components/visual-editor/index.js +10 -1
  119. package/src/components/visual-editor/use-edit-content-only-section-exit.js +55 -0
  120. package/src/components/visual-editor/use-padding-appender.js +70 -0
  121. package/src/dataviews/fields/content-preview/content-preview-view.tsx +2 -5
  122. package/src/hooks/pattern-overrides.js +0 -2
  123. package/src/store/actions.js +9 -1
  124. package/tsconfig.tsbuildinfo +1 -1
@@ -36,12 +36,10 @@ export function AddComment( {
36
36
  y,
37
37
  refs,
38
38
  } ) {
39
- const { clientId, blockCommentId } = useSelect( ( select ) => {
40
- const { getSelectedBlock } = select( blockEditorStore );
41
- const selectedBlock = getSelectedBlock();
39
+ const { clientId } = useSelect( ( select ) => {
40
+ const { getSelectedBlockClientId } = select( blockEditorStore );
42
41
  return {
43
- clientId: selectedBlock?.clientId,
44
- blockCommentId: selectedBlock?.attributes?.metadata?.noteId,
42
+ clientId: getSelectedBlockClientId(),
45
43
  };
46
44
  }, [] );
47
45
  const blockElement = useBlockElement( clientId );
@@ -53,11 +51,7 @@ export function AddComment( {
53
51
  toggleBlockSpotlight( clientId, false );
54
52
  };
55
53
 
56
- if (
57
- newNoteFormState !== 'open' ||
58
- ! clientId ||
59
- undefined !== blockCommentId
60
- ) {
54
+ if ( newNoteFormState !== 'open' || ! clientId ) {
61
55
  return null;
62
56
  }
63
57
 
@@ -16,6 +16,7 @@ import {
16
16
  } from '@wordpress/components';
17
17
  import { __ } from '@wordpress/i18n';
18
18
  import { useInstanceId, useDebounce } from '@wordpress/compose';
19
+ import { isKeyboardEvent } from '@wordpress/keycodes';
19
20
 
20
21
  /**
21
22
  * Internal dependencies
@@ -50,6 +51,12 @@ function CommentForm( {
50
51
  <VStack
51
52
  className="editor-collab-sidebar-panel__comment-form"
52
53
  spacing="4"
54
+ as="form"
55
+ onSubmit={ ( event ) => {
56
+ event.preventDefault();
57
+ onSubmit( inputComment );
58
+ setInputComment( '' );
59
+ } }
53
60
  >
54
61
  <VisuallyHidden as="label" htmlFor={ inputId }>
55
62
  { labelText ?? __( 'Note' ) }
@@ -63,6 +70,14 @@ function CommentForm( {
63
70
  } }
64
71
  rows={ 1 }
65
72
  maxRows={ 20 }
73
+ onKeyDown={ ( event ) => {
74
+ if (
75
+ isKeyboardEvent.primary( event, 'Enter' ) &&
76
+ ! isDisabled
77
+ ) {
78
+ event.target.parentNode.requestSubmit();
79
+ }
80
+ } }
66
81
  />
67
82
  <HStack spacing="2" justify="flex-end" wrap>
68
83
  <Button size="compact" variant="tertiary" onClick={ onCancel }>
@@ -72,10 +87,7 @@ function CommentForm( {
72
87
  size="compact"
73
88
  accessibleWhenDisabled
74
89
  variant="primary"
75
- onClick={ () => {
76
- onSubmit( inputComment );
77
- setInputComment( '' );
78
- } }
90
+ type="submit"
79
91
  disabled={ isDisabled }
80
92
  >
81
93
  <Truncate>{ submitButtonText }</Truncate>
@@ -66,6 +66,9 @@ export function Comments( {
66
66
  const [ blockRefs, setBlockRefs ] = useState( {} );
67
67
 
68
68
  const { setCanvasMinHeight } = unlock( useDispatch( editorStore ) );
69
+ const { selectBlock, toggleBlockSpotlight } = unlock(
70
+ useDispatch( blockEditorStore )
71
+ );
69
72
  const { blockCommentId, selectedBlockClientId, orderedBlockIds } =
70
73
  useSelect( ( select ) => {
71
74
  const {
@@ -93,11 +96,7 @@ export function Comments( {
93
96
  // add a "new note" entry to the threads. This special thread type
94
97
  // gets sorted and floated like regular threads, but shows an AddComment
95
98
  // component instead of a regular comment thread.
96
- if (
97
- isFloating &&
98
- newNoteFormState === 'open' &&
99
- undefined === blockCommentId
100
- ) {
99
+ if ( isFloating && newNoteFormState === 'open' ) {
101
100
  // Insert the new note entry at the correct location for its blockId.
102
101
  const newNoteThread = {
103
102
  id: 'new-note-thread',
@@ -124,7 +123,6 @@ export function Comments( {
124
123
  noteThreads,
125
124
  isFloating,
126
125
  newNoteFormState,
127
- blockCommentId,
128
126
  selectedBlockClientId,
129
127
  orderedBlockIds,
130
128
  ] );
@@ -160,8 +158,9 @@ export function Comments( {
160
158
  // Auto-select the related comment thread when a block is selected.
161
159
  useEffect( () => {
162
160
  // Fallback to 'new-note-thread' when showing the comment board for a new note.
163
- const fallback = newNoteFormState === 'open' ? 'new-note-thread' : null;
164
- setSelectedThread( blockCommentId ?? fallback );
161
+ setSelectedThread(
162
+ newNoteFormState === 'open' ? 'new-note-thread' : blockCommentId
163
+ );
165
164
  }, [ blockCommentId, newNoteFormState ] );
166
165
 
167
166
  const setBlockRef = useCallback( ( id, blockRef ) => {
@@ -314,24 +313,96 @@ export function Comments( {
314
313
  setCanvasMinHeight,
315
314
  ] );
316
315
 
316
+ const handleThreadNavigation = ( event, thread, isSelected ) => {
317
+ if ( event.defaultPrevented ) {
318
+ return;
319
+ }
320
+
321
+ const currentIndex = threads.findIndex( ( t ) => t.id === thread.id );
322
+
323
+ if (
324
+ ( event.key === 'Enter' || event.key === 'ArrowRight' ) &&
325
+ event.currentTarget === event.target &&
326
+ ! isSelected
327
+ ) {
328
+ // Expand thread.
329
+ setNewNoteFormState( 'closed' );
330
+ setSelectedThread( thread.id );
331
+ if ( !! thread.blockClientId ) {
332
+ // Pass `null` as the second parameter to prevent focusing the block.
333
+ selectBlock( thread.blockClientId, null );
334
+ toggleBlockSpotlight( thread.blockClientId, true );
335
+ }
336
+ } else if (
337
+ ( ( event.key === 'Enter' || event.key === 'ArrowLeft' ) &&
338
+ event.currentTarget === event.target &&
339
+ isSelected ) ||
340
+ event.key === 'Escape'
341
+ ) {
342
+ // Collapse thread.
343
+ setSelectedThread( null );
344
+ setNewNoteFormState( 'closed' );
345
+ if ( thread.blockClientId ) {
346
+ toggleBlockSpotlight( thread.blockClientId, false );
347
+ }
348
+ focusCommentThread( thread.id, commentSidebarRef.current );
349
+ } else if (
350
+ event.key === 'ArrowDown' &&
351
+ currentIndex < threads.length - 1 &&
352
+ event.currentTarget === event.target
353
+ ) {
354
+ // Move to the next thread.
355
+ const nextThread = threads[ currentIndex + 1 ];
356
+ focusCommentThread( nextThread.id, commentSidebarRef.current );
357
+ } else if (
358
+ event.key === 'ArrowUp' &&
359
+ currentIndex > 0 &&
360
+ event.currentTarget === event.target
361
+ ) {
362
+ // Move to the previous thread.
363
+ const prevThread = threads[ currentIndex - 1 ];
364
+ focusCommentThread( prevThread.id, commentSidebarRef.current );
365
+ } else if (
366
+ event.key === 'Home' &&
367
+ event.currentTarget === event.target
368
+ ) {
369
+ // Move to the first thread.
370
+ focusCommentThread( threads[ 0 ].id, commentSidebarRef.current );
371
+ } else if (
372
+ event.key === 'End' &&
373
+ event.currentTarget === event.target
374
+ ) {
375
+ // Move to the last thread.
376
+ focusCommentThread(
377
+ threads[ threads.length - 1 ].id,
378
+ commentSidebarRef.current
379
+ );
380
+ }
381
+ };
382
+
317
383
  const hasThreads = Array.isArray( threads ) && threads.length > 0;
318
- // This should no longer happen since https://github.com/WordPress/gutenberg/pull/72872.
384
+ // A special case for `template-locked` mode - https://github.com/WordPress/gutenberg/pull/72646.
319
385
  if ( ! hasThreads && ! isFloating ) {
320
- return null;
386
+ return (
387
+ <AddComment
388
+ onSubmit={ onAddReply }
389
+ newNoteFormState={ newNoteFormState }
390
+ setNewNoteFormState={ setNewNoteFormState }
391
+ commentSidebarRef={ commentSidebarRef }
392
+ />
393
+ );
321
394
  }
322
395
 
323
396
  return (
324
397
  <>
325
- { ! isFloating &&
326
- newNoteFormState === 'open' &&
327
- undefined === blockCommentId && (
328
- <AddComment
329
- onSubmit={ onAddReply }
330
- newNoteFormState={ newNoteFormState }
331
- setNewNoteFormState={ setNewNoteFormState }
332
- commentSidebarRef={ commentSidebarRef }
333
- />
334
- ) }
398
+ { ! isFloating && newNoteFormState === 'open' && (
399
+ <AddComment
400
+ onSubmit={ onAddReply }
401
+ newNoteFormState={ newNoteFormState }
402
+ setNewNoteFormState={ setNewNoteFormState }
403
+ commentSidebarRef={ commentSidebarRef }
404
+ />
405
+ ) }
335
406
  { threads.map( ( thread ) => (
336
407
  <Thread
337
408
  key={ thread.id }
@@ -351,6 +422,13 @@ export function Comments( {
351
422
  selectedThread={ selectedThread }
352
423
  commentLastUpdated={ commentLastUpdated }
353
424
  newNoteFormState={ newNoteFormState }
425
+ onKeyDown={ ( event ) =>
426
+ handleThreadNavigation(
427
+ event,
428
+ thread,
429
+ selectedThread === thread.id
430
+ )
431
+ }
354
432
  />
355
433
  ) ) }
356
434
  </>
@@ -374,6 +452,7 @@ function Thread( {
374
452
  selectedThread,
375
453
  commentLastUpdated,
376
454
  newNoteFormState,
455
+ onKeyDown,
377
456
  } ) {
378
457
  const { toggleBlockHighlight, selectBlock, toggleBlockSpotlight } = unlock(
379
458
  useDispatch( blockEditorStore )
@@ -470,27 +549,7 @@ function Thread( {
470
549
  onMouseLeave={ onMouseLeave }
471
550
  onFocus={ onMouseEnter }
472
551
  onBlur={ onMouseLeave }
473
- onKeyDown={ ( event ) => {
474
- if ( event.defaultPrevented ) {
475
- return;
476
- }
477
- // Expand or Collapse thread.
478
- if (
479
- event.key === 'Enter' &&
480
- event.currentTarget === event.target
481
- ) {
482
- if ( isSelected ) {
483
- unselectThread();
484
- } else {
485
- handleCommentSelect();
486
- }
487
- }
488
- // Collapse thread and focus the thread.
489
- if ( event.key === 'Escape' ) {
490
- unselectThread();
491
- focusCommentThread( thread.id, commentSidebarRef.current );
492
- }
493
- } }
552
+ onKeyDown={ onKeyDown }
494
553
  tabIndex={ 0 }
495
554
  role="treeitem"
496
555
  aria-label={ ariaLabel }
@@ -154,12 +154,12 @@ function NotesSidebar( { postId, mode } ) {
154
154
  return;
155
155
  }
156
156
 
157
- setNewNoteFormState( ! blockCommentId ? 'open' : 'closed' );
157
+ setNewNoteFormState( ! currentThread ? 'open' : 'closed' );
158
158
  focusCommentThread(
159
- blockCommentId,
159
+ currentThread?.id,
160
160
  commentSidebarRef.current,
161
161
  // Focus a comment thread when there's a selected block with a comment.
162
- ! blockCommentId ? 'textarea' : undefined
162
+ ! currentThread ? 'textarea' : undefined
163
163
  );
164
164
  toggleBlockSpotlight( clientId, true );
165
165
  }
@@ -170,7 +170,7 @@ function NotesSidebar( { postId, mode } ) {
170
170
 
171
171
  return (
172
172
  <>
173
- { blockCommentId && (
173
+ { !! currentThread && (
174
174
  <CommentAvatarIndicator
175
175
  thread={ currentThread }
176
176
  onClick={ openTheSidebar }
@@ -169,3 +169,18 @@
169
169
  margin-left: 0;
170
170
  }
171
171
  }
172
+
173
+ .show-icon-labels {
174
+ .comment-avatar-indicator {
175
+ width: auto;
176
+ // Hide the user avatars container when labels are set to display...
177
+ div {
178
+ display: none;
179
+ }
180
+ // ... and display labels.
181
+ &::after {
182
+ content: attr(aria-label);
183
+ font-size: $helptext-font-size;
184
+ }
185
+ }
186
+ }
@@ -315,24 +315,37 @@ const getEditedEntityContextualCommands = () =>
315
315
 
316
316
  const getPageContentFocusCommands = () =>
317
317
  function usePageContentFocusCommands() {
318
- const { onNavigateToEntityRecord, goBack, templateId, isPreviewMode } =
319
- useSelect( ( select ) => {
320
- const {
321
- getRenderingMode,
322
- getEditorSettings: _getEditorSettings,
323
- getCurrentTemplateId,
324
- } = unlock( select( editorStore ) );
325
- const editorSettings = _getEditorSettings();
326
- return {
327
- isTemplateHidden: getRenderingMode() === 'post-only',
328
- onNavigateToEntityRecord:
329
- editorSettings.onNavigateToEntityRecord,
330
- getEditorSettings: _getEditorSettings,
331
- goBack: editorSettings.onNavigateToPreviousEntityRecord,
332
- templateId: getCurrentTemplateId(),
333
- isPreviewMode: editorSettings.isPreviewMode,
334
- };
335
- }, [] );
318
+ const {
319
+ onNavigateToEntityRecord,
320
+ goBack,
321
+ templateId,
322
+ isPreviewMode,
323
+ canEditTemplate,
324
+ } = useSelect( ( select ) => {
325
+ const {
326
+ getRenderingMode,
327
+ getEditorSettings: _getEditorSettings,
328
+ getCurrentTemplateId,
329
+ } = unlock( select( editorStore ) );
330
+ const editorSettings = _getEditorSettings();
331
+ const _templateId = getCurrentTemplateId();
332
+ return {
333
+ isTemplateHidden: getRenderingMode() === 'post-only',
334
+ onNavigateToEntityRecord:
335
+ editorSettings.onNavigateToEntityRecord,
336
+ getEditorSettings: _getEditorSettings,
337
+ goBack: editorSettings.onNavigateToPreviousEntityRecord,
338
+ templateId: _templateId,
339
+ isPreviewMode: editorSettings.isPreviewMode,
340
+ canEditTemplate:
341
+ !! _templateId &&
342
+ select( coreStore ).canUser( 'update', {
343
+ kind: 'postType',
344
+ name: 'wp_template',
345
+ id: _templateId,
346
+ } ),
347
+ };
348
+ }, [] );
336
349
  const { editedRecord: template, hasResolved } = useEntityRecord(
337
350
  'postType',
338
351
  'wp_template',
@@ -345,7 +358,7 @@ const getPageContentFocusCommands = () =>
345
358
 
346
359
  const commands = [];
347
360
 
348
- if ( templateId && hasResolved ) {
361
+ if ( templateId && hasResolved && canEditTemplate ) {
349
362
  commands.push( {
350
363
  name: 'core/switch-to-template-focus',
351
364
  label: sprintf(
@@ -49,6 +49,8 @@ function Editor( {
49
49
  getResolutionError,
50
50
  hasFinishedResolution,
51
51
  getCurrentTheme,
52
+ __experimentalGetCurrentGlobalStylesId,
53
+ canUser,
52
54
  } = select( coreStore );
53
55
  const { getRenderingMode, getCurrentPostType } =
54
56
  select( editorStore );
@@ -57,6 +59,14 @@ function Editor( {
57
59
  const renderingMode = getRenderingMode();
58
60
  const currentPostType = getCurrentPostType();
59
61
  const _isBlockTheme = getCurrentTheme()?.is_block_theme;
62
+ const globalStylesId = __experimentalGetCurrentGlobalStylesId();
63
+ const userCanEditGlobalStyles = globalStylesId
64
+ ? canUser( 'update', {
65
+ kind: 'root',
66
+ name: 'globalStyles',
67
+ id: globalStylesId,
68
+ } )
69
+ : false;
60
70
 
61
71
  return {
62
72
  post: getEntityRecord( ...postArgs ),
@@ -76,6 +86,7 @@ function Editor( {
76
86
  isBlockTheme: _isBlockTheme,
77
87
  showGlobalStyles:
78
88
  _isBlockTheme &&
89
+ userCanEditGlobalStyles &&
79
90
  ( currentPostType === 'wp_template' ||
80
91
  renderingMode === 'template-locked' ),
81
92
  };
@@ -36,7 +36,7 @@ function identity( values ) {
36
36
  *
37
37
  * @param {Object} props The component props.
38
38
  * @param {Function} props.close The function to close the dialog.
39
- * @param {boolean} props.renderDialog Whether to render the component with modal dialog behavior.
39
+ * @param {boolean=} props.renderDialog Whether to render the component with modal dialog behavior.
40
40
  * @param {string} props.variant Changes the layout of the component. When an `inline` value is provided, the action buttons are rendered at the end of the component instead of at the start.
41
41
  *
42
42
  * @return {React.ReactNode} The rendered component.
@@ -188,8 +188,8 @@ export function useGlobalStyles() {
188
188
  /**
189
189
  * Hook to get a style value from global styles
190
190
  *
191
- * @param {string} path Style path (e.g., 'color.background')
192
- * @param {string} blockName Optional block name
191
+ * @param {string} path Style path (e.g., 'color.background')
192
+ * @param {string=} blockName Optional block name
193
193
  * @return {*} Style value
194
194
  */
195
195
  export function useStyle( path, blockName ) {
@@ -203,8 +203,8 @@ export function useStyle( path, blockName ) {
203
203
  /**
204
204
  * Hook to get a setting value from global styles
205
205
  *
206
- * @param {string} path Setting path (e.g., 'spacing.blockGap')
207
- * @param {string} blockName Optional block name
206
+ * @param {string} path Setting path (e.g., 'spacing.blockGap')
207
+ * @param {string=} blockName Optional block name
208
208
  * @return {*} Setting value
209
209
  */
210
210
  export function useSetting( path, blockName ) {
@@ -53,14 +53,17 @@ export default function PostTemplatePanel() {
53
53
  return canCreateTemplates;
54
54
  }, [] );
55
55
 
56
- const canViewTemplates = useSelect( ( select ) => {
57
- return (
58
- select( coreStore ).canUser( 'read', {
59
- kind: 'postType',
60
- name: 'wp_template',
61
- } ) ?? false
62
- );
63
- }, [] );
56
+ const canViewTemplates = useSelect(
57
+ ( select ) => {
58
+ return isVisible
59
+ ? select( coreStore ).canUser( 'read', {
60
+ kind: 'postType',
61
+ name: 'wp_template',
62
+ } )
63
+ : false;
64
+ },
65
+ [ isVisible ]
66
+ );
64
67
 
65
68
  if ( ( ! isBlockTheme || ! canViewTemplates ) && isVisible ) {
66
69
  return <ClassicThemeControl />;
@@ -28,15 +28,23 @@ import usePostTitleFocus from './use-post-title-focus';
28
28
  import usePostTitle from './use-post-title';
29
29
  import PostTypeSupportCheck from '../post-type-support-check';
30
30
 
31
- const PostTitle = forwardRef( ( _, forwardedRef ) => {
32
- const { placeholder } = useSelect( ( select ) => {
33
- const { getSettings } = select( blockEditorStore );
34
- const { titlePlaceholder } = getSettings();
31
+ import { unlock } from '../../lock-unlock';
35
32
 
36
- return {
37
- placeholder: titlePlaceholder,
38
- };
39
- }, [] );
33
+ const PostTitle = forwardRef( ( _, forwardedRef ) => {
34
+ const { placeholder, isEditingContentOnlySection } = useSelect(
35
+ ( select ) => {
36
+ const { getSettings, getEditedContentOnlySection } = unlock(
37
+ select( blockEditorStore )
38
+ );
39
+ const { titlePlaceholder } = getSettings();
40
+
41
+ return {
42
+ placeholder: titlePlaceholder,
43
+ isEditingContentOnlySection: !! getEditedContentOnlySection(),
44
+ };
45
+ },
46
+ []
47
+ );
40
48
 
41
49
  const [ isSelected, setIsSelected ] = useState( false );
42
50
 
@@ -169,11 +177,15 @@ const PostTitle = forwardRef( ( _, forwardedRef ) => {
169
177
  'is-selected': isSelected,
170
178
  } );
171
179
 
180
+ // Because the title is within the editor iframe, we can't use scss styles.
181
+ // Instead use an inline style to dim the block when it's disabled.
182
+ const style = isEditingContentOnlySection ? { opacity: 0.2 } : undefined;
183
+
172
184
  return (
173
185
  /* eslint-disable jsx-a11y/heading-has-content, jsx-a11y/no-noninteractive-element-to-interactive-role */
174
186
  <h1
175
187
  ref={ useMergeRefs( [ richTextRef, focusRef ] ) }
176
- contentEditable
188
+ contentEditable={ ! isEditingContentOnlySection }
177
189
  className={ className }
178
190
  aria-label={ decodedPlaceholder }
179
191
  role="textbox"
@@ -182,6 +194,7 @@ const PostTitle = forwardRef( ( _, forwardedRef ) => {
182
194
  onBlur={ onUnselect }
183
195
  onKeyDown={ onKeyDown }
184
196
  onPaste={ onPaste }
197
+ style={ style }
185
198
  />
186
199
  /* eslint-enable jsx-a11y/heading-has-content, jsx-a11y/no-noninteractive-element-to-interactive-role */
187
200
  );
@@ -38,6 +38,8 @@ import {
38
38
  TEMPLATE_POST_TYPE,
39
39
  } from '../../store/constants';
40
40
  import { useZoomOutModeExit } from './use-zoom-out-mode-exit';
41
+ import { usePaddingAppender } from './use-padding-appender';
42
+ import { useEditContentOnlySectionExit } from './use-edit-content-only-section-exit';
41
43
 
42
44
  const {
43
45
  LayoutStyle,
@@ -350,6 +352,10 @@ function VisualEditor( {
350
352
  return canvasMinHeight + scrollTop;
351
353
  }, [ canvasMinHeight ] );
352
354
 
355
+ const [ paddingAppenderRef, paddingStyle ] = usePaddingAppender(
356
+ ! isPreview && renderingMode === 'post-only' && ! isDesignPostType
357
+ );
358
+
353
359
  const iframeStyles = useMemo( () => {
354
360
  return [
355
361
  ...( styles ?? [] ),
@@ -366,6 +372,7 @@ function VisualEditor( {
366
372
  // which isn't a requirement in auto resize mode.
367
373
  enableResizing ? 'min-height:0!important;' : ''
368
374
  }}
375
+ ${ paddingStyle ? paddingStyle : '' }
369
376
  ${
370
377
  enableResizing
371
378
  ? `.block-editor-iframe__html{background:var(--wp-editor-canvas-background);display:flex;align-items:center;justify-content:center;min-height:100vh;}.block-editor-iframe__body{width:100%;}`
@@ -375,7 +382,7 @@ function VisualEditor( {
375
382
  // color to the iframe HTML element to match the background color of the editor canvas.
376
383
  },
377
384
  ];
378
- }, [ styles, enableResizing, calculatedMinHeight ] );
385
+ }, [ styles, enableResizing, calculatedMinHeight, paddingStyle ] );
379
386
 
380
387
  const typewriterRef = useTypewriter();
381
388
  contentRef = useMergeRefs( [
@@ -389,6 +396,8 @@ function VisualEditor( {
389
396
  isEnabled: renderingMode === 'template-locked',
390
397
  } ),
391
398
  useZoomOutModeExit(),
399
+ paddingAppenderRef,
400
+ useEditContentOnlySectionExit(),
392
401
  ] );
393
402
 
394
403
  return (
@@ -0,0 +1,55 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect, useDispatch } from '@wordpress/data';
5
+ import { useRefEffect } from '@wordpress/compose';
6
+ import { store as blockEditorStore } from '@wordpress/block-editor';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { unlock } from '../../lock-unlock';
12
+
13
+ /**
14
+ * Allows content only section editing to be exited by clicking outside of the
15
+ * edited blocks.
16
+ */
17
+ export function useEditContentOnlySectionExit() {
18
+ const { getEditedContentOnlySection } = unlock(
19
+ useSelect( blockEditorStore )
20
+ );
21
+ const { stopEditingContentOnlySection } = unlock(
22
+ useDispatch( blockEditorStore )
23
+ );
24
+
25
+ return useRefEffect(
26
+ ( node ) => {
27
+ function onClick( event ) {
28
+ const editedContentOnlySection = getEditedContentOnlySection();
29
+ if ( ! editedContentOnlySection ) {
30
+ return;
31
+ }
32
+
33
+ if ( ! event.defaultPrevented ) {
34
+ event.preventDefault();
35
+
36
+ // If the user clicks outside the edited block, stop editing.
37
+ if (
38
+ ! event.target.closest(
39
+ `[data-block="${ editedContentOnlySection }"]`
40
+ )
41
+ ) {
42
+ stopEditingContentOnlySection();
43
+ }
44
+ }
45
+ }
46
+
47
+ node.addEventListener( 'click', onClick );
48
+
49
+ return () => {
50
+ node.removeEventListener( 'click', onClick );
51
+ };
52
+ },
53
+ [ getEditedContentOnlySection, stopEditingContentOnlySection ]
54
+ );
55
+ }