@wordpress/editor 13.31.0 → 13.32.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 (157) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +857 -0
  3. package/build/components/block-settings-menu/plugin-block-settings-menu-item.js +107 -0
  4. package/build/components/block-settings-menu/plugin-block-settings-menu-item.js.map +1 -0
  5. package/build/components/commands/index.js +1 -1
  6. package/build/components/commands/index.js.map +1 -1
  7. package/build/components/deprecated.js +158 -0
  8. package/build/components/deprecated.js.map +1 -1
  9. package/build/components/document-bar/index.js +5 -8
  10. package/build/components/document-bar/index.js.map +1 -1
  11. package/build/components/editor-canvas/edit-template-blocks-notification.js +2 -39
  12. package/build/components/editor-canvas/edit-template-blocks-notification.js.map +1 -1
  13. package/build/components/editor-canvas/index.js +3 -0
  14. package/build/components/editor-canvas/index.js.map +1 -1
  15. package/build/components/entities-saved-states/index.js +11 -85
  16. package/build/components/entities-saved-states/index.js.map +1 -1
  17. package/build/components/index.js +24 -0
  18. package/build/components/index.js.map +1 -1
  19. package/build/components/inserter-sidebar/index.js +5 -1
  20. package/build/components/inserter-sidebar/index.js.map +1 -1
  21. package/build/components/list-view-sidebar/index.js +2 -1
  22. package/build/components/list-view-sidebar/index.js.map +1 -1
  23. package/build/components/pattern-overrides-panel/index.js +30 -0
  24. package/build/components/pattern-overrides-panel/index.js.map +1 -0
  25. package/build/components/plugin-post-publish-panel/index.js +68 -0
  26. package/build/components/plugin-post-publish-panel/index.js.map +1 -0
  27. package/build/components/plugin-pre-publish-panel/index.js +71 -0
  28. package/build/components/plugin-pre-publish-panel/index.js.map +1 -0
  29. package/build/components/post-actions/actions.js +455 -0
  30. package/build/components/post-actions/actions.js.map +1 -0
  31. package/build/components/post-card-panel/index.js +93 -0
  32. package/build/components/post-card-panel/index.js.map +1 -0
  33. package/build/components/post-title/index.native.js +1 -1
  34. package/build/components/post-title/index.native.js.map +1 -1
  35. package/build/components/provider/disable-non-page-content-blocks.js +36 -20
  36. package/build/components/provider/disable-non-page-content-blocks.js.map +1 -1
  37. package/build/components/provider/use-block-editor-settings.js +8 -9
  38. package/build/components/provider/use-block-editor-settings.js.map +1 -1
  39. package/build/components/template-areas/index.js +70 -0
  40. package/build/components/template-areas/index.js.map +1 -0
  41. package/build/hooks/use-select-nearest-editable-block.js +87 -0
  42. package/build/hooks/use-select-nearest-editable-block.js.map +1 -0
  43. package/build/private-apis.js +6 -0
  44. package/build/private-apis.js.map +1 -1
  45. package/build/store/actions.js +46 -6
  46. package/build/store/actions.js.map +1 -1
  47. package/build/store/constants.js +3 -1
  48. package/build/store/constants.js.map +1 -1
  49. package/build/store/private-actions.js +80 -1
  50. package/build/store/private-actions.js.map +1 -1
  51. package/build/store/private-selectors.js +56 -3
  52. package/build/store/private-selectors.js.map +1 -1
  53. package/build/store/reducer.js +14 -1
  54. package/build/store/reducer.js.map +1 -1
  55. package/build/store/selectors.js +21 -11
  56. package/build/store/selectors.js.map +1 -1
  57. package/build/store/utils/get-filtered-template-parts.js +71 -0
  58. package/build/store/utils/get-filtered-template-parts.js.map +1 -0
  59. package/build-module/components/block-settings-menu/plugin-block-settings-menu-item.js +100 -0
  60. package/build-module/components/block-settings-menu/plugin-block-settings-menu-item.js.map +1 -0
  61. package/build-module/components/commands/index.js +1 -1
  62. package/build-module/components/commands/index.js.map +1 -1
  63. package/build-module/components/deprecated.js +159 -0
  64. package/build-module/components/deprecated.js.map +1 -1
  65. package/build-module/components/document-bar/index.js +6 -9
  66. package/build-module/components/document-bar/index.js.map +1 -1
  67. package/build-module/components/editor-canvas/edit-template-blocks-notification.js +4 -41
  68. package/build-module/components/editor-canvas/edit-template-blocks-notification.js.map +1 -1
  69. package/build-module/components/editor-canvas/index.js +3 -0
  70. package/build-module/components/editor-canvas/index.js.map +1 -1
  71. package/build-module/components/entities-saved-states/index.js +11 -85
  72. package/build-module/components/entities-saved-states/index.js.map +1 -1
  73. package/build-module/components/index.js +3 -0
  74. package/build-module/components/index.js.map +1 -1
  75. package/build-module/components/inserter-sidebar/index.js +5 -1
  76. package/build-module/components/inserter-sidebar/index.js.map +1 -1
  77. package/build-module/components/list-view-sidebar/index.js +2 -1
  78. package/build-module/components/list-view-sidebar/index.js.map +1 -1
  79. package/build-module/components/pattern-overrides-panel/index.js +23 -0
  80. package/build-module/components/pattern-overrides-panel/index.js.map +1 -0
  81. package/build-module/components/plugin-post-publish-panel/index.js +61 -0
  82. package/build-module/components/plugin-post-publish-panel/index.js.map +1 -0
  83. package/build-module/components/plugin-pre-publish-panel/index.js +64 -0
  84. package/build-module/components/plugin-pre-publish-panel/index.js.map +1 -0
  85. package/build-module/components/post-actions/actions.js +444 -0
  86. package/build-module/components/post-actions/actions.js.map +1 -0
  87. package/build-module/components/post-card-panel/index.js +85 -0
  88. package/build-module/components/post-card-panel/index.js.map +1 -0
  89. package/build-module/components/post-title/index.native.js +1 -1
  90. package/build-module/components/post-title/index.native.js.map +1 -1
  91. package/build-module/components/provider/disable-non-page-content-blocks.js +36 -20
  92. package/build-module/components/provider/disable-non-page-content-blocks.js.map +1 -1
  93. package/build-module/components/provider/use-block-editor-settings.js +9 -10
  94. package/build-module/components/provider/use-block-editor-settings.js.map +1 -1
  95. package/build-module/components/template-areas/index.js +63 -0
  96. package/build-module/components/template-areas/index.js.map +1 -0
  97. package/build-module/hooks/use-select-nearest-editable-block.js +80 -0
  98. package/build-module/hooks/use-select-nearest-editable-block.js.map +1 -0
  99. package/build-module/private-apis.js +6 -0
  100. package/build-module/private-apis.js.map +1 -1
  101. package/build-module/store/actions.js +37 -3
  102. package/build-module/store/actions.js.map +1 -1
  103. package/build-module/store/constants.js +2 -0
  104. package/build-module/store/constants.js.map +1 -1
  105. package/build-module/store/private-actions.js +78 -0
  106. package/build-module/store/private-actions.js.map +1 -1
  107. package/build-module/store/private-selectors.js +54 -3
  108. package/build-module/store/private-selectors.js.map +1 -1
  109. package/build-module/store/reducer.js +13 -1
  110. package/build-module/store/reducer.js.map +1 -1
  111. package/build-module/store/selectors.js +19 -10
  112. package/build-module/store/selectors.js.map +1 -1
  113. package/build-module/store/utils/get-filtered-template-parts.js +64 -0
  114. package/build-module/store/utils/get-filtered-template-parts.js.map +1 -0
  115. package/build-style/style-rtl.css +70 -27
  116. package/build-style/style.css +70 -27
  117. package/package.json +35 -35
  118. package/src/components/block-settings-menu/plugin-block-settings-menu-item.js +108 -0
  119. package/src/components/commands/index.js +1 -1
  120. package/src/components/deprecated.js +157 -0
  121. package/src/components/document-bar/index.js +9 -15
  122. package/src/components/document-bar/style.scss +9 -12
  123. package/src/components/document-tools/style.scss +4 -11
  124. package/src/components/editor-canvas/edit-template-blocks-notification.js +6 -56
  125. package/src/components/editor-canvas/index.js +4 -0
  126. package/src/components/entities-saved-states/index.js +12 -113
  127. package/src/components/index.js +3 -0
  128. package/src/components/inserter-sidebar/index.js +7 -1
  129. package/src/components/list-view-sidebar/index.js +1 -0
  130. package/src/components/list-view-sidebar/style.scss +1 -1
  131. package/src/components/pattern-overrides-panel/index.js +26 -0
  132. package/src/components/plugin-post-publish-panel/index.js +64 -0
  133. package/src/components/plugin-post-publish-panel/test/__snapshots__/index.js.snap +39 -0
  134. package/src/components/plugin-post-publish-panel/test/index.js +33 -0
  135. package/src/components/plugin-pre-publish-panel/index.js +67 -0
  136. package/src/components/plugin-pre-publish-panel/test/index.js +33 -0
  137. package/src/components/post-actions/actions.js +582 -0
  138. package/src/components/post-card-panel/index.js +108 -0
  139. package/src/components/post-card-panel/style.scss +32 -0
  140. package/src/components/post-featured-image/style.scss +3 -2
  141. package/src/components/post-title/index.native.js +1 -1
  142. package/src/components/provider/disable-non-page-content-blocks.js +40 -20
  143. package/src/components/provider/test/disable-non-page-content-blocks.js +35 -14
  144. package/src/components/provider/use-block-editor-settings.js +11 -11
  145. package/src/components/template-areas/index.js +85 -0
  146. package/src/components/template-areas/style.scss +23 -0
  147. package/src/hooks/use-select-nearest-editable-block.js +95 -0
  148. package/src/private-apis.js +6 -0
  149. package/src/store/actions.js +37 -3
  150. package/src/store/constants.js +2 -0
  151. package/src/store/private-actions.js +111 -0
  152. package/src/store/private-selectors.js +105 -17
  153. package/src/store/reducer.js +13 -0
  154. package/src/store/selectors.js +50 -40
  155. package/src/store/utils/get-filtered-template-parts.js +69 -0
  156. package/src/store/utils/test/get-filtered-template-parts.js +189 -0
  157. package/src/style.scss +2 -0
@@ -4,6 +4,7 @@
4
4
  import { store as coreStore } from '@wordpress/core-data';
5
5
  import { __ } from '@wordpress/i18n';
6
6
  import { store as noticesStore } from '@wordpress/notices';
7
+ import { store as blockEditorStore } from '@wordpress/block-editor';
7
8
  import { store as preferencesStore } from '@wordpress/preferences';
8
9
 
9
10
  /**
@@ -109,3 +110,113 @@ export const hideBlockTypes =
109
110
  .dispatch( preferencesStore )
110
111
  .set( 'core', 'hiddenBlockTypes', [ ...mergedBlockNames ] );
111
112
  };
113
+
114
+ /**
115
+ * Save entity records marked as dirty.
116
+ *
117
+ * @param {Object} options Options for the action.
118
+ * @param {Function} [options.onSave] Callback when saving happens.
119
+ * @param {object[]} [options.dirtyEntityRecords] Array of dirty entities.
120
+ * @param {object[]} [options.entitiesToSkip] Array of entities to skip saving.
121
+ * @param {Function} [options.close] Callback when the actions is called. It should be consolidated with `onSave`.
122
+ */
123
+ export const saveDirtyEntities =
124
+ ( { onSave, dirtyEntityRecords = [], entitiesToSkip = [], close } = {} ) =>
125
+ ( { registry } ) => {
126
+ const PUBLISH_ON_SAVE_ENTITIES = [
127
+ { kind: 'postType', name: 'wp_navigation' },
128
+ ];
129
+ const saveNoticeId = 'site-editor-save-success';
130
+ const homeUrl = registry.select( coreStore ).getUnstableBase()?.home;
131
+ registry.dispatch( noticesStore ).removeNotice( saveNoticeId );
132
+ const entitiesToSave = dirtyEntityRecords.filter(
133
+ ( { kind, name, key, property } ) => {
134
+ return ! entitiesToSkip.some(
135
+ ( elt ) =>
136
+ elt.kind === kind &&
137
+ elt.name === name &&
138
+ elt.key === key &&
139
+ elt.property === property
140
+ );
141
+ }
142
+ );
143
+ if ( ! entitiesToSave.length ) {
144
+ return;
145
+ }
146
+ close?.( entitiesToSave );
147
+ const siteItemsToSave = [];
148
+ const pendingSavedRecords = [];
149
+ entitiesToSave.forEach( ( { kind, name, key, property } ) => {
150
+ if ( 'root' === kind && 'site' === name ) {
151
+ siteItemsToSave.push( property );
152
+ } else {
153
+ if (
154
+ PUBLISH_ON_SAVE_ENTITIES.some(
155
+ ( typeToPublish ) =>
156
+ typeToPublish.kind === kind &&
157
+ typeToPublish.name === name
158
+ )
159
+ ) {
160
+ registry
161
+ .dispatch( coreStore )
162
+ .editEntityRecord( kind, name, key, {
163
+ status: 'publish',
164
+ } );
165
+ }
166
+
167
+ pendingSavedRecords.push(
168
+ registry
169
+ .dispatch( coreStore )
170
+ .saveEditedEntityRecord( kind, name, key )
171
+ );
172
+ }
173
+ } );
174
+ if ( siteItemsToSave.length ) {
175
+ pendingSavedRecords.push(
176
+ registry
177
+ .dispatch( coreStore )
178
+ .__experimentalSaveSpecifiedEntityEdits(
179
+ 'root',
180
+ 'site',
181
+ undefined,
182
+ siteItemsToSave
183
+ )
184
+ );
185
+ }
186
+ registry
187
+ .dispatch( blockEditorStore )
188
+ .__unstableMarkLastChangeAsPersistent();
189
+ Promise.all( pendingSavedRecords )
190
+ .then( ( values ) => {
191
+ return onSave ? onSave( values ) : values;
192
+ } )
193
+ .then( ( values ) => {
194
+ if (
195
+ values.some( ( value ) => typeof value === 'undefined' )
196
+ ) {
197
+ registry
198
+ .dispatch( noticesStore )
199
+ .createErrorNotice( __( 'Saving failed.' ) );
200
+ } else {
201
+ registry
202
+ .dispatch( noticesStore )
203
+ .createSuccessNotice( __( 'Site updated.' ), {
204
+ type: 'snackbar',
205
+ id: saveNoticeId,
206
+ actions: [
207
+ {
208
+ label: __( 'View site' ),
209
+ url: homeUrl,
210
+ },
211
+ ],
212
+ } );
213
+ }
214
+ } )
215
+ .catch( ( error ) =>
216
+ registry
217
+ .dispatch( noticesStore )
218
+ .createErrorNotice(
219
+ `${ __( 'Saving failed.' ) } ${ error }`
220
+ )
221
+ );
222
+ };
@@ -1,13 +1,31 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import createSelector from 'rememo';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
4
9
  import { store as blockEditorStore } from '@wordpress/block-editor';
5
10
  import { createRegistrySelector } from '@wordpress/data';
11
+ import {
12
+ layout,
13
+ symbol,
14
+ navigation,
15
+ page as pageIcon,
16
+ verse,
17
+ } from '@wordpress/icons';
18
+ import { store as coreStore } from '@wordpress/core-data';
6
19
 
7
20
  /**
8
21
  * Internal dependencies
9
22
  */
10
- import { getRenderingMode } from './selectors';
23
+ import {
24
+ getRenderingMode,
25
+ __experimentalGetDefaultTemplatePartAreas,
26
+ } from './selectors';
27
+ import { TEMPLATE_PART_POST_TYPE } from './constants';
28
+ import { getFilteredTemplatePartBlocks } from './utils/get-filtered-template-parts';
11
29
 
12
30
  const EMPTY_INSERTION_POINT = {
13
31
  rootClientId: undefined,
@@ -22,30 +40,100 @@ const EMPTY_INSERTION_POINT = {
22
40
  *
23
41
  * @return {Object} The root client ID, index to insert at and starting filter value.
24
42
  */
25
- export const getInsertionPoint = createRegistrySelector(
26
- ( select ) => ( state ) => {
27
- if ( typeof state.blockInserterPanel === 'object' ) {
28
- return state.blockInserterPanel;
29
- }
43
+ export const getInsertionPoint = createRegistrySelector( ( select ) =>
44
+ createSelector(
45
+ ( state ) => {
46
+ if ( typeof state.blockInserterPanel === 'object' ) {
47
+ return state.blockInserterPanel;
48
+ }
49
+
50
+ if ( getRenderingMode( state ) === 'template-locked' ) {
51
+ const [ postContentClientId ] =
52
+ select( blockEditorStore ).getBlocksByName(
53
+ 'core/post-content'
54
+ );
55
+ if ( postContentClientId ) {
56
+ return {
57
+ rootClientId: postContentClientId,
58
+ insertionIndex: undefined,
59
+ filterValue: undefined,
60
+ };
61
+ }
62
+ }
30
63
 
31
- if ( getRenderingMode( state ) === 'template-locked' ) {
64
+ return EMPTY_INSERTION_POINT;
65
+ },
66
+ ( state ) => {
32
67
  const [ postContentClientId ] =
33
68
  select( blockEditorStore ).getBlocksByName(
34
69
  'core/post-content'
35
70
  );
36
- if ( postContentClientId ) {
37
- return {
38
- rootClientId: postContentClientId,
39
- insertionIndex: undefined,
40
- filterValue: undefined,
41
- };
42
- }
71
+ return [
72
+ state.blockInserterPanel,
73
+ getRenderingMode( state ),
74
+ postContentClientId,
75
+ ];
43
76
  }
44
-
45
- return EMPTY_INSERTION_POINT;
46
- }
77
+ )
47
78
  );
48
79
 
49
80
  export function getListViewToggleRef( state ) {
50
81
  return state.listViewToggleRef;
51
82
  }
83
+ const CARD_ICONS = {
84
+ wp_block: symbol,
85
+ wp_navigation: navigation,
86
+ page: pageIcon,
87
+ post: verse,
88
+ };
89
+
90
+ export const getPostIcon = createRegistrySelector(
91
+ ( select ) => ( state, postType, options ) => {
92
+ {
93
+ if (
94
+ postType === 'wp_template_part' ||
95
+ postType === 'wp_template'
96
+ ) {
97
+ return (
98
+ __experimentalGetDefaultTemplatePartAreas( state ).find(
99
+ ( item ) => options.area === item.area
100
+ )?.icon || layout
101
+ );
102
+ }
103
+ if ( CARD_ICONS[ postType ] ) {
104
+ return CARD_ICONS[ postType ];
105
+ }
106
+ const postTypeEntity = select( coreStore ).getPostType( postType );
107
+ // `icon` is the `menu_icon` property of a post type. We
108
+ // only handle `dashicons` for now, even if the `menu_icon`
109
+ // also supports urls and svg as values.
110
+ if ( postTypeEntity?.icon?.startsWith( 'dashicons-' ) ) {
111
+ return postTypeEntity.icon.slice( 10 );
112
+ }
113
+ return pageIcon;
114
+ }
115
+ }
116
+ );
117
+
118
+ /**
119
+ * Returns the template parts and their blocks for the current edited template.
120
+ *
121
+ * @param {Object} state Global application state.
122
+ * @return {Array} Template parts and their blocks in an array.
123
+ */
124
+ export const getCurrentTemplateTemplateParts = createRegistrySelector(
125
+ ( select ) => () => {
126
+ const templateParts = select( coreStore ).getEntityRecords(
127
+ 'postType',
128
+ TEMPLATE_PART_POST_TYPE,
129
+ { per_page: -1 }
130
+ );
131
+
132
+ const clientIds =
133
+ select( blockEditorStore ).getBlocksByName( 'core/template-part' );
134
+ const blocks =
135
+ select( blockEditorStore ).getBlocksByClientId( clientIds );
136
+
137
+ return getFilteredTemplatePartBlocks( blocks, templateParts );
138
+ }
139
+ );
@@ -360,6 +360,18 @@ export function listViewToggleRef( state = { current: null } ) {
360
360
  return state;
361
361
  }
362
362
 
363
+ export function publishSidebarActive( state = false, action ) {
364
+ switch ( action.type ) {
365
+ case 'OPEN_PUBLISH_SIDEBAR':
366
+ return true;
367
+ case 'CLOSE_PUBLISH_SIDEBAR':
368
+ return false;
369
+ case 'TOGGLE_PUBLISH_SIDEBAR':
370
+ return ! state;
371
+ }
372
+ return state;
373
+ }
374
+
363
375
  export default combineReducers( {
364
376
  postId,
365
377
  postType,
@@ -377,4 +389,5 @@ export default combineReducers( {
377
389
  blockInserterPanel,
378
390
  listViewPanel,
379
391
  listViewToggleRef,
392
+ publishSidebarActive,
380
393
  } );
@@ -105,16 +105,11 @@ export const isEditedPostDirty = createRegistrySelector(
105
105
  // inferred to contain unsaved values.
106
106
  const postType = getCurrentPostType( state );
107
107
  const postId = getCurrentPostId( state );
108
- if (
109
- select( coreStore ).hasEditsForEntityRecord(
110
- 'postType',
111
- postType,
112
- postId
113
- )
114
- ) {
115
- return true;
116
- }
117
- return false;
108
+ return select( coreStore ).hasEditsForEntityRecord(
109
+ 'postType',
110
+ postType,
111
+ postId
112
+ );
118
113
  }
119
114
  );
120
115
 
@@ -1112,10 +1107,7 @@ export function canUserUseUnfilteredHTML( state ) {
1112
1107
  */
1113
1108
  export const isPublishSidebarEnabled = createRegistrySelector(
1114
1109
  ( select ) => () =>
1115
- !! select( preferencesStore ).get(
1116
- 'core/edit-post',
1117
- 'isPublishSidebarEnabled'
1118
- )
1110
+ !! select( preferencesStore ).get( 'core', 'isPublishSidebarEnabled' )
1119
1111
  );
1120
1112
 
1121
1113
  /**
@@ -1706,8 +1698,8 @@ export function __experimentalGetDefaultTemplateTypes( state ) {
1706
1698
  export const __experimentalGetDefaultTemplatePartAreas = createSelector(
1707
1699
  ( state ) => {
1708
1700
  const areas =
1709
- getEditorSettings( state )?.defaultTemplatePartAreas || [];
1710
- return areas?.map( ( item ) => {
1701
+ getEditorSettings( state )?.defaultTemplatePartAreas ?? [];
1702
+ return areas.map( ( item ) => {
1711
1703
  return { ...item, icon: getTemplatePartIcon( item.icon ) };
1712
1704
  } );
1713
1705
  },
@@ -1735,7 +1727,7 @@ export const __experimentalGetDefaultTemplateType = createSelector(
1735
1727
  ) ?? EMPTY_OBJECT
1736
1728
  );
1737
1729
  },
1738
- ( state, slug ) => [ __experimentalGetDefaultTemplateTypes( state ), slug ]
1730
+ ( state ) => [ __experimentalGetDefaultTemplateTypes( state ) ]
1739
1731
  );
1740
1732
 
1741
1733
  /**
@@ -1746,32 +1738,39 @@ export const __experimentalGetDefaultTemplateType = createSelector(
1746
1738
  * @param {Object} template The template for which we need information.
1747
1739
  * @return {Object} Information about the template, including title, description, and icon.
1748
1740
  */
1749
- export function __experimentalGetTemplateInfo( state, template ) {
1750
- if ( ! template ) {
1751
- return EMPTY_OBJECT;
1752
- }
1741
+ export const __experimentalGetTemplateInfo = createSelector(
1742
+ ( state, template ) => {
1743
+ if ( ! template ) {
1744
+ return EMPTY_OBJECT;
1745
+ }
1753
1746
 
1754
- const { description, slug, title, area } = template;
1755
- const { title: defaultTitle, description: defaultDescription } =
1756
- __experimentalGetDefaultTemplateType( state, slug );
1747
+ const { description, slug, title, area } = template;
1748
+ const { title: defaultTitle, description: defaultDescription } =
1749
+ __experimentalGetDefaultTemplateType( state, slug );
1757
1750
 
1758
- const templateTitle = typeof title === 'string' ? title : title?.rendered;
1759
- const templateDescription =
1760
- typeof description === 'string' ? description : description?.raw;
1761
- const templateIcon =
1762
- __experimentalGetDefaultTemplatePartAreas( state ).find(
1763
- ( item ) => area === item.area
1764
- )?.icon || layout;
1751
+ const templateTitle =
1752
+ typeof title === 'string' ? title : title?.rendered;
1753
+ const templateDescription =
1754
+ typeof description === 'string' ? description : description?.raw;
1755
+ const templateIcon =
1756
+ __experimentalGetDefaultTemplatePartAreas( state ).find(
1757
+ ( item ) => area === item.area
1758
+ )?.icon || layout;
1765
1759
 
1766
- return {
1767
- title:
1768
- templateTitle && templateTitle !== slug
1769
- ? templateTitle
1770
- : defaultTitle || slug,
1771
- description: templateDescription || defaultDescription,
1772
- icon: templateIcon,
1773
- };
1774
- }
1760
+ return {
1761
+ title:
1762
+ templateTitle && templateTitle !== slug
1763
+ ? templateTitle
1764
+ : defaultTitle || slug,
1765
+ description: templateDescription || defaultDescription,
1766
+ icon: templateIcon,
1767
+ };
1768
+ },
1769
+ ( state ) => [
1770
+ __experimentalGetDefaultTemplateTypes( state ),
1771
+ __experimentalGetDefaultTemplatePartAreas( state ),
1772
+ ]
1773
+ );
1775
1774
 
1776
1775
  /**
1777
1776
  * Returns a post type label depending on the current post.
@@ -1789,3 +1788,14 @@ export const getPostTypeLabel = createRegistrySelector(
1789
1788
  return postType?.labels?.singular_name;
1790
1789
  }
1791
1790
  );
1791
+
1792
+ /**
1793
+ * Returns true if the publish sidebar is opened.
1794
+ *
1795
+ * @param {Object} state Global application state
1796
+ *
1797
+ * @return {boolean} Whether the publish sidebar is open.
1798
+ */
1799
+ export function isPublishSidebarOpened( state ) {
1800
+ return state.publishSidebarActive;
1801
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import memoize from 'memize';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { isTemplatePart } from '@wordpress/blocks';
10
+
11
+ const EMPTY_ARRAY = [];
12
+
13
+ /**
14
+ * Get a flattened and filtered list of template parts and the matching block for that template part.
15
+ *
16
+ * Takes a list of blocks defined within a template, and a list of template parts, and returns a
17
+ * flattened list of template parts and the matching block for that template part.
18
+ *
19
+ * @param {Array} blocks Blocks to flatten.
20
+ * @param {?Array} templateParts Available template parts.
21
+ * @return {Array} An array of template parts and their blocks.
22
+ */
23
+ function getFilteredTemplatePartBlocks( blocks = EMPTY_ARRAY, templateParts ) {
24
+ const templatePartsById = templateParts
25
+ ? // Key template parts by their ID.
26
+ templateParts.reduce(
27
+ ( newTemplateParts, part ) => ( {
28
+ ...newTemplateParts,
29
+ [ part.id ]: part,
30
+ } ),
31
+ {}
32
+ )
33
+ : {};
34
+
35
+ const result = [];
36
+
37
+ // Iterate over all blocks, recursing into inner blocks.
38
+ // Output will be based on a depth-first traversal.
39
+ const stack = [ ...blocks ];
40
+ while ( stack.length ) {
41
+ const { innerBlocks, ...block } = stack.shift();
42
+ // Place inner blocks at the beginning of the stack to preserve order.
43
+ stack.unshift( ...innerBlocks );
44
+
45
+ if ( isTemplatePart( block ) ) {
46
+ const {
47
+ attributes: { theme, slug },
48
+ } = block;
49
+ const templatePartId = `${ theme }//${ slug }`;
50
+ const templatePart = templatePartsById[ templatePartId ];
51
+
52
+ // Only add to output if the found template part block is in the list of available template parts.
53
+ if ( templatePart ) {
54
+ result.push( {
55
+ templatePart,
56
+ block,
57
+ } );
58
+ }
59
+ }
60
+ }
61
+
62
+ return result;
63
+ }
64
+
65
+ const memoizedGetFilteredTemplatePartBlocks = memoize(
66
+ getFilteredTemplatePartBlocks
67
+ );
68
+
69
+ export { memoizedGetFilteredTemplatePartBlocks as getFilteredTemplatePartBlocks };
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { getFilteredTemplatePartBlocks } from '../get-filtered-template-parts';
5
+
6
+ const NESTED_BLOCKS = [
7
+ {
8
+ clientId: '1',
9
+ name: 'core/group',
10
+ innerBlocks: [
11
+ {
12
+ clientId: '2',
13
+ name: 'core/template-part',
14
+ attributes: {
15
+ slug: 'header',
16
+ theme: 'my-theme',
17
+ },
18
+ innerBlocks: [
19
+ {
20
+ clientId: '3',
21
+ name: 'core/group',
22
+ innerBlocks: [],
23
+ },
24
+ ],
25
+ },
26
+ {
27
+ clientId: '4',
28
+ name: 'core/template-part',
29
+ attributes: {
30
+ slug: 'aside',
31
+ theme: 'my-theme',
32
+ },
33
+ innerBlocks: [],
34
+ },
35
+ ],
36
+ },
37
+ {
38
+ clientId: '5',
39
+ name: 'core/paragraph',
40
+ innerBlocks: [],
41
+ },
42
+ {
43
+ clientId: '6',
44
+ name: 'core/template-part',
45
+ attributes: {
46
+ slug: 'footer',
47
+ theme: 'my-theme',
48
+ },
49
+ innerBlocks: [],
50
+ },
51
+ ];
52
+
53
+ const FLATTENED_BLOCKS = [
54
+ {
55
+ block: {
56
+ clientId: '2',
57
+ name: 'core/template-part',
58
+ attributes: {
59
+ slug: 'header',
60
+ theme: 'my-theme',
61
+ },
62
+ },
63
+ templatePart: {
64
+ id: 'my-theme//header',
65
+ slug: 'header',
66
+ theme: 'my-theme',
67
+ },
68
+ },
69
+ {
70
+ block: {
71
+ clientId: '4',
72
+ name: 'core/template-part',
73
+ attributes: {
74
+ slug: 'aside',
75
+ theme: 'my-theme',
76
+ },
77
+ },
78
+ templatePart: {
79
+ id: 'my-theme//aside',
80
+ slug: 'aside',
81
+ theme: 'my-theme',
82
+ },
83
+ },
84
+ {
85
+ block: {
86
+ clientId: '6',
87
+ name: 'core/template-part',
88
+ attributes: {
89
+ slug: 'footer',
90
+ theme: 'my-theme',
91
+ },
92
+ },
93
+ templatePart: {
94
+ id: 'my-theme//footer',
95
+ slug: 'footer',
96
+ theme: 'my-theme',
97
+ },
98
+ },
99
+ ];
100
+
101
+ const SINGLE_TEMPLATE_PART_BLOCK = {
102
+ clientId: '1',
103
+ name: 'core/template-part',
104
+ innerBlocks: [],
105
+ attributes: {
106
+ slug: 'aside',
107
+ theme: 'my-theme',
108
+ },
109
+ };
110
+
111
+ const TEMPLATE_PARTS = [
112
+ {
113
+ id: 'my-theme//header',
114
+ slug: 'header',
115
+ theme: 'my-theme',
116
+ },
117
+ {
118
+ id: 'my-theme//aside',
119
+ slug: 'aside',
120
+ theme: 'my-theme',
121
+ },
122
+ {
123
+ id: 'my-theme//footer',
124
+ slug: 'footer',
125
+ theme: 'my-theme',
126
+ },
127
+ ];
128
+
129
+ describe( 'getFilteredTemplatePartBlocks', () => {
130
+ it( 'returns a flattened list of filtered template parts preserving a depth-first order', () => {
131
+ const flattenedFilteredTemplateParts = getFilteredTemplatePartBlocks(
132
+ NESTED_BLOCKS,
133
+ TEMPLATE_PARTS
134
+ );
135
+ expect( flattenedFilteredTemplateParts ).toEqual( FLATTENED_BLOCKS );
136
+ } );
137
+
138
+ it( 'returns a cached result when passed the same params', () => {
139
+ // Clear the cache and call the function twice.
140
+ getFilteredTemplatePartBlocks.clear();
141
+ getFilteredTemplatePartBlocks( NESTED_BLOCKS, TEMPLATE_PARTS );
142
+ expect(
143
+ getFilteredTemplatePartBlocks( NESTED_BLOCKS, TEMPLATE_PARTS )
144
+ ).toEqual( FLATTENED_BLOCKS );
145
+
146
+ // The function has been called twice with the same params, so the cache size should be 1.
147
+ /**
148
+ * TODO what should be done about this?
149
+ * Can it be tested another way?
150
+ * Is it necessary?
151
+ */
152
+ // const [ , , originalSize ] =
153
+ // getFilteredTemplatePartBlocks.getCache();
154
+ // expect( originalSize ).toBe( 1 );
155
+
156
+ // Call the function again, with different params.
157
+ expect(
158
+ getFilteredTemplatePartBlocks(
159
+ [ SINGLE_TEMPLATE_PART_BLOCK ],
160
+ TEMPLATE_PARTS
161
+ )
162
+ ).toEqual( [
163
+ {
164
+ block: {
165
+ clientId: '1',
166
+ name: 'core/template-part',
167
+ attributes: {
168
+ slug: 'aside',
169
+ theme: 'my-theme',
170
+ },
171
+ },
172
+ templatePart: {
173
+ id: 'my-theme//aside',
174
+ slug: 'aside',
175
+ theme: 'my-theme',
176
+ },
177
+ },
178
+ ] );
179
+
180
+ // The function has been called with different params, so the cache size should now be 2.
181
+ /**
182
+ * TODO what should be done about this?
183
+ * Can it be tested another way?
184
+ * Is it necessary?
185
+ */
186
+ // const [ , , finalSize ] = getFilteredTemplatePartBlocks.getCache();
187
+ // expect( finalSize ).toBe( 2 );
188
+ } );
189
+ } );
package/src/style.scss CHANGED
@@ -9,6 +9,7 @@
9
9
  @import "./components/inserter-sidebar/style.scss";
10
10
  @import "./components/list-view-sidebar/style.scss";
11
11
  @import "./components/post-author/style.scss";
12
+ @import "./components/post-card-panel/style.scss";
12
13
  @import "./components/post-excerpt/style.scss";
13
14
  @import "./components/post-featured-image/style.scss";
14
15
  @import "./components/post-format/style.scss";
@@ -29,4 +30,5 @@
29
30
  @import "./components/post-trash/style.scss";
30
31
  @import "./components/preview-dropdown/style.scss";
31
32
  @import "./components/table-of-contents/style.scss";
33
+ @import "./components/template-areas/style.scss";
32
34
  @import "./components/template-validation-notice/style.scss";