@wordpress/editor 14.35.1-next.16d95556a.0 → 14.36.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 (121) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/bindings/post-data.js +15 -15
  3. package/build/bindings/post-data.js.map +2 -2
  4. package/build/bindings/term-data.js +97 -95
  5. package/build/bindings/term-data.js.map +2 -2
  6. package/build/components/collab-sidebar/comments.js +69 -18
  7. package/build/components/collab-sidebar/comments.js.map +2 -2
  8. package/build/components/collab-sidebar/hooks.js +9 -1
  9. package/build/components/collab-sidebar/hooks.js.map +2 -2
  10. package/build/components/global-styles/index.js +8 -8
  11. package/build/components/global-styles/index.js.map +2 -2
  12. package/build/components/post-featured-image/index.js +1 -32
  13. package/build/components/post-featured-image/index.js.map +3 -3
  14. package/build/components/post-schedule/panel.js +1 -7
  15. package/build/components/post-schedule/panel.js.map +2 -2
  16. package/build/components/post-status/index.js +1 -7
  17. package/build/components/post-status/index.js.map +2 -2
  18. package/build/components/post-template/block-theme.js +1 -1
  19. package/build/components/post-template/block-theme.js.map +2 -2
  20. package/build/components/post-template/hooks.js +1 -1
  21. package/build/components/post-template/hooks.js.map +2 -2
  22. package/build/components/provider/index.js +3 -1
  23. package/build/components/provider/index.js.map +2 -2
  24. package/build/components/provider/use-block-editor-settings.js +9 -1
  25. package/build/components/provider/use-block-editor-settings.js.map +2 -2
  26. package/build/components/visual-editor/index.js +1 -7
  27. package/build/components/visual-editor/index.js.map +2 -2
  28. package/build/components/visual-editor/use-edit-content-only-section-exit.js +5 -6
  29. package/build/components/visual-editor/use-edit-content-only-section-exit.js.map +2 -2
  30. package/build/dataviews/store/private-actions.js +5 -1
  31. package/build/dataviews/store/private-actions.js.map +2 -2
  32. package/build/hooks/media-upload.js +89 -6
  33. package/build/hooks/media-upload.js.map +3 -3
  34. package/build/hooks/template-part-navigation-edit-button.js +8 -11
  35. package/build/hooks/template-part-navigation-edit-button.js.map +3 -3
  36. package/build/store/actions.js +1 -1
  37. package/build/store/actions.js.map +2 -2
  38. package/build/store/constants.js +8 -0
  39. package/build/store/constants.js.map +2 -2
  40. package/build-module/bindings/post-data.js +15 -15
  41. package/build-module/bindings/post-data.js.map +2 -2
  42. package/build-module/bindings/term-data.js +93 -95
  43. package/build-module/bindings/term-data.js.map +2 -2
  44. package/build-module/components/collab-sidebar/comments.js +69 -18
  45. package/build-module/components/collab-sidebar/comments.js.map +2 -2
  46. package/build-module/components/collab-sidebar/hooks.js +9 -1
  47. package/build-module/components/collab-sidebar/hooks.js.map +2 -2
  48. package/build-module/components/global-styles/index.js +8 -8
  49. package/build-module/components/global-styles/index.js.map +2 -2
  50. package/build-module/components/post-featured-image/index.js +3 -35
  51. package/build-module/components/post-featured-image/index.js.map +2 -2
  52. package/build-module/components/post-schedule/panel.js +1 -12
  53. package/build-module/components/post-schedule/panel.js.map +2 -2
  54. package/build-module/components/post-status/index.js +1 -12
  55. package/build-module/components/post-status/index.js.map +2 -2
  56. package/build-module/components/post-template/block-theme.js +1 -1
  57. package/build-module/components/post-template/block-theme.js.map +2 -2
  58. package/build-module/components/post-template/hooks.js +1 -1
  59. package/build-module/components/post-template/hooks.js.map +2 -2
  60. package/build-module/components/provider/index.js +3 -1
  61. package/build-module/components/provider/index.js.map +2 -2
  62. package/build-module/components/provider/use-block-editor-settings.js +9 -1
  63. package/build-module/components/provider/use-block-editor-settings.js.map +2 -2
  64. package/build-module/components/visual-editor/index.js +2 -7
  65. package/build-module/components/visual-editor/index.js.map +2 -2
  66. package/build-module/components/visual-editor/use-edit-content-only-section-exit.js +5 -6
  67. package/build-module/components/visual-editor/use-edit-content-only-section-exit.js.map +2 -2
  68. package/build-module/dataviews/store/private-actions.js +5 -1
  69. package/build-module/dataviews/store/private-actions.js.map +2 -2
  70. package/build-module/hooks/media-upload.js +67 -6
  71. package/build-module/hooks/media-upload.js.map +2 -2
  72. package/build-module/hooks/template-part-navigation-edit-button.js +10 -17
  73. package/build-module/hooks/template-part-navigation-edit-button.js.map +2 -2
  74. package/build-module/store/actions.js +1 -1
  75. package/build-module/store/actions.js.map +2 -2
  76. package/build-module/store/constants.js +7 -0
  77. package/build-module/store/constants.js.map +2 -2
  78. package/build-style/style-rtl.css +309 -40
  79. package/build-style/style.css +309 -40
  80. package/build-types/bindings/post-data.d.ts +2 -1
  81. package/build-types/bindings/term-data.d.ts +86 -8
  82. package/build-types/bindings/term-data.d.ts.map +1 -1
  83. package/build-types/components/collab-sidebar/comments.d.ts.map +1 -1
  84. package/build-types/components/collab-sidebar/hooks.d.ts.map +1 -1
  85. package/build-types/components/global-styles/index.d.ts.map +1 -1
  86. package/build-types/components/post-schedule/panel.d.ts.map +1 -1
  87. package/build-types/components/post-status/index.d.ts.map +1 -1
  88. package/build-types/components/post-template/block-theme.d.ts.map +1 -1
  89. package/build-types/components/provider/index.d.ts.map +1 -1
  90. package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
  91. package/build-types/components/visual-editor/index.d.ts.map +1 -1
  92. package/build-types/components/visual-editor/use-edit-content-only-section-exit.d.ts.map +1 -1
  93. package/build-types/dataviews/store/private-actions.d.ts +5 -0
  94. package/build-types/dataviews/store/private-actions.d.ts.map +1 -1
  95. package/build-types/store/actions.d.ts.map +1 -1
  96. package/build-types/store/constants.d.ts +1 -0
  97. package/build-types/store/constants.d.ts.map +1 -1
  98. package/package.json +40 -40
  99. package/src/bindings/post-data.js +21 -20
  100. package/src/bindings/term-data.js +124 -151
  101. package/src/bindings/test/post-data.js +210 -0
  102. package/src/bindings/test/term-data.js +406 -0
  103. package/src/components/collab-sidebar/comments.js +90 -24
  104. package/src/components/collab-sidebar/hooks.js +12 -1
  105. package/src/components/global-styles/index.js +8 -9
  106. package/src/components/global-styles-sidebar/style.scss +7 -0
  107. package/src/components/post-featured-image/index.js +1 -44
  108. package/src/components/post-schedule/panel.js +1 -13
  109. package/src/components/post-status/index.js +1 -13
  110. package/src/components/post-template/block-theme.js +4 -1
  111. package/src/components/post-template/hooks.js +1 -1
  112. package/src/components/provider/index.js +7 -2
  113. package/src/components/provider/use-block-editor-settings.js +8 -0
  114. package/src/components/visual-editor/index.js +1 -6
  115. package/src/components/visual-editor/use-edit-content-only-section-exit.js +9 -10
  116. package/src/dataviews/store/private-actions.ts +17 -1
  117. package/src/hooks/media-upload.js +75 -7
  118. package/src/hooks/template-part-navigation-edit-button.js +4 -15
  119. package/src/store/actions.js +2 -0
  120. package/src/store/constants.ts +6 -0
  121. package/tsconfig.tsbuildinfo +1 -1
@@ -69,22 +69,29 @@ export function Comments( {
69
69
  const { selectBlock, toggleBlockSpotlight } = unlock(
70
70
  useDispatch( blockEditorStore )
71
71
  );
72
- const { blockCommentId, selectedBlockClientId, orderedBlockIds } =
73
- useSelect( ( select ) => {
74
- const {
75
- getBlockAttributes,
76
- getSelectedBlockClientId,
77
- getClientIdsWithDescendants,
78
- } = select( blockEditorStore );
79
- const clientId = getSelectedBlockClientId();
80
- return {
81
- blockCommentId: clientId
82
- ? getBlockAttributes( clientId )?.metadata?.noteId
83
- : null,
84
- selectedBlockClientId: clientId,
85
- orderedBlockIds: getClientIdsWithDescendants(),
86
- };
87
- }, [] );
72
+
73
+ const {
74
+ blockCommentId,
75
+ selectedBlockClientId,
76
+ orderedBlockIds,
77
+ blockMode,
78
+ } = useSelect( ( select ) => {
79
+ const {
80
+ getBlockAttributes,
81
+ getSelectedBlockClientId,
82
+ getClientIdsWithDescendants,
83
+ getBlockMode,
84
+ } = select( blockEditorStore );
85
+ const clientId = getSelectedBlockClientId();
86
+ return {
87
+ blockCommentId: clientId
88
+ ? getBlockAttributes( clientId )?.metadata?.noteId
89
+ : null,
90
+ selectedBlockClientId: clientId,
91
+ orderedBlockIds: getClientIdsWithDescendants(),
92
+ blockMode: clientId ? getBlockMode( clientId ) : null,
93
+ };
94
+ }, [] );
88
95
 
89
96
  const relatedBlockElement = useBlockElement( selectedBlockClientId );
90
97
 
@@ -311,6 +318,7 @@ export function Comments( {
311
318
  threads,
312
319
  selectedThread,
313
320
  setCanvasMinHeight,
321
+ blockMode,
314
322
  ] );
315
323
 
316
324
  const handleThreadNavigation = ( event, thread, isSelected ) => {
@@ -470,6 +478,7 @@ function Thread( {
470
478
  selectedThread,
471
479
  commentLastUpdated,
472
480
  } );
481
+ const isKeyboardTabbingRef = useRef( false );
473
482
 
474
483
  const onMouseEnter = () => {
475
484
  debouncedToggleBlockHighlight( thread.blockClientId, true );
@@ -479,13 +488,48 @@ function Thread( {
479
488
  debouncedToggleBlockHighlight( thread.blockClientId, false );
480
489
  };
481
490
 
491
+ const onFocus = () => {
492
+ toggleBlockHighlight( thread.blockClientId, true );
493
+ };
494
+
495
+ const onBlur = ( event ) => {
496
+ const isNoteFocused = event.relatedTarget?.closest(
497
+ '.editor-collab-sidebar-panel__thread'
498
+ );
499
+ const isDialogFocused =
500
+ event.relatedTarget?.closest( '[role="dialog"]' );
501
+ const isTabbing = isKeyboardTabbingRef.current;
502
+
503
+ // When another note is clicked, do nothing because the current note is automatically closed.
504
+ if ( isNoteFocused && ! isTabbing ) {
505
+ return;
506
+ }
507
+ // When deleting a note, a dialog appears, but the note should not be collapsed.
508
+ if ( isDialogFocused ) {
509
+ return;
510
+ }
511
+ // When tabbing, do nothing if the focus is within the current note.
512
+ if (
513
+ isTabbing &&
514
+ event.currentTarget.contains( event.relatedTarget )
515
+ ) {
516
+ return;
517
+ }
518
+
519
+ // Closes a note that has lost focus when any of the following conditions are met:
520
+ // - An element other than a note is clicked.
521
+ // - Focus was lost by tabbing.
522
+ toggleBlockHighlight( thread.blockClientId, false );
523
+ unselectThread();
524
+ };
525
+
482
526
  const handleCommentSelect = () => {
483
527
  setNewNoteFormState( 'closed' );
484
528
  setSelectedThread( thread.id );
529
+ toggleBlockSpotlight( thread.blockClientId, true );
485
530
  if ( !! thread.blockClientId ) {
486
531
  // Pass `null` as the second parameter to prevent focusing the block.
487
532
  selectBlock( thread.blockClientId, null );
488
- toggleBlockSpotlight( thread.blockClientId, true );
489
533
  }
490
534
  };
491
535
 
@@ -547,9 +591,20 @@ function Thread( {
547
591
  onClick={ handleCommentSelect }
548
592
  onMouseEnter={ onMouseEnter }
549
593
  onMouseLeave={ onMouseLeave }
550
- onFocus={ onMouseEnter }
551
- onBlur={ onMouseLeave }
552
- onKeyDown={ onKeyDown }
594
+ onFocus={ onFocus }
595
+ onBlur={ onBlur }
596
+ onKeyUp={ ( event ) => {
597
+ if ( event.key === 'Tab' ) {
598
+ isKeyboardTabbingRef.current = false;
599
+ }
600
+ } }
601
+ onKeyDown={ ( event ) => {
602
+ if ( event.key === 'Tab' ) {
603
+ isKeyboardTabbingRef.current = true;
604
+ } else {
605
+ onKeyDown( event );
606
+ }
607
+ } }
553
608
  tabIndex={ 0 }
554
609
  role="treeitem"
555
610
  aria-label={ ariaLabel }
@@ -774,6 +829,14 @@ const CommentBoard = ( {
774
829
  ? actions.filter( ( item ) => item.isEligible( thread ) )
775
830
  : [];
776
831
 
832
+ const deleteConfirmMessage =
833
+ // When deleting a top level note, descendants will also be deleted.
834
+ thread.parent === 0
835
+ ? __(
836
+ "Are you sure you want to delete this note? This will also delete all of this note's replies."
837
+ )
838
+ : __( 'Are you sure you want to delete this reply?' );
839
+
777
840
  return (
778
841
  <VStack
779
842
  spacing="2"
@@ -828,7 +891,12 @@ const CommentBoard = ( {
828
891
  />
829
892
  }
830
893
  />
831
- <Menu.Popover>
894
+ <Menu.Popover
895
+ // The menu popover is rendered in a portal, which causes focus to be
896
+ // lost and the note to be collapsed unintentionally. To prevent this,
897
+ // the popover should be rendered as an inline.
898
+ modal={ false }
899
+ >
832
900
  { moreActions.map( ( action ) => (
833
901
  <Menu.Item
834
902
  key={ action.id }
@@ -909,9 +977,7 @@ const CommentBoard = ( {
909
977
  onCancel={ handleCancel }
910
978
  confirmButtonText={ __( 'Delete' ) }
911
979
  >
912
- { __(
913
- "Are you sure you want to delete this note? This will also delete all of this note's replies."
914
- ) }
980
+ { deleteConfirmMessage }
915
981
  </ConfirmDialog>
916
982
  ) }
917
983
  </VStack>
@@ -372,6 +372,17 @@ export function useFloatingThread( {
372
372
  const blockRef = useRef();
373
373
  useBlockElementRef( thread.blockClientId, blockRef );
374
374
 
375
+ const blockMode = useSelect(
376
+ ( select ) => {
377
+ return thread.blockClientId
378
+ ? select( blockEditorStore ).getBlockMode(
379
+ thread.blockClientId
380
+ )
381
+ : null;
382
+ },
383
+ [ thread.blockClientId ]
384
+ );
385
+
375
386
  const updateHeight = useCallback(
376
387
  ( id, newHeight ) => {
377
388
  setHeights( ( prev ) => {
@@ -400,7 +411,7 @@ export function useFloatingThread( {
400
411
  if ( blockRef.current ) {
401
412
  refs.setReference( blockRef.current );
402
413
  }
403
- }, [ blockRef, refs, commentLastUpdated ] );
414
+ }, [ blockRef, refs, commentLastUpdated, blockMode ] );
404
415
 
405
416
  // Track thread heights.
406
417
  useEffect( () => {
@@ -32,16 +32,15 @@ function useServerData() {
32
32
  const editorSettings = getEditorSettings();
33
33
 
34
34
  const canUserUploadMedia = canUser( 'create', {
35
- kind: 'root',
36
- name: 'media',
35
+ kind: 'postType',
36
+ name: 'attachment',
37
37
  } );
38
38
 
39
39
  return {
40
- styles: editorSettings?.styles || [],
41
- __unstableResolvedAssets:
42
- editorSettings?.__unstableResolvedAssets || {},
43
- colors: editorSettings?.colors || [],
44
- gradients: editorSettings?.gradients || [],
40
+ styles: editorSettings?.styles,
41
+ __unstableResolvedAssets: editorSettings?.__unstableResolvedAssets,
42
+ colors: editorSettings?.colors,
43
+ gradients: editorSettings?.gradients,
45
44
  __experimentalDiscussionSettings:
46
45
  editorSettings?.__experimentalDiscussionSettings,
47
46
  mediaUploadHandler: canUserUploadMedia ? uploadMedia : undefined,
@@ -64,10 +63,10 @@ function useServerData() {
64
63
  settings: {
65
64
  color: {
66
65
  palette: {
67
- theme: colors,
66
+ theme: colors ?? [],
68
67
  },
69
68
  gradients: {
70
- theme: gradients,
69
+ theme: gradients ?? [],
71
70
  },
72
71
  duotone: {
73
72
  theme: [],
@@ -116,4 +116,11 @@
116
116
  vertical-align: bottom;
117
117
  }
118
118
  }
119
+ .components-button {
120
+ &:hover {
121
+ svg {
122
+ fill: $white;
123
+ }
124
+ }
125
+ }
119
126
  }
@@ -25,19 +25,15 @@ import {
25
25
  MediaUpload,
26
26
  MediaUploadCheck,
27
27
  store as blockEditorStore,
28
- privateApis as blockEditorPrivateApis,
29
28
  } from '@wordpress/block-editor';
30
29
  import { store as coreStore } from '@wordpress/core-data';
31
30
 
32
31
  /**
33
32
  * Internal dependencies
34
33
  */
35
- import { unlock } from '../../lock-unlock';
36
34
  import PostFeaturedImageCheck from './check';
37
35
  import { store as editorStore } from '../../store';
38
36
 
39
- const { MediaUploadModal } = unlock( blockEditorPrivateApis );
40
-
41
37
  const ALLOWED_MEDIA_TYPES = [ 'image' ];
42
38
 
43
39
  // Used when labels from post type were not yet loaded or when they are not present.
@@ -52,45 +48,6 @@ const instructions = (
52
48
  </p>
53
49
  );
54
50
 
55
- /**
56
- * Conditional Media component that uses MediaUploadModal when experiment is enabled,
57
- * otherwise falls back to MediaUpload.
58
- *
59
- * @param {Object} root0 Component props.
60
- * @param {Function} root0.render Render prop function that receives { open } object.
61
- * @return {JSX.Element} The component.
62
- */
63
- function ConditionalMediaUpload( { render, ...props } ) {
64
- const [ isModalOpen, setIsModalOpen ] = useState( false );
65
- const mediaUpload = useSelect( ( select ) => {
66
- const { getSettings } = select( blockEditorStore );
67
- return getSettings().mediaUpload;
68
- }, [] );
69
-
70
- if ( window.__experimentalDataViewsMediaModal ) {
71
- return (
72
- <>
73
- { render && render( { open: () => setIsModalOpen( true ) } ) }
74
- <MediaUploadModal
75
- { ...props }
76
- isOpen={ isModalOpen }
77
- onClose={ () => {
78
- setIsModalOpen( false );
79
- props.onClose?.();
80
- } }
81
- onSelect={ ( media ) => {
82
- setIsModalOpen( false );
83
- props.onSelect?.( media );
84
- } }
85
- onUpload={ mediaUpload }
86
- />
87
- </>
88
- );
89
- }
90
-
91
- return <MediaUpload { ...props } render={ render } />;
92
- }
93
-
94
51
  function getMediaDetails( media, postId ) {
95
52
  if ( ! media ) {
96
53
  return {};
@@ -224,7 +181,7 @@ function PostFeaturedImage( {
224
181
  </div>
225
182
  ) }
226
183
  <MediaUploadCheck fallback={ instructions }>
227
- <ConditionalMediaUpload
184
+ <MediaUpload
228
185
  title={
229
186
  postType?.labels?.featured_image ||
230
187
  DEFAULT_FEATURE_IMAGE_LABEL
@@ -14,19 +14,7 @@ import PostScheduleForm from './index';
14
14
  import { usePostScheduleLabel } from './label';
15
15
  import PostPanelRow from '../post-panel-row';
16
16
  import { store as editorStore } from '../../store';
17
- import {
18
- TEMPLATE_POST_TYPE,
19
- TEMPLATE_PART_POST_TYPE,
20
- PATTERN_POST_TYPE,
21
- NAVIGATION_POST_TYPE,
22
- } from '../../store/constants';
23
-
24
- const DESIGN_POST_TYPES = [
25
- TEMPLATE_POST_TYPE,
26
- TEMPLATE_PART_POST_TYPE,
27
- PATTERN_POST_TYPE,
28
- NAVIGATION_POST_TYPE,
29
- ];
17
+ import { DESIGN_POST_TYPES } from '../../store/constants';
30
18
 
31
19
  /**
32
20
  * Renders the Post Schedule Panel component.
@@ -26,12 +26,7 @@ import {
26
26
  /**
27
27
  * Internal dependencies
28
28
  */
29
- import {
30
- TEMPLATE_POST_TYPE,
31
- TEMPLATE_PART_POST_TYPE,
32
- PATTERN_POST_TYPE,
33
- NAVIGATION_POST_TYPE,
34
- } from '../../store/constants';
29
+ import { DESIGN_POST_TYPES } from '../../store/constants';
35
30
  import PostPanelRow from '../post-panel-row';
36
31
  import PostSticky from '../post-sticky';
37
32
  import { PrivatePostSchedule } from '../post-schedule';
@@ -74,13 +69,6 @@ export const STATUS_OPTIONS = [
74
69
  },
75
70
  ];
76
71
 
77
- const DESIGN_POST_TYPES = [
78
- TEMPLATE_POST_TYPE,
79
- TEMPLATE_PART_POST_TYPE,
80
- PATTERN_POST_TYPE,
81
- NAVIGATION_POST_TYPE,
82
- ];
83
-
84
72
  export default function PostStatus() {
85
73
  const { status, date, password, postId, postType, canEdit } = useSelect(
86
74
  ( select ) => {
@@ -148,7 +148,10 @@ export default function BlockThemeControl( { id } ) {
148
148
  // duplication explicit, so there
149
149
  // wouldn't be an "edit" button for
150
150
  // static theme templates.
151
- if ( ! hasSpecificTemplate ) {
151
+ if (
152
+ ! hasSpecificTemplate &&
153
+ window?.__experimentalTemplateActivate
154
+ ) {
152
155
  const activeTemplates =
153
156
  await getEntityRecord(
154
157
  'root',
@@ -73,7 +73,7 @@ export function useAvailableTemplates( postType ) {
73
73
  allowSwitchingTemplate &&
74
74
  templates?.filter(
75
75
  ( template ) =>
76
- ( template.is_custom || template.type === 'wp_template' ) &&
76
+ template.is_custom &&
77
77
  template.slug !== currentTemplateSlug &&
78
78
  !! template.content.raw // Skip empty templates.
79
79
  ),
@@ -318,8 +318,13 @@ export const ExperimentalEditorProvider = withRegistryProvider(
318
318
  // Synchronizes the active post with the state
319
319
  useEffect( () => {
320
320
  setEditedPost( post.type, post.id );
321
- // Clear any notices dependent on the post context.
322
- removeNotice( 'template-activate-notice' );
321
+ if (
322
+ typeof window !== 'undefined' &&
323
+ window.__experimentalTemplateActivate
324
+ ) {
325
+ // Clear any notices dependent on the post context.
326
+ removeNotice( 'template-activate-notice' );
327
+ }
323
328
  }, [ post.type, post.id, setEditedPost, removeNotice ] );
324
329
 
325
330
  // Synchronize the editor settings as they change.
@@ -96,6 +96,7 @@ const {
96
96
  reusableBlocksSelectKey,
97
97
  sectionRootClientIdKey,
98
98
  mediaEditKey,
99
+ getMediaSelectKey,
99
100
  } = unlock( privateApis );
100
101
 
101
102
  /**
@@ -293,6 +294,13 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) {
293
294
  hasFixedToolbar,
294
295
  isDistractionFree,
295
296
  keepCaretInsideBlock,
297
+ [ getMediaSelectKey ]: ( select, attachmentId ) => {
298
+ return select( coreStore ).getEntityRecord(
299
+ 'postType',
300
+ 'attachment',
301
+ attachmentId
302
+ );
303
+ },
296
304
  [ mediaEditKey ]: hasUploadPermissions
297
305
  ? editMediaEntity
298
306
  : undefined,
@@ -36,6 +36,7 @@ import {
36
36
  PATTERN_POST_TYPE,
37
37
  TEMPLATE_PART_POST_TYPE,
38
38
  TEMPLATE_POST_TYPE,
39
+ DESIGN_POST_TYPES,
39
40
  } from '../../store/constants';
40
41
  import { useZoomOutModeExit } from './use-zoom-out-mode-exit';
41
42
  import { usePaddingAppender } from './use-padding-appender';
@@ -53,12 +54,6 @@ const {
53
54
  * These post types have a special editor where they don't allow you to fill the title
54
55
  * and they don't apply the layout styles.
55
56
  */
56
- const DESIGN_POST_TYPES = [
57
- PATTERN_POST_TYPE,
58
- TEMPLATE_POST_TYPE,
59
- NAVIGATION_POST_TYPE,
60
- TEMPLATE_PART_POST_TYPE,
61
- ];
62
57
 
63
58
  /**
64
59
  * Given an array of nested blocks, find the first Post Content
@@ -30,17 +30,16 @@ export function useEditContentOnlySectionExit() {
30
30
  return;
31
31
  }
32
32
 
33
- if ( ! event.defaultPrevented ) {
34
- event.preventDefault();
33
+ // Check if the click is outside the edited block first.
34
+ const isClickOutside = ! event.target.closest(
35
+ `[data-block="${ editedContentOnlySection }"]`
36
+ );
35
37
 
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
- }
38
+ // Only prevent default and stop editing if clicking outside.
39
+ // This allows default behavior (e.g., file dialogs) to work when clicking inside.
40
+ if ( isClickOutside && ! event.defaultPrevented ) {
41
+ event.preventDefault();
42
+ stopEditingContentOnlySection();
44
43
  }
45
44
  }
46
45
 
@@ -40,9 +40,16 @@ import {
40
40
  * Internal dependencies
41
41
  */
42
42
  import { store as editorStore } from '../../store';
43
+ import { DESIGN_POST_TYPES } from '../../store/constants';
43
44
  import postPreviewField from '../fields/content-preview';
44
45
  import { unlock } from '../../lock-unlock';
45
46
 
47
+ declare global {
48
+ interface Window {
49
+ __experimentalTemplateActivate?: boolean;
50
+ }
51
+ }
52
+
46
53
  export function registerEntityAction< Item >(
47
54
  kind: string,
48
55
  name: string,
@@ -148,6 +155,15 @@ export const registerPostTypeSchema =
148
155
  }
149
156
  }
150
157
 
158
+ // When template activation experiment is disabled, templates cannot be duplicated.
159
+ // @ts-ignore
160
+ if (
161
+ postTypeConfig.slug === 'wp_template' &&
162
+ ! window?.__experimentalTemplateActivate
163
+ ) {
164
+ canDuplicate = undefined;
165
+ }
166
+
151
167
  const actions = [
152
168
  postTypeConfig.viewable ? viewPost : undefined,
153
169
  !! postTypeConfig.supports?.revisions
@@ -181,7 +197,7 @@ export const registerPostTypeSchema =
181
197
  featuredImageField,
182
198
  postTypeConfig.supports?.author && authorField,
183
199
  statusField,
184
- dateField,
200
+ ! DESIGN_POST_TYPES.includes( postTypeConfig.slug ) && dateField,
185
201
  slugField,
186
202
  postTypeConfig.supports?.[ 'page-attributes' ] && parentField,
187
203
  postTypeConfig.supports?.comments && commentStatusField,
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
+ import { Component } from '@wordpress/element';
4
5
  import { addFilter } from '@wordpress/hooks';
6
+ import deprecated from '@wordpress/deprecated';
5
7
  import {
6
8
  MediaUpload,
7
9
  privateApis as mediaUtilsPrivateApis,
@@ -16,16 +18,82 @@ const { MediaUploadModal: MediaUploadModalComponent } = unlock(
16
18
  mediaUtilsPrivateApis
17
19
  );
18
20
 
21
+ /**
22
+ * Class component wrapper for MediaUploadModal to maintain compatibility
23
+ * with the stable MediaUpload component API (render prop pattern).
24
+ */
25
+ class MediaUploadModalWrapper extends Component {
26
+ constructor( props ) {
27
+ super( props );
28
+ this.state = {
29
+ isOpen: false,
30
+ };
31
+ this.openModal = this.openModal.bind( this );
32
+ this.closeModal = this.closeModal.bind( this );
33
+ }
34
+
35
+ openModal() {
36
+ this.setState( { isOpen: true } );
37
+ }
38
+
39
+ closeModal() {
40
+ this.setState( { isOpen: false } );
41
+ this.props.onClose?.();
42
+ }
43
+
44
+ render() {
45
+ const {
46
+ allowedTypes,
47
+ multiple,
48
+ value,
49
+ onSelect,
50
+ title,
51
+ modalClass,
52
+ render,
53
+ } = this.props;
54
+ const { isOpen } = this.state;
55
+
56
+ return (
57
+ <>
58
+ { render( { open: this.openModal } ) }
59
+ <MediaUploadModalComponent
60
+ allowedTypes={ allowedTypes }
61
+ multiple={ multiple }
62
+ value={ value }
63
+ onSelect={ ( media ) => {
64
+ onSelect( media );
65
+ this.closeModal();
66
+ } }
67
+ onClose={ this.closeModal }
68
+ title={ title }
69
+ isOpen={ isOpen }
70
+ modalClass={ modalClass }
71
+ />
72
+ </>
73
+ );
74
+ }
75
+ }
76
+
19
77
  if ( window.__experimentalDataViewsMediaModal ) {
20
- // Create a new filter for the MediaUploadModal component
78
+ // Use the wrapper component for editor.MediaUpload when the experimental flag is enabled
79
+ addFilter(
80
+ 'editor.MediaUpload',
81
+ 'core/editor/components/media-upload',
82
+ () => {
83
+ deprecated( 'Extending MediaUpload as a class component', {
84
+ since: '7.0',
85
+ version: '7.2',
86
+ hint: 'MediaUpload will become a function component in WordPress 7.2 Please update any custom implementations to use function components instead.',
87
+ } );
88
+ return MediaUploadModalWrapper;
89
+ }
90
+ );
91
+ } else {
21
92
  addFilter(
22
- 'editor.MediaUploadModal',
23
- 'core/editor/components/media-upload-modal',
93
+ 'editor.MediaUpload',
94
+ 'core/editor/components/media-upload',
24
95
  () => {
25
- return MediaUploadModalComponent;
96
+ return MediaUpload;
26
97
  }
27
98
  );
28
99
  }
29
- addFilter( 'editor.MediaUpload', 'core/editor/components/media-upload', () => {
30
- return MediaUpload;
31
- } );
@@ -6,14 +6,10 @@ import { createHigherOrderComponent } from '@wordpress/compose';
6
6
  import { useCallback } from '@wordpress/element';
7
7
  import { __ } from '@wordpress/i18n';
8
8
  import {
9
- BlockControls,
9
+ __unstableBlockToolbarLastItem as BlockToolbarLastItem,
10
10
  store as blockEditorStore,
11
11
  } from '@wordpress/block-editor';
12
- import {
13
- ToolbarButton,
14
- ToolbarGroup,
15
- __experimentalDivider as Divider,
16
- } from '@wordpress/components';
12
+ import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
17
13
  import { useSelect, useDispatch } from '@wordpress/data';
18
14
  import { store as interfaceStore } from '@wordpress/interface';
19
15
 
@@ -96,15 +92,8 @@ function TemplatePartNavigationEditButton( { clientId } ) {
96
92
  }
97
93
 
98
94
  return (
99
- <BlockControls group="other">
95
+ <BlockToolbarLastItem>
100
96
  <ToolbarGroup>
101
- { /*
102
- * Add a vertical divider to visually separate the "Edit navigation"
103
- * button from the template part's "Edit" button. Both buttons share
104
- * the same toolbar group ("other"), so without this divider they
105
- * would appear directly adjacent with no visual separation.
106
- */ }
107
- <Divider orientation="vertical" marginEnd={ 3 } />
108
97
  <ToolbarButton
109
98
  label={ __( 'Edit navigation' ) }
110
99
  onClick={ onEditNavigation }
@@ -112,7 +101,7 @@ function TemplatePartNavigationEditButton( { clientId } ) {
112
101
  { __( 'Edit navigation' ) }
113
102
  </ToolbarButton>
114
103
  </ToolbarGroup>
115
- </BlockControls>
104
+ </BlockToolbarLastItem>
116
105
  );
117
106
  }
118
107
 
@@ -274,6 +274,8 @@ export const savePost =
274
274
  dispatch( { type: 'REQUEST_POST_UPDATE_FINISH', options } );
275
275
 
276
276
  if (
277
+ typeof window !== 'undefined' &&
278
+ window.__experimentalTemplateActivate &&
277
279
  ! options.isAutosave &&
278
280
  previousRecord.type === 'wp_template' &&
279
281
  ( typeof previousRecord.id === 'number' ||