@wordpress/edit-site 4.7.0 → 4.10.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 (248) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/add-new-template/add-custom-generic-template-modal.js +84 -0
  3. package/build/components/add-new-template/add-custom-generic-template-modal.js.map +1 -0
  4. package/build/components/add-new-template/add-custom-template-modal.js +224 -0
  5. package/build/components/add-new-template/add-custom-template-modal.js.map +1 -0
  6. package/build/components/add-new-template/new-template.js +94 -33
  7. package/build/components/add-new-template/new-template.js.map +1 -1
  8. package/build/components/add-new-template/utils.js +405 -0
  9. package/build/components/add-new-template/utils.js.map +1 -0
  10. package/build/components/block-editor/block-inspector-button.js.map +1 -1
  11. package/build/components/block-editor/index.js.map +1 -1
  12. package/build/components/code-editor/index.js +17 -4
  13. package/build/components/code-editor/index.js.map +1 -1
  14. package/build/components/edit-template-part-menu-button/index.js.map +1 -1
  15. package/build/components/editor/index.js +16 -0
  16. package/build/components/editor/index.js.map +1 -1
  17. package/build/components/error-boundary/index.js +6 -0
  18. package/build/components/error-boundary/index.js.map +1 -1
  19. package/build/components/global-styles/context-menu.js +6 -3
  20. package/build/components/global-styles/context-menu.js.map +1 -1
  21. package/build/components/global-styles/dimensions-panel.js +2 -6
  22. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  23. package/build/components/global-styles/global-styles-provider.js +4 -2
  24. package/build/components/global-styles/global-styles-provider.js.map +1 -1
  25. package/build/components/global-styles/gradients-palette-panel.js +3 -7
  26. package/build/components/global-styles/gradients-palette-panel.js.map +1 -1
  27. package/build/components/global-styles/hooks.js +11 -2
  28. package/build/components/global-styles/hooks.js.map +1 -1
  29. package/build/components/global-styles/palette.js +2 -1
  30. package/build/components/global-styles/palette.js.map +1 -1
  31. package/build/components/global-styles/screen-block-list.js +4 -1
  32. package/build/components/global-styles/screen-block-list.js.map +1 -1
  33. package/build/components/global-styles/screen-button-color.js +80 -0
  34. package/build/components/global-styles/screen-button-color.js.map +1 -0
  35. package/build/components/global-styles/screen-color-palette.js +13 -17
  36. package/build/components/global-styles/screen-color-palette.js.map +1 -1
  37. package/build/components/global-styles/screen-colors.js +56 -8
  38. package/build/components/global-styles/screen-colors.js.map +1 -1
  39. package/build/components/global-styles/screen-link-color.js +48 -14
  40. package/build/components/global-styles/screen-link-color.js.map +1 -1
  41. package/build/components/global-styles/screen-root.js +4 -2
  42. package/build/components/global-styles/screen-root.js.map +1 -1
  43. package/build/components/global-styles/screen-style-variations.js +9 -1
  44. package/build/components/global-styles/screen-style-variations.js.map +1 -1
  45. package/build/components/global-styles/screen-typography-element.js +4 -0
  46. package/build/components/global-styles/screen-typography-element.js.map +1 -1
  47. package/build/components/global-styles/screen-typography.js +9 -1
  48. package/build/components/global-styles/screen-typography.js.map +1 -1
  49. package/build/components/global-styles/ui.js +11 -0
  50. package/build/components/global-styles/ui.js.map +1 -1
  51. package/build/components/global-styles/use-global-styles-output.js +199 -30
  52. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  53. package/build/components/global-styles/utils.js +4 -2
  54. package/build/components/global-styles/utils.js.map +1 -1
  55. package/build/components/header/index.js +28 -10
  56. package/build/components/header/index.js.map +1 -1
  57. package/build/components/header/mode-switcher/index.js.map +1 -1
  58. package/build/components/header/more-menu/copy-content-menu-item.js +1 -1
  59. package/build/components/header/more-menu/copy-content-menu-item.js.map +1 -1
  60. package/build/components/header/more-menu/site-export.js +4 -1
  61. package/build/components/header/more-menu/site-export.js.map +1 -1
  62. package/build/components/header/undo-redo/redo.js +13 -4
  63. package/build/components/header/undo-redo/redo.js.map +1 -1
  64. package/build/components/header/undo-redo/undo.js +13 -4
  65. package/build/components/header/undo-redo/undo.js.map +1 -1
  66. package/build/components/keyboard-shortcut-help-modal/config.js +17 -0
  67. package/build/components/keyboard-shortcut-help-modal/config.js.map +1 -1
  68. package/build/components/keyboard-shortcut-help-modal/index.js +1 -3
  69. package/build/components/keyboard-shortcut-help-modal/index.js.map +1 -1
  70. package/build/components/keyboard-shortcuts/index.js.map +1 -1
  71. package/build/components/list/actions/index.js.map +1 -1
  72. package/build/components/list/actions/rename-menu-item.js.map +1 -1
  73. package/build/components/list/added-by.js.map +1 -1
  74. package/build/components/navigation-sidebar/index.js.map +1 -1
  75. package/build/components/save-button/index.js.map +1 -1
  76. package/build/components/sidebar/index.js.map +1 -1
  77. package/build/components/sidebar/navigation-menu-sidebar/navigation-inspector.js.map +1 -1
  78. package/build/components/sidebar/template-card/index.js +19 -7
  79. package/build/components/sidebar/template-card/index.js.map +1 -1
  80. package/build/components/sidebar/template-card/template-actions.js +64 -0
  81. package/build/components/sidebar/template-card/template-actions.js.map +1 -0
  82. package/build/components/sidebar/template-card/template-areas.js.map +1 -1
  83. package/build/components/template-details/edit-template-title.js +11 -3
  84. package/build/components/template-details/edit-template-title.js.map +1 -1
  85. package/build/components/template-details/index.js +1 -20
  86. package/build/components/template-details/index.js.map +1 -1
  87. package/build/components/template-details/template-areas.js.map +1 -1
  88. package/build/components/template-part-converter/index.js.map +1 -1
  89. package/build/components/url-query-controller/index.js.map +1 -1
  90. package/build/store/actions.js.map +1 -1
  91. package/build/store/selectors.js +4 -1
  92. package/build/store/selectors.js.map +1 -1
  93. package/build-module/components/add-new-template/add-custom-generic-template-modal.js +77 -0
  94. package/build-module/components/add-new-template/add-custom-generic-template-modal.js.map +1 -0
  95. package/build-module/components/add-new-template/add-custom-template-modal.js +209 -0
  96. package/build-module/components/add-new-template/add-custom-template-modal.js.map +1 -0
  97. package/build-module/components/add-new-template/new-template.js +93 -34
  98. package/build-module/components/add-new-template/new-template.js.map +1 -1
  99. package/build-module/components/add-new-template/utils.js +365 -0
  100. package/build-module/components/add-new-template/utils.js.map +1 -0
  101. package/build-module/components/block-editor/block-inspector-button.js.map +1 -1
  102. package/build-module/components/block-editor/index.js.map +1 -1
  103. package/build-module/components/code-editor/index.js +18 -5
  104. package/build-module/components/code-editor/index.js.map +1 -1
  105. package/build-module/components/edit-template-part-menu-button/index.js.map +1 -1
  106. package/build-module/components/editor/index.js +16 -0
  107. package/build-module/components/editor/index.js.map +1 -1
  108. package/build-module/components/error-boundary/index.js +5 -0
  109. package/build-module/components/error-boundary/index.js.map +1 -1
  110. package/build-module/components/global-styles/context-menu.js +6 -3
  111. package/build-module/components/global-styles/context-menu.js.map +1 -1
  112. package/build-module/components/global-styles/dimensions-panel.js +2 -6
  113. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  114. package/build-module/components/global-styles/global-styles-provider.js +4 -2
  115. package/build-module/components/global-styles/global-styles-provider.js.map +1 -1
  116. package/build-module/components/global-styles/gradients-palette-panel.js +3 -5
  117. package/build-module/components/global-styles/gradients-palette-panel.js.map +1 -1
  118. package/build-module/components/global-styles/hooks.js +11 -2
  119. package/build-module/components/global-styles/hooks.js.map +1 -1
  120. package/build-module/components/global-styles/palette.js +2 -1
  121. package/build-module/components/global-styles/palette.js.map +1 -1
  122. package/build-module/components/global-styles/screen-block-list.js +4 -1
  123. package/build-module/components/global-styles/screen-block-list.js.map +1 -1
  124. package/build-module/components/global-styles/screen-button-color.js +67 -0
  125. package/build-module/components/global-styles/screen-button-color.js.map +1 -0
  126. package/build-module/components/global-styles/screen-color-palette.js +14 -19
  127. package/build-module/components/global-styles/screen-color-palette.js.map +1 -1
  128. package/build-module/components/global-styles/screen-colors.js +57 -9
  129. package/build-module/components/global-styles/screen-colors.js.map +1 -1
  130. package/build-module/components/global-styles/screen-link-color.js +47 -14
  131. package/build-module/components/global-styles/screen-link-color.js.map +1 -1
  132. package/build-module/components/global-styles/screen-root.js +4 -2
  133. package/build-module/components/global-styles/screen-root.js.map +1 -1
  134. package/build-module/components/global-styles/screen-style-variations.js +9 -1
  135. package/build-module/components/global-styles/screen-style-variations.js.map +1 -1
  136. package/build-module/components/global-styles/screen-typography-element.js +4 -0
  137. package/build-module/components/global-styles/screen-typography-element.js.map +1 -1
  138. package/build-module/components/global-styles/screen-typography.js +10 -2
  139. package/build-module/components/global-styles/screen-typography.js.map +1 -1
  140. package/build-module/components/global-styles/ui.js +10 -0
  141. package/build-module/components/global-styles/ui.js.map +1 -1
  142. package/build-module/components/global-styles/use-global-styles-output.js +198 -32
  143. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  144. package/build-module/components/global-styles/utils.js +5 -3
  145. package/build-module/components/global-styles/utils.js.map +1 -1
  146. package/build-module/components/header/index.js +29 -11
  147. package/build-module/components/header/index.js.map +1 -1
  148. package/build-module/components/header/mode-switcher/index.js.map +1 -1
  149. package/build-module/components/header/more-menu/copy-content-menu-item.js +1 -1
  150. package/build-module/components/header/more-menu/copy-content-menu-item.js.map +1 -1
  151. package/build-module/components/header/more-menu/site-export.js +4 -1
  152. package/build-module/components/header/more-menu/site-export.js.map +1 -1
  153. package/build-module/components/header/undo-redo/redo.js +9 -3
  154. package/build-module/components/header/undo-redo/redo.js.map +1 -1
  155. package/build-module/components/header/undo-redo/undo.js +9 -3
  156. package/build-module/components/header/undo-redo/undo.js.map +1 -1
  157. package/build-module/components/keyboard-shortcut-help-modal/config.js +17 -0
  158. package/build-module/components/keyboard-shortcut-help-modal/config.js.map +1 -1
  159. package/build-module/components/keyboard-shortcut-help-modal/index.js +1 -2
  160. package/build-module/components/keyboard-shortcut-help-modal/index.js.map +1 -1
  161. package/build-module/components/keyboard-shortcuts/index.js.map +1 -1
  162. package/build-module/components/list/actions/index.js.map +1 -1
  163. package/build-module/components/list/actions/rename-menu-item.js.map +1 -1
  164. package/build-module/components/list/added-by.js.map +1 -1
  165. package/build-module/components/navigation-sidebar/index.js.map +1 -1
  166. package/build-module/components/save-button/index.js.map +1 -1
  167. package/build-module/components/sidebar/index.js.map +1 -1
  168. package/build-module/components/sidebar/navigation-menu-sidebar/navigation-inspector.js.map +1 -1
  169. package/build-module/components/sidebar/template-card/index.js +18 -7
  170. package/build-module/components/sidebar/template-card/index.js.map +1 -1
  171. package/build-module/components/sidebar/template-card/template-actions.js +49 -0
  172. package/build-module/components/sidebar/template-card/template-actions.js.map +1 -0
  173. package/build-module/components/sidebar/template-card/template-areas.js.map +1 -1
  174. package/build-module/components/template-details/edit-template-title.js +12 -3
  175. package/build-module/components/template-details/edit-template-title.js.map +1 -1
  176. package/build-module/components/template-details/index.js +2 -21
  177. package/build-module/components/template-details/index.js.map +1 -1
  178. package/build-module/components/template-details/template-areas.js.map +1 -1
  179. package/build-module/components/template-part-converter/index.js.map +1 -1
  180. package/build-module/components/url-query-controller/index.js.map +1 -1
  181. package/build-module/store/actions.js.map +1 -1
  182. package/build-module/store/selectors.js +5 -2
  183. package/build-module/store/selectors.js.map +1 -1
  184. package/build-style/style-rtl.css +188 -42
  185. package/build-style/style.css +188 -42
  186. package/package.json +29 -29
  187. package/src/components/add-new-template/add-custom-generic-template-modal.js +97 -0
  188. package/src/components/add-new-template/add-custom-template-modal.js +265 -0
  189. package/src/components/add-new-template/new-template.js +194 -70
  190. package/src/components/add-new-template/style.scss +137 -0
  191. package/src/components/add-new-template/utils.js +369 -0
  192. package/src/components/block-editor/block-inspector-button.js +2 -3
  193. package/src/components/block-editor/index.js +4 -9
  194. package/src/components/code-editor/index.js +15 -5
  195. package/src/components/edit-template-part-menu-button/index.js +2 -3
  196. package/src/components/editor/index.js +15 -5
  197. package/src/components/error-boundary/index.js +5 -0
  198. package/src/components/global-styles/context-menu.js +3 -0
  199. package/src/components/global-styles/dimensions-panel.js +2 -7
  200. package/src/components/global-styles/global-styles-provider.js +12 -17
  201. package/src/components/global-styles/gradients-palette-panel.js +2 -5
  202. package/src/components/global-styles/hooks.js +20 -3
  203. package/src/components/global-styles/palette.js +4 -1
  204. package/src/components/global-styles/screen-block-list.js +14 -5
  205. package/src/components/global-styles/screen-button-color.js +102 -0
  206. package/src/components/global-styles/screen-color-palette.js +25 -27
  207. package/src/components/global-styles/screen-colors.js +58 -7
  208. package/src/components/global-styles/screen-link-color.js +65 -23
  209. package/src/components/global-styles/screen-root.js +12 -5
  210. package/src/components/global-styles/screen-style-variations.js +10 -4
  211. package/src/components/global-styles/screen-typography-element.js +4 -0
  212. package/src/components/global-styles/screen-typography.js +17 -2
  213. package/src/components/global-styles/style.scss +14 -8
  214. package/src/components/global-styles/test/use-global-styles-output.js +250 -16
  215. package/src/components/global-styles/ui.js +13 -0
  216. package/src/components/global-styles/use-global-styles-output.js +246 -32
  217. package/src/components/global-styles/utils.js +5 -2
  218. package/src/components/header/index.js +38 -13
  219. package/src/components/header/mode-switcher/index.js +4 -4
  220. package/src/components/header/more-menu/copy-content-menu-item.js +3 -4
  221. package/src/components/header/more-menu/site-export.js +5 -3
  222. package/src/components/header/style.scss +53 -5
  223. package/src/components/header/undo-redo/redo.js +6 -1
  224. package/src/components/header/undo-redo/undo.js +6 -1
  225. package/src/components/keyboard-shortcut-help-modal/config.js +12 -0
  226. package/src/components/keyboard-shortcut-help-modal/index.js +1 -2
  227. package/src/components/keyboard-shortcut-help-modal/style.scss +0 -5
  228. package/src/components/keyboard-shortcuts/index.js +6 -10
  229. package/src/components/list/actions/index.js +2 -3
  230. package/src/components/list/actions/rename-menu-item.js +4 -6
  231. package/src/components/list/added-by.js +4 -3
  232. package/src/components/list/style.scss +0 -8
  233. package/src/components/navigation-sidebar/index.js +2 -4
  234. package/src/components/save-button/index.js +2 -4
  235. package/src/components/sidebar/index.js +6 -6
  236. package/src/components/sidebar/navigation-menu-sidebar/navigation-inspector.js +6 -9
  237. package/src/components/sidebar/template-card/index.js +17 -9
  238. package/src/components/sidebar/template-card/style.scss +49 -35
  239. package/src/components/sidebar/template-card/template-actions.js +43 -0
  240. package/src/components/sidebar/template-card/template-areas.js +6 -6
  241. package/src/components/template-details/edit-template-title.js +10 -2
  242. package/src/components/template-details/index.js +4 -21
  243. package/src/components/template-details/template-areas.js +6 -6
  244. package/src/components/template-part-converter/index.js +2 -3
  245. package/src/components/test/error-boundary.js +38 -0
  246. package/src/components/url-query-controller/index.js +2 -3
  247. package/src/store/actions.js +257 -233
  248. package/src/store/selectors.js +19 -14
@@ -0,0 +1,369 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { get } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useSelect } from '@wordpress/data';
10
+ import { store as coreStore } from '@wordpress/core-data';
11
+ import { store as editorStore } from '@wordpress/editor';
12
+ import { decodeEntities } from '@wordpress/html-entities';
13
+ import { useMemo } from '@wordpress/element';
14
+ import { __, sprintf } from '@wordpress/i18n';
15
+ import { blockMeta, post } from '@wordpress/icons';
16
+
17
+ /**
18
+ * @typedef IHasNameAndId
19
+ * @property {string|number} id The entity's id.
20
+ * @property {string} name The entity's name.
21
+ */
22
+
23
+ /**
24
+ * Helper util to map records to add a `name` prop from a
25
+ * provided path, in order to handle all entities in the same
26
+ * fashion(implementing`IHasNameAndId` interface).
27
+ *
28
+ * @param {Object[]} entities The array of entities.
29
+ * @param {string} path The path to map a `name` property from the entity.
30
+ * @return {IHasNameAndId[]} An array of enitities that now implement the `IHasNameAndId` interface.
31
+ */
32
+ export const mapToIHasNameAndId = ( entities, path ) => {
33
+ return ( entities || [] ).map( ( entity ) => ( {
34
+ ...entity,
35
+ name: decodeEntities( get( entity, path ) ),
36
+ } ) );
37
+ };
38
+
39
+ /**
40
+ * @typedef {Object} EntitiesInfo
41
+ * @property {boolean} hasEntities If an entity has available records(posts, terms, etc..).
42
+ * @property {number[]} existingEntitiesIds An array of the existing entities ids.
43
+ */
44
+
45
+ /**
46
+ * @typedef {Object} EntityConfig
47
+ * @property {string} entityName The entity's name.
48
+ * @property {Function} getOrderBy Getter for an entity's `orderBy` query parameter, given the object
49
+ * {search} as argument.
50
+ * @property {Function} getIcon Getter function for returning an entity's icon for the menu item.
51
+ * @property {Function} getTitle Getter function for returning an entity's title for the menu item.
52
+ * @property {Function} getDescription Getter function for returning an entity's description for the menu item.
53
+ * @property {string} recordNamePath The path to an entity's properties to use as a `name`. If not provided
54
+ * is assumed that `name` property exists.
55
+ * @property {string} templatePrefix The template prefix to create new templates and check against existing
56
+ * templates. For example custom post types need a `single-` prefix to all
57
+ * templates(`single-post-hello`), whereas `pages` don't (`page-hello`).
58
+ * @property {string} templateSlug If this property is provided, it is going to be used for the creation of
59
+ * new templates and the check against existing templates in the place
60
+ * of the actual entity's `slug`. An example is `Tag` templates where the
61
+ * the Tag's taxonomy slug is `post_tag`, but template hierarchy is based
62
+ * on `tag` alias.
63
+ */
64
+
65
+ const taxonomyBaseConfig = {
66
+ entityName: 'taxonomy',
67
+ getOrderBy: ( { search } ) => ( search ? 'name' : 'count' ),
68
+ getIcon: () => blockMeta,
69
+ getTitle: ( labels ) =>
70
+ sprintf(
71
+ // translators: %s: Name of the taxonomy e.g: "Cagegory".
72
+ __( 'Single taxonomy: %s' ),
73
+ labels.singular_name
74
+ ),
75
+ getDescription: ( labels ) =>
76
+ sprintf(
77
+ // translators: %s: Name of the taxonomy e.g: "Product Categories".
78
+ __( 'Displays a single taxonomy: %s.' ),
79
+ labels.singular_name
80
+ ),
81
+ };
82
+ const postTypeBaseConfig = {
83
+ entityName: 'postType',
84
+ getOrderBy: ( { search } ) => ( search ? 'relevance' : 'modified' ),
85
+ recordNamePath: 'title.rendered',
86
+ // `icon` is the `menu_icon` property of a post type. We
87
+ // only handle `dashicons` for now, even if the `menu_icon`
88
+ // also supports urls and svg as values.
89
+ getIcon: ( _icon ) =>
90
+ _icon?.startsWith( 'dashicons-' ) ? _icon.slice( 10 ) : post,
91
+ getTitle: ( labels ) =>
92
+ sprintf(
93
+ // translators: %s: Name of the post type e.g: "Post".
94
+ __( 'Single item: %s' ),
95
+ labels.singular_name
96
+ ),
97
+ getDescription: ( labels ) =>
98
+ sprintf(
99
+ // translators: %s: Name of the post type e.g: "Post".
100
+ __( 'Displays a single item: %s.' ),
101
+ labels.singular_name
102
+ ),
103
+ };
104
+ export const entitiesConfig = {
105
+ postType: {
106
+ ...postTypeBaseConfig,
107
+ templatePrefix: 'single-',
108
+ },
109
+ page: { ...postTypeBaseConfig },
110
+ taxonomy: {
111
+ ...taxonomyBaseConfig,
112
+ templatePrefix: 'taxonomy-',
113
+ },
114
+ category: { ...taxonomyBaseConfig },
115
+ tag: { ...taxonomyBaseConfig, templateSlug: 'tag' },
116
+ };
117
+
118
+ export const useExistingTemplates = () => {
119
+ return useSelect(
120
+ ( select ) =>
121
+ select( coreStore ).getEntityRecords( 'postType', 'wp_template', {
122
+ per_page: -1,
123
+ } ),
124
+ []
125
+ );
126
+ };
127
+
128
+ export const useDefaultTemplateTypes = () => {
129
+ return useSelect(
130
+ ( select ) =>
131
+ select( editorStore ).__experimentalGetDefaultTemplateTypes(),
132
+ []
133
+ );
134
+ };
135
+
136
+ const usePublicPostTypes = () => {
137
+ const postTypes = useSelect(
138
+ ( select ) => select( coreStore ).getPostTypes( { per_page: -1 } ),
139
+ []
140
+ );
141
+ return useMemo( () => {
142
+ const excludedPostTypes = [ 'attachment' ];
143
+ return postTypes?.filter(
144
+ ( { viewable, slug } ) =>
145
+ viewable && ! excludedPostTypes.includes( slug )
146
+ );
147
+ }, [ postTypes ] );
148
+ };
149
+
150
+ // `page` post type is a special case in the template hierarchy,
151
+ // so we exclude it from the list of post types and we handle it
152
+ // separately.
153
+ export const usePostTypes = () => {
154
+ const postTypes = usePublicPostTypes();
155
+ return useMemo( () => {
156
+ return postTypes?.filter( ( { slug } ) => slug !== 'page' );
157
+ }, [ postTypes ] );
158
+ };
159
+ export const usePostTypePage = () => {
160
+ const postTypes = usePublicPostTypes();
161
+ return useMemo( () => {
162
+ return postTypes?.filter( ( { slug } ) => slug === 'page' );
163
+ }, [ postTypes ] );
164
+ };
165
+
166
+ const usePublicTaxonomies = () => {
167
+ const taxonomies = useSelect(
168
+ ( select ) => select( coreStore ).getTaxonomies( { per_page: -1 } ),
169
+ []
170
+ );
171
+ return useMemo( () => {
172
+ return taxonomies?.filter(
173
+ ( { visibility } ) => visibility?.publicly_queryable
174
+ );
175
+ }, [ taxonomies ] );
176
+ };
177
+
178
+ /**
179
+ * `category` and `post_tag` are handled specifically in template
180
+ * hierarchy so we need to differentiate them and return the rest,
181
+ * e.g. `category-$slug` and `taxonomy-$taxonomy-$term`.
182
+ */
183
+ export const useTaxonomies = () => {
184
+ const taxonomies = usePublicTaxonomies();
185
+ const specialTaxonomies = [ 'category', 'post_tag' ];
186
+ return useMemo(
187
+ () =>
188
+ taxonomies?.filter(
189
+ ( { slug } ) => ! specialTaxonomies.includes( slug )
190
+ ),
191
+ [ taxonomies ]
192
+ );
193
+ };
194
+
195
+ export const useTaxonomyCategory = () => {
196
+ const taxonomies = usePublicTaxonomies();
197
+ return useMemo(
198
+ () => taxonomies?.filter( ( { slug } ) => slug === 'category' ),
199
+ [ taxonomies ]
200
+ );
201
+ };
202
+ export const useTaxonomyTag = () => {
203
+ const taxonomies = usePublicTaxonomies();
204
+ return useMemo(
205
+ () => taxonomies?.filter( ( { slug } ) => slug === 'post_tag' ),
206
+ [ taxonomies ]
207
+ );
208
+ };
209
+
210
+ /**
211
+ * Helper hook that returns information about an entity having
212
+ * records that we can create a specific template for.
213
+ *
214
+ * For example we can search for `terms` in `taxonomy` entity or
215
+ * `posts` in `postType` entity.
216
+ *
217
+ * First we need to find the existing records with an associated template,
218
+ * to query afterwards for any remaing record, by excluding them.
219
+ *
220
+ * @param {string[]} existingTemplates The existing templates.
221
+ * @param {Object[]} entities The array of entities we need to get extra information.
222
+ * @param {EntityConfig} entityConfig The entity config.
223
+ * @return {Record<string,EntitiesInfo>} An object with the `entities.slug` as `keys` and EntitiesInfo as values.
224
+ */
225
+ const useEntitiesInfo = (
226
+ existingTemplates,
227
+ entities,
228
+ { entityName, templatePrefix, templateSlug }
229
+ ) => {
230
+ const slugsToExcludePerEntity = useMemo( () => {
231
+ return entities?.reduce( ( accumulator, entity ) => {
232
+ let _prefix = `${ templateSlug || entity.slug }-`;
233
+ if ( templatePrefix ) {
234
+ _prefix = templatePrefix + _prefix;
235
+ }
236
+ const slugsWithTemplates = ( existingTemplates || [] ).reduce(
237
+ ( _accumulator, existingTemplate ) => {
238
+ if ( existingTemplate.slug.startsWith( _prefix ) ) {
239
+ _accumulator.push(
240
+ existingTemplate.slug.substring( _prefix.length )
241
+ );
242
+ }
243
+ return _accumulator;
244
+ },
245
+ []
246
+ );
247
+ if ( slugsWithTemplates.length ) {
248
+ accumulator[ entity.slug ] = slugsWithTemplates;
249
+ }
250
+ return accumulator;
251
+ }, {} );
252
+ }, [ entities, existingTemplates ] );
253
+ const recordsToExcludePerEntity = useSelect(
254
+ ( select ) => {
255
+ if ( ! slugsToExcludePerEntity ) {
256
+ return;
257
+ }
258
+ return Object.entries( slugsToExcludePerEntity ).reduce(
259
+ ( accumulator, [ slug, slugsWithTemplates ] ) => {
260
+ const postsWithTemplates = select(
261
+ coreStore
262
+ ).getEntityRecords( entityName, slug, {
263
+ _fields: 'id',
264
+ context: 'view',
265
+ slug: slugsWithTemplates,
266
+ } );
267
+ if ( postsWithTemplates?.length ) {
268
+ accumulator[ slug ] = postsWithTemplates;
269
+ }
270
+ return accumulator;
271
+ },
272
+ {}
273
+ );
274
+ },
275
+ [ slugsToExcludePerEntity ]
276
+ );
277
+ const entitiesInfo = useSelect(
278
+ ( select ) => {
279
+ return entities?.reduce( ( accumulator, { slug } ) => {
280
+ const existingEntitiesIds =
281
+ recordsToExcludePerEntity?.[ slug ]?.map(
282
+ ( { id } ) => id
283
+ ) || [];
284
+ accumulator[ slug ] = {
285
+ hasEntities: !! select( coreStore ).getEntityRecords(
286
+ entityName,
287
+ slug,
288
+ {
289
+ per_page: 1,
290
+ _fields: 'id',
291
+ context: 'view',
292
+ exclude: existingEntitiesIds,
293
+ }
294
+ )?.length,
295
+ existingEntitiesIds,
296
+ };
297
+ return accumulator;
298
+ }, {} );
299
+ },
300
+ [ entities, recordsToExcludePerEntity ]
301
+ );
302
+ return entitiesInfo;
303
+ };
304
+
305
+ export const useExtraTemplates = (
306
+ entities,
307
+ entityConfig,
308
+ onClickMenuItem
309
+ ) => {
310
+ const existingTemplates = useExistingTemplates();
311
+ const defaultTemplateTypes = useDefaultTemplateTypes();
312
+ const entitiesInfo = useEntitiesInfo(
313
+ existingTemplates,
314
+ entities,
315
+ entityConfig
316
+ );
317
+ const existingTemplateSlugs = ( existingTemplates || [] ).map(
318
+ ( { slug } ) => slug
319
+ );
320
+ const extraTemplates = ( entities || [] ).reduce(
321
+ ( accumulator, entity ) => {
322
+ const { slug, labels, icon } = entity;
323
+ const slugForGeneralTemplate = entityConfig.templateSlug || slug;
324
+ // We need to check if the general template is part of the
325
+ // defaultTemplateTypes. If it is, just use that info and
326
+ // augment it with the specific template functionality.
327
+ const defaultTemplateType = defaultTemplateTypes?.find(
328
+ ( { slug: _slug } ) => _slug === slugForGeneralTemplate
329
+ );
330
+ const generalTemplateSlug =
331
+ defaultTemplateType?.slug ||
332
+ `${ entityConfig.templatePrefix }${ slug }`;
333
+ const hasGeneralTemplate =
334
+ existingTemplateSlugs?.includes( generalTemplateSlug );
335
+ const menuItem = defaultTemplateType
336
+ ? { ...defaultTemplateType }
337
+ : {
338
+ slug: generalTemplateSlug,
339
+ title: entityConfig.getTitle( labels ),
340
+ description: entityConfig.getDescription( labels ),
341
+ icon: entityConfig.getIcon?.( icon ),
342
+ };
343
+ const hasEntities = entitiesInfo?.[ slug ]?.hasEntities;
344
+ // We have a different template creation flow only if they have entities.
345
+ if ( hasEntities ) {
346
+ menuItem.onClick = ( template ) => {
347
+ onClickMenuItem( {
348
+ type: entityConfig.entityName,
349
+ slug,
350
+ config: entityConfig,
351
+ labels,
352
+ hasGeneralTemplate,
353
+ template,
354
+ postsToExclude:
355
+ entitiesInfo[ slug ].existingEntitiesIds,
356
+ } );
357
+ };
358
+ }
359
+ // We don't need to add the menu item if there are no
360
+ // entities and the general template exists.
361
+ if ( ! hasGeneralTemplate || hasEntities ) {
362
+ accumulator.push( menuItem );
363
+ }
364
+ return accumulator;
365
+ },
366
+ []
367
+ );
368
+ return extraTemplates;
369
+ };
@@ -30,9 +30,8 @@ export default function BlockInspectorButton( { onClick = () => {} } ) {
30
30
  } ),
31
31
  []
32
32
  );
33
- const { enableComplementaryArea, disableComplementaryArea } = useDispatch(
34
- interfaceStore
35
- );
33
+ const { enableComplementaryArea, disableComplementaryArea } =
34
+ useDispatch( interfaceStore );
36
35
 
37
36
  const label = isBlockInspectorOpen
38
37
  ? __( 'Hide more settings' )
@@ -51,12 +51,8 @@ const LAYOUT = {
51
51
  export default function BlockEditor( { setIsInserterOpen } ) {
52
52
  const { storedSettings, templateType, templateId, page } = useSelect(
53
53
  ( select ) => {
54
- const {
55
- getSettings,
56
- getEditedPostType,
57
- getEditedPostId,
58
- getPage,
59
- } = select( editSiteStore );
54
+ const { getSettings, getEditedPostType, getEditedPostId, getPage } =
55
+ select( editSiteStore );
60
56
 
61
57
  return {
62
58
  storedSettings: getSettings( setIsInserterOpen ),
@@ -78,9 +74,8 @@ export default function BlockEditor( { setIsInserterOpen } ) {
78
74
  const { restBlockPatterns, restBlockPatternCategories } = useSelect(
79
75
  ( select ) => ( {
80
76
  restBlockPatterns: select( coreStore ).getBlockPatterns(),
81
- restBlockPatternCategories: select(
82
- coreStore
83
- ).getBlockPatternCategories(),
77
+ restBlockPatternCategories:
78
+ select( coreStore ).getBlockPatternCategories(),
84
79
  } ),
85
80
  []
86
81
  );
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { parse } from '@wordpress/blocks';
4
+ import { parse, __unstableSerializeAndClean } from '@wordpress/blocks';
5
5
  import { useEntityBlockEditor, useEntityProp } from '@wordpress/core-data';
6
6
  import { useSelect, useDispatch } from '@wordpress/data';
7
7
  import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
@@ -32,10 +32,20 @@ export default function CodeEditor() {
32
32
  'postType',
33
33
  templateType
34
34
  );
35
- const content =
36
- contentStructure instanceof Function
37
- ? contentStructure( { blocks } )
38
- : contentStructure;
35
+
36
+ // Replicates the logic found in getEditedPostContent().
37
+ let content;
38
+ if ( contentStructure instanceof Function ) {
39
+ content = contentStructure( { blocks } );
40
+ } else if ( blocks ) {
41
+ // If we have parsed blocks already, they should be our source of truth.
42
+ // Parsing applies block deprecations and legacy block conversions that
43
+ // unparsed content will not have.
44
+ content = __unstableSerializeAndClean( blocks );
45
+ } else {
46
+ content = contentStructure;
47
+ }
48
+
39
49
  const { switchEditorMode } = useDispatch( editSiteStore );
40
50
  return (
41
51
  <div className="edit-site-code-editor">
@@ -34,9 +34,8 @@ function EditTemplatePartMenuItem( { selectedClientId, onClose } ) {
34
34
  const { params } = useLocation();
35
35
  const selectedTemplatePart = useSelect(
36
36
  ( select ) => {
37
- const block = select( blockEditorStore ).getBlock(
38
- selectedClientId
39
- );
37
+ const block =
38
+ select( blockEditorStore ).getBlock( selectedClientId );
40
39
 
41
40
  if ( block && isTemplatePart( block ) ) {
42
41
  const { theme, slug } = block.attributes;
@@ -47,6 +47,17 @@ import { GlobalStylesProvider } from '../global-styles/global-styles-provider';
47
47
  import useTitle from '../routes/use-title';
48
48
 
49
49
  const interfaceLabels = {
50
+ /* translators: accessibility text for the editor top bar landmark region. */
51
+ header: __( 'Editor top bar' ),
52
+ /* translators: accessibility text for the editor content landmark region. */
53
+ body: __( 'Editor content' ),
54
+ /* translators: accessibility text for the editor settings landmark region. */
55
+ sidebar: __( 'Editor settings' ),
56
+ /* translators: accessibility text for the editor publish landmark region. */
57
+ actions: __( 'Editor publish' ),
58
+ /* translators: accessibility text for the editor footer landmark region. */
59
+ footer: __( 'Editor footer' ),
60
+ /* translators: accessibility text for the navigation sidebar landmark region. */
50
61
  drawer: __( 'Navigation Sidebar' ),
51
62
  };
52
63
 
@@ -119,10 +130,8 @@ function Editor( { onError } ) {
119
130
  const { setPage, setIsInserterOpened } = useDispatch( editSiteStore );
120
131
  const { enableComplementaryArea } = useDispatch( interfaceStore );
121
132
 
122
- const [
123
- isEntitiesSavedStatesOpen,
124
- setIsEntitiesSavedStatesOpen,
125
- ] = useState( false );
133
+ const [ isEntitiesSavedStatesOpen, setIsEntitiesSavedStatesOpen ] =
134
+ useState( false );
126
135
  const openEntitiesSavedStates = useCallback(
127
136
  () => setIsEntitiesSavedStatesOpen( true ),
128
137
  []
@@ -217,7 +226,8 @@ function Editor( { onError } ) {
217
226
  <InterfaceSkeleton
218
227
  labels={ {
219
228
  ...interfaceLabels,
220
- secondarySidebar: secondarySidebarLabel,
229
+ secondarySidebar:
230
+ secondarySidebarLabel,
221
231
  } }
222
232
  className={
223
233
  showIconLabels &&
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { Component } from '@wordpress/element';
5
5
  import { __ } from '@wordpress/i18n';
6
+ import { doAction } from '@wordpress/hooks';
6
7
 
7
8
  /**
8
9
  * Internal dependencies
@@ -20,6 +21,10 @@ export default class ErrorBoundary extends Component {
20
21
  };
21
22
  }
22
23
 
24
+ componentDidCatch( error ) {
25
+ doAction( 'editor.ErrorBoundary.errorLogged', error );
26
+ }
27
+
23
28
  static getDerivedStateFromError( error ) {
24
29
  return { error };
25
30
  }
@@ -27,6 +27,7 @@ function ContextMenu( { name, parentMenu = '' } ) {
27
27
  <NavigationButtonAsItem
28
28
  icon={ typography }
29
29
  path={ parentMenu + '/typography' }
30
+ aria-label={ __( 'Typography styles' ) }
30
31
  >
31
32
  { __( 'Typography' ) }
32
33
  </NavigationButtonAsItem>
@@ -35,6 +36,7 @@ function ContextMenu( { name, parentMenu = '' } ) {
35
36
  <NavigationButtonAsItem
36
37
  icon={ color }
37
38
  path={ parentMenu + '/colors' }
39
+ aria-label={ __( 'Colors styles' ) }
38
40
  >
39
41
  { __( 'Colors' ) }
40
42
  </NavigationButtonAsItem>
@@ -43,6 +45,7 @@ function ContextMenu( { name, parentMenu = '' } ) {
43
45
  <NavigationButtonAsItem
44
46
  icon={ layout }
45
47
  path={ parentMenu + '/layout' }
48
+ aria-label={ __( 'Layout styles' ) }
46
49
  >
47
50
  { __( 'Layout' ) }
48
51
  </NavigationButtonAsItem>
@@ -43,13 +43,8 @@ function useHasMargin( name ) {
43
43
  function useHasGap( name ) {
44
44
  const supports = getSupportedGlobalStylesPanels( name );
45
45
  const [ settings ] = useSetting( 'spacing.blockGap', name );
46
- // Do not show the gap control panel for block-level global styles
47
- // as they do not work on the frontend.
48
- // See: https://github.com/WordPress/gutenberg/pull/39845.
49
- // We can revert this condition when they're working again.
50
- return !! name
51
- ? false
52
- : settings && supports.includes( '--wp--style--block-gap' );
46
+
47
+ return settings && supports.includes( 'blockGap' );
53
48
  }
54
49
 
55
50
  function filterValuesBySides( values, sides ) {
@@ -1,14 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import {
5
- mergeWith,
6
- pickBy,
7
- isEmpty,
8
- isObject,
9
- identity,
10
- mapValues,
11
- } from 'lodash';
4
+ import { mergeWith, pickBy, isEmpty, mapValues } from 'lodash';
12
5
 
13
6
  /**
14
7
  * WordPress dependencies
@@ -22,6 +15,8 @@ import { store as coreStore } from '@wordpress/core-data';
22
15
  */
23
16
  import { GlobalStylesContext } from './context';
24
17
 
18
+ const identity = ( x ) => x;
19
+
25
20
  function mergeTreesCustomizer( _, srcValue ) {
26
21
  // We only pass as arrays the presets,
27
22
  // in which case we want the new array of values
@@ -36,7 +31,11 @@ export function mergeBaseAndUserConfigs( base, user ) {
36
31
  }
37
32
 
38
33
  const cleanEmptyObject = ( object ) => {
39
- if ( ! isObject( object ) || Array.isArray( object ) ) {
34
+ if (
35
+ object === null ||
36
+ typeof object !== 'object' ||
37
+ Array.isArray( object )
38
+ ) {
40
39
  return object;
41
40
  }
42
41
  const cleanedNestedObjects = pickBy(
@@ -48,9 +47,8 @@ const cleanEmptyObject = ( object ) => {
48
47
 
49
48
  function useGlobalStylesUserConfig() {
50
49
  const { globalStylesId, settings, styles } = useSelect( ( select ) => {
51
- const _globalStylesId = select(
52
- coreStore
53
- ).__experimentalGetCurrentGlobalStylesId();
50
+ const _globalStylesId =
51
+ select( coreStore ).__experimentalGetCurrentGlobalStylesId();
54
52
  const record = _globalStylesId
55
53
  ? select( coreStore ).getEditedEntityRecord(
56
54
  'root',
@@ -108,11 +106,8 @@ function useGlobalStylesBaseConfig() {
108
106
  }
109
107
 
110
108
  function useGlobalStylesContext() {
111
- const [
112
- isUserConfigReady,
113
- userConfig,
114
- setUserConfig,
115
- ] = useGlobalStylesUserConfig();
109
+ const [ isUserConfigReady, userConfig, setUserConfig ] =
110
+ useGlobalStylesUserConfig();
116
111
  const [ isBaseConfigReady, baseConfig ] = useGlobalStylesBaseConfig();
117
112
  const mergedConfig = useMemo( () => {
118
113
  if ( ! baseConfig || ! userConfig ) {
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { noop } from 'lodash';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -20,6 +15,8 @@ import { __ } from '@wordpress/i18n';
20
15
  import { useSetting } from './hooks';
21
16
  import Subtitle from './subtitle';
22
17
 
18
+ const noop = () => {};
19
+
23
20
  export default function GradientPalettePanel( { name } ) {
24
21
  const [ themeGradients, setThemeGradients ] = useSetting(
25
22
  'color.gradients.theme',
@@ -27,9 +27,10 @@ export const useGlobalStylesReset = () => {
27
27
  const canReset = !! config && ! isEqual( config, EMPTY_CONFIG );
28
28
  return [
29
29
  canReset,
30
- useCallback( () => setUserConfig( () => EMPTY_CONFIG ), [
31
- setUserConfig,
32
- ] ),
30
+ useCallback(
31
+ () => setUserConfig( () => EMPTY_CONFIG ),
32
+ [ setUserConfig ]
33
+ ),
33
34
  ];
34
35
  };
35
36
 
@@ -158,6 +159,7 @@ const ROOT_BLOCK_SUPPORTS = [
158
159
  'backgroundColor',
159
160
  'color',
160
161
  'linkColor',
162
+ 'buttonColor',
161
163
  'fontFamily',
162
164
  'fontSize',
163
165
  'fontStyle',
@@ -180,6 +182,21 @@ export function getSupportedGlobalStylesPanels( name ) {
180
182
  }
181
183
 
182
184
  const supportKeys = [];
185
+
186
+ // Check for blockGap support.
187
+ // Block spacing support doesn't map directly to a single style property, so needs to be handled separately.
188
+ // Also, only allow `blockGap` support if serialization has not been skipped, to be sure global spacing can be applied.
189
+ if (
190
+ blockType?.supports?.spacing?.blockGap &&
191
+ blockType?.supports?.spacing?.__experimentalSkipSerialization !==
192
+ true &&
193
+ ! blockType?.supports?.spacing?.__experimentalSkipSerialization?.some?.(
194
+ ( spacingType ) => spacingType === 'blockGap'
195
+ )
196
+ ) {
197
+ supportKeys.push( 'blockGap' );
198
+ }
199
+
183
200
  Object.keys( STYLE_PROPERTY ).forEach( ( styleName ) => {
184
201
  if ( ! STYLE_PROPERTY[ styleName ].support ) {
185
202
  return;
@@ -58,7 +58,10 @@ function Palette( { name } ) {
58
58
  <VStack spacing={ 3 }>
59
59
  <Subtitle>{ __( 'Palette' ) }</Subtitle>
60
60
  <ItemGroup isBordered isSeparated>
61
- <NavigationButtonAsItem path={ screenPath }>
61
+ <NavigationButtonAsItem
62
+ path={ screenPath }
63
+ aria-label={ __( 'Color palettes' ) }
64
+ >
62
65
  <HStack
63
66
  direction={
64
67
  colors.length === 0 ? 'row-reverse' : 'row'