@wordpress/edit-site 4.11.0 → 4.13.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 (180) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/build/components/add-new-template/add-custom-template-modal.js +4 -2
  3. package/build/components/add-new-template/add-custom-template-modal.js.map +1 -1
  4. package/build/components/add-new-template/new-template.js +29 -4
  5. package/build/components/add-new-template/new-template.js.map +1 -1
  6. package/build/components/add-new-template/utils.js +152 -28
  7. package/build/components/add-new-template/utils.js.map +1 -1
  8. package/build/components/block-editor/resizable-editor.js +26 -12
  9. package/build/components/block-editor/resizable-editor.js.map +1 -1
  10. package/build/components/global-styles/border-panel.js +3 -3
  11. package/build/components/global-styles/border-panel.js.map +1 -1
  12. package/build/components/global-styles/dimensions-panel.js +98 -8
  13. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  14. package/build/components/global-styles/hooks.js +4 -4
  15. package/build/components/global-styles/hooks.js.map +1 -1
  16. package/build/components/global-styles/screen-color-palette.js +1 -1
  17. package/build/components/global-styles/screen-color-palette.js.map +1 -1
  18. package/build/components/global-styles/screen-colors.js +51 -7
  19. package/build/components/global-styles/screen-colors.js.map +1 -1
  20. package/build/components/global-styles/screen-heading-color.js +157 -0
  21. package/build/components/global-styles/screen-heading-color.js.map +1 -0
  22. package/build/components/global-styles/screen-typography-element.js +4 -0
  23. package/build/components/global-styles/screen-typography-element.js.map +1 -1
  24. package/build/components/global-styles/screen-typography.js +5 -0
  25. package/build/components/global-styles/screen-typography.js.map +1 -1
  26. package/build/components/global-styles/typography-panel.js +82 -14
  27. package/build/components/global-styles/typography-panel.js.map +1 -1
  28. package/build/components/global-styles/typography-utils.js +217 -0
  29. package/build/components/global-styles/typography-utils.js.map +1 -0
  30. package/build/components/global-styles/ui.js +11 -0
  31. package/build/components/global-styles/ui.js.map +1 -1
  32. package/build/components/global-styles/use-global-styles-output.js +102 -49
  33. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  34. package/build/components/global-styles/utils.js +54 -5
  35. package/build/components/global-styles/utils.js.map +1 -1
  36. package/build/components/header/document-actions/index.js +1 -0
  37. package/build/components/header/document-actions/index.js.map +1 -1
  38. package/build/components/header/index.js +20 -6
  39. package/build/components/header/index.js.map +1 -1
  40. package/build/components/header/mode-switcher/index.js +0 -4
  41. package/build/components/header/mode-switcher/index.js.map +1 -1
  42. package/build/components/header/more-menu/index.js +13 -3
  43. package/build/components/header/more-menu/index.js.map +1 -1
  44. package/build/components/header/undo-redo/redo.js +2 -1
  45. package/build/components/header/undo-redo/redo.js.map +1 -1
  46. package/build/components/list/actions/index.js +1 -1
  47. package/build/components/list/actions/index.js.map +1 -1
  48. package/build/components/save-button/index.js +2 -3
  49. package/build/components/save-button/index.js.map +1 -1
  50. package/build/components/sidebar/default-sidebar.js +11 -1
  51. package/build/components/sidebar/default-sidebar.js.map +1 -1
  52. package/build/components/sidebar/navigation-menu-sidebar/navigation-menu.js +2 -2
  53. package/build/components/sidebar/navigation-menu-sidebar/navigation-menu.js.map +1 -1
  54. package/build/components/sidebar/plugin-sidebar/index.js +11 -1
  55. package/build/components/sidebar/plugin-sidebar/index.js.map +1 -1
  56. package/build/components/sidebar/template-card/template-actions.js +1 -1
  57. package/build/components/sidebar/template-card/template-actions.js.map +1 -1
  58. package/build/components/template-details/edit-template-title.js +1 -1
  59. package/build/components/template-details/edit-template-title.js.map +1 -1
  60. package/build/components/template-details/index.js +19 -9
  61. package/build/components/template-details/index.js.map +1 -1
  62. package/build/components/template-details/template-areas.js +1 -1
  63. package/build/components/template-details/template-areas.js.map +1 -1
  64. package/build/components/template-details/template-part-area-selector.js +47 -0
  65. package/build/components/template-details/template-part-area-selector.js.map +1 -0
  66. package/build/components/template-part-converter/convert-to-template-part.js +4 -1
  67. package/build/components/template-part-converter/convert-to-template-part.js.map +1 -1
  68. package/build/index.js +1 -1
  69. package/build/index.js.map +1 -1
  70. package/build-module/components/add-new-template/add-custom-template-modal.js +4 -2
  71. package/build-module/components/add-new-template/add-custom-template-modal.js.map +1 -1
  72. package/build-module/components/add-new-template/new-template.js +29 -6
  73. package/build-module/components/add-new-template/new-template.js.map +1 -1
  74. package/build-module/components/add-new-template/utils.js +147 -26
  75. package/build-module/components/add-new-template/utils.js.map +1 -1
  76. package/build-module/components/block-editor/resizable-editor.js +26 -12
  77. package/build-module/components/block-editor/resizable-editor.js.map +1 -1
  78. package/build-module/components/global-styles/border-panel.js +3 -3
  79. package/build-module/components/global-styles/border-panel.js.map +1 -1
  80. package/build-module/components/global-styles/dimensions-panel.js +96 -9
  81. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  82. package/build-module/components/global-styles/hooks.js +4 -4
  83. package/build-module/components/global-styles/hooks.js.map +1 -1
  84. package/build-module/components/global-styles/screen-color-palette.js +1 -1
  85. package/build-module/components/global-styles/screen-color-palette.js.map +1 -1
  86. package/build-module/components/global-styles/screen-colors.js +51 -7
  87. package/build-module/components/global-styles/screen-colors.js.map +1 -1
  88. package/build-module/components/global-styles/screen-heading-color.js +143 -0
  89. package/build-module/components/global-styles/screen-heading-color.js.map +1 -0
  90. package/build-module/components/global-styles/screen-typography-element.js +4 -0
  91. package/build-module/components/global-styles/screen-typography-element.js.map +1 -1
  92. package/build-module/components/global-styles/screen-typography.js +5 -0
  93. package/build-module/components/global-styles/screen-typography.js.map +1 -1
  94. package/build-module/components/global-styles/typography-panel.js +83 -15
  95. package/build-module/components/global-styles/typography-panel.js.map +1 -1
  96. package/build-module/components/global-styles/typography-utils.js +204 -0
  97. package/build-module/components/global-styles/typography-utils.js.map +1 -0
  98. package/build-module/components/global-styles/ui.js +10 -0
  99. package/build-module/components/global-styles/ui.js.map +1 -1
  100. package/build-module/components/global-styles/use-global-styles-output.js +102 -53
  101. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  102. package/build-module/components/global-styles/utils.js +53 -5
  103. package/build-module/components/global-styles/utils.js.map +1 -1
  104. package/build-module/components/header/document-actions/index.js +1 -0
  105. package/build-module/components/header/document-actions/index.js.map +1 -1
  106. package/build-module/components/header/index.js +22 -8
  107. package/build-module/components/header/index.js.map +1 -1
  108. package/build-module/components/header/mode-switcher/index.js +0 -4
  109. package/build-module/components/header/mode-switcher/index.js.map +1 -1
  110. package/build-module/components/header/more-menu/index.js +13 -3
  111. package/build-module/components/header/more-menu/index.js.map +1 -1
  112. package/build-module/components/header/undo-redo/redo.js +3 -2
  113. package/build-module/components/header/undo-redo/redo.js.map +1 -1
  114. package/build-module/components/list/actions/index.js +1 -1
  115. package/build-module/components/list/actions/index.js.map +1 -1
  116. package/build-module/components/save-button/index.js +3 -4
  117. package/build-module/components/save-button/index.js.map +1 -1
  118. package/build-module/components/sidebar/default-sidebar.js +9 -1
  119. package/build-module/components/sidebar/default-sidebar.js.map +1 -1
  120. package/build-module/components/sidebar/navigation-menu-sidebar/navigation-menu.js +3 -3
  121. package/build-module/components/sidebar/navigation-menu-sidebar/navigation-menu.js.map +1 -1
  122. package/build-module/components/sidebar/plugin-sidebar/index.js +9 -1
  123. package/build-module/components/sidebar/plugin-sidebar/index.js.map +1 -1
  124. package/build-module/components/sidebar/template-card/template-actions.js +1 -1
  125. package/build-module/components/sidebar/template-card/template-actions.js.map +1 -1
  126. package/build-module/components/template-details/edit-template-title.js +1 -1
  127. package/build-module/components/template-details/edit-template-title.js.map +1 -1
  128. package/build-module/components/template-details/index.js +19 -10
  129. package/build-module/components/template-details/index.js.map +1 -1
  130. package/build-module/components/template-details/template-areas.js +1 -1
  131. package/build-module/components/template-details/template-areas.js.map +1 -1
  132. package/build-module/components/template-details/template-part-area-selector.js +35 -0
  133. package/build-module/components/template-details/template-part-area-selector.js.map +1 -0
  134. package/build-module/components/template-part-converter/convert-to-template-part.js +3 -1
  135. package/build-module/components/template-part-converter/convert-to-template-part.js.map +1 -1
  136. package/build-module/index.js +1 -1
  137. package/build-module/index.js.map +1 -1
  138. package/build-style/style-rtl.css +15 -8
  139. package/build-style/style.css +15 -8
  140. package/package.json +29 -29
  141. package/src/components/add-new-template/add-custom-template-modal.js +12 -3
  142. package/src/components/add-new-template/new-template.js +53 -24
  143. package/src/components/add-new-template/utils.js +145 -16
  144. package/src/components/block-editor/resizable-editor.js +28 -18
  145. package/src/components/editor/style.scss +1 -0
  146. package/src/components/global-styles/border-panel.js +3 -3
  147. package/src/components/global-styles/dimensions-panel.js +139 -33
  148. package/src/components/global-styles/hooks.js +4 -3
  149. package/src/components/global-styles/screen-color-palette.js +1 -1
  150. package/src/components/global-styles/screen-colors.js +46 -4
  151. package/src/components/global-styles/screen-heading-color.js +201 -0
  152. package/src/components/global-styles/screen-typography-element.js +4 -0
  153. package/src/components/global-styles/screen-typography.js +6 -0
  154. package/src/components/global-styles/style.scss +14 -6
  155. package/src/components/global-styles/test/typography-utils.js +130 -0
  156. package/src/components/global-styles/test/use-global-styles-output.js +79 -3
  157. package/src/components/global-styles/test/utils.js +68 -8
  158. package/src/components/global-styles/typography-panel.js +119 -48
  159. package/src/components/global-styles/typography-utils.js +228 -0
  160. package/src/components/global-styles/ui.js +13 -0
  161. package/src/components/global-styles/use-global-styles-output.js +119 -61
  162. package/src/components/global-styles/utils.js +39 -4
  163. package/src/components/header/document-actions/index.js +3 -0
  164. package/src/components/header/index.js +32 -4
  165. package/src/components/header/mode-switcher/index.js +0 -3
  166. package/src/components/header/more-menu/index.js +7 -2
  167. package/src/components/header/undo-redo/redo.js +6 -2
  168. package/src/components/list/actions/index.js +3 -1
  169. package/src/components/save-button/index.js +10 -13
  170. package/src/components/sidebar/default-sidebar.js +12 -0
  171. package/src/components/sidebar/navigation-menu-sidebar/navigation-menu.js +1 -5
  172. package/src/components/sidebar/plugin-sidebar/index.js +12 -0
  173. package/src/components/sidebar/template-card/template-actions.js +3 -1
  174. package/src/components/template-details/edit-template-title.js +7 -3
  175. package/src/components/template-details/index.js +23 -8
  176. package/src/components/template-details/style.scss +0 -5
  177. package/src/components/template-details/template-areas.js +3 -1
  178. package/src/components/template-details/template-part-area-selector.js +38 -0
  179. package/src/components/template-part-converter/convert-to-template-part.js +3 -1
  180. package/src/index.js +1 -1
@@ -722,6 +722,12 @@ textarea.edit-site-code-editor-text-area.edit-site-code-editor-text-area:-ms-inp
722
722
  border-radius: 2px;
723
723
  }
724
724
 
725
+ .edit-site-typography-panel__full-width-control {
726
+ grid-column: 1/-1;
727
+ max-width: 100%;
728
+ }
729
+
730
+ .edit-site-global-styles-screen-heading-color,
725
731
  .edit-site-global-styles-screen-typography {
726
732
  margin: 16px;
727
733
  }
@@ -741,6 +747,7 @@ textarea.edit-site-code-editor-text-area.edit-site-code-editor-text-area:-ms-inp
741
747
  }
742
748
  .edit-site-global-styles-screen-colors .component-color-indicator {
743
749
  background: linear-gradient(-45deg, transparent 48%, #ddd 48%, #ddd 52%, transparent 52%);
750
+ flex-shrink: 0;
744
751
  }
745
752
 
746
753
  .edit-site-global-styles-header__description,
@@ -770,10 +777,6 @@ textarea.edit-site-code-editor-text-area.edit-site-code-editor-text-area:-ms-inp
770
777
  padding: 16px;
771
778
  }
772
779
 
773
- .edit-site-screen-background-color__control .block-editor-color-gradient-control__tab-panel {
774
- padding: 16px;
775
- }
776
-
777
780
  .edit-site-global-styles-variations_item {
778
781
  box-sizing: border-box;
779
782
  }
@@ -798,6 +801,13 @@ textarea.edit-site-code-editor-text-area.edit-site-code-editor-text-area:-ms-inp
798
801
 
799
802
  .edit-site-global-styles__color-indicator-wrapper {
800
803
  height: 24px;
804
+ flex-shrink: 0;
805
+ }
806
+
807
+ .edit-site-global-styles__color-label {
808
+ white-space: nowrap;
809
+ overflow: hidden;
810
+ text-overflow: ellipsis;
801
811
  }
802
812
 
803
813
  .edit-site-header {
@@ -1892,6 +1902,7 @@ html.wp-toolbar {
1892
1902
  position: relative;
1893
1903
  height: 100%;
1894
1904
  display: block;
1905
+ overflow: hidden;
1895
1906
  }
1896
1907
  .edit-site-visual-editor iframe {
1897
1908
  display: block;
@@ -1953,11 +1964,7 @@ body.is-fullscreen-mode .edit-site .components-editor-notices__snackbar {
1953
1964
  .edit-site-template-details .edit-site-template-details__group + .edit-site-template-details__group {
1954
1965
  border-top: 1px solid #ccc;
1955
1966
  }
1956
- .edit-site-template-details .edit-site-template-details__title {
1957
- margin: 0;
1958
- }
1959
1967
  .edit-site-template-details .edit-site-template-details__description {
1960
- margin: 12px 0 0;
1961
1968
  color: #757575;
1962
1969
  }
1963
1970
  .edit-site-template-details .edit-site-template-details__group.edit-site-template-details__template-areas {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/edit-site",
3
- "version": "4.11.0",
3
+ "version": "4.13.0",
4
4
  "description": "Edit Site Page module for WordPress.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -27,33 +27,33 @@
27
27
  "react-native": "src/index",
28
28
  "dependencies": {
29
29
  "@babel/runtime": "^7.16.0",
30
- "@wordpress/a11y": "^3.14.0",
31
- "@wordpress/api-fetch": "^6.11.0",
32
- "@wordpress/block-editor": "^9.6.0",
33
- "@wordpress/block-library": "^7.11.0",
34
- "@wordpress/blocks": "^11.13.0",
35
- "@wordpress/components": "^19.16.0",
36
- "@wordpress/compose": "^5.12.0",
37
- "@wordpress/core-data": "^4.12.0",
38
- "@wordpress/data": "^6.14.0",
39
- "@wordpress/deprecated": "^3.14.0",
40
- "@wordpress/editor": "^12.13.0",
41
- "@wordpress/element": "^4.12.0",
42
- "@wordpress/hooks": "^3.14.0",
43
- "@wordpress/html-entities": "^3.14.0",
44
- "@wordpress/i18n": "^4.14.0",
45
- "@wordpress/icons": "^9.5.0",
46
- "@wordpress/interface": "^4.13.0",
47
- "@wordpress/keyboard-shortcuts": "^3.12.0",
48
- "@wordpress/keycodes": "^3.14.0",
49
- "@wordpress/media-utils": "^4.5.0",
50
- "@wordpress/notices": "^3.14.0",
51
- "@wordpress/plugins": "^4.12.0",
52
- "@wordpress/preferences": "^2.6.0",
53
- "@wordpress/reusable-blocks": "^3.12.0",
54
- "@wordpress/style-engine": "^0.13.0",
55
- "@wordpress/url": "^3.15.0",
56
- "@wordpress/viewport": "^4.12.0",
30
+ "@wordpress/a11y": "^3.16.0",
31
+ "@wordpress/api-fetch": "^6.13.0",
32
+ "@wordpress/block-editor": "^9.8.0",
33
+ "@wordpress/block-library": "^7.13.0",
34
+ "@wordpress/blocks": "^11.15.0",
35
+ "@wordpress/components": "^20.0.0",
36
+ "@wordpress/compose": "^5.14.0",
37
+ "@wordpress/core-data": "^4.14.0",
38
+ "@wordpress/data": "^7.0.0",
39
+ "@wordpress/deprecated": "^3.16.0",
40
+ "@wordpress/editor": "^12.15.0",
41
+ "@wordpress/element": "^4.14.0",
42
+ "@wordpress/hooks": "^3.16.0",
43
+ "@wordpress/html-entities": "^3.16.0",
44
+ "@wordpress/i18n": "^4.16.0",
45
+ "@wordpress/icons": "^9.7.0",
46
+ "@wordpress/interface": "^4.15.0",
47
+ "@wordpress/keyboard-shortcuts": "^3.14.0",
48
+ "@wordpress/keycodes": "^3.16.0",
49
+ "@wordpress/media-utils": "^4.7.0",
50
+ "@wordpress/notices": "^3.16.0",
51
+ "@wordpress/plugins": "^4.14.0",
52
+ "@wordpress/preferences": "^2.8.0",
53
+ "@wordpress/reusable-blocks": "^3.14.0",
54
+ "@wordpress/style-engine": "^0.15.0",
55
+ "@wordpress/url": "^3.17.0",
56
+ "@wordpress/viewport": "^4.14.0",
57
57
  "classnames": "^2.3.1",
58
58
  "downloadjs": "^1.4.7",
59
59
  "history": "^5.1.0",
@@ -68,5 +68,5 @@
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
71
- "gitHead": "0315dbc240cb2aa146d7c1bafd251f004b88300e"
71
+ "gitHead": "171b87c7465b93e685e081c5f57f153507363c95"
72
72
  }
@@ -192,9 +192,18 @@ function AddCustomTemplateModal( { onClose, onSelect, entityForSuggestions } ) {
192
192
  isBlock
193
193
  as={ Button }
194
194
  onClick={ () => {
195
- const { slug, title, description } =
196
- entityForSuggestions.template;
197
- onSelect( { slug, title, description } );
195
+ const {
196
+ slug,
197
+ title,
198
+ description,
199
+ templatePrefix,
200
+ } = entityForSuggestions.template;
201
+ onSelect( {
202
+ slug,
203
+ title,
204
+ description,
205
+ templatePrefix,
206
+ } );
198
207
  } }
199
208
  >
200
209
  <Text as="span" weight={ 600 }>
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
+ import apiFetch from '@wordpress/api-fetch';
5
+ import { addQueryArgs } from '@wordpress/url';
4
6
  import {
5
7
  DropdownMenu,
6
8
  MenuGroup,
@@ -26,7 +28,7 @@ import {
26
28
  tag,
27
29
  layout as customGenericTemplateIcon,
28
30
  } from '@wordpress/icons';
29
- import { __ } from '@wordpress/i18n';
31
+ import { __, sprintf } from '@wordpress/i18n';
30
32
  import { store as noticesStore } from '@wordpress/notices';
31
33
 
32
34
  /**
@@ -38,6 +40,7 @@ import {
38
40
  useDefaultTemplateTypes,
39
41
  useTaxonomiesMenuItems,
40
42
  usePostTypeMenuItems,
43
+ useAuthorMenuItem,
41
44
  } from './utils';
42
45
  import AddCustomGenericTemplateModal from './add-custom-generic-template-modal';
43
46
  import { useHistory } from '../routes';
@@ -85,12 +88,25 @@ export default function NewTemplate( { postType } ) {
85
88
 
86
89
  const history = useHistory();
87
90
  const { saveEntityRecord } = useDispatch( coreStore );
88
- const { createErrorNotice } = useDispatch( noticesStore );
91
+ const { createErrorNotice, createSuccessNotice } =
92
+ useDispatch( noticesStore );
89
93
  const { setTemplate } = useDispatch( editSiteStore );
90
94
 
91
95
  async function createTemplate( template, isWPSuggestion = true ) {
92
96
  try {
93
- const { title, description, slug } = template;
97
+ const { title, description, slug, templatePrefix } = template;
98
+ let templateContent = template.content;
99
+ // Try to find fallback content from existing templates.
100
+ if ( ! templateContent ) {
101
+ const fallbackTemplate = await apiFetch( {
102
+ path: addQueryArgs( '/wp/v2/templates/lookup', {
103
+ slug,
104
+ is_custom: ! isWPSuggestion,
105
+ template_prefix: templatePrefix,
106
+ } ),
107
+ } );
108
+ templateContent = fallbackTemplate.content;
109
+ }
94
110
  const newTemplate = await saveEntityRecord(
95
111
  'postType',
96
112
  'wp_template',
@@ -100,6 +116,7 @@ export default function NewTemplate( { postType } ) {
100
116
  slug: slug.toString(),
101
117
  status: 'publish',
102
118
  title,
119
+ content: templateContent,
103
120
  // This adds a post meta field in template that is part of `is_custom` value calculation.
104
121
  is_wp_suggestion: isWPSuggestion,
105
122
  },
@@ -114,8 +131,16 @@ export default function NewTemplate( { postType } ) {
114
131
  postId: newTemplate.id,
115
132
  postType: newTemplate.type,
116
133
  } );
117
-
118
- // TODO: Add a success notice?
134
+ createSuccessNotice(
135
+ sprintf(
136
+ // translators: %s: Title of the created template e.g: "Category".
137
+ __( '"%s" successfully created.' ),
138
+ title
139
+ ),
140
+ {
141
+ type: 'snackbar',
142
+ }
143
+ );
119
144
  } catch ( error ) {
120
145
  const errorMessage =
121
146
  error.message && error.code !== 'unknown_error'
@@ -243,26 +268,30 @@ function useMissingTemplates(
243
268
  useTaxonomiesMenuItems( onClickMenuItem );
244
269
  const { defaultPostTypesMenuItems, postTypesMenuItems } =
245
270
  usePostTypeMenuItems( onClickMenuItem );
246
- [ ...defaultTaxonomiesMenuItems, ...defaultPostTypesMenuItems ].forEach(
247
- ( menuItem ) => {
248
- if ( ! menuItem ) {
249
- return;
250
- }
251
- const matchIndex = enhancedMissingDefaultTemplateTypes.findIndex(
252
- ( template ) => template.slug === menuItem.slug
253
- );
254
- // Some default template types might have been filtered above from
255
- // `missingDefaultTemplates` because they only check for the general
256
- // template. So here we either replace or append the item, augmented
257
- // with the check if it has available specific item to create a
258
- // template for.
259
- if ( matchIndex > -1 ) {
260
- enhancedMissingDefaultTemplateTypes[ matchIndex ] = menuItem;
261
- } else {
262
- enhancedMissingDefaultTemplateTypes.push( menuItem );
263
- }
271
+
272
+ const authorMenuItem = useAuthorMenuItem( onClickMenuItem );
273
+ [
274
+ ...defaultTaxonomiesMenuItems,
275
+ ...defaultPostTypesMenuItems,
276
+ authorMenuItem,
277
+ ].forEach( ( menuItem ) => {
278
+ if ( ! menuItem ) {
279
+ return;
264
280
  }
265
- );
281
+ const matchIndex = enhancedMissingDefaultTemplateTypes.findIndex(
282
+ ( template ) => template.slug === menuItem.slug
283
+ );
284
+ // Some default template types might have been filtered above from
285
+ // `missingDefaultTemplates` because they only check for the general
286
+ // template. So here we either replace or append the item, augmented
287
+ // with the check if it has available specific item to create a
288
+ // template for.
289
+ if ( matchIndex > -1 ) {
290
+ enhancedMissingDefaultTemplateTypes[ matchIndex ] = menuItem;
291
+ } else {
292
+ enhancedMissingDefaultTemplateTypes.push( menuItem );
293
+ }
294
+ } );
266
295
  // Update the sort order to match the DEFAULT_TEMPLATE_SLUGS order.
267
296
  enhancedMissingDefaultTemplateTypes?.sort( ( template1, template2 ) => {
268
297
  return (
@@ -10,7 +10,7 @@ import { useSelect } from '@wordpress/data';
10
10
  import { store as coreStore } from '@wordpress/core-data';
11
11
  import { store as editorStore } from '@wordpress/editor';
12
12
  import { decodeEntities } from '@wordpress/html-entities';
13
- import { useMemo } from '@wordpress/element';
13
+ import { useMemo, useCallback } from '@wordpress/element';
14
14
  import { __, sprintf } from '@wordpress/i18n';
15
15
  import { blockMeta, post } from '@wordpress/icons';
16
16
 
@@ -152,7 +152,10 @@ export const usePostTypeMenuItems = ( onClickMenuItem ) => {
152
152
  );
153
153
  }
154
154
  const menuItem = defaultTemplateType
155
- ? { ...defaultTemplateType }
155
+ ? {
156
+ ...defaultTemplateType,
157
+ templatePrefix: templatePrefixes[ slug ],
158
+ }
156
159
  : {
157
160
  slug: generalTemplateSlug,
158
161
  title: menuItemTitle,
@@ -167,6 +170,7 @@ export const usePostTypeMenuItems = ( onClickMenuItem ) => {
167
170
  icon: icon?.startsWith( 'dashicons-' )
168
171
  ? icon.slice( 10 )
169
172
  : post,
173
+ templatePrefix: templatePrefixes[ slug ],
170
174
  };
171
175
  const hasEntities = postTypesInfo?.[ slug ]?.hasEntities;
172
176
  // We have a different template creation flow only if they have entities.
@@ -201,15 +205,16 @@ export const usePostTypeMenuItems = ( onClickMenuItem ) => {
201
205
  if ( _needsUniqueIdentifier ) {
202
206
  title = sprintf(
203
207
  // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the template title and %2$s is the slug of the post type, e.g. "Project: Hello (project_type)"
204
- __( '%1$s %2$s' ),
208
+ __( '%1$s (%2$s)' ),
205
209
  title,
206
- `(${ slug })`
210
+ slug
207
211
  );
208
212
  }
209
213
  return {
210
214
  title,
211
215
  description,
212
216
  slug: `${ templatePrefixes[ slug ] }-${ suggestion.slug }`,
217
+ templatePrefix: templatePrefixes[ slug ],
213
218
  };
214
219
  },
215
220
  },
@@ -317,7 +322,10 @@ export const useTaxonomiesMenuItems = ( onClickMenuItem ) => {
317
322
  );
318
323
  }
319
324
  const menuItem = defaultTemplateType
320
- ? { ...defaultTemplateType }
325
+ ? {
326
+ ...defaultTemplateType,
327
+ templatePrefix: templatePrefixes[ slug ],
328
+ }
321
329
  : {
322
330
  slug: generalTemplateSlug,
323
331
  title: menuItemTitle,
@@ -327,6 +335,7 @@ export const useTaxonomiesMenuItems = ( onClickMenuItem ) => {
327
335
  labels.singular_name
328
336
  ),
329
337
  icon: blockMeta,
338
+ templatePrefix: templatePrefixes[ slug ],
330
339
  };
331
340
  const hasEntities = taxonomiesInfo?.[ slug ]?.hasEntities;
332
341
  // We have a different template creation flow only if they have entities.
@@ -360,15 +369,16 @@ export const useTaxonomiesMenuItems = ( onClickMenuItem ) => {
360
369
  if ( _needsUniqueIdentifier ) {
361
370
  title = sprintf(
362
371
  // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the template title and %2$s is the slug of the taxonomy, e.g. "Category: shoes (product_tag)"
363
- __( '%1$s %2$s' ),
372
+ __( '%1$s (%2$s)' ),
364
373
  title,
365
- `(${ slug })`
374
+ slug
366
375
  );
367
376
  }
368
377
  return {
369
378
  title,
370
379
  description,
371
380
  slug: `${ templatePrefixes[ slug ] }-${ suggestion.slug }`,
381
+ templatePrefix: templatePrefixes[ slug ],
372
382
  };
373
383
  },
374
384
  },
@@ -408,14 +418,120 @@ export const useTaxonomiesMenuItems = ( onClickMenuItem ) => {
408
418
  return taxonomiesMenuItems;
409
419
  };
410
420
 
421
+ function useAuthorNeedsUniqueIndentifier() {
422
+ const authors = useSelect(
423
+ ( select ) =>
424
+ select( coreStore ).getUsers( { who: 'authors', per_page: -1 } ),
425
+ []
426
+ );
427
+ const authorsCountByName = useMemo( () => {
428
+ return ( authors || [] ).reduce( ( authorsCount, { name } ) => {
429
+ authorsCount[ name ] = ( authorsCount[ name ] || 0 ) + 1;
430
+ return authorsCount;
431
+ }, {} );
432
+ }, [ authors ] );
433
+ return useCallback(
434
+ ( name ) => {
435
+ return authorsCountByName[ name ] > 1;
436
+ },
437
+ [ authorsCountByName ]
438
+ );
439
+ }
440
+
441
+ const USE_AUTHOR_MENU_ITEM_TEMPLATE_PREFIX = { user: 'author' };
442
+ const USE_AUTHOR_MENU_ITEM_QUERY_PARAMETERS = { user: { who: 'authors' } };
443
+ export function useAuthorMenuItem( onClickMenuItem ) {
444
+ const existingTemplates = useExistingTemplates();
445
+ const defaultTemplateTypes = useDefaultTemplateTypes();
446
+ const authorInfo = useEntitiesInfo(
447
+ 'root',
448
+ USE_AUTHOR_MENU_ITEM_TEMPLATE_PREFIX,
449
+ USE_AUTHOR_MENU_ITEM_QUERY_PARAMETERS
450
+ );
451
+ const authorNeedsUniqueId = useAuthorNeedsUniqueIndentifier();
452
+ let authorMenuItem = defaultTemplateTypes?.find(
453
+ ( { slug } ) => slug === 'author'
454
+ );
455
+ if ( ! authorMenuItem ) {
456
+ authorMenuItem = {
457
+ description: __(
458
+ 'Displays latest posts written by a single author.'
459
+ ),
460
+ slug: 'author',
461
+ title: 'Author',
462
+ };
463
+ }
464
+ const hasGeneralTemplate = !! existingTemplates?.find(
465
+ ( { slug } ) => slug === 'author'
466
+ );
467
+ if ( authorInfo.user?.hasEntities ) {
468
+ authorMenuItem = { ...authorMenuItem, templatePrefix: 'author' };
469
+ authorMenuItem.onClick = ( template ) => {
470
+ onClickMenuItem( {
471
+ type: 'root',
472
+ slug: 'user',
473
+ config: {
474
+ queryArgs: ( { search } ) => {
475
+ return {
476
+ _fields: 'id,name,slug,link',
477
+ orderBy: search ? 'name' : 'registered_date',
478
+ exclude: authorInfo.user.existingEntitiesIds,
479
+ who: 'authors',
480
+ };
481
+ },
482
+ getSpecificTemplate: ( suggestion ) => {
483
+ const needsUniqueId = authorNeedsUniqueId(
484
+ suggestion.name
485
+ );
486
+ const title = needsUniqueId
487
+ ? sprintf(
488
+ // translators: %1$s: Represents the name of an author e.g: "Jorge", %2$s: Represents the slug of an author e.g: "author-jorge-slug".
489
+ __( 'Author: %1$s (%2$s)' ),
490
+ suggestion.name,
491
+ suggestion.slug
492
+ )
493
+ : sprintf(
494
+ // translators: %s: Represents the name of an author e.g: "Jorge".
495
+ __( 'Author: %s' ),
496
+ suggestion.name
497
+ );
498
+ const description = sprintf(
499
+ // translators: %s: Represents the name of an author e.g: "Jorge".
500
+ __( 'Template for Author: %s' ),
501
+ suggestion.name
502
+ );
503
+ return {
504
+ title,
505
+ description,
506
+ slug: `author-${ suggestion.slug }`,
507
+ templatePrefix: 'author',
508
+ };
509
+ },
510
+ },
511
+ labels: {
512
+ singular_name: __( 'Author' ),
513
+ search_items: __( 'Search Authors' ),
514
+ not_found: __( 'No authors found.' ),
515
+ all_items: __( 'All Authors' ),
516
+ },
517
+ hasGeneralTemplate,
518
+ template,
519
+ } );
520
+ };
521
+ }
522
+ if ( ! hasGeneralTemplate || authorInfo.user?.hasEntities ) {
523
+ return authorMenuItem;
524
+ }
525
+ }
526
+
411
527
  /**
412
528
  * Helper hook that filters all the existing templates by the given
413
529
  * object with the entity's slug as key and the template prefix as value.
414
530
  *
415
531
  * Example:
416
- * `existingTemplates` is: [ { slug: tag-apple }, { slug: page-about }, { slug: tag } ]
532
+ * `existingTemplates` is: [ { slug: 'tag-apple' }, { slug: 'page-about' }, { slug: 'tag' } ]
417
533
  * `templatePrefixes` is: { post_tag: 'tag' }
418
- * It will return: { post_tag: [apple] }
534
+ * It will return: { post_tag: ['apple'] }
419
535
  *
420
536
  * Note: We append the `-` to the given template prefix in this function for our checks.
421
537
  *
@@ -456,11 +572,16 @@ const useExistingTemplateSlugs = ( templatePrefixes ) => {
456
572
  * Helper hook that finds the existing records with an associated template,
457
573
  * as they need to be excluded from the template suggestions.
458
574
  *
459
- * @param {string} entityName The entity's name.
460
- * @param {Record<string,string>} templatePrefixes An object with the entity's slug as key and the template prefix as value.
575
+ * @param {string} entityName The entity's name.
576
+ * @param {Record<string,string>} templatePrefixes An object with the entity's slug as key and the template prefix as value.
577
+ * @param {Record<string,Object>} additionalQueryParameters An object with the entity's slug as key and additional query parameters as value.
461
578
  * @return {Record<string,EntitiesInfo>} An object with the entity's slug as key and the existing records as value.
462
579
  */
463
- const useTemplatesToExclude = ( entityName, templatePrefixes ) => {
580
+ const useTemplatesToExclude = (
581
+ entityName,
582
+ templatePrefixes,
583
+ additionalQueryParameters = {}
584
+ ) => {
464
585
  const slugsToExcludePerEntity =
465
586
  useExistingTemplateSlugs( templatePrefixes );
466
587
  const recordsToExcludePerEntity = useSelect(
@@ -473,6 +594,7 @@ const useTemplatesToExclude = ( entityName, templatePrefixes ) => {
473
594
  _fields: 'id',
474
595
  context: 'view',
475
596
  slug: slugsWithTemplates,
597
+ ...additionalQueryParameters[ slug ],
476
598
  } );
477
599
  if ( entitiesWithTemplates?.length ) {
478
600
  accumulator[ slug ] = entitiesWithTemplates;
@@ -497,14 +619,20 @@ const useTemplatesToExclude = ( entityName, templatePrefixes ) => {
497
619
  * First we need to find the existing records with an associated template,
498
620
  * to query afterwards for any remaining record, by excluding them.
499
621
  *
500
- * @param {string} entityName The entity's name.
501
- * @param {Record<string,string>} templatePrefixes An object with the entity's slug as key and the template prefix as value.
622
+ * @param {string} entityName The entity's name.
623
+ * @param {Record<string,string>} templatePrefixes An object with the entity's slug as key and the template prefix as value.
624
+ * @param {Record<string,Object>} additionalQueryParameters An object with the entity's slug as key and additional query parameters as value.
502
625
  * @return {Record<string,EntitiesInfo>} An object with the entity's slug as key and the EntitiesInfo as value.
503
626
  */
504
- const useEntitiesInfo = ( entityName, templatePrefixes ) => {
627
+ const useEntitiesInfo = (
628
+ entityName,
629
+ templatePrefixes,
630
+ additionalQueryParameters = {}
631
+ ) => {
505
632
  const recordsToExcludePerEntity = useTemplatesToExclude(
506
633
  entityName,
507
- templatePrefixes
634
+ templatePrefixes,
635
+ additionalQueryParameters
508
636
  );
509
637
  const entitiesInfo = useSelect(
510
638
  ( select ) => {
@@ -523,6 +651,7 @@ const useEntitiesInfo = ( entityName, templatePrefixes ) => {
523
651
  _fields: 'id',
524
652
  context: 'view',
525
653
  exclude: existingEntitiesIds,
654
+ ...additionalQueryParameters[ slug ],
526
655
  }
527
656
  )?.length,
528
657
  existingEntitiesIds,
@@ -57,20 +57,31 @@ function ResizableEditor( { enableResizing, settings, children, ...props } ) {
57
57
  return;
58
58
  }
59
59
 
60
- let animationFrame = null;
60
+ let timeoutId = null;
61
61
 
62
62
  function resizeHeight() {
63
- if ( ! animationFrame ) {
64
- // Throttle the updates on animation frame.
65
- animationFrame = iframe.contentWindow.requestAnimationFrame(
66
- () => {
67
- setHeight(
68
- iframe.contentDocument.documentElement
69
- .scrollHeight
70
- );
71
- animationFrame = null;
63
+ if ( ! timeoutId ) {
64
+ // Throttle the updates on timeout. This code previously
65
+ // used `requestAnimationFrame`, but that seems to not
66
+ // always work before an iframe is ready.
67
+ timeoutId = iframe.contentWindow.setTimeout( () => {
68
+ const { readyState } = iframe.contentDocument;
69
+
70
+ // Continue deferring the timeout until the document is ready.
71
+ // Only then will it have a height.
72
+ if (
73
+ readyState !== 'interactive' &&
74
+ readyState !== 'complete'
75
+ ) {
76
+ resizeHeight();
77
+ return;
72
78
  }
73
- );
79
+
80
+ setHeight( iframe.contentDocument.body.scrollHeight );
81
+ timeoutId = null;
82
+
83
+ // 30 frames per second.
84
+ }, 1000 / 30 );
74
85
  }
75
86
  }
76
87
 
@@ -82,11 +93,10 @@ function ResizableEditor( { enableResizing, settings, children, ...props } ) {
82
93
  resizeObserver = new iframe.contentWindow.ResizeObserver(
83
94
  resizeHeight
84
95
  );
85
- // Observing the <html> rather than the <body> because the latter
86
- // gets destroyed and remounted after initialization in <Iframe>.
87
- resizeObserver.observe(
88
- iframe.contentDocument.documentElement
89
- );
96
+
97
+ // Observe the body, since the `html` element seems to always
98
+ // have a height of `100%`.
99
+ resizeObserver.observe( iframe.contentDocument.body );
90
100
 
91
101
  resizeHeight();
92
102
  }
@@ -97,7 +107,7 @@ function ResizableEditor( { enableResizing, settings, children, ...props } ) {
97
107
  registerObserver();
98
108
 
99
109
  return () => {
100
- iframe.contentWindow?.cancelAnimationFrame( animationFrame );
110
+ iframe.contentWindow?.clearTimeout( timeoutId );
101
111
  resizeObserver?.disconnect();
102
112
  iframe.removeEventListener( 'load', registerObserver );
103
113
  };
@@ -153,7 +163,7 @@ function ResizableEditor( { enableResizing, settings, children, ...props } ) {
153
163
  } }
154
164
  >
155
165
  <Iframe
156
- style={ enableResizing ? undefined : deviceStyles }
166
+ style={ enableResizing ? { height } : deviceStyles }
157
167
  head={
158
168
  <>
159
169
  <EditorStyles styles={ settings.styles } />
@@ -32,6 +32,7 @@ html.wp-toolbar {
32
32
  position: relative;
33
33
  height: 100%;
34
34
  display: block;
35
+ overflow: hidden;
35
36
 
36
37
  iframe {
37
38
  display: block;
@@ -179,11 +179,11 @@ export default function BorderPanel( { name } ) {
179
179
  <BorderBoxControl
180
180
  colors={ colors }
181
181
  enableAlpha={ true }
182
+ enableStyle={ showBorderStyle }
182
183
  onChange={ onBorderChange }
183
- showStyle={ showBorderStyle }
184
- value={ border }
185
- popoverPlacement="left-start"
186
184
  popoverOffset={ 40 }
185
+ popoverPlacement="left-start"
186
+ value={ border }
187
187
  __experimentalHasMultipleOrigins={ true }
188
188
  __experimentalIsRenderedInSidebar={ true }
189
189
  />