@wordpress/editor 14.0.2 → 14.0.4

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 (63) hide show
  1. package/build/bindings/pattern-overrides.js +8 -5
  2. package/build/bindings/pattern-overrides.js.map +1 -1
  3. package/build/components/editor/index.js +5 -0
  4. package/build/components/editor/index.js.map +1 -1
  5. package/build/components/editor-interface/index.js +3 -2
  6. package/build/components/editor-interface/index.js.map +1 -1
  7. package/build/components/global-styles-provider/index.js +6 -65
  8. package/build/components/global-styles-provider/index.js.map +1 -1
  9. package/build/components/post-actions/actions.js +62 -13
  10. package/build/components/post-actions/actions.js.map +1 -1
  11. package/build/components/post-actions/index.js +4 -1
  12. package/build/components/post-actions/index.js.map +1 -1
  13. package/build/components/post-excerpt/index.js +2 -1
  14. package/build/components/post-excerpt/index.js.map +1 -1
  15. package/build/components/post-excerpt/panel.js +2 -1
  16. package/build/components/post-excerpt/panel.js.map +1 -1
  17. package/build/components/save-publish-panels/index.js +30 -16
  18. package/build/components/save-publish-panels/index.js.map +1 -1
  19. package/build/hooks/pattern-overrides.js +3 -3
  20. package/build/hooks/pattern-overrides.js.map +1 -1
  21. package/build/store/private-actions.js +9 -5
  22. package/build/store/private-actions.js.map +1 -1
  23. package/build-module/bindings/pattern-overrides.js +8 -5
  24. package/build-module/bindings/pattern-overrides.js.map +1 -1
  25. package/build-module/components/editor/index.js +5 -0
  26. package/build-module/components/editor/index.js.map +1 -1
  27. package/build-module/components/editor-interface/index.js +3 -2
  28. package/build-module/components/editor-interface/index.js.map +1 -1
  29. package/build-module/components/global-styles-provider/index.js +7 -66
  30. package/build-module/components/global-styles-provider/index.js.map +1 -1
  31. package/build-module/components/post-actions/actions.js +63 -14
  32. package/build-module/components/post-actions/actions.js.map +1 -1
  33. package/build-module/components/post-actions/index.js +4 -1
  34. package/build-module/components/post-actions/index.js.map +1 -1
  35. package/build-module/components/post-excerpt/index.js +2 -1
  36. package/build-module/components/post-excerpt/index.js.map +1 -1
  37. package/build-module/components/post-excerpt/panel.js +2 -1
  38. package/build-module/components/post-excerpt/panel.js.map +1 -1
  39. package/build-module/components/save-publish-panels/index.js +30 -16
  40. package/build-module/components/save-publish-panels/index.js.map +1 -1
  41. package/build-module/hooks/pattern-overrides.js +4 -2
  42. package/build-module/hooks/pattern-overrides.js.map +1 -1
  43. package/build-module/store/private-actions.js +9 -5
  44. package/build-module/store/private-actions.js.map +1 -1
  45. package/build-style/style-rtl.css +19 -2
  46. package/build-style/style.css +19 -2
  47. package/package.json +14 -14
  48. package/src/bindings/pattern-overrides.js +9 -10
  49. package/src/components/editor/index.js +6 -0
  50. package/src/components/editor-interface/index.js +17 -8
  51. package/src/components/global-styles-provider/index.js +7 -90
  52. package/src/components/post-actions/actions.js +109 -21
  53. package/src/components/post-actions/index.js +1 -1
  54. package/src/components/post-card-panel/style.scss +1 -0
  55. package/src/components/post-excerpt/index.js +4 -1
  56. package/src/components/post-excerpt/panel.js +2 -1
  57. package/src/components/post-featured-image/style.scss +15 -0
  58. package/src/components/post-publish-panel/style.scss +1 -1
  59. package/src/components/post-url/style.scss +4 -0
  60. package/src/components/save-publish-panels/index.js +33 -23
  61. package/src/components/visual-editor/style.scss +3 -1
  62. package/src/hooks/pattern-overrides.js +6 -4
  63. package/src/store/private-actions.js +54 -21
@@ -55,6 +55,7 @@ export default function EditorInterface( {
55
55
  disableIframe,
56
56
  autoFocus,
57
57
  customSaveButton,
58
+ customSavePanel,
58
59
  forceDisableBlockTools,
59
60
  title,
60
61
  iframeProps,
@@ -212,14 +213,22 @@ export default function EditorInterface( {
212
213
  )
213
214
  }
214
215
  actions={
215
- <SavePublishPanels
216
- closeEntitiesSavedStates={ closeEntitiesSavedStates }
217
- isEntitiesSavedStatesOpen={ entitiesSavedStatesCallback }
218
- setEntitiesSavedStatesCallback={
219
- setEntitiesSavedStatesCallback
220
- }
221
- forceIsDirtyPublishPanel={ forceIsDirty }
222
- />
216
+ ! isPreviewMode
217
+ ? customSavePanel || (
218
+ <SavePublishPanels
219
+ closeEntitiesSavedStates={
220
+ closeEntitiesSavedStates
221
+ }
222
+ isEntitiesSavedStatesOpen={
223
+ entitiesSavedStatesCallback
224
+ }
225
+ setEntitiesSavedStatesCallback={
226
+ setEntitiesSavedStatesCallback
227
+ }
228
+ forceIsDirtyPublishPanel={ forceIsDirty }
229
+ />
230
+ )
231
+ : undefined
223
232
  }
224
233
  shortcuts={ {
225
234
  previous: previousShortcut,
@@ -7,17 +7,15 @@ import { isPlainObject } from 'is-plain-object';
7
7
  /**
8
8
  * WordPress dependencies
9
9
  */
10
- import { registerBlockStyle, store as blocksStore } from '@wordpress/blocks';
11
10
  import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
12
11
  import { store as coreStore } from '@wordpress/core-data';
13
12
  import { useSelect, useDispatch } from '@wordpress/data';
14
- import { useEffect, useMemo, useCallback } from '@wordpress/element';
13
+ import { useMemo, useCallback } from '@wordpress/element';
15
14
 
16
15
  /**
17
16
  * Internal dependencies
18
17
  */
19
18
  import { unlock } from '../../lock-unlock';
20
- import setNestedValue from '../../utils/set-nested-value';
21
19
 
22
20
  const { GlobalStylesContext, cleanEmptyObject } = unlock(
23
21
  blockEditorPrivateApis
@@ -32,85 +30,6 @@ export function mergeBaseAndUserConfigs( base, user ) {
32
30
  } );
33
31
  }
34
32
 
35
- /**
36
- * Resolves shared block style variation definitions from the user origin
37
- * under their respective block types and registers the block style if required.
38
- *
39
- * @param {Object} userConfig Current user origin global styles data.
40
- * @return {Object} Updated global styles data.
41
- */
42
- function useResolvedBlockStyleVariationsConfig( userConfig ) {
43
- const { getBlockStyles } = useSelect( blocksStore );
44
- const sharedVariations = userConfig?.styles?.blocks?.variations;
45
-
46
- // Collect block style variation definitions to merge and unregistered
47
- // block styles for automatic registration.
48
- const [ userConfigToMerge, unregisteredStyles ] = useMemo( () => {
49
- if ( ! sharedVariations ) {
50
- return [];
51
- }
52
-
53
- const variationsConfigToMerge = {};
54
- const unregisteredBlockStyles = [];
55
-
56
- Object.entries( sharedVariations ).forEach(
57
- ( [ variationName, variation ] ) => {
58
- if ( ! variation?.blockTypes?.length ) {
59
- return;
60
- }
61
-
62
- variation.blockTypes.forEach( ( blockName ) => {
63
- const blockStyles = getBlockStyles( blockName );
64
- const registeredBlockStyle = blockStyles.find(
65
- ( { name } ) => name === variationName
66
- );
67
-
68
- if ( ! registeredBlockStyle ) {
69
- unregisteredBlockStyles.push( [
70
- blockName,
71
- {
72
- name: variationName,
73
- label: variationName,
74
- },
75
- ] );
76
- }
77
-
78
- const path = [
79
- 'styles',
80
- 'blocks',
81
- blockName,
82
- 'variations',
83
- variationName,
84
- ];
85
- setNestedValue( variationsConfigToMerge, path, variation );
86
- } );
87
- }
88
- );
89
-
90
- return [ variationsConfigToMerge, unregisteredBlockStyles ];
91
- }, [ sharedVariations, getBlockStyles ] );
92
-
93
- // Automatically register missing block styles from variations.
94
- useEffect(
95
- () =>
96
- unregisteredStyles?.forEach( ( unregisteredStyle ) =>
97
- registerBlockStyle( ...unregisteredStyle )
98
- ),
99
- [ unregisteredStyles ]
100
- );
101
-
102
- // Merge shared block style variation definitions into overall user config.
103
- const updatedConfig = useMemo( () => {
104
- if ( ! userConfigToMerge ) {
105
- return userConfig;
106
- }
107
-
108
- return deepmerge( userConfigToMerge, userConfig );
109
- }, [ userConfigToMerge, userConfig ] );
110
-
111
- return updatedConfig;
112
- }
113
-
114
33
  function useGlobalStylesUserConfig() {
115
34
  const { globalStylesId, isReady, settings, styles, _links } = useSelect(
116
35
  ( select ) => {
@@ -199,7 +118,7 @@ function useGlobalStylesUserConfig() {
199
118
  options
200
119
  );
201
120
  },
202
- [ globalStylesId ]
121
+ [ globalStylesId, editEntityRecord, getEditedEntityRecord ]
203
122
  );
204
123
 
205
124
  return [ isReady, config, setConfig ];
@@ -219,28 +138,26 @@ export function useGlobalStylesContext() {
219
138
  const [ isUserConfigReady, userConfig, setUserConfig ] =
220
139
  useGlobalStylesUserConfig();
221
140
  const [ isBaseConfigReady, baseConfig ] = useGlobalStylesBaseConfig();
222
- const userConfigWithVariations =
223
- useResolvedBlockStyleVariationsConfig( userConfig );
224
141
 
225
142
  const mergedConfig = useMemo( () => {
226
- if ( ! baseConfig || ! userConfigWithVariations ) {
143
+ if ( ! baseConfig || ! userConfig ) {
227
144
  return {};
228
145
  }
229
146
 
230
- return mergeBaseAndUserConfigs( baseConfig, userConfigWithVariations );
231
- }, [ userConfigWithVariations, baseConfig ] );
147
+ return mergeBaseAndUserConfigs( baseConfig, userConfig );
148
+ }, [ userConfig, baseConfig ] );
232
149
 
233
150
  const context = useMemo( () => {
234
151
  return {
235
152
  isReady: isUserConfigReady && isBaseConfigReady,
236
- user: userConfigWithVariations,
153
+ user: userConfig,
237
154
  base: baseConfig,
238
155
  merged: mergedConfig,
239
156
  setUserConfig,
240
157
  };
241
158
  }, [
242
159
  mergedConfig,
243
- userConfigWithVariations,
160
+ userConfig,
244
161
  baseConfig,
245
162
  setUserConfig,
246
163
  isUserConfigReady,
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { external, trash, backup } from '@wordpress/icons';
5
5
  import { addQueryArgs } from '@wordpress/url';
6
- import { useDispatch, useSelect } from '@wordpress/data';
6
+ import { useDispatch, useSelect, useRegistry } from '@wordpress/data';
7
7
  import { decodeEntities } from '@wordpress/html-entities';
8
8
  import { store as coreStore } from '@wordpress/core-data';
9
9
  import { __, _n, sprintf, _x } from '@wordpress/i18n';
@@ -155,6 +155,24 @@ const deletePostAction = {
155
155
  },
156
156
  };
157
157
 
158
+ function useCanUserEligibilityCheckPostType( capability, resource, action ) {
159
+ const registry = useRegistry();
160
+ return useMemo(
161
+ () => ( {
162
+ ...action,
163
+ isEligible( item ) {
164
+ return (
165
+ action.isEligible( item ) &&
166
+ registry
167
+ .select( coreStore )
168
+ .canUser( capability, resource, item.id )
169
+ );
170
+ },
171
+ } ),
172
+ [ action, registry, capability, resource ]
173
+ );
174
+ }
175
+
158
176
  const trashPostAction = {
159
177
  id: 'move-to-trash',
160
178
  label: __( 'Move to Trash' ),
@@ -239,7 +257,11 @@ const trashPostAction = {
239
257
  } else if ( items[ 0 ].type === 'page' ) {
240
258
  successMessage = sprintf(
241
259
  /* translators: The number of items. */
242
- __( '%s items moved to trash.' ),
260
+ _n(
261
+ '%s item moved to trash.',
262
+ '%s items moved to trash.',
263
+ items.length
264
+ ),
243
265
  items.length
244
266
  );
245
267
  } else {
@@ -323,12 +345,20 @@ const trashPostAction = {
323
345
  },
324
346
  };
325
347
 
326
- function usePermanentlyDeletePostAction() {
348
+ function useTrashPostAction( resource ) {
349
+ return useCanUserEligibilityCheckPostType(
350
+ 'delete',
351
+ resource,
352
+ trashPostAction
353
+ );
354
+ }
355
+
356
+ function usePermanentlyDeletePostAction( resource ) {
327
357
  const { createSuccessNotice, createErrorNotice } =
328
358
  useDispatch( noticesStore );
329
359
  const { deleteEntityRecord } = useDispatch( coreStore );
330
360
 
331
- return useMemo(
361
+ const permanentlyDeletePostAction = useMemo(
332
362
  () => ( {
333
363
  id: 'permanently-delete',
334
364
  label: __( 'Permanently delete' ),
@@ -428,15 +458,20 @@ function usePermanentlyDeletePostAction() {
428
458
  } ),
429
459
  [ createSuccessNotice, createErrorNotice, deleteEntityRecord ]
430
460
  );
461
+ return useCanUserEligibilityCheckPostType(
462
+ 'delete',
463
+ resource,
464
+ permanentlyDeletePostAction
465
+ );
431
466
  }
432
467
 
433
- function useRestorePostAction() {
468
+ function useRestorePostAction( resource ) {
434
469
  const { createSuccessNotice, createErrorNotice } =
435
470
  useDispatch( noticesStore );
436
471
  const { editEntityRecord, saveEditedEntityRecord } =
437
472
  useDispatch( coreStore );
438
473
 
439
- return useMemo(
474
+ const restorePostAction = useMemo(
440
475
  () => ( {
441
476
  id: 'restore',
442
477
  label: __( 'Restore' ),
@@ -562,6 +597,11 @@ function useRestorePostAction() {
562
597
  saveEditedEntityRecord,
563
598
  ]
564
599
  );
600
+ return useCanUserEligibilityCheckPostType(
601
+ 'update',
602
+ resource,
603
+ restorePostAction
604
+ );
565
605
  }
566
606
 
567
607
  const viewPostAction = {
@@ -583,6 +623,7 @@ const viewPostAction = {
583
623
 
584
624
  const postRevisionsAction = {
585
625
  id: 'view-post-revisions',
626
+ context: 'list',
586
627
  label( items ) {
587
628
  const revisionsCount =
588
629
  items[ 0 ]._links?.[ 'version-history' ]?.[ 0 ]?.count ?? 0;
@@ -722,6 +763,14 @@ const renamePostAction = {
722
763
  },
723
764
  };
724
765
 
766
+ function useRenamePostAction( resource ) {
767
+ return useCanUserEligibilityCheckPostType(
768
+ 'update',
769
+ resource,
770
+ renamePostAction
771
+ );
772
+ }
773
+
725
774
  const duplicatePostAction = {
726
775
  id: 'duplicate-post',
727
776
  label: _x( 'Duplicate', 'action label' ),
@@ -1029,19 +1078,33 @@ export const duplicateTemplatePartAction = {
1029
1078
  },
1030
1079
  };
1031
1080
 
1032
- export function usePostActions( postType, onActionPerformed ) {
1033
- const { postTypeObject } = useSelect(
1081
+ export function usePostActions( { postType, onActionPerformed, context } ) {
1082
+ const {
1083
+ postTypeObject,
1084
+ resource,
1085
+ cachedCanUserResolvers,
1086
+ userCanCreatePostType,
1087
+ } = useSelect(
1034
1088
  ( select ) => {
1035
- const { getPostType } = select( coreStore );
1089
+ const { getPostType, getCachedResolvers, canUser } =
1090
+ select( coreStore );
1091
+ const _postTypeObject = getPostType( postType );
1092
+ const _resource = _postTypeObject?.rest_base || '';
1036
1093
  return {
1037
- postTypeObject: getPostType( postType ),
1094
+ postTypeObject: _postTypeObject,
1095
+ resource: _resource,
1096
+ cachedCanUserResolvers: getCachedResolvers()?.canUser,
1097
+ userCanCreatePostType: canUser( 'create', _resource ),
1038
1098
  };
1039
1099
  },
1040
1100
  [ postType ]
1041
1101
  );
1042
1102
 
1043
- const permanentlyDeletePostAction = usePermanentlyDeletePostAction();
1044
- const restorePostAction = useRestorePostAction();
1103
+ const trashPostActionForPostType = useTrashPostAction( resource );
1104
+ const permanentlyDeletePostActionForPostType =
1105
+ usePermanentlyDeletePostAction( resource );
1106
+ const renamePostActionForPostType = useRenamePostAction( resource );
1107
+ const restorePostActionForPostType = useRestorePostAction( resource );
1045
1108
  const isTemplateOrTemplatePart = [
1046
1109
  TEMPLATE_POST_TYPE,
1047
1110
  TEMPLATE_PART_POST_TYPE,
@@ -1055,7 +1118,7 @@ export function usePostActions( postType, onActionPerformed ) {
1055
1118
  return [];
1056
1119
  }
1057
1120
 
1058
- const actions = [
1121
+ let actions = [
1059
1122
  postTypeObject?.viewable && viewPostAction,
1060
1123
  supportsRevisions && postRevisionsAction,
1061
1124
  globalThis.IS_GUTENBERG_PLUGIN
@@ -1063,16 +1126,33 @@ export function usePostActions( postType, onActionPerformed ) {
1063
1126
  ! isPattern &&
1064
1127
  duplicatePostAction
1065
1128
  : false,
1066
- isTemplateOrTemplatePart && duplicateTemplatePartAction,
1067
- isPattern && duplicatePatternAction,
1068
- supportsTitle && renamePostAction,
1129
+ isTemplateOrTemplatePart &&
1130
+ userCanCreatePostType &&
1131
+ duplicateTemplatePartAction,
1132
+ isPattern && userCanCreatePostType && duplicatePatternAction,
1133
+ supportsTitle && renamePostActionForPostType,
1069
1134
  isPattern && exportPatternAsJSONAction,
1070
- isTemplateOrTemplatePart ? resetTemplateAction : restorePostAction,
1135
+ isTemplateOrTemplatePart
1136
+ ? resetTemplateAction
1137
+ : restorePostActionForPostType,
1071
1138
  isTemplateOrTemplatePart || isPattern
1072
1139
  ? deletePostAction
1073
- : trashPostAction,
1074
- ! isTemplateOrTemplatePart && permanentlyDeletePostAction,
1140
+ : trashPostActionForPostType,
1141
+ ! isTemplateOrTemplatePart &&
1142
+ permanentlyDeletePostActionForPostType,
1075
1143
  ].filter( Boolean );
1144
+ // Filter actions based on provided context. If not provided
1145
+ // all actions are returned. We'll have a single entry for getting the actions
1146
+ // and the consumer should provide the context to filter the actions, if needed.
1147
+ // Actions should also provide the `context` they support, if it's specific, to
1148
+ // compare with the provided context to get all the actions.
1149
+ // Right now the only supported context is `list`.
1150
+ actions = actions.filter( ( action ) => {
1151
+ if ( ! action.context ) {
1152
+ return true;
1153
+ }
1154
+ return action.context === context;
1155
+ } );
1076
1156
 
1077
1157
  if ( onActionPerformed ) {
1078
1158
  for ( let i = 0; i < actions.length; ++i ) {
@@ -1116,15 +1196,23 @@ export function usePostActions( postType, onActionPerformed ) {
1116
1196
  }
1117
1197
 
1118
1198
  return actions;
1199
+ // We are making this use memo depend on cachedCanUserResolvers as a way to make the component using this hook re-render
1200
+ // when user capabilities are resolved. This makes sure the isEligible functions of actions dependent on capabilities are re-evaluated.
1201
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1119
1202
  }, [
1120
1203
  isTemplateOrTemplatePart,
1121
1204
  isPattern,
1122
1205
  postTypeObject?.viewable,
1123
- permanentlyDeletePostAction,
1124
- restorePostAction,
1206
+ permanentlyDeletePostActionForPostType,
1207
+ restorePostActionForPostType,
1208
+ renamePostActionForPostType,
1209
+ trashPostActionForPostType,
1125
1210
  onActionPerformed,
1126
1211
  isLoaded,
1127
1212
  supportsRevisions,
1128
1213
  supportsTitle,
1214
+ context,
1215
+ userCanCreatePostType,
1216
+ cachedCanUserResolvers,
1129
1217
  ] );
1130
1218
  }
@@ -42,7 +42,7 @@ export default function PostActions( { onActionPerformed, buttonProps } ) {
42
42
  postType: _postType,
43
43
  };
44
44
  }, [] );
45
- const allActions = usePostActions( postType, onActionPerformed );
45
+ const allActions = usePostActions( { postType, onActionPerformed } );
46
46
 
47
47
  const actions = useMemo( () => {
48
48
  return allActions.filter( ( action ) => {
@@ -14,6 +14,7 @@
14
14
  flex-wrap: wrap;
15
15
  column-gap: $grid-unit-10;
16
16
  row-gap: $grid-unit-05;
17
+ word-break: break-word;
17
18
  }
18
19
  }
19
20
 
@@ -5,6 +5,7 @@ import { __ } from '@wordpress/i18n';
5
5
  import { ExternalLink, TextareaControl } from '@wordpress/components';
6
6
  import { useDispatch, useSelect } from '@wordpress/data';
7
7
  import { useState } from '@wordpress/element';
8
+ import { decodeEntities } from '@wordpress/html-entities';
8
9
 
9
10
  /**
10
11
  * Internal dependencies
@@ -52,7 +53,9 @@ export default function PostExcerpt( {
52
53
  []
53
54
  );
54
55
  const { editPost } = useDispatch( editorStore );
55
- const [ localExcerpt, setLocalExcerpt ] = useState( excerpt );
56
+ const [ localExcerpt, setLocalExcerpt ] = useState(
57
+ decodeEntities( excerpt )
58
+ );
56
59
  const updatePost = ( value ) => {
57
60
  editPost( { [ usedAttribute ]: value } );
58
61
  };
@@ -13,6 +13,7 @@ import { useDispatch, useSelect } from '@wordpress/data';
13
13
  import { useMemo, useState } from '@wordpress/element';
14
14
  import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor';
15
15
  import { store as coreStore } from '@wordpress/core-data';
16
+ import { decodeEntities } from '@wordpress/html-entities';
16
17
 
17
18
  /**
18
19
  * Internal dependencies
@@ -173,7 +174,7 @@ function PrivateExcerpt() {
173
174
  }
174
175
  const excerptText = !! excerpt && (
175
176
  <Text align="left" numberOfLines={ 4 } truncate>
176
- { excerpt }
177
+ { decodeEntities( excerpt ) }
177
178
  </Text>
178
179
  );
179
180
  if ( ! allowEditing ) {
@@ -20,6 +20,21 @@
20
20
  opacity: 1;
21
21
  }
22
22
  }
23
+
24
+ .components-drop-zone__content {
25
+ border-radius: $radius-block-ui;
26
+ }
27
+
28
+ // Align text and icons horizontally to avoid clipping when the featured image is not set.
29
+ &:has(.editor-post-featured-image__toggle) .components-drop-zone .components-drop-zone__content-inner {
30
+ display: flex;
31
+ align-items: center;
32
+ gap: $grid-unit-10;
33
+
34
+ .components-drop-zone__content-icon {
35
+ margin: 0;
36
+ }
37
+ }
23
38
  }
24
39
 
25
40
  .editor-post-featured-image__toggle,
@@ -196,7 +196,7 @@
196
196
  position: fixed;
197
197
  z-index: z-index(".editor-post-publish-panel");
198
198
  background: $white;
199
- top: 0;
199
+ top: $admin-bar-height-big;
200
200
  bottom: 0;
201
201
  right: 0;
202
202
  left: 0;
@@ -26,3 +26,7 @@
26
26
  .editor-post-url__input input.components-input-control__input {
27
27
  padding-inline-start: 0 !important;
28
28
  }
29
+
30
+ .editor-post-url__panel-toggle {
31
+ word-break: break-word;
32
+ }
@@ -30,20 +30,28 @@ export default function SavePublishPanels( {
30
30
  useDispatch( editorStore );
31
31
  const {
32
32
  publishSidebarOpened,
33
- hasNonPostEntityChanges,
34
- hasPostMetaChanges,
35
- } = useSelect(
36
- ( select ) => ( {
37
- publishSidebarOpened:
38
- select( editorStore ).isPublishSidebarOpened(),
39
- hasNonPostEntityChanges:
40
- select( editorStore ).hasNonPostEntityChanges(),
41
- hasPostMetaChanges: unlock(
42
- select( editorStore )
43
- ).hasPostMetaChanges(),
44
- } ),
45
- []
46
- );
33
+ isPublishable,
34
+ isDirty,
35
+ hasOtherEntitiesChanges,
36
+ } = useSelect( ( select ) => {
37
+ const {
38
+ isPublishSidebarOpened,
39
+ isEditedPostPublishable,
40
+ isCurrentPostPublished,
41
+ isEditedPostDirty,
42
+ hasNonPostEntityChanges,
43
+ } = select( editorStore );
44
+ const _hasOtherEntitiesChanges =
45
+ hasNonPostEntityChanges() ||
46
+ unlock( select( editorStore ) ).hasPostMetaChanges();
47
+ return {
48
+ publishSidebarOpened: isPublishSidebarOpened(),
49
+ isPublishable:
50
+ ! isCurrentPostPublished() && isEditedPostPublishable(),
51
+ isDirty: _hasOtherEntitiesChanges || isEditedPostDirty(),
52
+ hasOtherEntitiesChanges: _hasOtherEntitiesChanges,
53
+ };
54
+ }, [] );
47
55
 
48
56
  const openEntitiesSavedStates = useCallback(
49
57
  () => setEntitiesSavedStatesCallback( true ),
@@ -62,29 +70,31 @@ export default function SavePublishPanels( {
62
70
  PostPublishExtension={ PluginPostPublishPanel.Slot }
63
71
  />
64
72
  );
65
- } else if ( hasNonPostEntityChanges || hasPostMetaChanges ) {
73
+ } else if ( isPublishable && ! hasOtherEntitiesChanges ) {
66
74
  unmountableContent = (
67
- <div className="editor-layout__toggle-entities-saved-states-panel">
75
+ <div className="editor-layout__toggle-publish-panel">
68
76
  <Button
69
77
  variant="secondary"
70
- className="editor-layout__toggle-entities-saved-states-panel-button"
71
- onClick={ openEntitiesSavedStates }
78
+ className="editor-layout__toggle-publish-panel-button"
79
+ onClick={ togglePublishSidebar }
72
80
  aria-expanded={ false }
73
81
  >
74
- { __( 'Open save panel' ) }
82
+ { __( 'Open publish panel' ) }
75
83
  </Button>
76
84
  </div>
77
85
  );
78
86
  } else {
79
87
  unmountableContent = (
80
- <div className="editor-layout__toggle-publish-panel">
88
+ <div className="editor-layout__toggle-entities-saved-states-panel">
81
89
  <Button
82
90
  variant="secondary"
83
- className="editor-layout__toggle-publish-panel-button"
84
- onClick={ togglePublishSidebar }
91
+ className="editor-layout__toggle-entities-saved-states-panel-button"
92
+ onClick={ openEntitiesSavedStates }
85
93
  aria-expanded={ false }
94
+ disabled={ ! isDirty }
95
+ __experimentalIsFocusable
86
96
  >
87
- { __( 'Open publish panel' ) }
97
+ { __( 'Open save panel' ) }
88
98
  </Button>
89
99
  </div>
90
100
  );
@@ -3,6 +3,8 @@
3
3
  height: 100%;
4
4
  display: block;
5
5
  background-color: $gray-300;
6
+ // Make this a stacking context to contain the z-index of children elements.
7
+ isolation: isolate;
6
8
 
7
9
  // Centralize the editor horizontally (flex-direction is column).
8
10
  align-items: center;
@@ -17,7 +19,7 @@
17
19
 
18
20
  // In the iframed canvas this keeps extra scrollbars from appearing (when block toolbars overflow). In the
19
21
  // legacy (non-iframed) canvas, overflow must not be hidden in order to maintain support for sticky positioning.
20
- .is-iframed {
22
+ &.is-iframed {
21
23
  overflow: hidden;
22
24
  }
23
25
 
@@ -14,6 +14,8 @@ import { store as blocksStore } from '@wordpress/blocks';
14
14
  import { store as editorStore } from '../store';
15
15
  import { unlock } from '../lock-unlock';
16
16
 
17
+ /** @typedef {import('@wordpress/blocks').WPBlockSettings} WPBlockSettings */
18
+
17
19
  const {
18
20
  PatternOverridesControls,
19
21
  ResetOverridesControl,
@@ -34,9 +36,8 @@ const {
34
36
  */
35
37
  const withPatternOverrideControls = createHigherOrderComponent(
36
38
  ( BlockEdit ) => ( props ) => {
37
- const isSupportedBlock = Object.keys(
38
- PARTIAL_SYNCING_SUPPORTED_BLOCKS
39
- ).includes( props.name );
39
+ const isSupportedBlock =
40
+ !! PARTIAL_SYNCING_SUPPORTED_BLOCKS[ props.name ];
40
41
 
41
42
  return (
42
43
  <>
@@ -47,7 +48,8 @@ const withPatternOverrideControls = createHigherOrderComponent(
47
48
  { isSupportedBlock && <PatternOverridesBlockControls /> }
48
49
  </>
49
50
  );
50
- }
51
+ },
52
+ 'withPatternOverrideControls'
51
53
  );
52
54
 
53
55
  // Split into a separate component to avoid a store subscription