@wordpress/editor 14.18.0 → 14.19.1

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 (160) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +2 -0
  3. package/build/components/document-bar/index.js +3 -2
  4. package/build/components/document-bar/index.js.map +1 -1
  5. package/build/components/document-outline/index.js +37 -17
  6. package/build/components/document-outline/index.js.map +1 -1
  7. package/build/components/document-outline/item.js +32 -20
  8. package/build/components/document-outline/item.js.map +1 -1
  9. package/build/components/entities-saved-states/entity-record-item.js +4 -3
  10. package/build/components/entities-saved-states/entity-record-item.js.map +1 -1
  11. package/build/components/entities-saved-states/entity-type-list.js +2 -1
  12. package/build/components/entities-saved-states/entity-type-list.js.map +1 -1
  13. package/build/components/entities-saved-states/index.js +66 -42
  14. package/build/components/entities-saved-states/index.js.map +1 -1
  15. package/build/components/post-actions/index.js +19 -28
  16. package/build/components/post-actions/index.js.map +1 -1
  17. package/build/components/post-card-panel/index.js +5 -5
  18. package/build/components/post-card-panel/index.js.map +1 -1
  19. package/build/components/post-featured-image/index.js +4 -2
  20. package/build/components/post-featured-image/index.js.map +1 -1
  21. package/build/components/post-template/block-theme.js +68 -48
  22. package/build/components/post-template/block-theme.js.map +1 -1
  23. package/build/components/post-template/classic-theme.js +30 -18
  24. package/build/components/post-template/classic-theme.js.map +1 -1
  25. package/build/components/post-template/create-new-template-modal.js +3 -1
  26. package/build/components/post-template/create-new-template-modal.js.map +1 -1
  27. package/build/components/post-template/panel.js +3 -11
  28. package/build/components/post-template/panel.js.map +1 -1
  29. package/build/components/preferences-modal/block-visibility.js +2 -1
  30. package/build/components/preferences-modal/block-visibility.js.map +1 -1
  31. package/build/components/preferences-modal/index.js +1 -1
  32. package/build/components/preferences-modal/index.js.map +1 -1
  33. package/build/components/preview-dropdown/index.js +6 -3
  34. package/build/components/preview-dropdown/index.js.map +1 -1
  35. package/build/components/provider/index.js +19 -18
  36. package/build/components/provider/index.js.map +1 -1
  37. package/build/components/sidebar/header.js +1 -1
  38. package/build/components/sidebar/header.js.map +1 -1
  39. package/build/components/sidebar/index.js +1 -1
  40. package/build/components/sidebar/index.js.map +1 -1
  41. package/build/components/start-page-options/index.js +148 -23
  42. package/build/components/start-page-options/index.js.map +1 -1
  43. package/build/components/visual-editor/edit-template-blocks-notification.js +1 -1
  44. package/build/components/visual-editor/edit-template-blocks-notification.js.map +1 -1
  45. package/build/store/private-actions.js +29 -2
  46. package/build/store/private-actions.js.map +1 -1
  47. package/build/store/private-selectors.js +45 -1
  48. package/build/store/private-selectors.js.map +1 -1
  49. package/build/store/selectors.js +8 -7
  50. package/build/store/selectors.js.map +1 -1
  51. package/build/utils/media-upload/index.js +5 -2
  52. package/build/utils/media-upload/index.js.map +1 -1
  53. package/build-module/components/document-bar/index.js +3 -2
  54. package/build-module/components/document-bar/index.js.map +1 -1
  55. package/build-module/components/document-outline/index.js +38 -18
  56. package/build-module/components/document-outline/index.js.map +1 -1
  57. package/build-module/components/document-outline/item.js +32 -20
  58. package/build-module/components/document-outline/item.js.map +1 -1
  59. package/build-module/components/entities-saved-states/entity-record-item.js +4 -3
  60. package/build-module/components/entities-saved-states/entity-record-item.js.map +1 -1
  61. package/build-module/components/entities-saved-states/entity-type-list.js +2 -1
  62. package/build-module/components/entities-saved-states/entity-type-list.js.map +1 -1
  63. package/build-module/components/entities-saved-states/index.js +67 -43
  64. package/build-module/components/entities-saved-states/index.js.map +1 -1
  65. package/build-module/components/post-actions/index.js +19 -28
  66. package/build-module/components/post-actions/index.js.map +1 -1
  67. package/build-module/components/post-card-panel/index.js +5 -5
  68. package/build-module/components/post-card-panel/index.js.map +1 -1
  69. package/build-module/components/post-featured-image/index.js +4 -2
  70. package/build-module/components/post-featured-image/index.js.map +1 -1
  71. package/build-module/components/post-template/block-theme.js +68 -48
  72. package/build-module/components/post-template/block-theme.js.map +1 -1
  73. package/build-module/components/post-template/classic-theme.js +30 -18
  74. package/build-module/components/post-template/classic-theme.js.map +1 -1
  75. package/build-module/components/post-template/create-new-template-modal.js +3 -1
  76. package/build-module/components/post-template/create-new-template-modal.js.map +1 -1
  77. package/build-module/components/post-template/panel.js +3 -11
  78. package/build-module/components/post-template/panel.js.map +1 -1
  79. package/build-module/components/preferences-modal/block-visibility.js +2 -1
  80. package/build-module/components/preferences-modal/block-visibility.js.map +1 -1
  81. package/build-module/components/preferences-modal/index.js +1 -1
  82. package/build-module/components/preferences-modal/index.js.map +1 -1
  83. package/build-module/components/preview-dropdown/index.js +6 -3
  84. package/build-module/components/preview-dropdown/index.js.map +1 -1
  85. package/build-module/components/provider/index.js +19 -18
  86. package/build-module/components/provider/index.js.map +1 -1
  87. package/build-module/components/sidebar/header.js +1 -1
  88. package/build-module/components/sidebar/header.js.map +1 -1
  89. package/build-module/components/sidebar/index.js +1 -1
  90. package/build-module/components/sidebar/index.js.map +1 -1
  91. package/build-module/components/start-page-options/index.js +148 -24
  92. package/build-module/components/start-page-options/index.js.map +1 -1
  93. package/build-module/components/visual-editor/edit-template-blocks-notification.js +1 -1
  94. package/build-module/components/visual-editor/edit-template-blocks-notification.js.map +1 -1
  95. package/build-module/store/private-actions.js +25 -0
  96. package/build-module/store/private-actions.js.map +1 -1
  97. package/build-module/store/private-selectors.js +44 -1
  98. package/build-module/store/private-selectors.js.map +1 -1
  99. package/build-module/store/selectors.js +8 -7
  100. package/build-module/store/selectors.js.map +1 -1
  101. package/build-module/utils/media-upload/index.js +5 -2
  102. package/build-module/utils/media-upload/index.js.map +1 -1
  103. package/build-style/style-rtl.css +51 -9
  104. package/build-style/style.css +51 -9
  105. package/build-types/components/document-outline/index.d.ts.map +1 -1
  106. package/build-types/components/document-outline/item.d.ts +2 -1
  107. package/build-types/components/document-outline/item.d.ts.map +1 -1
  108. package/build-types/components/entities-saved-states/entity-record-item.d.ts.map +1 -1
  109. package/build-types/components/entities-saved-states/entity-type-list.d.ts.map +1 -1
  110. package/build-types/components/entities-saved-states/index.d.ts +6 -2
  111. package/build-types/components/entities-saved-states/index.d.ts.map +1 -1
  112. package/build-types/components/post-actions/index.d.ts.map +1 -1
  113. package/build-types/components/post-card-panel/index.d.ts.map +1 -1
  114. package/build-types/components/post-template/block-theme.d.ts.map +1 -1
  115. package/build-types/components/post-template/classic-theme.d.ts.map +1 -1
  116. package/build-types/components/post-template/create-new-template-modal.d.ts.map +1 -1
  117. package/build-types/components/post-template/panel.d.ts.map +1 -1
  118. package/build-types/components/preferences-modal/block-visibility.d.ts.map +1 -1
  119. package/build-types/components/preview-dropdown/index.d.ts.map +1 -1
  120. package/build-types/components/provider/index.d.ts.map +1 -1
  121. package/build-types/components/start-page-options/index.d.ts +2 -1
  122. package/build-types/components/start-page-options/index.d.ts.map +1 -1
  123. package/build-types/store/private-actions.d.ts +4 -0
  124. package/build-types/store/private-actions.d.ts.map +1 -1
  125. package/build-types/store/private-selectors.d.ts +13 -0
  126. package/build-types/store/private-selectors.d.ts.map +1 -1
  127. package/build-types/store/selectors.d.ts.map +1 -1
  128. package/build-types/utils/media-upload/index.d.ts +3 -1
  129. package/build-types/utils/media-upload/index.d.ts.map +1 -1
  130. package/package.json +37 -37
  131. package/src/components/document-bar/index.js +2 -2
  132. package/src/components/document-outline/index.js +49 -17
  133. package/src/components/document-outline/item.js +38 -23
  134. package/src/components/document-outline/style.scss +2 -0
  135. package/src/components/entities-saved-states/entity-record-item.js +2 -4
  136. package/src/components/entities-saved-states/entity-type-list.js +6 -2
  137. package/src/components/entities-saved-states/index.js +98 -58
  138. package/src/components/entities-saved-states/style.scss +40 -5
  139. package/src/components/post-actions/index.js +19 -37
  140. package/src/components/post-card-panel/index.js +9 -7
  141. package/src/components/post-featured-image/index.js +2 -0
  142. package/src/components/post-template/block-theme.js +76 -56
  143. package/src/components/post-template/classic-theme.js +31 -15
  144. package/src/components/post-template/create-new-template-modal.js +1 -0
  145. package/src/components/post-template/panel.js +2 -12
  146. package/src/components/post-text-editor/style.scss +0 -2
  147. package/src/components/preferences-modal/block-visibility.js +2 -1
  148. package/src/components/preferences-modal/index.js +1 -1
  149. package/src/components/preview-dropdown/index.js +8 -6
  150. package/src/components/provider/index.js +21 -30
  151. package/src/components/sidebar/header.js +1 -1
  152. package/src/components/sidebar/index.js +1 -1
  153. package/src/components/start-page-options/index.js +149 -23
  154. package/src/components/start-page-options/style.scss +27 -0
  155. package/src/components/visual-editor/edit-template-blocks-notification.js +1 -1
  156. package/src/store/private-actions.js +33 -0
  157. package/src/store/private-selectors.js +59 -4
  158. package/src/store/selectors.js +9 -14
  159. package/src/utils/media-upload/index.js +3 -0
  160. package/tsconfig.tsbuildinfo +1 -1
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import { useDispatch, useSelect } from '@wordpress/data';
6
- import { useRef } from '@wordpress/element';
6
+ import { useRef, useMemo } from '@wordpress/element';
7
7
  import { create, getTextContent } from '@wordpress/rich-text';
8
8
  import { store as blockEditorStore } from '@wordpress/block-editor';
9
9
  import { store as coreStore } from '@wordpress/core-data';
@@ -83,16 +83,13 @@ function EmptyOutlineIllustration() {
83
83
  * @return {Array} An array of heading blocks enhanced with the properties described above.
84
84
  */
85
85
  const computeOutlineHeadings = ( blocks = [] ) => {
86
- return blocks.flatMap( ( block = {} ) => {
87
- if ( block.name === 'core/heading' ) {
88
- return {
89
- ...block,
90
- level: block.attributes.level,
91
- isEmpty: isEmptyHeading( block ),
92
- };
93
- }
94
- return computeOutlineHeadings( block.innerBlocks );
95
- } );
86
+ return blocks
87
+ .filter( ( block ) => block.name === 'core/heading' )
88
+ .map( ( block ) => ( {
89
+ ...block,
90
+ level: block.attributes.level,
91
+ isEmpty: isEmptyHeading( block ),
92
+ } ) );
96
93
  };
97
94
 
98
95
  const isEmptyHeading = ( heading ) =>
@@ -113,22 +110,48 @@ export default function DocumentOutline( {
113
110
  hasOutlineItemsDisabled,
114
111
  } ) {
115
112
  const { selectBlock } = useDispatch( blockEditorStore );
116
- const { blocks, title, isTitleSupported } = useSelect( ( select ) => {
117
- const { getBlocks } = select( blockEditorStore );
113
+ const { title, isTitleSupported } = useSelect( ( select ) => {
118
114
  const { getEditedPostAttribute } = select( editorStore );
119
115
  const { getPostType } = select( coreStore );
120
116
  const postType = getPostType( getEditedPostAttribute( 'type' ) );
121
-
122
117
  return {
123
118
  title: getEditedPostAttribute( 'title' ),
124
- blocks: getBlocks(),
125
119
  isTitleSupported: postType?.supports?.title ?? false,
126
120
  };
127
121
  } );
122
+ const blocks = useSelect( ( select ) => {
123
+ const { getClientIdsWithDescendants, getBlock } =
124
+ select( blockEditorStore );
125
+ const clientIds = getClientIdsWithDescendants();
126
+ // Note: Don't modify data inside the `Array.map` callback,
127
+ // all compulations should happen in `computeOutlineHeadings`.
128
+ return clientIds.map( ( id ) => getBlock( id ) );
129
+ } );
130
+ const contentBlocks = useSelect( ( select ) => {
131
+ // When rendering in `post-only` mode all blocks are considered content blocks.
132
+ if ( select( editorStore ).getRenderingMode() === 'post-only' ) {
133
+ return undefined;
134
+ }
135
+
136
+ const { getBlocksByName, getClientIdsOfDescendants } =
137
+ select( blockEditorStore );
138
+ const [ postContentClientId ] = getBlocksByName( 'core/post-content' );
139
+
140
+ // Do nothing if there's no post content block.
141
+ if ( ! postContentClientId ) {
142
+ return undefined;
143
+ }
144
+
145
+ return getClientIdsOfDescendants( postContentClientId );
146
+ }, [] );
128
147
 
129
148
  const prevHeadingLevelRef = useRef( 1 );
130
149
 
131
- const headings = computeOutlineHeadings( blocks );
150
+ const headings = useMemo(
151
+ () => computeOutlineHeadings( blocks ),
152
+ [ blocks ]
153
+ );
154
+
132
155
  if ( headings.length < 1 ) {
133
156
  return (
134
157
  <div className="editor-document-outline has-no-headings">
@@ -154,6 +177,12 @@ export default function DocumentOutline( {
154
177
  );
155
178
  const hasMultipleH1 = countByLevel[ 1 ] > 1;
156
179
 
180
+ function isContentBlock( clientId ) {
181
+ return Array.isArray( contentBlocks )
182
+ ? contentBlocks.includes( clientId )
183
+ : true;
184
+ }
185
+
157
186
  return (
158
187
  <div className="document-outline">
159
188
  <ul>
@@ -187,7 +216,10 @@ export default function DocumentOutline( {
187
216
  key={ item.clientId }
188
217
  level={ `H${ item.level }` }
189
218
  isValid={ isValid }
190
- isDisabled={ hasOutlineItemsDisabled }
219
+ isDisabled={
220
+ hasOutlineItemsDisabled ||
221
+ ! isContentBlock( item.clientId )
222
+ }
191
223
  href={ `#block-${ item.clientId }` }
192
224
  onSelect={ () => {
193
225
  selectBlock( item.clientId );
@@ -6,32 +6,47 @@ import clsx from 'clsx';
6
6
  const TableOfContentsItem = ( {
7
7
  children,
8
8
  isValid,
9
+ isDisabled,
9
10
  level,
10
11
  href,
11
12
  onSelect,
12
- } ) => (
13
- <li
14
- className={ clsx(
15
- 'document-outline__item',
16
- `is-${ level.toLowerCase() }`,
17
- {
18
- 'is-invalid': ! isValid,
19
- }
20
- ) }
21
- >
22
- <a
23
- href={ href }
24
- className="document-outline__button"
25
- onClick={ onSelect }
13
+ } ) => {
14
+ function handleClick( event ) {
15
+ if ( isDisabled ) {
16
+ event.preventDefault();
17
+ return;
18
+ }
19
+ onSelect();
20
+ }
21
+
22
+ return (
23
+ <li
24
+ className={ clsx(
25
+ 'document-outline__item',
26
+ `is-${ level.toLowerCase() }`,
27
+ {
28
+ 'is-invalid': ! isValid,
29
+ 'is-disabled': isDisabled,
30
+ }
31
+ ) }
26
32
  >
27
- <span
28
- className="document-outline__emdash"
29
- aria-hidden="true"
30
- ></span>
31
- <strong className="document-outline__level">{ level }</strong>
32
- <span className="document-outline__item-content">{ children }</span>
33
- </a>
34
- </li>
35
- );
33
+ <a
34
+ href={ href }
35
+ className="document-outline__button"
36
+ aria-disabled={ isDisabled }
37
+ onClick={ handleClick }
38
+ >
39
+ <span
40
+ className="document-outline__emdash"
41
+ aria-hidden="true"
42
+ ></span>
43
+ <strong className="document-outline__level">{ level }</strong>
44
+ <span className="document-outline__item-content">
45
+ { children }
46
+ </span>
47
+ </a>
48
+ </li>
49
+ );
50
+ };
36
51
 
37
52
  export default TableOfContentsItem;
@@ -53,8 +53,10 @@
53
53
  text-align: left;
54
54
  border-radius: $radius-small;
55
55
 
56
+ &[aria-disabled="true"],
56
57
  &:disabled {
57
58
  cursor: default;
59
+ color: $gray-700;
58
60
  }
59
61
 
60
62
  &:focus {
@@ -36,10 +36,7 @@ export default function EntityRecordItem( { record, checked, onChange } ) {
36
36
  );
37
37
 
38
38
  const { default_template_types: templateTypes = [] } =
39
- select( coreStore ).getEntityRecord(
40
- 'root',
41
- '__unstableBase'
42
- ) ?? {};
39
+ select( coreStore ).getCurrentTheme() ?? {};
43
40
 
44
41
  return {
45
42
  entityRecordTitle: getTemplateInfo( {
@@ -64,6 +61,7 @@ export default function EntityRecordItem( { record, checked, onChange } ) {
64
61
  }
65
62
  checked={ checked }
66
63
  onChange={ onChange }
64
+ className="entities-saved-states__change-control"
67
65
  />
68
66
  </PanelRow>
69
67
  { hasPostMetaChanges && (
@@ -26,7 +26,7 @@ function getEntityDescription( entity, count ) {
26
26
  : __( 'These changes will affect your whole site.' );
27
27
  case 'wp_template':
28
28
  return __(
29
- 'This change will affect pages and posts that use this template.'
29
+ 'This change will affect other parts of your site that use this template.'
30
30
  );
31
31
  case 'page':
32
32
  case 'post':
@@ -94,7 +94,11 @@ export default function EntityTypeList( {
94
94
  }
95
95
 
96
96
  return (
97
- <PanelBody title={ entityLabel } initialOpen>
97
+ <PanelBody
98
+ title={ entityLabel }
99
+ initialOpen
100
+ className="entities-saved-states__panel-body"
101
+ >
98
102
  <EntityDescription record={ firstRecord } count={ count } />
99
103
  { list.map( ( record ) => {
100
104
  return (
@@ -1,3 +1,8 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
@@ -32,15 +37,21 @@ function identity( values ) {
32
37
  * @param {Object} props The component props.
33
38
  * @param {Function} props.close The function to close the dialog.
34
39
  * @param {boolean} props.renderDialog Whether to render the component with modal dialog behavior.
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.
35
41
  *
36
42
  * @return {React.ReactNode} The rendered component.
37
43
  */
38
- export default function EntitiesSavedStates( { close, renderDialog } ) {
44
+ export default function EntitiesSavedStates( {
45
+ close,
46
+ renderDialog,
47
+ variant,
48
+ } ) {
39
49
  const isDirtyProps = useIsDirty();
40
50
  return (
41
51
  <EntitiesSavedStatesExtensible
42
52
  close={ close }
43
53
  renderDialog={ renderDialog }
54
+ variant={ variant }
44
55
  { ...isDirtyProps }
45
56
  />
46
57
  );
@@ -60,6 +71,7 @@ export default function EntitiesSavedStates( { close, renderDialog } ) {
60
71
  * @param {boolean} props.isDirty Flag indicating if there are dirty entities.
61
72
  * @param {Function} props.setUnselectedEntities Function to set unselected entities.
62
73
  * @param {Array} props.unselectedEntities Array of unselected entities.
74
+ * @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.
63
75
  *
64
76
  * @return {React.ReactNode} The rendered component.
65
77
  */
@@ -74,6 +86,7 @@ export function EntitiesSavedStatesExtensible( {
74
86
  isDirty,
75
87
  setUnselectedEntities,
76
88
  unselectedEntities,
89
+ variant = 'default',
77
90
  } ) {
78
91
  const saveButtonRef = useRef();
79
92
  const { saveDirtyEntities } = unlock( useDispatch( editorStore ) );
@@ -109,83 +122,100 @@ export function EntitiesSavedStatesExtensible( {
109
122
  const [ saveDialogRef, saveDialogProps ] = useDialog( {
110
123
  onClose: () => dismissPanel(),
111
124
  } );
112
- const dialogLabel = useInstanceId( EntitiesSavedStatesExtensible, 'label' );
113
- const dialogDescription = useInstanceId(
125
+ const dialogLabelId = useInstanceId(
126
+ EntitiesSavedStatesExtensible,
127
+ 'entities-saved-states__panel-label'
128
+ );
129
+ const dialogDescriptionId = useInstanceId(
114
130
  EntitiesSavedStatesExtensible,
115
- 'description'
131
+ 'entities-saved-states__panel-description'
116
132
  );
117
133
 
118
134
  const selectItemsToSaveDescription = !! dirtyEntityRecords.length
119
135
  ? __( 'Select the items you want to save.' )
120
136
  : undefined;
121
137
 
138
+ const isInline = variant === 'inline';
139
+
140
+ const actionButtons = (
141
+ <>
142
+ <FlexItem
143
+ isBlock={ isInline ? false : true }
144
+ as={ Button }
145
+ variant={ isInline ? 'tertiary' : 'secondary' }
146
+ size={ isInline ? undefined : 'compact' }
147
+ onClick={ dismissPanel }
148
+ >
149
+ { __( 'Cancel' ) }
150
+ </FlexItem>
151
+ <FlexItem
152
+ isBlock={ isInline ? false : true }
153
+ as={ Button }
154
+ ref={ saveButtonRef }
155
+ variant="primary"
156
+ size={ isInline ? undefined : 'compact' }
157
+ disabled={ ! saveEnabled }
158
+ accessibleWhenDisabled
159
+ onClick={ () =>
160
+ saveDirtyEntities( {
161
+ onSave,
162
+ dirtyEntityRecords,
163
+ entitiesToSkip: unselectedEntities,
164
+ close,
165
+ } )
166
+ }
167
+ className="editor-entities-saved-states__save-button"
168
+ >
169
+ { saveLabel }
170
+ </FlexItem>
171
+ </>
172
+ );
173
+
122
174
  return (
123
175
  <div
124
176
  ref={ renderDialog ? saveDialogRef : undefined }
125
177
  { ...( renderDialog && saveDialogProps ) }
126
- className="entities-saved-states__panel"
178
+ className={ clsx( 'entities-saved-states__panel', {
179
+ 'is-inline': isInline,
180
+ } ) }
127
181
  role={ renderDialog ? 'dialog' : undefined }
128
- aria-labelledby={ renderDialog ? dialogLabel : undefined }
129
- aria-describedby={ renderDialog ? dialogDescription : undefined }
182
+ aria-labelledby={ renderDialog ? dialogLabelId : undefined }
183
+ aria-describedby={ renderDialog ? dialogDescriptionId : undefined }
130
184
  >
131
- <Flex className="entities-saved-states__panel-header" gap={ 2 }>
132
- <FlexItem
133
- isBlock
134
- as={ Button }
135
- variant="secondary"
136
- size="compact"
137
- onClick={ dismissPanel }
138
- >
139
- { __( 'Cancel' ) }
140
- </FlexItem>
141
- <FlexItem
142
- isBlock
143
- as={ Button }
144
- ref={ saveButtonRef }
145
- variant="primary"
146
- size="compact"
147
- disabled={ ! saveEnabled }
148
- accessibleWhenDisabled
149
- onClick={ () =>
150
- saveDirtyEntities( {
151
- onSave,
152
- dirtyEntityRecords,
153
- entitiesToSkip: unselectedEntities,
154
- close,
155
- } )
156
- }
157
- className="editor-entities-saved-states__save-button"
158
- >
159
- { saveLabel }
160
- </FlexItem>
161
- </Flex>
185
+ { ! isInline && (
186
+ <Flex className="entities-saved-states__panel-header" gap={ 2 }>
187
+ { actionButtons }
188
+ </Flex>
189
+ ) }
162
190
 
163
191
  <div className="entities-saved-states__text-prompt">
164
- <div
165
- className="entities-saved-states__text-prompt--header-wrapper"
166
- id={ renderDialog ? dialogLabel : undefined }
167
- >
168
- <strong className="entities-saved-states__text-prompt--header">
192
+ <div className="entities-saved-states__text-prompt--header-wrapper">
193
+ <strong
194
+ id={ renderDialog ? dialogLabelId : undefined }
195
+ className="entities-saved-states__text-prompt--header"
196
+ >
169
197
  { __( 'Are you ready to save?' ) }
170
198
  </strong>
171
- { additionalPrompt }
172
199
  </div>
173
- <p id={ renderDialog ? dialogDescription : undefined }>
174
- { isDirty
175
- ? createInterpolateElement(
176
- sprintf(
177
- /* translators: %d: number of site changes waiting to be saved. */
178
- _n(
179
- 'There is <strong>%d site change</strong> waiting to be saved.',
180
- 'There are <strong>%d site changes</strong> waiting to be saved.',
200
+ <div id={ renderDialog ? dialogDescriptionId : undefined }>
201
+ { additionalPrompt }
202
+ <p className="entities-saved-states__text-prompt--changes-count">
203
+ { isDirty
204
+ ? createInterpolateElement(
205
+ sprintf(
206
+ /* translators: %d: number of site changes waiting to be saved. */
207
+ _n(
208
+ 'There is <strong>%d site change</strong> waiting to be saved.',
209
+ 'There are <strong>%d site changes</strong> waiting to be saved.',
210
+ dirtyEntityRecords.length
211
+ ),
181
212
  dirtyEntityRecords.length
182
213
  ),
183
- dirtyEntityRecords.length
184
- ),
185
- { strong: <strong /> }
186
- )
187
- : selectItemsToSaveDescription }
188
- </p>
214
+ { strong: <strong /> }
215
+ )
216
+ : selectItemsToSaveDescription }
217
+ </p>
218
+ </div>
189
219
  </div>
190
220
 
191
221
  { sortedPartitionedSavables.map( ( list ) => {
@@ -198,6 +228,16 @@ export function EntitiesSavedStatesExtensible( {
198
228
  />
199
229
  );
200
230
  } ) }
231
+
232
+ { isInline && (
233
+ <Flex
234
+ direction="row"
235
+ justify="flex-end"
236
+ className="entities-saved-states__panel-footer"
237
+ >
238
+ { actionButtons }
239
+ </Flex>
240
+ ) }
201
241
  </div>
202
242
  );
203
243
  }
@@ -16,14 +16,49 @@
16
16
  }
17
17
  }
18
18
 
19
- .entities-saved-states__description-heading {
20
- font-size: $default-font-size;
19
+ .entities-saved-states__panel.is-inline {
20
+ .entities-saved-states__text-prompt {
21
+ padding: 0;
22
+ }
23
+
24
+ .entities-saved-states__panel-body {
25
+ padding-left: 0;
26
+ padding-right: 0;
27
+ border: 0;
28
+
29
+ > h2 {
30
+ margin-left: -1 * $grid-unit-20;
31
+ margin-right: -1 * $grid-unit-20;
32
+ margin-bottom: 0;
33
+
34
+ button {
35
+ font-size: $font-size-x-small;
36
+ text-transform: uppercase;
37
+ }
38
+ }
39
+ }
40
+
41
+ .entities-saved-states__text-prompt--header-wrapper {
42
+ display: none;
43
+ }
44
+
45
+ .entities-saved-states__text-prompt--changes-count {
46
+ margin-top: 0;
47
+ margin-bottom: $grid-unit-10;
48
+ }
49
+
50
+ .entities-saved-states__panel-footer {
51
+ margin-top: $grid-unit-20;
52
+ }
53
+ }
54
+
55
+ .entities-saved-states__change-control {
56
+ flex: 1;
21
57
  }
22
58
 
23
59
  .entities-saved-states__changes {
24
- color: $gray-700;
25
- font-size: $helptext-font-size;
26
- margin: $grid-unit-10 $grid-unit-20 0 $grid-unit-20;
60
+ font-size: $default-font-size;
61
+ margin: $grid-unit-05 $grid-unit-20 0 $grid-unit-30;
27
62
  list-style: disc;
28
63
 
29
64
  li {
@@ -20,57 +20,39 @@ import { usePostActions } from './actions';
20
20
 
21
21
  const { Menu, kebabCase } = unlock( componentsPrivateApis );
22
22
 
23
- function useEditedEntityRecordsWithPermissions( postType, postIds ) {
24
- const { items, permissions } = useSelect(
23
+ export default function PostActions( { postType, postId, onActionPerformed } ) {
24
+ const [ activeModalAction, setActiveModalAction ] = useState( null );
25
+
26
+ const { item, permissions } = useSelect(
25
27
  ( select ) => {
26
28
  const { getEditedEntityRecord, getEntityRecordPermissions } =
27
29
  unlock( select( coreStore ) );
28
30
  return {
29
- items: postIds.map( ( postId ) =>
30
- getEditedEntityRecord( 'postType', postType, postId )
31
- ),
32
- permissions: postIds.map( ( postId ) =>
33
- getEntityRecordPermissions( 'postType', postType, postId )
31
+ item: getEditedEntityRecord( 'postType', postType, postId ),
32
+ permissions: getEntityRecordPermissions(
33
+ 'postType',
34
+ postType,
35
+ postId
34
36
  ),
35
37
  };
36
38
  },
37
- [ postIds, postType ]
39
+ [ postId, postType ]
38
40
  );
39
-
40
- return useMemo( () => {
41
- return items.map( ( item, index ) => ( {
41
+ const itemWithPermissions = useMemo( () => {
42
+ return {
42
43
  ...item,
43
- permissions: permissions[ index ],
44
- } ) );
45
- }, [ items, permissions ] );
46
- }
47
-
48
- export default function PostActions( { postType, postId, onActionPerformed } ) {
49
- const [ activeModalAction, setActiveModalAction ] = useState( null );
50
- const _postIds = useMemo( () => {
51
- if ( Array.isArray( postId ) ) {
52
- return postId;
53
- }
54
- return postId ? [ postId ] : [];
55
- }, [ postId ] );
56
-
57
- const itemsWithPermissions = useEditedEntityRecordsWithPermissions(
58
- postType,
59
- _postIds
60
- );
44
+ permissions,
45
+ };
46
+ }, [ item, permissions ] );
61
47
  const allActions = usePostActions( { postType, onActionPerformed } );
62
48
 
63
49
  const actions = useMemo( () => {
64
50
  return allActions.filter( ( action ) => {
65
51
  return (
66
- ( ! action.isEligible ||
67
- itemsWithPermissions.some( ( itemWithPermissions ) =>
68
- action.isEligible( itemWithPermissions )
69
- ) ) &&
70
- ( itemsWithPermissions.length < 2 || action.supportsBulk )
52
+ ! action.isEligible || action.isEligible( itemWithPermissions )
71
53
  );
72
54
  } );
73
- }, [ allActions, itemsWithPermissions ] );
55
+ }, [ allActions, itemWithPermissions ] );
74
56
 
75
57
  return (
76
58
  <>
@@ -90,7 +72,7 @@ export default function PostActions( { postType, postId, onActionPerformed } ) {
90
72
  <Menu.Popover>
91
73
  <ActionsDropdownMenuGroup
92
74
  actions={ actions }
93
- items={ itemsWithPermissions }
75
+ items={ [ itemWithPermissions ] }
94
76
  setActiveModalAction={ setActiveModalAction }
95
77
  />
96
78
  </Menu.Popover>
@@ -98,7 +80,7 @@ export default function PostActions( { postType, postId, onActionPerformed } ) {
98
80
  { !! activeModalAction && (
99
81
  <ActionModal
100
82
  action={ activeModalAction }
101
- items={ itemsWithPermissions }
83
+ items={ [ itemWithPermissions ] }
102
84
  closeModal={ () => setActiveModalAction( null ) }
103
85
  />
104
86
  ) }
@@ -48,7 +48,7 @@ export default function PostCardPanel( {
48
48
  );
49
49
  const { postTitle, icon, labels } = useSelect(
50
50
  ( select ) => {
51
- const { getEditedEntityRecord, getEntityRecord, getPostType } =
51
+ const { getEditedEntityRecord, getCurrentTheme, getPostType } =
52
52
  select( coreStore );
53
53
  const { getPostIcon } = unlock( select( editorStore ) );
54
54
  let _title = '';
@@ -59,7 +59,7 @@ export default function PostCardPanel( {
59
59
  );
60
60
  if ( postIds.length === 1 ) {
61
61
  const { default_template_types: templateTypes = [] } =
62
- getEntityRecord( 'root', '__unstableBase' ) ?? {};
62
+ getCurrentTheme() ?? {};
63
63
 
64
64
  const _templateInfo = [
65
65
  TEMPLATE_POST_TYPE,
@@ -118,11 +118,13 @@ export default function PostCardPanel( {
118
118
  <Badge>{ pageTypeBadge }</Badge>
119
119
  ) }
120
120
  </Text>
121
- <PostActions
122
- postType={ postType }
123
- postId={ postId }
124
- onActionPerformed={ onActionPerformed }
125
- />
121
+ { postIds.length === 1 && (
122
+ <PostActions
123
+ postType={ postType }
124
+ postId={ postIds[ 0 ] }
125
+ onActionPerformed={ onActionPerformed }
126
+ />
127
+ ) }
126
128
  </HStack>
127
129
  { postIds.length > 1 && (
128
130
  <Text className="editor-post-card-panel__description">
@@ -125,6 +125,7 @@ function PostFeaturedImage( {
125
125
  noticeOperations.removeAllNotices();
126
126
  noticeOperations.createErrorNotice( message );
127
127
  },
128
+ multiple: false,
128
129
  } );
129
130
  }
130
131
 
@@ -345,6 +346,7 @@ const applyWithDispatch = withDispatch(
345
346
  noticeOperations.removeAllNotices();
346
347
  noticeOperations.createErrorNotice( message );
347
348
  },
349
+ multiple: false,
348
350
  } );
349
351
  },
350
352
  onRemoveImage() {