@wordpress/editor 13.24.1 → 13.26.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 (307) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE.md +1 -1
  3. package/build/components/document-bar/index.js +164 -0
  4. package/build/components/document-bar/index.js.map +1 -0
  5. package/build/components/document-outline/index.js +82 -1
  6. package/build/components/document-outline/index.js.map +1 -1
  7. package/build/components/document-tools/index.js +160 -0
  8. package/build/components/document-tools/index.js.map +1 -0
  9. package/build/components/editor-canvas/edit-template-blocks-notification.js +106 -0
  10. package/build/components/editor-canvas/edit-template-blocks-notification.js.map +1 -0
  11. package/build/components/editor-canvas/index.js +298 -0
  12. package/build/components/editor-canvas/index.js.map +1 -0
  13. package/build/components/entities-saved-states/index.js +3 -1
  14. package/build/components/entities-saved-states/index.js.map +1 -1
  15. package/build/components/global-keyboard-shortcuts/index.js +12 -2
  16. package/build/components/global-keyboard-shortcuts/index.js.map +1 -1
  17. package/build/components/global-keyboard-shortcuts/register-shortcuts.js +9 -0
  18. package/build/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  19. package/build/components/index.js +72 -8
  20. package/build/components/index.js.map +1 -1
  21. package/build/components/index.native.js +9 -1
  22. package/build/components/index.native.js.map +1 -1
  23. package/build/components/inserter-sidebar/index.js +77 -0
  24. package/build/components/inserter-sidebar/index.js.map +1 -0
  25. package/build/components/list-view-sidebar/index.js +150 -0
  26. package/build/components/list-view-sidebar/index.js.map +1 -0
  27. package/build/components/list-view-sidebar/list-view-outline.js +28 -0
  28. package/build/components/list-view-sidebar/list-view-outline.js.map +1 -0
  29. package/build/components/offline-status/index.native.js +85 -0
  30. package/build/components/offline-status/index.native.js.map +1 -0
  31. package/build/components/page-attributes/panel.js +63 -0
  32. package/build/components/page-attributes/panel.js.map +1 -0
  33. package/build/components/post-discussion/panel.js +59 -0
  34. package/build/components/post-discussion/panel.js.map +1 -0
  35. package/build/components/post-excerpt/check.js +19 -0
  36. package/build/components/post-excerpt/check.js.map +1 -1
  37. package/build/components/post-excerpt/panel.js +55 -0
  38. package/build/components/post-excerpt/panel.js.map +1 -0
  39. package/build/components/post-excerpt/plugin.js +72 -0
  40. package/build/components/post-excerpt/plugin.js.map +1 -0
  41. package/build/components/post-featured-image/index.js +5 -8
  42. package/build/components/post-featured-image/index.js.map +1 -1
  43. package/build/components/post-featured-image/panel.js +60 -0
  44. package/build/components/post-featured-image/panel.js.map +1 -0
  45. package/build/components/post-last-revision/panel.js +27 -0
  46. package/build/components/post-last-revision/panel.js.map +1 -0
  47. package/build/components/post-publish-button/index.js +1 -0
  48. package/build/components/post-publish-button/index.js.map +1 -1
  49. package/build/components/post-publish-panel/maybe-upload-media.js +2 -4
  50. package/build/components/post-publish-panel/maybe-upload-media.js.map +1 -1
  51. package/build/components/post-saved-state/index.js +13 -8
  52. package/build/components/post-saved-state/index.js.map +1 -1
  53. package/build/components/post-schedule/panel.js +1 -1
  54. package/build/components/post-schedule/panel.js.map +1 -1
  55. package/build/components/post-taxonomies/panel.js +68 -0
  56. package/build/components/post-taxonomies/panel.js.map +1 -0
  57. package/build/components/post-template/block-theme.js +100 -0
  58. package/build/components/post-template/block-theme.js.map +1 -0
  59. package/build/components/post-template/classic-theme.js +171 -0
  60. package/build/components/post-template/classic-theme.js.map +1 -0
  61. package/build/components/post-template/create-new-template-modal.js +98 -0
  62. package/build/components/post-template/create-new-template-modal.js.map +1 -0
  63. package/build/components/post-template/create-new-template.js +55 -0
  64. package/build/components/post-template/create-new-template.js.map +1 -0
  65. package/build/components/post-template/hooks.js +88 -0
  66. package/build/components/post-template/hooks.js.map +1 -0
  67. package/build/components/post-template/panel.js +70 -0
  68. package/build/components/post-template/panel.js.map +1 -0
  69. package/build/components/post-template/reset-default-template.js +48 -0
  70. package/build/components/post-template/reset-default-template.js.map +1 -0
  71. package/build/components/post-template/swap-template-button.js +86 -0
  72. package/build/components/post-template/swap-template-button.js.map +1 -0
  73. package/build/components/post-title/index.native.js +25 -15
  74. package/build/components/post-title/index.native.js.map +1 -1
  75. package/build/components/post-view-link/index.js +58 -0
  76. package/build/components/post-view-link/index.js.map +1 -0
  77. package/build/components/post-visibility/check.js +5 -17
  78. package/build/components/post-visibility/check.js.map +1 -1
  79. package/build/components/preview-dropdown/index.js +118 -0
  80. package/build/components/preview-dropdown/index.js.map +1 -0
  81. package/build/components/provider/index.js +24 -82
  82. package/build/components/provider/index.js.map +1 -1
  83. package/build/components/provider/index.native.js +36 -8
  84. package/build/components/provider/index.native.js.map +1 -1
  85. package/build/components/provider/navigation-block-editing-mode.js +40 -0
  86. package/build/components/provider/navigation-block-editing-mode.js.map +1 -0
  87. package/build/components/provider/use-block-editor-settings.js +38 -15
  88. package/build/components/provider/use-block-editor-settings.js.map +1 -1
  89. package/build/hooks/index.js +1 -0
  90. package/build/hooks/index.js.map +1 -1
  91. package/build/hooks/pattern-partial-syncing.js +49 -0
  92. package/build/hooks/pattern-partial-syncing.js.map +1 -0
  93. package/build/private-apis.js +14 -0
  94. package/build/private-apis.js.map +1 -1
  95. package/build/store/actions.js +161 -13
  96. package/build/store/actions.js.map +1 -1
  97. package/build/store/defaults.js +2 -1
  98. package/build/store/defaults.js.map +1 -1
  99. package/build/store/index.js +5 -0
  100. package/build/store/index.js.map +1 -1
  101. package/build/store/private-actions.js +52 -0
  102. package/build/store/private-actions.js.map +1 -0
  103. package/build/store/private-selectors.js +52 -0
  104. package/build/store/private-selectors.js.map +1 -0
  105. package/build/store/reducer.js +109 -27
  106. package/build/store/reducer.js.map +1 -1
  107. package/build/store/reducer.native.js +0 -1
  108. package/build/store/reducer.native.js.map +1 -1
  109. package/build/store/selectors.js +104 -9
  110. package/build/store/selectors.js.map +1 -1
  111. package/build/utils/media-upload/index.js +8 -2
  112. package/build/utils/media-upload/index.js.map +1 -1
  113. package/build-module/components/document-bar/index.js +156 -0
  114. package/build-module/components/document-bar/index.js.map +1 -0
  115. package/build-module/components/document-outline/index.js +82 -1
  116. package/build-module/components/document-outline/index.js.map +1 -1
  117. package/build-module/components/document-tools/index.js +151 -0
  118. package/build-module/components/document-tools/index.js.map +1 -0
  119. package/build-module/components/editor-canvas/edit-template-blocks-notification.js +100 -0
  120. package/build-module/components/editor-canvas/edit-template-blocks-notification.js.map +1 -0
  121. package/build-module/components/editor-canvas/index.js +289 -0
  122. package/build-module/components/editor-canvas/index.js.map +1 -0
  123. package/build-module/components/entities-saved-states/index.js +3 -1
  124. package/build-module/components/entities-saved-states/index.js.map +1 -1
  125. package/build-module/components/global-keyboard-shortcuts/index.js +12 -2
  126. package/build-module/components/global-keyboard-shortcuts/index.js.map +1 -1
  127. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js +9 -0
  128. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  129. package/build-module/components/index.js +9 -1
  130. package/build-module/components/index.js.map +1 -1
  131. package/build-module/components/index.native.js +1 -0
  132. package/build-module/components/index.native.js.map +1 -1
  133. package/build-module/components/inserter-sidebar/index.js +70 -0
  134. package/build-module/components/inserter-sidebar/index.js.map +1 -0
  135. package/build-module/components/list-view-sidebar/index.js +142 -0
  136. package/build-module/components/list-view-sidebar/index.js.map +1 -0
  137. package/build-module/components/list-view-sidebar/list-view-outline.js +20 -0
  138. package/build-module/components/list-view-sidebar/list-view-outline.js.map +1 -0
  139. package/build-module/components/offline-status/index.native.js +77 -0
  140. package/build-module/components/offline-status/index.native.js.map +1 -0
  141. package/build-module/components/page-attributes/panel.js +53 -0
  142. package/build-module/components/page-attributes/panel.js.map +1 -0
  143. package/build-module/components/post-discussion/panel.js +50 -0
  144. package/build-module/components/post-discussion/panel.js.map +1 -0
  145. package/build-module/components/post-excerpt/check.js +19 -0
  146. package/build-module/components/post-excerpt/check.js.map +1 -1
  147. package/build-module/components/post-excerpt/panel.js +48 -0
  148. package/build-module/components/post-excerpt/panel.js.map +1 -0
  149. package/build-module/components/post-excerpt/plugin.js +64 -0
  150. package/build-module/components/post-excerpt/plugin.js.map +1 -0
  151. package/build-module/components/post-featured-image/index.js +5 -8
  152. package/build-module/components/post-featured-image/index.js.map +1 -1
  153. package/build-module/components/post-featured-image/panel.js +51 -0
  154. package/build-module/components/post-featured-image/panel.js.map +1 -0
  155. package/build-module/components/post-last-revision/panel.js +18 -0
  156. package/build-module/components/post-last-revision/panel.js.map +1 -0
  157. package/build-module/components/post-publish-button/index.js +1 -0
  158. package/build-module/components/post-publish-button/index.js.map +1 -1
  159. package/build-module/components/post-publish-panel/maybe-upload-media.js +2 -4
  160. package/build-module/components/post-publish-panel/maybe-upload-media.js.map +1 -1
  161. package/build-module/components/post-saved-state/index.js +13 -8
  162. package/build-module/components/post-saved-state/index.js.map +1 -1
  163. package/build-module/components/post-schedule/panel.js +1 -1
  164. package/build-module/components/post-schedule/panel.js.map +1 -1
  165. package/build-module/components/post-taxonomies/panel.js +59 -0
  166. package/build-module/components/post-taxonomies/panel.js.map +1 -0
  167. package/build-module/components/post-template/block-theme.js +92 -0
  168. package/build-module/components/post-template/block-theme.js.map +1 -0
  169. package/build-module/components/post-template/classic-theme.js +162 -0
  170. package/build-module/components/post-template/classic-theme.js.map +1 -0
  171. package/build-module/components/post-template/create-new-template-modal.js +91 -0
  172. package/build-module/components/post-template/create-new-template-modal.js.map +1 -0
  173. package/build-module/components/post-template/create-new-template.js +47 -0
  174. package/build-module/components/post-template/create-new-template.js.map +1 -0
  175. package/build-module/components/post-template/hooks.js +78 -0
  176. package/build-module/components/post-template/hooks.js.map +1 -0
  177. package/build-module/components/post-template/panel.js +62 -0
  178. package/build-module/components/post-template/panel.js.map +1 -0
  179. package/build-module/components/post-template/reset-default-template.js +41 -0
  180. package/build-module/components/post-template/reset-default-template.js.map +1 -0
  181. package/build-module/components/post-template/swap-template-button.js +79 -0
  182. package/build-module/components/post-template/swap-template-button.js.map +1 -0
  183. package/build-module/components/post-title/index.native.js +26 -16
  184. package/build-module/components/post-title/index.native.js.map +1 -1
  185. package/build-module/components/post-view-link/index.js +51 -0
  186. package/build-module/components/post-view-link/index.js.map +1 -0
  187. package/build-module/components/post-visibility/check.js +6 -16
  188. package/build-module/components/post-visibility/check.js.map +1 -1
  189. package/build-module/components/preview-dropdown/index.js +110 -0
  190. package/build-module/components/preview-dropdown/index.js.map +1 -0
  191. package/build-module/components/provider/index.js +25 -83
  192. package/build-module/components/provider/index.js.map +1 -1
  193. package/build-module/components/provider/index.native.js +37 -9
  194. package/build-module/components/provider/index.native.js.map +1 -1
  195. package/build-module/components/provider/navigation-block-editing-mode.js +34 -0
  196. package/build-module/components/provider/navigation-block-editing-mode.js.map +1 -0
  197. package/build-module/components/provider/use-block-editor-settings.js +38 -15
  198. package/build-module/components/provider/use-block-editor-settings.js.map +1 -1
  199. package/build-module/hooks/index.js +1 -0
  200. package/build-module/hooks/index.js.map +1 -1
  201. package/build-module/hooks/pattern-partial-syncing.js +46 -0
  202. package/build-module/hooks/pattern-partial-syncing.js.map +1 -0
  203. package/build-module/private-apis.js +14 -0
  204. package/build-module/private-apis.js.map +1 -1
  205. package/build-module/store/actions.js +147 -9
  206. package/build-module/store/actions.js.map +1 -1
  207. package/build-module/store/defaults.js +2 -1
  208. package/build-module/store/defaults.js.map +1 -1
  209. package/build-module/store/index.js +5 -0
  210. package/build-module/store/index.js.map +1 -1
  211. package/build-module/store/private-actions.js +44 -0
  212. package/build-module/store/private-actions.js.map +1 -0
  213. package/build-module/store/private-selectors.js +43 -0
  214. package/build-module/store/private-selectors.js.map +1 -0
  215. package/build-module/store/reducer.js +103 -26
  216. package/build-module/store/reducer.js.map +1 -1
  217. package/build-module/store/reducer.native.js +1 -2
  218. package/build-module/store/reducer.native.js.map +1 -1
  219. package/build-module/store/selectors.js +93 -6
  220. package/build-module/store/selectors.js.map +1 -1
  221. package/build-module/utils/media-upload/index.js +8 -2
  222. package/build-module/utils/media-upload/index.js.map +1 -1
  223. package/build-style/style-rtl.css +433 -0
  224. package/build-style/style.css +433 -0
  225. package/package.json +32 -31
  226. package/src/components/document-bar/index.js +193 -0
  227. package/src/components/document-bar/style.scss +130 -0
  228. package/src/components/document-outline/index.js +48 -1
  229. package/src/components/document-outline/style.scss +12 -0
  230. package/src/components/document-tools/index.js +177 -0
  231. package/src/components/document-tools/style.scss +98 -0
  232. package/src/components/editor-canvas/edit-template-blocks-notification.js +108 -0
  233. package/src/components/editor-canvas/index.js +386 -0
  234. package/src/components/editor-canvas/style.scss +5 -0
  235. package/src/components/entities-saved-states/index.js +3 -1
  236. package/src/components/entities-saved-states/style.scss +4 -0
  237. package/src/components/global-keyboard-shortcuts/index.js +12 -2
  238. package/src/components/global-keyboard-shortcuts/register-shortcuts.js +10 -0
  239. package/src/components/index.js +9 -1
  240. package/src/components/index.native.js +1 -0
  241. package/src/components/inserter-sidebar/index.js +73 -0
  242. package/src/components/inserter-sidebar/style.scss +22 -0
  243. package/src/components/list-view-sidebar/index.js +169 -0
  244. package/src/components/list-view-sidebar/list-view-outline.js +37 -0
  245. package/src/components/list-view-sidebar/style.scss +84 -0
  246. package/src/components/offline-status/index.native.js +101 -0
  247. package/src/components/offline-status/style.native.scss +28 -0
  248. package/src/components/offline-status/test/index.native.js +108 -0
  249. package/src/components/page-attributes/panel.js +62 -0
  250. package/src/components/post-discussion/panel.js +57 -0
  251. package/src/components/post-excerpt/check.js +18 -0
  252. package/src/components/post-excerpt/panel.js +57 -0
  253. package/src/components/post-excerpt/plugin.js +61 -0
  254. package/src/components/post-excerpt/test/plugin.js +36 -0
  255. package/src/components/post-featured-image/index.js +3 -7
  256. package/src/components/post-featured-image/panel.js +55 -0
  257. package/src/components/post-last-revision/panel.js +22 -0
  258. package/src/components/post-last-revision/style.scss +10 -0
  259. package/src/components/post-publish-button/index.js +1 -0
  260. package/src/components/post-publish-panel/maybe-upload-media.js +3 -8
  261. package/src/components/post-saved-state/index.js +9 -8
  262. package/src/components/post-saved-state/test/__snapshots__/index.js.snap +2 -2
  263. package/src/components/post-schedule/panel.js +1 -1
  264. package/src/components/post-taxonomies/panel.js +66 -0
  265. package/src/components/post-template/block-theme.js +110 -0
  266. package/src/components/post-template/classic-theme.js +213 -0
  267. package/src/components/post-template/create-new-template-modal.js +139 -0
  268. package/src/components/post-template/create-new-template.js +50 -0
  269. package/src/components/post-template/hooks.js +95 -0
  270. package/src/components/post-template/panel.js +66 -0
  271. package/src/components/post-template/reset-default-template.js +43 -0
  272. package/src/components/post-template/style.scss +52 -0
  273. package/src/components/post-template/swap-template-button.js +86 -0
  274. package/src/components/post-title/index.native.js +32 -18
  275. package/src/components/post-title/style.scss +1 -0
  276. package/src/components/post-title/test/__snapshots__/index.native.js.snap +25 -0
  277. package/src/components/post-title/test/index.native.js +78 -0
  278. package/src/components/post-view-link/index.js +47 -0
  279. package/src/components/post-visibility/check.js +10 -15
  280. package/src/components/post-visibility/test/check.js +24 -13
  281. package/src/components/preview-dropdown/index.js +133 -0
  282. package/src/components/preview-dropdown/style.scss +5 -0
  283. package/src/components/provider/index.js +28 -118
  284. package/src/components/provider/index.native.js +55 -14
  285. package/src/components/provider/navigation-block-editing-mode.js +37 -0
  286. package/src/components/provider/use-block-editor-settings.js +42 -17
  287. package/src/hooks/index.js +1 -0
  288. package/src/hooks/pattern-partial-syncing.js +73 -0
  289. package/src/private-apis.js +14 -0
  290. package/src/store/actions.js +160 -9
  291. package/src/store/defaults.js +1 -0
  292. package/src/store/index.js +5 -0
  293. package/src/store/private-actions.js +61 -0
  294. package/src/store/private-selectors.js +51 -0
  295. package/src/store/reducer.js +103 -26
  296. package/src/store/reducer.native.js +0 -2
  297. package/src/store/selectors.js +144 -42
  298. package/src/store/test/actions.js +56 -0
  299. package/src/store/test/reducer.js +98 -0
  300. package/src/store/test/selectors.js +137 -147
  301. package/src/style.scss +7 -0
  302. package/src/utils/media-upload/index.js +9 -2
  303. package/build/components/post-template/index.js +0 -66
  304. package/build/components/post-template/index.js.map +0 -1
  305. package/build-module/components/post-template/index.js +0 -57
  306. package/build-module/components/post-template/index.js.map +0 -1
  307. package/src/components/post-template/index.js +0 -64
@@ -0,0 +1,213 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { SelectControl, Dropdown, Button, Notice } from '@wordpress/components';
6
+ import { useSelect, useDispatch } from '@wordpress/data';
7
+ import { store as coreStore } from '@wordpress/core-data';
8
+ import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor';
9
+ import { useState, useMemo } from '@wordpress/element';
10
+ import { addTemplate } from '@wordpress/icons';
11
+ import { store as noticesStore } from '@wordpress/notices';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import { store as editorStore } from '../../store';
17
+ import CreateNewTemplateModal from './create-new-template-modal';
18
+ import { useAllowSwitchingTemplates } from './hooks';
19
+
20
+ const POPOVER_PROPS = {
21
+ className: 'editor-post-template__dropdown',
22
+ placement: 'bottom-start',
23
+ };
24
+
25
+ function PostTemplateToggle( { isOpen, onClick } ) {
26
+ const templateTitle = useSelect( ( select ) => {
27
+ const templateSlug =
28
+ select( editorStore ).getEditedPostAttribute( 'template' );
29
+
30
+ const { supportsTemplateMode, availableTemplates } =
31
+ select( editorStore ).getEditorSettings();
32
+ if ( ! supportsTemplateMode && availableTemplates[ templateSlug ] ) {
33
+ return availableTemplates[ templateSlug ];
34
+ }
35
+ const template =
36
+ select( coreStore ).canUser( 'create', 'templates' ) &&
37
+ select( editorStore ).getCurrentTemplateId();
38
+ return (
39
+ template?.title ||
40
+ template?.slug ||
41
+ availableTemplates?.[ templateSlug ]
42
+ );
43
+ }, [] );
44
+
45
+ return (
46
+ <Button
47
+ className="edit-post-post-template__toggle"
48
+ variant="tertiary"
49
+ aria-expanded={ isOpen }
50
+ aria-label={ __( 'Template options' ) }
51
+ onClick={ onClick }
52
+ >
53
+ { templateTitle ?? __( 'Default template' ) }
54
+ </Button>
55
+ );
56
+ }
57
+
58
+ function PostTemplateDropdownContent( { onClose } ) {
59
+ const allowSwitchingTemplate = useAllowSwitchingTemplates();
60
+ const {
61
+ availableTemplates,
62
+ fetchedTemplates,
63
+ selectedTemplateSlug,
64
+ canCreate,
65
+ canEdit,
66
+ } = useSelect(
67
+ ( select ) => {
68
+ const { canUser, getEntityRecords } = select( coreStore );
69
+ const editorSettings = select( editorStore ).getEditorSettings();
70
+ const canCreateTemplates = canUser( 'create', 'templates' );
71
+
72
+ return {
73
+ availableTemplates: editorSettings.availableTemplates,
74
+ fetchedTemplates: canCreateTemplates
75
+ ? getEntityRecords( 'postType', 'wp_template', {
76
+ post_type:
77
+ select( editorStore ).getCurrentPostType(),
78
+ per_page: -1,
79
+ } )
80
+ : undefined,
81
+ selectedTemplateSlug:
82
+ select( editorStore ).getEditedPostAttribute( 'template' ),
83
+ canCreate:
84
+ allowSwitchingTemplate &&
85
+ canCreateTemplates &&
86
+ editorSettings.supportsTemplateMode,
87
+ canEdit:
88
+ allowSwitchingTemplate &&
89
+ canCreateTemplates &&
90
+ editorSettings.supportsTemplateMode &&
91
+ !! select( editorStore ).getCurrentTemplateId(),
92
+ };
93
+ },
94
+ [ allowSwitchingTemplate ]
95
+ );
96
+
97
+ const options = useMemo(
98
+ () =>
99
+ Object.entries( {
100
+ ...availableTemplates,
101
+ ...Object.fromEntries(
102
+ ( fetchedTemplates ?? [] ).map( ( { slug, title } ) => [
103
+ slug,
104
+ title.rendered,
105
+ ] )
106
+ ),
107
+ } ).map( ( [ slug, title ] ) => ( { value: slug, label: title } ) ),
108
+ [ availableTemplates, fetchedTemplates ]
109
+ );
110
+
111
+ const selectedOption =
112
+ options.find( ( option ) => option.value === selectedTemplateSlug ) ??
113
+ options.find( ( option ) => ! option.value ); // The default option has '' value.
114
+
115
+ const { editPost } = useDispatch( editorStore );
116
+ const { getEditorSettings } = useSelect( editorStore );
117
+ const { createSuccessNotice } = useDispatch( noticesStore );
118
+ const { setRenderingMode } = useDispatch( editorStore );
119
+ const [ isCreateModalOpen, setIsCreateModalOpen ] = useState( false );
120
+
121
+ return (
122
+ <div className="editor-post-template__classic-theme-dropdown">
123
+ <InspectorPopoverHeader
124
+ title={ __( 'Template' ) }
125
+ help={ __(
126
+ 'Templates define the way content is displayed when viewing your site.'
127
+ ) }
128
+ actions={
129
+ canCreate
130
+ ? [
131
+ {
132
+ icon: addTemplate,
133
+ label: __( 'Add template' ),
134
+ onClick: () => setIsCreateModalOpen( true ),
135
+ },
136
+ ]
137
+ : []
138
+ }
139
+ onClose={ onClose }
140
+ />
141
+ { ! allowSwitchingTemplate ? (
142
+ <Notice status="warning" isDismissible={ false }>
143
+ { __( 'The posts page template cannot be changed.' ) }
144
+ </Notice>
145
+ ) : (
146
+ <SelectControl
147
+ __next40pxDefaultSize
148
+ __nextHasNoMarginBottom
149
+ hideLabelFromVision
150
+ label={ __( 'Template' ) }
151
+ value={ selectedOption?.value ?? '' }
152
+ options={ options }
153
+ onChange={ ( slug ) =>
154
+ editPost( { template: slug || '' } )
155
+ }
156
+ />
157
+ ) }
158
+ { canEdit && (
159
+ <p>
160
+ <Button
161
+ variant="link"
162
+ onClick={ () => {
163
+ setRenderingMode( 'template-only' );
164
+ onClose();
165
+ createSuccessNotice(
166
+ __(
167
+ 'Editing template. Changes made here affect all posts and pages that use the template.'
168
+ ),
169
+ {
170
+ type: 'snackbar',
171
+ actions: [
172
+ {
173
+ label: __( 'Go back' ),
174
+ onClick: () =>
175
+ setRenderingMode(
176
+ getEditorSettings()
177
+ .defaultRenderingMode
178
+ ),
179
+ },
180
+ ],
181
+ }
182
+ );
183
+ } }
184
+ >
185
+ { __( 'Edit template' ) }
186
+ </Button>
187
+ </p>
188
+ ) }
189
+ { isCreateModalOpen && (
190
+ <CreateNewTemplateModal
191
+ onClose={ () => setIsCreateModalOpen( false ) }
192
+ />
193
+ ) }
194
+ </div>
195
+ );
196
+ }
197
+
198
+ function ClassicThemeControl() {
199
+ return (
200
+ <Dropdown
201
+ popoverProps={ POPOVER_PROPS }
202
+ focusOnMount
203
+ renderToggle={ ( { isOpen, onToggle } ) => (
204
+ <PostTemplateToggle isOpen={ isOpen } onClick={ onToggle } />
205
+ ) }
206
+ renderContent={ ( { onClose } ) => (
207
+ <PostTemplateDropdownContent onClose={ onClose } />
208
+ ) }
209
+ />
210
+ );
211
+ }
212
+
213
+ export default ClassicThemeControl;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect, useDispatch } from '@wordpress/data';
5
+ import { useState } from '@wordpress/element';
6
+ import { serialize, createBlock } from '@wordpress/blocks';
7
+ import {
8
+ Modal,
9
+ TextControl,
10
+ Button,
11
+ __experimentalHStack as HStack,
12
+ __experimentalVStack as VStack,
13
+ } from '@wordpress/components';
14
+ import { __ } from '@wordpress/i18n';
15
+ import { cleanForSlug } from '@wordpress/url';
16
+
17
+ /**
18
+ * Internal dependencies
19
+ */
20
+ import { unlock } from '../../lock-unlock';
21
+ import { store as editorStore } from '../../store';
22
+
23
+ const DEFAULT_TITLE = __( 'Custom Template' );
24
+
25
+ export default function CreateNewTemplateModal( { onClose } ) {
26
+ const defaultBlockTemplate = useSelect(
27
+ ( select ) =>
28
+ select( editorStore ).getEditorSettings().defaultBlockTemplate,
29
+ []
30
+ );
31
+
32
+ const { createTemplate, setRenderingMode } = unlock(
33
+ useDispatch( editorStore )
34
+ );
35
+
36
+ const [ title, setTitle ] = useState( '' );
37
+
38
+ const [ isBusy, setIsBusy ] = useState( false );
39
+
40
+ const cancel = () => {
41
+ setTitle( '' );
42
+ onClose();
43
+ };
44
+
45
+ const submit = async ( event ) => {
46
+ event.preventDefault();
47
+
48
+ if ( isBusy ) {
49
+ return;
50
+ }
51
+
52
+ setIsBusy( true );
53
+
54
+ const newTemplateContent =
55
+ defaultBlockTemplate ??
56
+ serialize( [
57
+ createBlock(
58
+ 'core/group',
59
+ {
60
+ tagName: 'header',
61
+ layout: { inherit: true },
62
+ },
63
+ [
64
+ createBlock( 'core/site-title' ),
65
+ createBlock( 'core/site-tagline' ),
66
+ ]
67
+ ),
68
+ createBlock( 'core/separator' ),
69
+ createBlock(
70
+ 'core/group',
71
+ {
72
+ tagName: 'main',
73
+ },
74
+ [
75
+ createBlock(
76
+ 'core/group',
77
+ {
78
+ layout: { inherit: true },
79
+ },
80
+ [ createBlock( 'core/post-title' ) ]
81
+ ),
82
+ createBlock( 'core/post-content', {
83
+ layout: { inherit: true },
84
+ } ),
85
+ ]
86
+ ),
87
+ ] );
88
+
89
+ await createTemplate( {
90
+ slug: cleanForSlug( title || DEFAULT_TITLE ),
91
+ content: newTemplateContent,
92
+ title: title || DEFAULT_TITLE,
93
+ } );
94
+
95
+ setIsBusy( false );
96
+ cancel();
97
+ setRenderingMode( 'template-only' );
98
+ };
99
+
100
+ return (
101
+ <Modal
102
+ title={ __( 'Create custom template' ) }
103
+ onRequestClose={ cancel }
104
+ >
105
+ <form
106
+ className="editor-post-template__create-form"
107
+ onSubmit={ submit }
108
+ >
109
+ <VStack spacing="3">
110
+ <TextControl
111
+ __nextHasNoMarginBottom
112
+ label={ __( 'Name' ) }
113
+ value={ title }
114
+ onChange={ setTitle }
115
+ placeholder={ DEFAULT_TITLE }
116
+ disabled={ isBusy }
117
+ help={ __(
118
+ 'Describe the template, e.g. "Post with sidebar". A custom template can be manually applied to any post or page.'
119
+ ) }
120
+ />
121
+ <HStack justify="right">
122
+ <Button variant="tertiary" onClick={ cancel }>
123
+ { __( 'Cancel' ) }
124
+ </Button>
125
+
126
+ <Button
127
+ variant="primary"
128
+ type="submit"
129
+ isBusy={ isBusy }
130
+ aria-disabled={ isBusy }
131
+ >
132
+ { __( 'Create' ) }
133
+ </Button>
134
+ </HStack>
135
+ </VStack>
136
+ </form>
137
+ </Modal>
138
+ );
139
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { MenuItem } from '@wordpress/components';
5
+ import { __ } from '@wordpress/i18n';
6
+ import { useSelect } from '@wordpress/data';
7
+ import { store as coreStore } from '@wordpress/core-data';
8
+ import { useState } from '@wordpress/element';
9
+
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import CreateNewTemplateModal from './create-new-template-modal';
14
+ import { useAllowSwitchingTemplates } from './hooks';
15
+
16
+ export default function CreateNewTemplate( { onClick } ) {
17
+ const { canCreateTemplates } = useSelect( ( select ) => {
18
+ const { canUser } = select( coreStore );
19
+ return {
20
+ canCreateTemplates: canUser( 'create', 'templates' ),
21
+ };
22
+ }, [] );
23
+ const [ isCreateModalOpen, setIsCreateModalOpen ] = useState( false );
24
+ const allowSwitchingTemplate = useAllowSwitchingTemplates();
25
+
26
+ // The default template in a post is indicated by an empty string.
27
+ if ( ! canCreateTemplates || ! allowSwitchingTemplate ) {
28
+ return null;
29
+ }
30
+ return (
31
+ <>
32
+ <MenuItem
33
+ onClick={ () => {
34
+ setIsCreateModalOpen( true );
35
+ } }
36
+ >
37
+ { __( 'Create new template' ) }
38
+ </MenuItem>
39
+
40
+ { isCreateModalOpen && (
41
+ <CreateNewTemplateModal
42
+ onClose={ () => {
43
+ setIsCreateModalOpen( false );
44
+ onClick();
45
+ } }
46
+ />
47
+ ) }
48
+ </>
49
+ );
50
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect } from '@wordpress/data';
5
+ import { useMemo } from '@wordpress/element';
6
+ import { store as coreStore } from '@wordpress/core-data';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { store as editorStore } from '../../store';
12
+
13
+ export function useEditedPostContext() {
14
+ return useSelect( ( select ) => {
15
+ const { getCurrentPostId, getCurrentPostType } = select( editorStore );
16
+ return {
17
+ postId: getCurrentPostId(),
18
+ postType: getCurrentPostType(),
19
+ };
20
+ }, [] );
21
+ }
22
+ export function useAllowSwitchingTemplates() {
23
+ const { postType, postId } = useEditedPostContext();
24
+ return useSelect(
25
+ ( select ) => {
26
+ const { getEntityRecord, getEntityRecords } = select( coreStore );
27
+ const siteSettings = getEntityRecord( 'root', 'site' );
28
+ const templates = getEntityRecords( 'postType', 'wp_template', {
29
+ per_page: -1,
30
+ } );
31
+ const isPostsPage = +postId === siteSettings?.page_for_posts;
32
+ // If current page is set front page or posts page, we also need
33
+ // to check if the current theme has a template for it. If not
34
+ const isFrontPage =
35
+ postType === 'page' &&
36
+ +postId === siteSettings?.page_on_front &&
37
+ templates?.some( ( { slug } ) => slug === 'front-page' );
38
+ return ! isPostsPage && ! isFrontPage;
39
+ },
40
+ [ postId, postType ]
41
+ );
42
+ }
43
+
44
+ function useTemplates( postType ) {
45
+ return useSelect(
46
+ ( select ) =>
47
+ select( coreStore ).getEntityRecords( 'postType', 'wp_template', {
48
+ per_page: -1,
49
+ post_type: postType,
50
+ } ),
51
+ [ postType ]
52
+ );
53
+ }
54
+
55
+ export function useAvailableTemplates( postType ) {
56
+ const currentTemplateSlug = useCurrentTemplateSlug();
57
+ const allowSwitchingTemplate = useAllowSwitchingTemplates();
58
+ const templates = useTemplates( postType );
59
+ return useMemo(
60
+ () =>
61
+ allowSwitchingTemplate &&
62
+ templates?.filter(
63
+ ( template ) =>
64
+ template.is_custom &&
65
+ template.slug !== currentTemplateSlug &&
66
+ !! template.content.raw // Skip empty templates.
67
+ ),
68
+ [ templates, currentTemplateSlug, allowSwitchingTemplate ]
69
+ );
70
+ }
71
+
72
+ export function useCurrentTemplateSlug() {
73
+ const { postType, postId } = useEditedPostContext();
74
+ const templates = useTemplates( postType );
75
+ const entityTemplate = useSelect(
76
+ ( select ) => {
77
+ const post = select( coreStore ).getEditedEntityRecord(
78
+ 'postType',
79
+ postType,
80
+ postId
81
+ );
82
+ return post?.template;
83
+ },
84
+ [ postType, postId ]
85
+ );
86
+
87
+ if ( ! entityTemplate ) {
88
+ return;
89
+ }
90
+ // If a page has a `template` set and is not included in the list
91
+ // of the theme's templates, do not return it, in order to resolve
92
+ // to the current theme's default template.
93
+ return templates?.find( ( template ) => template.slug === entityTemplate )
94
+ ?.slug;
95
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect } from '@wordpress/data';
5
+ import { __ } from '@wordpress/i18n';
6
+ import { store as coreStore } from '@wordpress/core-data';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { store as editorStore } from '../../store';
12
+ import ClassicThemeControl from './classic-theme';
13
+ import BlockThemeControl from './block-theme';
14
+ import PostPanelRow from '../post-panel-row';
15
+
16
+ export default function PostTemplatePanel() {
17
+ const { templateId, isBlockTheme } = useSelect( ( select ) => {
18
+ const { getCurrentTemplateId, getEditorSettings } =
19
+ select( editorStore );
20
+ return {
21
+ templateId: getCurrentTemplateId(),
22
+ isBlockTheme: getEditorSettings().__unstableIsBlockBasedTheme,
23
+ };
24
+ }, [] );
25
+
26
+ const isVisible = useSelect( ( select ) => {
27
+ const postTypeSlug = select( editorStore ).getCurrentPostType();
28
+ const postType = select( coreStore ).getPostType( postTypeSlug );
29
+ if ( ! postType?.viewable ) {
30
+ return false;
31
+ }
32
+
33
+ const settings = select( editorStore ).getEditorSettings();
34
+ const hasTemplates =
35
+ !! settings.availableTemplates &&
36
+ Object.keys( settings.availableTemplates ).length > 0;
37
+ if ( hasTemplates ) {
38
+ return true;
39
+ }
40
+
41
+ if ( ! settings.supportsTemplateMode ) {
42
+ return false;
43
+ }
44
+
45
+ const canCreateTemplates =
46
+ select( coreStore ).canUser( 'create', 'templates' ) ?? false;
47
+ return canCreateTemplates;
48
+ }, [] );
49
+
50
+ if ( ! isBlockTheme && isVisible ) {
51
+ return (
52
+ <PostPanelRow label={ __( 'Template' ) }>
53
+ <ClassicThemeControl />
54
+ </PostPanelRow>
55
+ );
56
+ }
57
+
58
+ if ( isBlockTheme && !! templateId ) {
59
+ return (
60
+ <PostPanelRow label={ __( 'Template' ) }>
61
+ <BlockThemeControl id={ templateId } />
62
+ </PostPanelRow>
63
+ );
64
+ }
65
+ return null;
66
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { MenuItem } from '@wordpress/components';
5
+ import { __ } from '@wordpress/i18n';
6
+ import { useDispatch } from '@wordpress/data';
7
+ import { store as coreStore } from '@wordpress/core-data';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import {
13
+ useAllowSwitchingTemplates,
14
+ useCurrentTemplateSlug,
15
+ useEditedPostContext,
16
+ } from './hooks';
17
+
18
+ export default function ResetDefaultTemplate( { onClick } ) {
19
+ const currentTemplateSlug = useCurrentTemplateSlug();
20
+ const allowSwitchingTemplate = useAllowSwitchingTemplates();
21
+ const { postType, postId } = useEditedPostContext();
22
+ const { editEntityRecord } = useDispatch( coreStore );
23
+ // The default template in a post is indicated by an empty string.
24
+ if ( ! currentTemplateSlug || ! allowSwitchingTemplate ) {
25
+ return null;
26
+ }
27
+ return (
28
+ <MenuItem
29
+ onClick={ () => {
30
+ editEntityRecord(
31
+ 'postType',
32
+ postType,
33
+ postId,
34
+ { template: '' },
35
+ { undoIgnore: true }
36
+ );
37
+ onClick();
38
+ } }
39
+ >
40
+ { __( 'Use default template' ) }
41
+ </MenuItem>
42
+ );
43
+ }
@@ -0,0 +1,52 @@
1
+ .editor-post-template__swap-template-modal {
2
+ z-index: z-index(".editor-post-template__swap-template-modal");
3
+ }
4
+
5
+ .editor-post-template__swap-template-modal-content .block-editor-block-patterns-list {
6
+ column-count: 2;
7
+ column-gap: $grid-unit-30;
8
+
9
+ // Small top padding required to avoid cutting off the visible outline when hovering items
10
+ padding-top: $border-width-focus-fallback;
11
+
12
+ @include break-medium() {
13
+ column-count: 3;
14
+ }
15
+
16
+ @include break-wide() {
17
+ column-count: 4;
18
+ }
19
+
20
+ .block-editor-block-patterns-list__list-item {
21
+ break-inside: avoid-column;
22
+ }
23
+
24
+ .block-editor-block-patterns-list__item {
25
+ // Avoid to override the BlockPatternList component
26
+ // default hover and focus styles.
27
+ &:not(:focus):not(:hover) .block-editor-block-preview__container {
28
+ box-shadow: 0 0 0 1px $gray-300;
29
+ }
30
+ }
31
+ }
32
+
33
+ .editor-post-template__dropdown {
34
+ .components-popover__content {
35
+ min-width: 240px;
36
+ }
37
+ .components-button.is-pressed,
38
+ .components-button.is-pressed:hover {
39
+ background: inherit;
40
+ color: inherit;
41
+ }
42
+ }
43
+
44
+ .editor-post-template__create-form {
45
+ @include break-medium() {
46
+ width: $grid-unit * 40;
47
+ }
48
+ }
49
+
50
+ .editor-post-template__classic-theme-dropdown {
51
+ padding: $grid-unit-10;
52
+ }