@wordpress/editor 13.33.0 → 13.34.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 (303) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +132 -20
  3. package/build/components/autosave-monitor/index.js +15 -12
  4. package/build/components/autosave-monitor/index.js.map +1 -1
  5. package/build/components/character-count/index.js +5 -0
  6. package/build/components/character-count/index.js.map +1 -1
  7. package/build/components/collapsible-block-toolbar/index.js +73 -0
  8. package/build/components/collapsible-block-toolbar/index.js.map +1 -0
  9. package/build/components/document-outline/check.js +8 -0
  10. package/build/components/document-outline/check.js.map +1 -1
  11. package/build/components/document-outline/index.js +11 -0
  12. package/build/components/document-outline/index.js.map +1 -1
  13. package/build/components/document-tools/index.js +0 -7
  14. package/build/components/document-tools/index.js.map +1 -1
  15. package/build/components/editor-canvas/edit-template-blocks-notification.js +1 -1
  16. package/build/components/editor-canvas/edit-template-blocks-notification.js.map +1 -1
  17. package/build/components/editor-history/redo.js +11 -0
  18. package/build/components/editor-history/redo.js.map +1 -1
  19. package/build/components/editor-history/undo.js +11 -0
  20. package/build/components/editor-history/undo.js.map +1 -1
  21. package/build/components/editor-snackbars/index.js +6 -0
  22. package/build/components/editor-snackbars/index.js.map +1 -1
  23. package/build/components/global-keyboard-shortcuts/index.js +7 -0
  24. package/build/components/global-keyboard-shortcuts/index.js.map +1 -1
  25. package/build/components/global-keyboard-shortcuts/register-shortcuts.js +34 -0
  26. package/build/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  27. package/build/components/inserter-sidebar/index.js +1 -4
  28. package/build/components/inserter-sidebar/index.js.map +1 -1
  29. package/build/components/keyboard-shortcut-help-modal/config.js +4 -0
  30. package/build/components/keyboard-shortcut-help-modal/config.js.map +1 -1
  31. package/build/components/list-view-sidebar/index.js +1 -1
  32. package/build/components/list-view-sidebar/index.js.map +1 -1
  33. package/build/components/more-menu/copy-content-menu-item.js +59 -0
  34. package/build/components/more-menu/copy-content-menu-item.js.map +1 -0
  35. package/build/components/more-menu/index.js +119 -0
  36. package/build/components/more-menu/index.js.map +1 -0
  37. package/build/components/more-menu/tools-more-menu-group.js +23 -0
  38. package/build/components/more-menu/tools-more-menu-group.js.map +1 -0
  39. package/build/components/more-menu/view-more-menu-group.js +24 -0
  40. package/build/components/more-menu/view-more-menu-group.js.map +1 -0
  41. package/build/components/page-attributes/check.js +8 -0
  42. package/build/components/page-attributes/check.js.map +1 -1
  43. package/build/components/page-attributes/order.js +7 -0
  44. package/build/components/page-attributes/order.js.map +1 -1
  45. package/build/components/page-attributes/panel.js +14 -8
  46. package/build/components/page-attributes/panel.js.map +1 -1
  47. package/build/components/page-attributes/parent.js +7 -0
  48. package/build/components/page-attributes/parent.js.map +1 -1
  49. package/build/components/plugin-post-publish-panel/index.js +1 -1
  50. package/build/components/plugin-post-publish-panel/index.js.map +1 -1
  51. package/build/components/plugin-pre-publish-panel/index.js +1 -1
  52. package/build/components/plugin-pre-publish-panel/index.js.map +1 -1
  53. package/build/components/plugin-sidebar/index.js +1 -1
  54. package/build/components/plugin-sidebar/index.js.map +1 -1
  55. package/build/components/post-actions/actions.js +100 -5
  56. package/build/components/post-actions/actions.js.map +1 -1
  57. package/build/components/post-actions/index.js +30 -7
  58. package/build/components/post-actions/index.js.map +1 -1
  59. package/build/components/post-author/check.js +9 -0
  60. package/build/components/post-author/check.js.map +1 -1
  61. package/build/components/post-author/index.js +6 -0
  62. package/build/components/post-author/index.js.map +1 -1
  63. package/build/components/post-author/panel.js +5 -0
  64. package/build/components/post-author/panel.js.map +1 -1
  65. package/build/components/post-card-panel/index.js +14 -49
  66. package/build/components/post-card-panel/index.js.map +1 -1
  67. package/build/components/post-content-information/index.js +72 -0
  68. package/build/components/post-content-information/index.js.map +1 -0
  69. package/build/components/post-discussion/panel.js +9 -7
  70. package/build/components/post-discussion/panel.js.map +1 -1
  71. package/build/components/post-excerpt/check.js +0 -19
  72. package/build/components/post-excerpt/check.js.map +1 -1
  73. package/build/components/post-excerpt/index.js +52 -12
  74. package/build/components/post-excerpt/index.js.map +1 -1
  75. package/build/components/post-excerpt/panel.js +118 -7
  76. package/build/components/post-excerpt/panel.js.map +1 -1
  77. package/build/components/post-excerpt/plugin.js +2 -2
  78. package/build/components/post-excerpt/plugin.js.map +1 -1
  79. package/build/components/post-featured-image/index.js +1 -1
  80. package/build/components/post-featured-image/index.js.map +1 -1
  81. package/build/components/post-last-edited-panel/index.js +33 -0
  82. package/build/components/post-last-edited-panel/index.js.map +1 -0
  83. package/build/components/post-publish-button/post-publish-button-or-toggle.js +100 -0
  84. package/build/components/post-publish-button/post-publish-button-or-toggle.js.map +1 -0
  85. package/build/components/post-schedule/panel.js +1 -1
  86. package/build/components/post-schedule/panel.js.map +1 -1
  87. package/build/components/post-status/index.js +9 -9
  88. package/build/components/post-status/index.js.map +1 -1
  89. package/build/components/post-template/block-theme.js +2 -2
  90. package/build/components/post-template/block-theme.js.map +1 -1
  91. package/build/components/post-template/classic-theme.js +0 -1
  92. package/build/components/post-template/classic-theme.js.map +1 -1
  93. package/build/components/post-template/swap-template-button.js +2 -5
  94. package/build/components/post-template/swap-template-button.js.map +1 -1
  95. package/build/components/post-url/index.js +2 -1
  96. package/build/components/post-url/index.js.map +1 -1
  97. package/build/components/post-url/panel.js +1 -1
  98. package/build/components/post-url/panel.js.map +1 -1
  99. package/build/components/preferences-modal/enable-publish-sidebar.js +34 -0
  100. package/build/components/preferences-modal/enable-publish-sidebar.js.map +1 -0
  101. package/build/components/preferences-modal/index.js +8 -2
  102. package/build/components/preferences-modal/index.js.map +1 -1
  103. package/build/components/preview-dropdown/index.js +3 -1
  104. package/build/components/preview-dropdown/index.js.map +1 -1
  105. package/build/components/provider/disable-non-page-content-blocks.js +23 -16
  106. package/build/components/provider/disable-non-page-content-blocks.js.map +1 -1
  107. package/build/components/save-publish-panels/index.js +89 -0
  108. package/build/components/save-publish-panels/index.js.map +1 -0
  109. package/build/components/start-page-options/index.js +5 -1
  110. package/build/components/start-page-options/index.js.map +1 -1
  111. package/build/private-apis.js +18 -2
  112. package/build/private-apis.js.map +1 -1
  113. package/build/store/selectors.js +3 -1
  114. package/build/store/selectors.js.map +1 -1
  115. package/build-module/components/autosave-monitor/index.js +15 -13
  116. package/build-module/components/autosave-monitor/index.js.map +1 -1
  117. package/build-module/components/character-count/index.js +6 -0
  118. package/build-module/components/character-count/index.js.map +1 -1
  119. package/build-module/components/collapsible-block-toolbar/index.js +65 -0
  120. package/build-module/components/collapsible-block-toolbar/index.js.map +1 -0
  121. package/build-module/components/document-outline/check.js +9 -0
  122. package/build-module/components/document-outline/check.js.map +1 -1
  123. package/build-module/components/document-outline/index.js +11 -0
  124. package/build-module/components/document-outline/index.js.map +1 -1
  125. package/build-module/components/document-tools/index.js +1 -8
  126. package/build-module/components/document-tools/index.js.map +1 -1
  127. package/build-module/components/editor-canvas/edit-template-blocks-notification.js +1 -1
  128. package/build-module/components/editor-canvas/edit-template-blocks-notification.js.map +1 -1
  129. package/build-module/components/editor-history/redo.js +11 -0
  130. package/build-module/components/editor-history/redo.js.map +1 -1
  131. package/build-module/components/editor-history/undo.js +11 -0
  132. package/build-module/components/editor-history/undo.js.map +1 -1
  133. package/build-module/components/editor-snackbars/index.js +6 -0
  134. package/build-module/components/editor-snackbars/index.js.map +1 -1
  135. package/build-module/components/global-keyboard-shortcuts/index.js +8 -0
  136. package/build-module/components/global-keyboard-shortcuts/index.js.map +1 -1
  137. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js +35 -0
  138. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  139. package/build-module/components/inserter-sidebar/index.js +2 -5
  140. package/build-module/components/inserter-sidebar/index.js.map +1 -1
  141. package/build-module/components/keyboard-shortcut-help-modal/config.js +4 -0
  142. package/build-module/components/keyboard-shortcut-help-modal/config.js.map +1 -1
  143. package/build-module/components/list-view-sidebar/index.js +1 -1
  144. package/build-module/components/list-view-sidebar/index.js.map +1 -1
  145. package/build-module/components/more-menu/copy-content-menu-item.js +52 -0
  146. package/build-module/components/more-menu/copy-content-menu-item.js.map +1 -0
  147. package/build-module/components/more-menu/index.js +111 -0
  148. package/build-module/components/more-menu/index.js.map +1 -0
  149. package/build-module/components/more-menu/tools-more-menu-group.js +16 -0
  150. package/build-module/components/more-menu/tools-more-menu-group.js.map +1 -0
  151. package/build-module/components/more-menu/view-more-menu-group.js +17 -0
  152. package/build-module/components/more-menu/view-more-menu-group.js.map +1 -0
  153. package/build-module/components/page-attributes/check.js +9 -0
  154. package/build-module/components/page-attributes/check.js.map +1 -1
  155. package/build-module/components/page-attributes/order.js +7 -0
  156. package/build-module/components/page-attributes/order.js.map +1 -1
  157. package/build-module/components/page-attributes/panel.js +13 -6
  158. package/build-module/components/page-attributes/panel.js.map +1 -1
  159. package/build-module/components/page-attributes/parent.js +7 -0
  160. package/build-module/components/page-attributes/parent.js.map +1 -1
  161. package/build-module/components/plugin-post-publish-panel/index.js +1 -1
  162. package/build-module/components/plugin-post-publish-panel/index.js.map +1 -1
  163. package/build-module/components/plugin-pre-publish-panel/index.js +1 -1
  164. package/build-module/components/plugin-pre-publish-panel/index.js.map +1 -1
  165. package/build-module/components/plugin-sidebar/index.js +1 -1
  166. package/build-module/components/plugin-sidebar/index.js.map +1 -1
  167. package/build-module/components/post-actions/actions.js +100 -6
  168. package/build-module/components/post-actions/actions.js.map +1 -1
  169. package/build-module/components/post-actions/index.js +31 -8
  170. package/build-module/components/post-actions/index.js.map +1 -1
  171. package/build-module/components/post-author/check.js +10 -0
  172. package/build-module/components/post-author/check.js.map +1 -1
  173. package/build-module/components/post-author/index.js +6 -0
  174. package/build-module/components/post-author/index.js.map +1 -1
  175. package/build-module/components/post-author/panel.js +6 -0
  176. package/build-module/components/post-author/panel.js.map +1 -1
  177. package/build-module/components/post-card-panel/index.js +16 -51
  178. package/build-module/components/post-card-panel/index.js.map +1 -1
  179. package/build-module/components/post-content-information/index.js +66 -0
  180. package/build-module/components/post-content-information/index.js.map +1 -0
  181. package/build-module/components/post-discussion/panel.js +8 -6
  182. package/build-module/components/post-discussion/panel.js.map +1 -1
  183. package/build-module/components/post-excerpt/check.js +0 -19
  184. package/build-module/components/post-excerpt/check.js.map +1 -1
  185. package/build-module/components/post-excerpt/index.js +52 -11
  186. package/build-module/components/post-excerpt/index.js.map +1 -1
  187. package/build-module/components/post-excerpt/panel.js +118 -8
  188. package/build-module/components/post-excerpt/panel.js.map +1 -1
  189. package/build-module/components/post-excerpt/plugin.js +2 -2
  190. package/build-module/components/post-excerpt/plugin.js.map +1 -1
  191. package/build-module/components/post-featured-image/index.js +1 -1
  192. package/build-module/components/post-featured-image/index.js.map +1 -1
  193. package/build-module/components/post-last-edited-panel/index.js +26 -0
  194. package/build-module/components/post-last-edited-panel/index.js.map +1 -0
  195. package/build-module/components/post-publish-button/post-publish-button-or-toggle.js +91 -0
  196. package/build-module/components/post-publish-button/post-publish-button-or-toggle.js.map +1 -0
  197. package/build-module/components/post-schedule/panel.js +1 -1
  198. package/build-module/components/post-schedule/panel.js.map +1 -1
  199. package/build-module/components/post-status/index.js +9 -9
  200. package/build-module/components/post-status/index.js.map +1 -1
  201. package/build-module/components/post-template/block-theme.js +2 -2
  202. package/build-module/components/post-template/block-theme.js.map +1 -1
  203. package/build-module/components/post-template/classic-theme.js +0 -1
  204. package/build-module/components/post-template/classic-theme.js.map +1 -1
  205. package/build-module/components/post-template/swap-template-button.js +3 -6
  206. package/build-module/components/post-template/swap-template-button.js.map +1 -1
  207. package/build-module/components/post-url/index.js +2 -1
  208. package/build-module/components/post-url/index.js.map +1 -1
  209. package/build-module/components/post-url/panel.js +1 -1
  210. package/build-module/components/post-url/panel.js.map +1 -1
  211. package/build-module/components/preferences-modal/enable-publish-sidebar.js +27 -0
  212. package/build-module/components/preferences-modal/enable-publish-sidebar.js.map +1 -0
  213. package/build-module/components/preferences-modal/index.js +8 -2
  214. package/build-module/components/preferences-modal/index.js.map +1 -1
  215. package/build-module/components/preview-dropdown/index.js +3 -1
  216. package/build-module/components/preview-dropdown/index.js.map +1 -1
  217. package/build-module/components/provider/disable-non-page-content-blocks.js +24 -17
  218. package/build-module/components/provider/disable-non-page-content-blocks.js.map +1 -1
  219. package/build-module/components/save-publish-panels/index.js +80 -0
  220. package/build-module/components/save-publish-panels/index.js.map +1 -0
  221. package/build-module/components/start-page-options/index.js +5 -1
  222. package/build-module/components/start-page-options/index.js.map +1 -1
  223. package/build-module/private-apis.js +18 -2
  224. package/build-module/private-apis.js.map +1 -1
  225. package/build-module/store/selectors.js +3 -1
  226. package/build-module/store/selectors.js.map +1 -1
  227. package/build-style/style-rtl.css +207 -30
  228. package/build-style/style.css +207 -30
  229. package/package.json +35 -35
  230. package/src/components/autosave-monitor/index.js +15 -12
  231. package/src/components/character-count/index.js +5 -0
  232. package/src/components/collapsible-block-toolbar/index.js +77 -0
  233. package/src/components/collapsible-block-toolbar/style.scss +80 -0
  234. package/src/components/document-outline/check.js +8 -0
  235. package/src/components/document-outline/index.js +10 -0
  236. package/src/components/document-tools/index.js +0 -5
  237. package/src/components/editor-canvas/edit-template-blocks-notification.js +1 -1
  238. package/src/components/editor-history/redo.js +10 -0
  239. package/src/components/editor-history/undo.js +10 -0
  240. package/src/components/editor-snackbars/index.js +5 -0
  241. package/src/components/entities-saved-states/style.scss +7 -0
  242. package/src/components/global-keyboard-shortcuts/index.js +7 -0
  243. package/src/components/global-keyboard-shortcuts/register-shortcuts.js +41 -0
  244. package/src/components/inserter-sidebar/index.js +2 -5
  245. package/src/components/keyboard-shortcut-help-modal/config.js +10 -1
  246. package/src/components/list-view-sidebar/index.js +1 -1
  247. package/src/components/list-view-sidebar/style.scss +2 -2
  248. package/src/components/more-menu/copy-content-menu-item.js +51 -0
  249. package/src/components/more-menu/index.js +158 -0
  250. package/src/components/more-menu/tools-more-menu-group.js +11 -0
  251. package/src/components/more-menu/view-more-menu-group.js +13 -0
  252. package/src/components/page-attributes/check.js +8 -0
  253. package/src/components/page-attributes/order.js +6 -0
  254. package/src/components/page-attributes/panel.js +21 -17
  255. package/src/components/page-attributes/parent.js +6 -0
  256. package/src/components/plugin-post-publish-panel/index.js +1 -1
  257. package/src/components/plugin-pre-publish-panel/index.js +1 -1
  258. package/src/components/plugin-sidebar/index.js +1 -1
  259. package/src/components/post-actions/actions.js +120 -5
  260. package/src/components/post-actions/index.js +41 -7
  261. package/src/components/post-author/check.js +9 -0
  262. package/src/components/post-author/index.js +5 -0
  263. package/src/components/post-author/panel.js +5 -0
  264. package/src/components/post-card-panel/index.js +27 -82
  265. package/src/components/post-card-panel/style.scss +2 -6
  266. package/src/components/post-content-information/index.js +83 -0
  267. package/src/components/post-content-information/style.scss +6 -0
  268. package/src/components/post-discussion/panel.js +24 -20
  269. package/src/components/post-excerpt/check.js +0 -18
  270. package/src/components/post-excerpt/index.js +66 -15
  271. package/src/components/post-excerpt/panel.js +196 -19
  272. package/src/components/post-excerpt/plugin.js +2 -2
  273. package/src/components/post-excerpt/style.scss +24 -0
  274. package/src/components/post-featured-image/index.js +1 -1
  275. package/src/components/post-featured-image/style.scss +2 -8
  276. package/src/components/post-last-edited-panel/index.js +35 -0
  277. package/src/components/post-last-edited-panel/style.scss +6 -0
  278. package/src/components/post-panel-row/style.scss +3 -3
  279. package/src/components/post-publish-button/post-publish-button-or-toggle.js +102 -0
  280. package/src/components/post-publish-button/test/post-publish-button-or-toggle.js +63 -0
  281. package/src/components/post-publish-panel/style.scss +43 -0
  282. package/src/components/post-publish-panel/test/__snapshots__/index.js.snap +225 -75
  283. package/src/components/post-schedule/panel.js +1 -1
  284. package/src/components/post-schedule/style.scss +2 -4
  285. package/src/components/post-status/index.js +84 -76
  286. package/src/components/post-status/style.scss +0 -1
  287. package/src/components/post-template/block-theme.js +2 -2
  288. package/src/components/post-template/classic-theme.js +0 -1
  289. package/src/components/post-template/swap-template-button.js +3 -6
  290. package/src/components/post-text-editor/style.scss +1 -1
  291. package/src/components/post-title/style.scss +1 -1
  292. package/src/components/post-url/index.js +1 -0
  293. package/src/components/post-url/panel.js +1 -1
  294. package/src/components/preferences-modal/enable-publish-sidebar.js +28 -0
  295. package/src/components/preferences-modal/index.js +14 -0
  296. package/src/components/preview-dropdown/index.js +3 -1
  297. package/src/components/provider/disable-non-page-content-blocks.js +23 -21
  298. package/src/components/save-publish-panels/index.js +96 -0
  299. package/src/components/save-publish-panels/style.scss +36 -0
  300. package/src/components/start-page-options/index.js +6 -1
  301. package/src/private-apis.js +18 -2
  302. package/src/store/selectors.js +3 -1
  303. package/src/style.scss +4 -0
@@ -40,6 +40,12 @@ export const getItemPriority = ( name, searchValue ) => {
40
40
  return Infinity;
41
41
  };
42
42
 
43
+ /**
44
+ * Renders the Page Attributes Parent component. A dropdown menu in an editor interface
45
+ * for selecting the parent page of a given page.
46
+ *
47
+ * @return {Component|null} The component to be rendered. Return null if post type is not hierarchical.
48
+ */
43
49
  export function PageAttributesParent() {
44
50
  const { editPost } = useDispatch( editorStore );
45
51
  const [ fieldValue, setFieldValue ] = useState( false );
@@ -21,7 +21,7 @@ const { Fill, Slot } = createSlotFill( 'PluginPostPublishPanel' );
21
21
  * ```jsx
22
22
  * // Using ESNext syntax
23
23
  * import { __ } from '@wordpress/i18n';
24
- * import { PluginPostPublishPanel } from '@wordpress/edit-post';
24
+ * import { PluginPostPublishPanel } from '@wordpress/editor';
25
25
  *
26
26
  * const MyPluginPostPublishPanel = () => (
27
27
  * <PluginPostPublishPanel
@@ -24,7 +24,7 @@ const { Fill, Slot } = createSlotFill( 'PluginPrePublishPanel' );
24
24
  * ```jsx
25
25
  * // Using ESNext syntax
26
26
  * import { __ } from '@wordpress/i18n';
27
- * import { PluginPrePublishPanel } from '@wordpress/edit-post';
27
+ * import { PluginPrePublishPanel } from '@wordpress/editor';
28
28
  *
29
29
  * const MyPluginPrePublishPanel = () => (
30
30
  * <PluginPrePublishPanel
@@ -60,7 +60,7 @@ import { store as editorStore } from '../../store';
60
60
  * // Using ESNext syntax
61
61
  * import { __ } from '@wordpress/i18n';
62
62
  * import { PanelBody } from '@wordpress/components';
63
- * import { PluginSidebar } from '@wordpress/edit-post';
63
+ * import { PluginSidebar } from '@wordpress/editor';
64
64
  * import { more } from '@wordpress/icons';
65
65
  *
66
66
  * const MyPluginSidebar = () => (
@@ -6,7 +6,7 @@ import { addQueryArgs } from '@wordpress/url';
6
6
  import { useDispatch } from '@wordpress/data';
7
7
  import { decodeEntities } from '@wordpress/html-entities';
8
8
  import { store as coreStore } from '@wordpress/core-data';
9
- import { __, _n, sprintf } from '@wordpress/i18n';
9
+ import { __, _n, sprintf, _x } from '@wordpress/i18n';
10
10
  import { store as noticesStore } from '@wordpress/notices';
11
11
  import { useMemo, useState } from '@wordpress/element';
12
12
 
@@ -104,7 +104,7 @@ const trashPostAction = {
104
104
  }
105
105
  createSuccessNotice( successMessage, {
106
106
  type: 'snackbar',
107
- id: 'edit-site-page-trashed',
107
+ id: 'trash-post-action',
108
108
  } );
109
109
  } else {
110
110
  // If there was at lease one failure.
@@ -217,7 +217,7 @@ function usePermanentlyDeletePostAction() {
217
217
  }
218
218
  createSuccessNotice( successMessage, {
219
219
  type: 'snackbar',
220
- id: 'edit-site-post-permanently-deleted',
220
+ id: 'permanently-delete-post-action',
221
221
  } );
222
222
  if ( onActionPerformed ) {
223
223
  onActionPerformed( posts );
@@ -328,7 +328,7 @@ function useRestorePostAction() {
328
328
  ),
329
329
  {
330
330
  type: 'snackbar',
331
- id: 'edit-site-post-restored',
331
+ id: 'restore-post-action',
332
332
  }
333
333
  );
334
334
  if ( onActionPerformed ) {
@@ -500,6 +500,120 @@ const renamePostAction = {
500
500
  },
501
501
  };
502
502
 
503
+ export const duplicatePostAction = {
504
+ id: 'duplicate-post',
505
+ label: _x( 'Duplicate', 'action label' ),
506
+ isEligible( { status } ) {
507
+ return status !== 'trash';
508
+ },
509
+ RenderModal: ( { items, closeModal, onActionPerformed } ) => {
510
+ const [ item ] = items;
511
+ const [ isCreatingPage, setIsCreatingPage ] = useState( false );
512
+ const [ title, setTitle ] = useState(
513
+ sprintf(
514
+ /* translators: %s: Existing item title */
515
+ __( '%s (Copy)' ),
516
+ getItemTitle( item )
517
+ )
518
+ );
519
+
520
+ const { saveEntityRecord } = useDispatch( coreStore );
521
+ const { createSuccessNotice, createErrorNotice } =
522
+ useDispatch( noticesStore );
523
+
524
+ async function createPage( event ) {
525
+ event.preventDefault();
526
+
527
+ if ( isCreatingPage ) {
528
+ return;
529
+ }
530
+ setIsCreatingPage( true );
531
+ try {
532
+ const newItem = await saveEntityRecord(
533
+ 'postType',
534
+ item.type,
535
+ {
536
+ status: 'draft',
537
+ title,
538
+ slug: title || __( 'No title' ),
539
+ author: item.author,
540
+ comment_status: item.comment_status,
541
+ content:
542
+ typeof item.content === 'string'
543
+ ? item.content
544
+ : item.content.raw,
545
+ excerpt: item.excerpt.raw,
546
+ meta: item.meta,
547
+ parent: item.parent,
548
+ password: item.password,
549
+ template: item.template,
550
+ format: item.format,
551
+ featured_media: item.featured_media,
552
+ menu_order: item.menu_order,
553
+ ping_status: item.ping_status,
554
+ categories: item.categories,
555
+ tags: item.tags,
556
+ },
557
+ { throwOnError: true }
558
+ );
559
+
560
+ createSuccessNotice(
561
+ sprintf(
562
+ // translators: %s: Title of the created template e.g: "Category".
563
+ __( '"%s" successfully created.' ),
564
+ newItem.title?.rendered || title
565
+ ),
566
+ {
567
+ id: 'duplicate-post-action',
568
+ type: 'snackbar',
569
+ }
570
+ );
571
+
572
+ if ( onActionPerformed ) {
573
+ onActionPerformed( [ newItem ] );
574
+ }
575
+ } catch ( error ) {
576
+ const errorMessage =
577
+ error.message && error.code !== 'unknown_error'
578
+ ? error.message
579
+ : __( 'An error occurred while duplicating the page.' );
580
+
581
+ createErrorNotice( errorMessage, {
582
+ type: 'snackbar',
583
+ } );
584
+ } finally {
585
+ setIsCreatingPage( false );
586
+ closeModal();
587
+ }
588
+ }
589
+ return (
590
+ <form onSubmit={ createPage }>
591
+ <VStack spacing={ 3 }>
592
+ <TextControl
593
+ label={ __( 'Title' ) }
594
+ onChange={ setTitle }
595
+ placeholder={ __( 'No title' ) }
596
+ value={ title }
597
+ />
598
+ <HStack spacing={ 2 } justify="end">
599
+ <Button variant="tertiary" onClick={ closeModal }>
600
+ { __( 'Cancel' ) }
601
+ </Button>
602
+ <Button
603
+ variant="primary"
604
+ type="submit"
605
+ isBusy={ isCreatingPage }
606
+ aria-disabled={ isCreatingPage }
607
+ >
608
+ { _x( 'Duplicate', 'action label' ) }
609
+ </Button>
610
+ </HStack>
611
+ </VStack>
612
+ </form>
613
+ );
614
+ },
615
+ };
616
+
503
617
  const resetTemplateAction = {
504
618
  id: 'reset-template',
505
619
  label: __( 'Reset' ),
@@ -538,7 +652,7 @@ const resetTemplateAction = {
538
652
  ),
539
653
  {
540
654
  type: 'snackbar',
541
- id: 'edit-site-template-reverted',
655
+ id: 'revert-template-action',
542
656
  }
543
657
  );
544
658
  } catch ( error ) {
@@ -782,6 +896,7 @@ export function usePostActions( onActionPerformed, actionIds = null ) {
782
896
  deleteTemplateAction,
783
897
  permanentlyDeletePostAction,
784
898
  postRevisionsAction,
899
+ duplicatePostAction,
785
900
  renamePostAction,
786
901
  renameTemplateAction,
787
902
  trashPostAction,
@@ -2,7 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { useSelect } from '@wordpress/data';
5
- import { useState } from '@wordpress/element';
5
+ import { useState, useMemo } from '@wordpress/element';
6
6
  import { __ } from '@wordpress/i18n';
7
7
  import {
8
8
  privateApis as componentsPrivateApis,
@@ -31,14 +31,25 @@ const {
31
31
  kebabCase,
32
32
  } = unlock( componentsPrivateApis );
33
33
 
34
- const POST_ACTIONS_WHILE_EDITING = [
34
+ let POST_ACTIONS_WHILE_EDITING = [
35
35
  'view-post',
36
36
  'view-post-revisions',
37
37
  'rename-post',
38
38
  'move-to-trash',
39
39
  ];
40
40
 
41
+ if ( process.env.IS_GUTENBERG_PLUGIN ) {
42
+ POST_ACTIONS_WHILE_EDITING = [
43
+ 'view-post',
44
+ 'view-post-revisions',
45
+ 'duplicate-post',
46
+ 'rename-post',
47
+ 'move-to-trash',
48
+ ];
49
+ }
50
+
41
51
  export default function PostActions( { onActionPerformed, buttonProps } ) {
52
+ const [ isActionsMenuOpen, setIsActionsMenuOpen ] = useState( false );
42
53
  const { postType, item } = useSelect( ( select ) => {
43
54
  const { getCurrentPostType, getCurrentPost } = select( editorStore );
44
55
  return {
@@ -46,11 +57,17 @@ export default function PostActions( { onActionPerformed, buttonProps } ) {
46
57
  item: getCurrentPost(),
47
58
  };
48
59
  } );
49
- const actions = usePostActions(
60
+ const allActions = usePostActions(
50
61
  onActionPerformed,
51
62
  POST_ACTIONS_WHILE_EDITING
52
63
  );
53
64
 
65
+ const actions = useMemo( () => {
66
+ return allActions.filter( ( action ) => {
67
+ return ! action.isEligible || action.isEligible( item );
68
+ } );
69
+ }, [ allActions, item ] );
70
+
54
71
  if (
55
72
  [
56
73
  TEMPLATE_POST_TYPE,
@@ -62,6 +79,7 @@ export default function PostActions( { onActionPerformed, buttonProps } ) {
62
79
  }
63
80
  return (
64
81
  <DropdownMenu
82
+ open={ isActionsMenuOpen }
65
83
  trigger={
66
84
  <Button
67
85
  size="small"
@@ -69,12 +87,22 @@ export default function PostActions( { onActionPerformed, buttonProps } ) {
69
87
  label={ __( 'Actions' ) }
70
88
  disabled={ ! actions.length }
71
89
  className="editor-all-actions-button"
90
+ onClick={ () =>
91
+ setIsActionsMenuOpen( ! isActionsMenuOpen )
92
+ }
72
93
  { ...buttonProps }
73
94
  />
74
95
  }
96
+ onOpenChange={ setIsActionsMenuOpen }
75
97
  placement="bottom-end"
76
98
  >
77
- <ActionsDropdownMenuGroup actions={ actions } item={ item } />
99
+ <ActionsDropdownMenuGroup
100
+ actions={ actions }
101
+ item={ item }
102
+ onClose={ () => {
103
+ setIsActionsMenuOpen( false );
104
+ } }
105
+ />
78
106
  </DropdownMenu>
79
107
  );
80
108
  }
@@ -97,7 +125,8 @@ function DropdownMenuItemTrigger( { action, onClick } ) {
97
125
  }
98
126
 
99
127
  // Copied as is from packages/dataviews/src/item-actions.js
100
- function ActionWithModal( { action, item, ActionTrigger } ) {
128
+ // With an added onClose prop.
129
+ function ActionWithModal( { action, item, ActionTrigger, onClose } ) {
101
130
  const [ isModalOpen, setIsModalOpen ] = useState( false );
102
131
  const actionTriggerProps = {
103
132
  action,
@@ -120,7 +149,10 @@ function ActionWithModal( { action, item, ActionTrigger } ) {
120
149
  >
121
150
  <RenderModal
122
151
  items={ [ item ] }
123
- closeModal={ () => setIsModalOpen( false ) }
152
+ closeModal={ () => {
153
+ setIsModalOpen( false );
154
+ onClose();
155
+ } }
124
156
  />
125
157
  </Modal>
126
158
  ) }
@@ -129,7 +161,8 @@ function ActionWithModal( { action, item, ActionTrigger } ) {
129
161
  }
130
162
 
131
163
  // Copied as is from packages/dataviews/src/item-actions.js
132
- function ActionsDropdownMenuGroup( { actions, item } ) {
164
+ // With an added onClose prop.
165
+ function ActionsDropdownMenuGroup( { actions, item, onClose } ) {
133
166
  return (
134
167
  <DropdownMenuGroup>
135
168
  { actions.map( ( action ) => {
@@ -140,6 +173,7 @@ function ActionsDropdownMenuGroup( { actions, item } ) {
140
173
  action={ action }
141
174
  item={ item }
142
175
  ActionTrigger={ DropdownMenuItemTrigger }
176
+ onClose={ onClose }
143
177
  />
144
178
  );
145
179
  }
@@ -11,6 +11,15 @@ import PostTypeSupportCheck from '../post-type-support-check';
11
11
  import { store as editorStore } from '../../store';
12
12
  import { AUTHORS_QUERY } from './constants';
13
13
 
14
+ /**
15
+ * Wrapper component that renders its children only if the post type supports the author.
16
+ *
17
+ * @param {Object} props The component props.
18
+ * @param {Element} props.children Children to be rendered.
19
+ *
20
+ * @return {Component|null} The component to be rendered. Return `null` if the post type doesn't
21
+ * supports the author or if there are no authors available.
22
+ */
14
23
  export default function PostAuthorCheck( { children } ) {
15
24
  const { hasAssignAuthorAction, hasAuthors } = useSelect( ( select ) => {
16
25
  const post = select( editorStore ).getCurrentPost();
@@ -13,6 +13,11 @@ import { AUTHORS_QUERY } from './constants';
13
13
 
14
14
  const minimumUsersForCombobox = 25;
15
15
 
16
+ /**
17
+ * Renders the component for selecting the post author.
18
+ *
19
+ * @return {Component} The component to be rendered.
20
+ */
16
21
  function PostAuthor() {
17
22
  const showCombobox = useSelect( ( select ) => {
18
23
  const authors = select( coreStore ).getUsers( AUTHORS_QUERY );
@@ -5,6 +5,11 @@ import PostAuthorCheck from './check';
5
5
  import PostAuthorForm from './index';
6
6
  import PostPanelRow from '../post-panel-row';
7
7
 
8
+ /**
9
+ * Renders the Post Author Panel component.
10
+ *
11
+ * @return {Component} The component to be rendered.
12
+ */
8
13
  export function PostAuthor() {
9
14
  return (
10
15
  <PostAuthorCheck>
@@ -15,11 +15,8 @@ import {
15
15
  } from '@wordpress/components';
16
16
  import { store as coreStore } from '@wordpress/core-data';
17
17
  import { useSelect } from '@wordpress/data';
18
- import { __, _x, _n, sprintf } from '@wordpress/i18n';
19
- import { humanTimeDiff } from '@wordpress/date';
18
+ import { __ } from '@wordpress/i18n';
20
19
  import { decodeEntities } from '@wordpress/html-entities';
21
- import { count as wordCount } from '@wordpress/wordcount';
22
- import { useMemo } from '@wordpress/element';
23
20
 
24
21
  /**
25
22
  * Internal dependencies
@@ -28,54 +25,55 @@ import { store as editorStore } from '../../store';
28
25
  import {
29
26
  TEMPLATE_POST_TYPE,
30
27
  TEMPLATE_PART_POST_TYPE,
28
+ PATTERN_POST_TYPE,
31
29
  } from '../../store/constants';
30
+ import { PrivatePostExcerptPanel } from '../post-excerpt/panel';
31
+ import PostLastEditedPanel from '../post-last-edited-panel';
32
32
  import { unlock } from '../../lock-unlock';
33
33
  import TemplateAreas from '../template-areas';
34
34
 
35
35
  export default function PostCardPanel( { className, actions } ) {
36
- const { modified, title, templateInfo, icon, postType, isPostsPage } =
37
- useSelect( ( select ) => {
36
+ const { title, showPostContentPanels, icon, postType } = useSelect(
37
+ ( select ) => {
38
38
  const {
39
39
  getEditedPostAttribute,
40
40
  getCurrentPostType,
41
41
  getCurrentPostId,
42
42
  __experimentalGetTemplateInfo,
43
43
  } = select( editorStore );
44
- const { getEditedEntityRecord, getEntityRecord } =
45
- select( coreStore );
46
- const siteSettings = getEntityRecord( 'root', 'site' );
44
+ const { getEditedEntityRecord } = select( coreStore );
47
45
  const _type = getCurrentPostType();
48
46
  const _id = getCurrentPostId();
49
47
  const _record = getEditedEntityRecord( 'postType', _type, _id );
50
- const _templateInfo = __experimentalGetTemplateInfo( _record );
48
+ const _templateInfo =
49
+ [ TEMPLATE_POST_TYPE, TEMPLATE_PART_POST_TYPE ].includes(
50
+ _type
51
+ ) && __experimentalGetTemplateInfo( _record );
51
52
  return {
52
53
  title:
53
54
  _templateInfo?.title || getEditedPostAttribute( 'title' ),
54
- modified: getEditedPostAttribute( 'modified' ),
55
55
  id: _id,
56
56
  postType: _type,
57
- templateInfo: _templateInfo,
58
57
  icon: unlock( select( editorStore ) ).getPostIcon( _type, {
59
58
  area: _record?.area,
60
59
  } ),
61
- isPostsPage: +_id === siteSettings?.page_for_posts,
60
+ // Post excerpt panel and Last Edited info are rendered in different place depending on the post type.
61
+ // So we cannot make this check inside the PostExcerpt or PostLastEditedPanel component based on the current edited entity.
62
+ showPostContentPanels: [
63
+ TEMPLATE_POST_TYPE,
64
+ TEMPLATE_PART_POST_TYPE,
65
+ PATTERN_POST_TYPE,
66
+ ].includes( _type ),
62
67
  };
63
- }, [] );
64
- const description = templateInfo?.description;
65
- const lastEditedText =
66
- modified &&
67
- sprintf(
68
- // translators: %s: Human-readable time difference, e.g. "2 days ago".
69
- __( 'Last edited %s.' ),
70
- humanTimeDiff( modified )
71
- );
72
- const showPostContentInfo =
73
- ! isPostsPage &&
74
- ! [ TEMPLATE_POST_TYPE, TEMPLATE_PART_POST_TYPE ].includes( postType );
68
+ },
69
+ []
70
+ );
75
71
  return (
76
72
  <PanelBody>
77
73
  <div
78
- className={ classnames( 'editor-post-card-panel', className ) }
74
+ className={ classnames( 'editor-post-card-panel', className, {
75
+ 'has-description': showPostContentPanels,
76
+ } ) }
79
77
  >
80
78
  <HStack
81
79
  spacing={ 2 }
@@ -98,18 +96,13 @@ export default function PostCardPanel( { className, actions } ) {
98
96
  { actions }
99
97
  </HStack>
100
98
  <VStack className="editor-post-card-panel__content">
101
- { ( description ||
102
- lastEditedText ||
103
- showPostContentInfo ) && (
99
+ { showPostContentPanels && (
104
100
  <VStack
105
101
  className="editor-post-card-panel__description"
106
102
  spacing={ 2 }
107
103
  >
108
- { description && <Text>{ description }</Text> }
109
- { showPostContentInfo && <PostContentInfo /> }
110
- { lastEditedText && (
111
- <Text>{ lastEditedText }</Text>
112
- ) }
104
+ <PrivatePostExcerptPanel />
105
+ <PostLastEditedPanel />
113
106
  </VStack>
114
107
  ) }
115
108
  { postType === TEMPLATE_POST_TYPE && <TemplateAreas /> }
@@ -118,51 +111,3 @@ export default function PostCardPanel( { className, actions } ) {
118
111
  </PanelBody>
119
112
  );
120
113
  }
121
-
122
- // Taken from packages/editor/src/components/time-to-read/index.js.
123
- const AVERAGE_READING_RATE = 189;
124
-
125
- // This component renders the wordcount and reading time for the post.
126
- function PostContentInfo() {
127
- const postContent = useSelect(
128
- ( select ) => select( editorStore ).getEditedPostAttribute( 'content' ),
129
- []
130
- );
131
- /*
132
- * translators: If your word count is based on single characters (e.g. East Asian characters),
133
- * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
134
- * Do not translate into your own language.
135
- */
136
- const wordCountType = _x( 'words', 'Word count type. Do not translate!' );
137
- const wordsCounted = useMemo(
138
- () => ( postContent ? wordCount( postContent, wordCountType ) : 0 ),
139
- [ postContent, wordCountType ]
140
- );
141
- if ( ! wordsCounted ) {
142
- return null;
143
- }
144
- const readingTime = Math.round( wordsCounted / AVERAGE_READING_RATE );
145
- const wordsCountText = sprintf(
146
- // translators: %s: the number of words in the post.
147
- _n( '%s word', '%s words', wordsCounted ),
148
- wordsCounted.toLocaleString()
149
- );
150
- const minutesText =
151
- readingTime <= 1
152
- ? __( '1 minute' )
153
- : sprintf(
154
- // translators: %s: the number of minutes to read the post.
155
- _n( '%s minute', '%s minutes', readingTime ),
156
- readingTime.toLocaleString()
157
- );
158
- return (
159
- <Text>
160
- { sprintf(
161
- /* translators: 1: How many words a post has. 2: the number of minutes to read the post (e.g. 130 words, 2 minutes read time.) */
162
- __( '%1$s, %2$s read time.' ),
163
- wordsCountText,
164
- minutesText
165
- ) }
166
- </Text>
167
- );
168
- }
@@ -20,13 +20,9 @@
20
20
  &__header {
21
21
  display: flex;
22
22
  justify-content: space-between;
23
- margin: 0 0 $grid-unit-10;
24
23
  }
25
24
 
26
- &__description {
27
- color: $gray-700;
28
- & .components-text {
29
- color: inherit;
30
- }
25
+ &.has-description &__header {
26
+ margin-bottom: $grid-unit-10;
31
27
  }
32
28
  }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __experimentalText as Text } from '@wordpress/components';
5
+ import { useSelect } from '@wordpress/data';
6
+ import { __, _x, _n, sprintf } from '@wordpress/i18n';
7
+ import { count as wordCount } from '@wordpress/wordcount';
8
+ import { useMemo } from '@wordpress/element';
9
+ import { store as coreStore } from '@wordpress/core-data';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { store as editorStore } from '../../store';
15
+ import {
16
+ TEMPLATE_POST_TYPE,
17
+ TEMPLATE_PART_POST_TYPE,
18
+ } from '../../store/constants';
19
+
20
+ // Taken from packages/editor/src/components/time-to-read/index.js.
21
+ const AVERAGE_READING_RATE = 189;
22
+
23
+ // This component renders the wordcount and reading time for the post.
24
+ export default function PostContentInformation() {
25
+ const { postContent } = useSelect( ( select ) => {
26
+ const { getEditedPostAttribute, getCurrentPostType, getCurrentPostId } =
27
+ select( editorStore );
28
+ const { getEntityRecord } = select( coreStore );
29
+ const siteSettings = getEntityRecord( 'root', 'site' );
30
+ const postType = getCurrentPostType();
31
+ const _id = getCurrentPostId();
32
+ const isPostsPage = +_id === siteSettings?.page_for_posts;
33
+ const showPostContentInfo =
34
+ ! isPostsPage &&
35
+ ! [ TEMPLATE_POST_TYPE, TEMPLATE_PART_POST_TYPE ].includes(
36
+ postType
37
+ );
38
+ return {
39
+ postContent:
40
+ showPostContentInfo && getEditedPostAttribute( 'content' ),
41
+ };
42
+ }, [] );
43
+
44
+ /*
45
+ * translators: If your word count is based on single characters (e.g. East Asian characters),
46
+ * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
47
+ * Do not translate into your own language.
48
+ */
49
+ const wordCountType = _x( 'words', 'Word count type. Do not translate!' );
50
+ const wordsCounted = useMemo(
51
+ () => ( postContent ? wordCount( postContent, wordCountType ) : 0 ),
52
+ [ postContent, wordCountType ]
53
+ );
54
+ if ( ! wordsCounted ) {
55
+ return null;
56
+ }
57
+ const readingTime = Math.round( wordsCounted / AVERAGE_READING_RATE );
58
+ const wordsCountText = sprintf(
59
+ // translators: %s: the number of words in the post.
60
+ _n( '%s word', '%s words', wordsCounted ),
61
+ wordsCounted.toLocaleString()
62
+ );
63
+ const minutesText =
64
+ readingTime <= 1
65
+ ? __( '1 minute' )
66
+ : sprintf(
67
+ // translators: %s: the number of minutes to read the post.
68
+ _n( '%s minute', '%s minutes', readingTime ),
69
+ readingTime.toLocaleString()
70
+ );
71
+ return (
72
+ <div className="editor-post-content-information">
73
+ <Text>
74
+ { sprintf(
75
+ /* translators: 1: How many words a post has. 2: the number of minutes to read the post (e.g. 130 words, 2 minutes read time.) */
76
+ __( '%1$s, %2$s read time.' ),
77
+ wordsCountText,
78
+ minutesText
79
+ ) }
80
+ </Text>
81
+ </div>
82
+ );
83
+ }
@@ -0,0 +1,6 @@
1
+ .editor-post-content-information {
2
+ color: $gray-700;
3
+ & .components-text {
4
+ color: inherit;
5
+ }
6
+ }