@wordpress/edit-site 4.11.0 → 4.12.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 (127) hide show
  1. package/CHANGELOG.md +2 -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 +22 -2
  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/global-styles/dimensions-panel.js +6 -2
  9. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  10. package/build/components/global-styles/hooks.js +1 -1
  11. package/build/components/global-styles/hooks.js.map +1 -1
  12. package/build/components/global-styles/screen-color-palette.js +1 -1
  13. package/build/components/global-styles/screen-color-palette.js.map +1 -1
  14. package/build/components/global-styles/screen-colors.js +51 -7
  15. package/build/components/global-styles/screen-colors.js.map +1 -1
  16. package/build/components/global-styles/screen-heading-color.js +157 -0
  17. package/build/components/global-styles/screen-heading-color.js.map +1 -0
  18. package/build/components/global-styles/screen-typography-element.js +4 -0
  19. package/build/components/global-styles/screen-typography-element.js.map +1 -1
  20. package/build/components/global-styles/screen-typography.js +5 -0
  21. package/build/components/global-styles/screen-typography.js.map +1 -1
  22. package/build/components/global-styles/typography-panel.js +73 -12
  23. package/build/components/global-styles/typography-panel.js.map +1 -1
  24. package/build/components/global-styles/typography-utils.js +217 -0
  25. package/build/components/global-styles/typography-utils.js.map +1 -0
  26. package/build/components/global-styles/ui.js +11 -0
  27. package/build/components/global-styles/ui.js.map +1 -1
  28. package/build/components/global-styles/use-global-styles-output.js +61 -40
  29. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  30. package/build/components/global-styles/utils.js +17 -2
  31. package/build/components/global-styles/utils.js.map +1 -1
  32. package/build/components/header/index.js +15 -4
  33. package/build/components/header/index.js.map +1 -1
  34. package/build/components/header/undo-redo/redo.js +2 -1
  35. package/build/components/header/undo-redo/redo.js.map +1 -1
  36. package/build/components/list/actions/index.js +1 -1
  37. package/build/components/list/actions/index.js.map +1 -1
  38. package/build/components/save-button/index.js +2 -3
  39. package/build/components/save-button/index.js.map +1 -1
  40. package/build/components/sidebar/navigation-menu-sidebar/navigation-menu.js +2 -2
  41. package/build/components/sidebar/navigation-menu-sidebar/navigation-menu.js.map +1 -1
  42. package/build/components/sidebar/template-card/template-actions.js +1 -1
  43. package/build/components/sidebar/template-card/template-actions.js.map +1 -1
  44. package/build/components/template-details/index.js +1 -1
  45. package/build/components/template-details/index.js.map +1 -1
  46. package/build/components/template-details/template-areas.js +1 -1
  47. package/build/components/template-details/template-areas.js.map +1 -1
  48. package/build/components/template-part-converter/convert-to-template-part.js +4 -1
  49. package/build/components/template-part-converter/convert-to-template-part.js.map +1 -1
  50. package/build-module/components/add-new-template/add-custom-template-modal.js +4 -2
  51. package/build-module/components/add-new-template/add-custom-template-modal.js.map +1 -1
  52. package/build-module/components/add-new-template/new-template.js +21 -3
  53. package/build-module/components/add-new-template/new-template.js.map +1 -1
  54. package/build-module/components/add-new-template/utils.js +147 -26
  55. package/build-module/components/add-new-template/utils.js.map +1 -1
  56. package/build-module/components/global-styles/dimensions-panel.js +6 -2
  57. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  58. package/build-module/components/global-styles/hooks.js +1 -1
  59. package/build-module/components/global-styles/hooks.js.map +1 -1
  60. package/build-module/components/global-styles/screen-color-palette.js +1 -1
  61. package/build-module/components/global-styles/screen-color-palette.js.map +1 -1
  62. package/build-module/components/global-styles/screen-colors.js +51 -7
  63. package/build-module/components/global-styles/screen-colors.js.map +1 -1
  64. package/build-module/components/global-styles/screen-heading-color.js +143 -0
  65. package/build-module/components/global-styles/screen-heading-color.js.map +1 -0
  66. package/build-module/components/global-styles/screen-typography-element.js +4 -0
  67. package/build-module/components/global-styles/screen-typography-element.js.map +1 -1
  68. package/build-module/components/global-styles/screen-typography.js +5 -0
  69. package/build-module/components/global-styles/screen-typography.js.map +1 -1
  70. package/build-module/components/global-styles/typography-panel.js +74 -13
  71. package/build-module/components/global-styles/typography-panel.js.map +1 -1
  72. package/build-module/components/global-styles/typography-utils.js +204 -0
  73. package/build-module/components/global-styles/typography-utils.js.map +1 -0
  74. package/build-module/components/global-styles/ui.js +10 -0
  75. package/build-module/components/global-styles/ui.js.map +1 -1
  76. package/build-module/components/global-styles/use-global-styles-output.js +61 -44
  77. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  78. package/build-module/components/global-styles/utils.js +16 -2
  79. package/build-module/components/global-styles/utils.js.map +1 -1
  80. package/build-module/components/header/index.js +17 -6
  81. package/build-module/components/header/index.js.map +1 -1
  82. package/build-module/components/header/undo-redo/redo.js +3 -2
  83. package/build-module/components/header/undo-redo/redo.js.map +1 -1
  84. package/build-module/components/list/actions/index.js +1 -1
  85. package/build-module/components/list/actions/index.js.map +1 -1
  86. package/build-module/components/save-button/index.js +3 -4
  87. package/build-module/components/save-button/index.js.map +1 -1
  88. package/build-module/components/sidebar/navigation-menu-sidebar/navigation-menu.js +3 -3
  89. package/build-module/components/sidebar/navigation-menu-sidebar/navigation-menu.js.map +1 -1
  90. package/build-module/components/sidebar/template-card/template-actions.js +1 -1
  91. package/build-module/components/sidebar/template-card/template-actions.js.map +1 -1
  92. package/build-module/components/template-details/index.js +1 -1
  93. package/build-module/components/template-details/index.js.map +1 -1
  94. package/build-module/components/template-details/template-areas.js +1 -1
  95. package/build-module/components/template-details/template-areas.js.map +1 -1
  96. package/build-module/components/template-part-converter/convert-to-template-part.js +3 -1
  97. package/build-module/components/template-part-converter/convert-to-template-part.js.map +1 -1
  98. package/build-style/style-rtl.css +13 -4
  99. package/build-style/style.css +13 -4
  100. package/package.json +29 -29
  101. package/src/components/add-new-template/add-custom-template-modal.js +12 -3
  102. package/src/components/add-new-template/new-template.js +40 -20
  103. package/src/components/add-new-template/utils.js +145 -16
  104. package/src/components/global-styles/dimensions-panel.js +5 -3
  105. package/src/components/global-styles/hooks.js +1 -0
  106. package/src/components/global-styles/screen-color-palette.js +1 -1
  107. package/src/components/global-styles/screen-colors.js +46 -4
  108. package/src/components/global-styles/screen-heading-color.js +201 -0
  109. package/src/components/global-styles/screen-typography-element.js +4 -0
  110. package/src/components/global-styles/screen-typography.js +6 -0
  111. package/src/components/global-styles/style.scss +13 -6
  112. package/src/components/global-styles/test/typography-utils.js +130 -0
  113. package/src/components/global-styles/test/use-global-styles-output.js +66 -3
  114. package/src/components/global-styles/typography-panel.js +85 -16
  115. package/src/components/global-styles/typography-utils.js +228 -0
  116. package/src/components/global-styles/ui.js +13 -0
  117. package/src/components/global-styles/use-global-styles-output.js +79 -48
  118. package/src/components/global-styles/utils.js +10 -0
  119. package/src/components/header/index.js +28 -3
  120. package/src/components/header/undo-redo/redo.js +6 -2
  121. package/src/components/list/actions/index.js +3 -1
  122. package/src/components/save-button/index.js +10 -13
  123. package/src/components/sidebar/navigation-menu-sidebar/navigation-menu.js +1 -5
  124. package/src/components/sidebar/template-card/template-actions.js +3 -1
  125. package/src/components/template-details/index.js +3 -1
  126. package/src/components/template-details/template-areas.js +3 -1
  127. package/src/components/template-part-converter/convert-to-template-part.js +3 -1
@@ -722,6 +722,11 @@ 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__half-width-control {
726
+ width: calc((100% - 24px) / 2);
727
+ }
728
+
729
+ .edit-site-global-styles-screen-heading-color,
725
730
  .edit-site-global-styles-screen-typography {
726
731
  margin: 16px;
727
732
  }
@@ -741,6 +746,7 @@ textarea.edit-site-code-editor-text-area.edit-site-code-editor-text-area:-ms-inp
741
746
  }
742
747
  .edit-site-global-styles-screen-colors .component-color-indicator {
743
748
  background: linear-gradient(-45deg, transparent 48%, #ddd 48%, #ddd 52%, transparent 52%);
749
+ flex-shrink: 0;
744
750
  }
745
751
 
746
752
  .edit-site-global-styles-header__description,
@@ -770,10 +776,6 @@ textarea.edit-site-code-editor-text-area.edit-site-code-editor-text-area:-ms-inp
770
776
  padding: 16px;
771
777
  }
772
778
 
773
- .edit-site-screen-background-color__control .block-editor-color-gradient-control__tab-panel {
774
- padding: 16px;
775
- }
776
-
777
779
  .edit-site-global-styles-variations_item {
778
780
  box-sizing: border-box;
779
781
  }
@@ -798,6 +800,13 @@ textarea.edit-site-code-editor-text-area.edit-site-code-editor-text-area:-ms-inp
798
800
 
799
801
  .edit-site-global-styles__color-indicator-wrapper {
800
802
  height: 24px;
803
+ flex-shrink: 0;
804
+ }
805
+
806
+ .edit-site-global-styles__color-label {
807
+ white-space: nowrap;
808
+ overflow: hidden;
809
+ text-overflow: ellipsis;
801
810
  }
802
811
 
803
812
  .edit-site-header {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/edit-site",
3
- "version": "4.11.0",
3
+ "version": "4.12.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.15.0",
31
+ "@wordpress/api-fetch": "^6.12.0",
32
+ "@wordpress/block-editor": "^9.7.0",
33
+ "@wordpress/block-library": "^7.12.0",
34
+ "@wordpress/blocks": "^11.14.0",
35
+ "@wordpress/components": "^19.17.0",
36
+ "@wordpress/compose": "^5.13.0",
37
+ "@wordpress/core-data": "^4.13.0",
38
+ "@wordpress/data": "^6.15.0",
39
+ "@wordpress/deprecated": "^3.15.0",
40
+ "@wordpress/editor": "^12.14.0",
41
+ "@wordpress/element": "^4.13.0",
42
+ "@wordpress/hooks": "^3.15.0",
43
+ "@wordpress/html-entities": "^3.15.0",
44
+ "@wordpress/i18n": "^4.15.0",
45
+ "@wordpress/icons": "^9.6.0",
46
+ "@wordpress/interface": "^4.14.0",
47
+ "@wordpress/keyboard-shortcuts": "^3.13.0",
48
+ "@wordpress/keycodes": "^3.15.0",
49
+ "@wordpress/media-utils": "^4.6.0",
50
+ "@wordpress/notices": "^3.15.0",
51
+ "@wordpress/plugins": "^4.13.0",
52
+ "@wordpress/preferences": "^2.7.0",
53
+ "@wordpress/reusable-blocks": "^3.13.0",
54
+ "@wordpress/style-engine": "^0.14.0",
55
+ "@wordpress/url": "^3.16.0",
56
+ "@wordpress/viewport": "^4.13.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": "08358f53b627a15148c3a3e433cdf58cf8714aa4"
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,
@@ -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';
@@ -90,7 +93,19 @@ export default function NewTemplate( { postType } ) {
90
93
 
91
94
  async function createTemplate( template, isWPSuggestion = true ) {
92
95
  try {
93
- const { title, description, slug } = template;
96
+ const { title, description, slug, templatePrefix } = template;
97
+ let templateContent = template.content;
98
+ // Try to find fallback content from existing templates.
99
+ if ( ! templateContent ) {
100
+ const fallbackTemplate = await apiFetch( {
101
+ path: addQueryArgs( '/wp/v2/templates/lookup', {
102
+ slug,
103
+ is_custom: ! isWPSuggestion,
104
+ template_prefix: templatePrefix,
105
+ } ),
106
+ } );
107
+ templateContent = fallbackTemplate.content;
108
+ }
94
109
  const newTemplate = await saveEntityRecord(
95
110
  'postType',
96
111
  'wp_template',
@@ -100,6 +115,7 @@ export default function NewTemplate( { postType } ) {
100
115
  slug: slug.toString(),
101
116
  status: 'publish',
102
117
  title,
118
+ content: templateContent,
103
119
  // This adds a post meta field in template that is part of `is_custom` value calculation.
104
120
  is_wp_suggestion: isWPSuggestion,
105
121
  },
@@ -243,26 +259,30 @@ function useMissingTemplates(
243
259
  useTaxonomiesMenuItems( onClickMenuItem );
244
260
  const { defaultPostTypesMenuItems, postTypesMenuItems } =
245
261
  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
- }
262
+
263
+ const authorMenuItem = useAuthorMenuItem( onClickMenuItem );
264
+ [
265
+ ...defaultTaxonomiesMenuItems,
266
+ ...defaultPostTypesMenuItems,
267
+ authorMenuItem,
268
+ ].forEach( ( menuItem ) => {
269
+ if ( ! menuItem ) {
270
+ return;
264
271
  }
265
- );
272
+ const matchIndex = enhancedMissingDefaultTemplateTypes.findIndex(
273
+ ( template ) => template.slug === menuItem.slug
274
+ );
275
+ // Some default template types might have been filtered above from
276
+ // `missingDefaultTemplates` because they only check for the general
277
+ // template. So here we either replace or append the item, augmented
278
+ // with the check if it has available specific item to create a
279
+ // template for.
280
+ if ( matchIndex > -1 ) {
281
+ enhancedMissingDefaultTemplateTypes[ matchIndex ] = menuItem;
282
+ } else {
283
+ enhancedMissingDefaultTemplateTypes.push( menuItem );
284
+ }
285
+ } );
266
286
  // Update the sort order to match the DEFAULT_TEMPLATE_SLUGS order.
267
287
  enhancedMissingDefaultTemplateTypes?.sort( ( template1, template2 ) => {
268
288
  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,
@@ -160,8 +160,9 @@ function usePaddingProps( name ) {
160
160
  setRawPadding( padding );
161
161
  };
162
162
  const resetPaddingValue = () => setPaddingValues( {} );
163
- const hasPaddingValue = () =>
164
- !! paddingValues && Object.keys( paddingValues ).length;
163
+ const [ userSetPaddingValue ] = useStyle( 'spacing.padding', name, 'user' );
164
+ // The `hasPaddingValue` check does not need a parsed value, as `userSetPaddingValue` will be `undefined` if not set.
165
+ const hasPaddingValue = () => !! userSetPaddingValue;
165
166
 
166
167
  return {
167
168
  paddingValues,
@@ -204,7 +205,8 @@ function useMarginProps( name ) {
204
205
  function useBlockGapProps( name ) {
205
206
  const [ gapValue, setGapValue ] = useStyle( 'spacing.blockGap', name );
206
207
  const resetGapValue = () => setGapValue( undefined );
207
- const hasGapValue = () => !! gapValue;
208
+ const [ userSetGapValue ] = useStyle( 'spacing.blockGap', name, 'user' );
209
+ const hasGapValue = () => !! userSetGapValue;
208
210
  return {
209
211
  gapValue,
210
212
  setGapValue,
@@ -170,6 +170,7 @@ const ROOT_BLOCK_SUPPORTS = [
170
170
  'padding',
171
171
  'contentSize',
172
172
  'wideSize',
173
+ 'blockGap',
173
174
  ];
174
175
 
175
176
  export function getSupportedGlobalStylesPanels( name ) {
@@ -24,7 +24,7 @@ function ScreenColorPalette( { name } ) {
24
24
  tabs={ [
25
25
  {
26
26
  name: 'solid',
27
- title: 'Solid color',
27
+ title: 'Solid',
28
28
  value: 'solid',
29
29
  },
30
30
  {
@@ -45,7 +45,9 @@ function BackgroundColorItem( { name, parentMenu } ) {
45
45
  data-testid="background-color-indicator"
46
46
  />
47
47
  </ColorIndicatorWrapper>
48
- <FlexItem>{ __( 'Background' ) }</FlexItem>
48
+ <FlexItem className="edit-site-global-styles__color-label">
49
+ { __( 'Background' ) }
50
+ </FlexItem>
49
51
  </HStack>
50
52
  </NavigationButtonAsItem>
51
53
  );
@@ -72,7 +74,9 @@ function TextColorItem( { name, parentMenu } ) {
72
74
  data-testid="text-color-indicator"
73
75
  />
74
76
  </ColorIndicatorWrapper>
75
- <FlexItem>{ __( 'Text' ) }</FlexItem>
77
+ <FlexItem className="edit-site-global-styles__color-label">
78
+ { __( 'Text' ) }
79
+ </FlexItem>
76
80
  </HStack>
77
81
  </NavigationButtonAsItem>
78
82
  );
@@ -102,7 +106,39 @@ function LinkColorItem( { name, parentMenu } ) {
102
106
  <ColorIndicator colorValue={ colorHover } />
103
107
  </ColorIndicatorWrapper>
104
108
  </ZStack>
105
- <FlexItem>{ __( 'Links' ) }</FlexItem>
109
+ <FlexItem className="edit-site-global-styles__color-label">
110
+ { __( 'Links' ) }
111
+ </FlexItem>
112
+ </HStack>
113
+ </NavigationButtonAsItem>
114
+ );
115
+ }
116
+
117
+ function HeadingColorItem( { name, parentMenu } ) {
118
+ const supports = getSupportedGlobalStylesPanels( name );
119
+ const hasSupport = supports.includes( 'color' );
120
+ const [ color ] = useStyle( 'elements.heading.color.text', name );
121
+ const [ bgColor ] = useStyle( 'elements.heading.color.background', name );
122
+
123
+ if ( ! hasSupport ) {
124
+ return null;
125
+ }
126
+
127
+ return (
128
+ <NavigationButtonAsItem
129
+ path={ parentMenu + '/colors/heading' }
130
+ aria-label={ __( 'Colors heading styles' ) }
131
+ >
132
+ <HStack justify="flex-start">
133
+ <ZStack isLayered={ false } offset={ -8 }>
134
+ <ColorIndicatorWrapper expanded={ false }>
135
+ <ColorIndicator colorValue={ bgColor } />
136
+ </ColorIndicatorWrapper>
137
+ <ColorIndicatorWrapper expanded={ false }>
138
+ <ColorIndicator colorValue={ color } />
139
+ </ColorIndicatorWrapper>
140
+ </ZStack>
141
+ <FlexItem>{ __( 'Headings' ) }</FlexItem>
106
142
  </HStack>
107
143
  </NavigationButtonAsItem>
108
144
  );
@@ -129,7 +165,9 @@ function ButtonColorItem( { name, parentMenu } ) {
129
165
  <ColorIndicator colorValue={ color } />
130
166
  </ColorIndicatorWrapper>
131
167
  </ZStack>
132
- <FlexItem>{ __( 'Buttons' ) }</FlexItem>
168
+ <FlexItem className="edit-site-global-styles__color-label">
169
+ { __( 'Buttons' ) }
170
+ </FlexItem>
133
171
  </HStack>
134
172
  </NavigationButtonAsItem>
135
173
  );
@@ -166,6 +204,10 @@ function ScreenColors( { name } ) {
166
204
  name={ name }
167
205
  parentMenu={ parentMenu }
168
206
  />
207
+ <HeadingColorItem
208
+ name={ name }
209
+ parentMenu={ parentMenu }
210
+ />
169
211
  <ButtonColorItem
170
212
  name={ name }
171
213
  parentMenu={ parentMenu }