@wordpress/edit-site 4.9.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 (199) 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 +82 -61
  5. package/build/components/add-new-template/add-custom-template-modal.js.map +1 -1
  6. package/build/components/add-new-template/new-template.js +94 -81
  7. package/build/components/add-new-template/new-template.js.map +1 -1
  8. package/build/components/add-new-template/utils.js +574 -57
  9. package/build/components/add-new-template/utils.js.map +1 -1
  10. package/build/components/block-editor/index.js +1 -3
  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/editor/index.js +16 -0
  15. package/build/components/editor/index.js.map +1 -1
  16. package/build/components/error-boundary/index.js +6 -0
  17. package/build/components/error-boundary/index.js.map +1 -1
  18. package/build/components/global-styles/dimensions-panel.js +191 -21
  19. package/build/components/global-styles/dimensions-panel.js.map +1 -1
  20. package/build/components/global-styles/global-styles-provider.js +4 -2
  21. package/build/components/global-styles/global-styles-provider.js.map +1 -1
  22. package/build/components/global-styles/hooks.js +11 -2
  23. package/build/components/global-styles/hooks.js.map +1 -1
  24. package/build/components/global-styles/screen-color-palette.js +13 -17
  25. package/build/components/global-styles/screen-color-palette.js.map +1 -1
  26. package/build/components/global-styles/screen-colors.js +59 -7
  27. package/build/components/global-styles/screen-colors.js.map +1 -1
  28. package/build/components/global-styles/screen-heading-color.js +157 -0
  29. package/build/components/global-styles/screen-heading-color.js.map +1 -0
  30. package/build/components/global-styles/screen-link-color.js +48 -14
  31. package/build/components/global-styles/screen-link-color.js.map +1 -1
  32. package/build/components/global-styles/screen-typography-element.js +4 -0
  33. package/build/components/global-styles/screen-typography-element.js.map +1 -1
  34. package/build/components/global-styles/screen-typography.js +5 -0
  35. package/build/components/global-styles/screen-typography.js.map +1 -1
  36. package/build/components/global-styles/typography-panel.js +73 -12
  37. package/build/components/global-styles/typography-panel.js.map +1 -1
  38. package/build/components/global-styles/typography-utils.js +217 -0
  39. package/build/components/global-styles/typography-utils.js.map +1 -0
  40. package/build/components/global-styles/ui.js +11 -0
  41. package/build/components/global-styles/ui.js.map +1 -1
  42. package/build/components/global-styles/use-global-styles-output.js +298 -61
  43. package/build/components/global-styles/use-global-styles-output.js.map +1 -1
  44. package/build/components/global-styles/utils.js +49 -3
  45. package/build/components/global-styles/utils.js.map +1 -1
  46. package/build/components/header/index.js +22 -10
  47. package/build/components/header/index.js.map +1 -1
  48. package/build/components/header/undo-redo/redo.js +2 -1
  49. package/build/components/header/undo-redo/redo.js.map +1 -1
  50. package/build/components/keyboard-shortcut-help-modal/index.js +1 -3
  51. package/build/components/keyboard-shortcut-help-modal/index.js.map +1 -1
  52. package/build/components/list/actions/index.js +1 -1
  53. package/build/components/list/actions/index.js.map +1 -1
  54. package/build/components/save-button/index.js +2 -3
  55. package/build/components/save-button/index.js.map +1 -1
  56. package/build/components/sidebar/navigation-menu-sidebar/navigation-menu.js +2 -2
  57. package/build/components/sidebar/navigation-menu-sidebar/navigation-menu.js.map +1 -1
  58. package/build/components/sidebar/template-card/template-actions.js +1 -1
  59. package/build/components/sidebar/template-card/template-actions.js.map +1 -1
  60. package/build/components/template-details/edit-template-title.js +11 -3
  61. package/build/components/template-details/edit-template-title.js.map +1 -1
  62. package/build/components/template-details/index.js +2 -21
  63. package/build/components/template-details/index.js.map +1 -1
  64. package/build/components/template-details/template-areas.js +1 -1
  65. package/build/components/template-details/template-areas.js.map +1 -1
  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/hooks/index.js +2 -0
  69. package/build/hooks/index.js.map +1 -1
  70. package/build/hooks/template-part-edit.js +86 -0
  71. package/build/hooks/template-part-edit.js.map +1 -0
  72. package/build/store/selectors.js +4 -1
  73. package/build/store/selectors.js.map +1 -1
  74. package/build-module/components/add-new-template/add-custom-generic-template-modal.js +77 -0
  75. package/build-module/components/add-new-template/add-custom-generic-template-modal.js.map +1 -0
  76. package/build-module/components/add-new-template/add-custom-template-modal.js +82 -61
  77. package/build-module/components/add-new-template/add-custom-template-modal.js.map +1 -1
  78. package/build-module/components/add-new-template/new-template.js +96 -84
  79. package/build-module/components/add-new-template/new-template.js.map +1 -1
  80. package/build-module/components/add-new-template/utils.js +555 -50
  81. package/build-module/components/add-new-template/utils.js.map +1 -1
  82. package/build-module/components/block-editor/index.js +1 -2
  83. package/build-module/components/block-editor/index.js.map +1 -1
  84. package/build-module/components/code-editor/index.js +18 -5
  85. package/build-module/components/code-editor/index.js.map +1 -1
  86. package/build-module/components/editor/index.js +16 -0
  87. package/build-module/components/editor/index.js.map +1 -1
  88. package/build-module/components/error-boundary/index.js +5 -0
  89. package/build-module/components/error-boundary/index.js.map +1 -1
  90. package/build-module/components/global-styles/dimensions-panel.js +191 -22
  91. package/build-module/components/global-styles/dimensions-panel.js.map +1 -1
  92. package/build-module/components/global-styles/global-styles-provider.js +4 -2
  93. package/build-module/components/global-styles/global-styles-provider.js.map +1 -1
  94. package/build-module/components/global-styles/hooks.js +11 -2
  95. package/build-module/components/global-styles/hooks.js.map +1 -1
  96. package/build-module/components/global-styles/screen-color-palette.js +14 -19
  97. package/build-module/components/global-styles/screen-color-palette.js.map +1 -1
  98. package/build-module/components/global-styles/screen-colors.js +59 -7
  99. package/build-module/components/global-styles/screen-colors.js.map +1 -1
  100. package/build-module/components/global-styles/screen-heading-color.js +143 -0
  101. package/build-module/components/global-styles/screen-heading-color.js.map +1 -0
  102. package/build-module/components/global-styles/screen-link-color.js +47 -14
  103. package/build-module/components/global-styles/screen-link-color.js.map +1 -1
  104. package/build-module/components/global-styles/screen-typography-element.js +4 -0
  105. package/build-module/components/global-styles/screen-typography-element.js.map +1 -1
  106. package/build-module/components/global-styles/screen-typography.js +5 -0
  107. package/build-module/components/global-styles/screen-typography.js.map +1 -1
  108. package/build-module/components/global-styles/typography-panel.js +74 -13
  109. package/build-module/components/global-styles/typography-panel.js.map +1 -1
  110. package/build-module/components/global-styles/typography-utils.js +204 -0
  111. package/build-module/components/global-styles/typography-utils.js.map +1 -0
  112. package/build-module/components/global-styles/ui.js +10 -0
  113. package/build-module/components/global-styles/ui.js.map +1 -1
  114. package/build-module/components/global-styles/use-global-styles-output.js +294 -69
  115. package/build-module/components/global-styles/use-global-styles-output.js.map +1 -1
  116. package/build-module/components/global-styles/utils.js +47 -4
  117. package/build-module/components/global-styles/utils.js.map +1 -1
  118. package/build-module/components/header/index.js +25 -12
  119. package/build-module/components/header/index.js.map +1 -1
  120. package/build-module/components/header/undo-redo/redo.js +3 -2
  121. package/build-module/components/header/undo-redo/redo.js.map +1 -1
  122. package/build-module/components/keyboard-shortcut-help-modal/index.js +1 -2
  123. package/build-module/components/keyboard-shortcut-help-modal/index.js.map +1 -1
  124. package/build-module/components/list/actions/index.js +1 -1
  125. package/build-module/components/list/actions/index.js.map +1 -1
  126. package/build-module/components/save-button/index.js +3 -4
  127. package/build-module/components/save-button/index.js.map +1 -1
  128. package/build-module/components/sidebar/navigation-menu-sidebar/navigation-menu.js +3 -3
  129. package/build-module/components/sidebar/navigation-menu-sidebar/navigation-menu.js.map +1 -1
  130. package/build-module/components/sidebar/template-card/template-actions.js +1 -1
  131. package/build-module/components/sidebar/template-card/template-actions.js.map +1 -1
  132. package/build-module/components/template-details/edit-template-title.js +12 -3
  133. package/build-module/components/template-details/edit-template-title.js.map +1 -1
  134. package/build-module/components/template-details/index.js +3 -22
  135. package/build-module/components/template-details/index.js.map +1 -1
  136. package/build-module/components/template-details/template-areas.js +1 -1
  137. package/build-module/components/template-details/template-areas.js.map +1 -1
  138. package/build-module/components/template-part-converter/convert-to-template-part.js +3 -1
  139. package/build-module/components/template-part-converter/convert-to-template-part.js.map +1 -1
  140. package/build-module/hooks/index.js +1 -0
  141. package/build-module/hooks/index.js.map +1 -1
  142. package/build-module/hooks/template-part-edit.js +67 -0
  143. package/build-module/hooks/template-part-edit.js.map +1 -0
  144. package/build-module/store/selectors.js +5 -2
  145. package/build-module/store/selectors.js.map +1 -1
  146. package/build-style/style-rtl.css +55 -48
  147. package/build-style/style.css +55 -48
  148. package/package.json +29 -29
  149. package/src/components/add-new-template/add-custom-generic-template-modal.js +97 -0
  150. package/src/components/add-new-template/add-custom-template-modal.js +93 -68
  151. package/src/components/add-new-template/new-template.js +126 -95
  152. package/src/components/add-new-template/style.scss +41 -8
  153. package/src/components/add-new-template/utils.js +622 -80
  154. package/src/components/block-editor/index.js +0 -2
  155. package/src/components/code-editor/index.js +15 -5
  156. package/src/components/editor/index.js +11 -0
  157. package/src/components/error-boundary/index.js +5 -0
  158. package/src/components/global-styles/dimensions-panel.js +214 -24
  159. package/src/components/global-styles/global-styles-provider.js +8 -9
  160. package/src/components/global-styles/hooks.js +18 -0
  161. package/src/components/global-styles/screen-color-palette.js +25 -27
  162. package/src/components/global-styles/screen-colors.js +55 -7
  163. package/src/components/global-styles/screen-heading-color.js +201 -0
  164. package/src/components/global-styles/screen-link-color.js +65 -23
  165. package/src/components/global-styles/screen-typography-element.js +4 -0
  166. package/src/components/global-styles/screen-typography.js +6 -0
  167. package/src/components/global-styles/style.scss +14 -11
  168. package/src/components/global-styles/test/typography-utils.js +130 -0
  169. package/src/components/global-styles/test/use-global-styles-output.js +296 -2
  170. package/src/components/global-styles/typography-panel.js +85 -16
  171. package/src/components/global-styles/typography-utils.js +228 -0
  172. package/src/components/global-styles/ui.js +13 -0
  173. package/src/components/global-styles/use-global-styles-output.js +387 -89
  174. package/src/components/global-styles/utils.js +43 -2
  175. package/src/components/header/index.js +37 -13
  176. package/src/components/header/style.scss +5 -3
  177. package/src/components/header/undo-redo/redo.js +6 -2
  178. package/src/components/keyboard-shortcut-help-modal/index.js +1 -2
  179. package/src/components/keyboard-shortcut-help-modal/style.scss +0 -5
  180. package/src/components/list/actions/index.js +3 -1
  181. package/src/components/list/style.scss +0 -8
  182. package/src/components/save-button/index.js +10 -13
  183. package/src/components/sidebar/navigation-menu-sidebar/navigation-menu.js +1 -5
  184. package/src/components/sidebar/style.scss +4 -0
  185. package/src/components/sidebar/template-card/template-actions.js +3 -1
  186. package/src/components/template-details/edit-template-title.js +10 -2
  187. package/src/components/template-details/index.js +7 -22
  188. package/src/components/template-details/template-areas.js +3 -1
  189. package/src/components/template-part-converter/convert-to-template-part.js +3 -1
  190. package/src/components/test/error-boundary.js +38 -0
  191. package/src/hooks/index.js +1 -0
  192. package/src/hooks/template-part-edit.js +82 -0
  193. package/src/store/selectors.js +11 -5
  194. package/src/style.scss +0 -1
  195. package/build/components/edit-template-part-menu-button/index.js +0 -90
  196. package/build/components/edit-template-part-menu-button/index.js.map +0 -1
  197. package/build-module/components/edit-template-part-menu-button/index.js +0 -72
  198. package/build-module/components/edit-template-part-menu-button/index.js.map +0 -1
  199. package/src/components/edit-template-part-menu-button/index.js +0 -82
@@ -8,14 +8,53 @@ import { get } from 'lodash';
8
8
 
9
9
  import { useSelect } from '@wordpress/data';
10
10
  import { store as coreStore } from '@wordpress/core-data';
11
+ import { store as editorStore } from '@wordpress/editor';
11
12
  import { decodeEntities } from '@wordpress/html-entities';
12
- import { useMemo } from '@wordpress/element';
13
- export const usePostTypes = () => {
13
+ import { useMemo, useCallback } from '@wordpress/element';
14
+ import { __, sprintf } from '@wordpress/i18n';
15
+ import { blockMeta, post } from '@wordpress/icons';
16
+ /**
17
+ * @typedef IHasNameAndId
18
+ * @property {string|number} id The entity's id.
19
+ * @property {string} name The entity's name.
20
+ */
21
+
22
+ /**
23
+ * Helper util to map records to add a `name` prop from a
24
+ * provided path, in order to handle all entities in the same
25
+ * fashion(implementing`IHasNameAndId` interface).
26
+ *
27
+ * @param {Object[]} entities The array of entities.
28
+ * @param {string} path The path to map a `name` property from the entity.
29
+ * @return {IHasNameAndId[]} An array of enitities that now implement the `IHasNameAndId` interface.
30
+ */
31
+
32
+ export const mapToIHasNameAndId = (entities, path) => {
33
+ return (entities || []).map(entity => ({ ...entity,
34
+ name: decodeEntities(get(entity, path))
35
+ }));
36
+ };
37
+ /**
38
+ * @typedef {Object} EntitiesInfo
39
+ * @property {boolean} hasEntities If an entity has available records(posts, terms, etc..).
40
+ * @property {number[]} existingEntitiesIds An array of the existing entities ids.
41
+ */
42
+
43
+ export const useExistingTemplates = () => {
44
+ return useSelect(select => select(coreStore).getEntityRecords('postType', 'wp_template', {
45
+ per_page: -1
46
+ }), []);
47
+ };
48
+ export const useDefaultTemplateTypes = () => {
49
+ return useSelect(select => select(editorStore).__experimentalGetDefaultTemplateTypes(), []);
50
+ };
51
+
52
+ const usePublicPostTypes = () => {
14
53
  const postTypes = useSelect(select => select(coreStore).getPostTypes({
15
54
  per_page: -1
16
55
  }), []);
17
56
  return useMemo(() => {
18
- const excludedPostTypes = ['attachment', 'page'];
57
+ const excludedPostTypes = ['attachment'];
19
58
  return postTypes === null || postTypes === void 0 ? void 0 : postTypes.filter(_ref => {
20
59
  let {
21
60
  viewable,
@@ -25,95 +64,561 @@ export const usePostTypes = () => {
25
64
  });
26
65
  }, [postTypes]);
27
66
  };
28
- /**
29
- * @typedef {Object} PostTypeEntitiesInfo
30
- * @property {boolean} hasEntities If a postType has available entities.
31
- * @property {number[]} existingPosts An array of the existing entities ids.
32
- */
33
67
 
68
+ const usePublicTaxonomies = () => {
69
+ const taxonomies = useSelect(select => select(coreStore).getTaxonomies({
70
+ per_page: -1
71
+ }), []);
72
+ return useMemo(() => {
73
+ return taxonomies === null || taxonomies === void 0 ? void 0 : taxonomies.filter(_ref2 => {
74
+ let {
75
+ visibility
76
+ } = _ref2;
77
+ return visibility === null || visibility === void 0 ? void 0 : visibility.publicly_queryable;
78
+ });
79
+ }, [taxonomies]);
80
+ };
81
+
82
+ export const usePostTypeMenuItems = onClickMenuItem => {
83
+ const publicPostTypes = usePublicPostTypes();
84
+ const existingTemplates = useExistingTemplates();
85
+ const defaultTemplateTypes = useDefaultTemplateTypes(); // `page`is a special case in template hierarchy.
86
+
87
+ const templatePrefixes = useMemo(() => publicPostTypes === null || publicPostTypes === void 0 ? void 0 : publicPostTypes.reduce((accumulator, _ref3) => {
88
+ let {
89
+ slug
90
+ } = _ref3;
91
+ let suffix = slug;
92
+
93
+ if (slug !== 'page') {
94
+ suffix = `single-${suffix}`;
95
+ }
96
+
97
+ accumulator[slug] = suffix;
98
+ return accumulator;
99
+ }, {}), [publicPostTypes]); // We need to keep track of naming conflicts. If a conflict
100
+ // occurs, we need to add slug.
101
+
102
+ const postTypeLabels = publicPostTypes === null || publicPostTypes === void 0 ? void 0 : publicPostTypes.reduce((accumulator, _ref4) => {
103
+ let {
104
+ labels
105
+ } = _ref4;
106
+ const singularName = labels.singular_name.toLowerCase();
107
+ accumulator[singularName] = (accumulator[singularName] || 0) + 1;
108
+ return accumulator;
109
+ }, {});
110
+
111
+ const needsUniqueIdentifier = (labels, slug) => {
112
+ const singularName = labels.singular_name.toLowerCase();
113
+ return postTypeLabels[singularName] > 1 && singularName !== slug;
114
+ };
115
+
116
+ const postTypesInfo = useEntitiesInfo('postType', templatePrefixes);
117
+ const existingTemplateSlugs = (existingTemplates || []).map(_ref5 => {
118
+ let {
119
+ slug
120
+ } = _ref5;
121
+ return slug;
122
+ });
123
+ const menuItems = (publicPostTypes || []).reduce((accumulator, postType) => {
124
+ var _postTypesInfo$slug;
125
+
126
+ const {
127
+ slug,
128
+ labels,
129
+ icon
130
+ } = postType; // We need to check if the general template is part of the
131
+ // defaultTemplateTypes. If it is, just use that info and
132
+ // augment it with the specific template functionality.
133
+
134
+ const generalTemplateSlug = templatePrefixes[slug];
135
+ const defaultTemplateType = defaultTemplateTypes === null || defaultTemplateTypes === void 0 ? void 0 : defaultTemplateTypes.find(_ref6 => {
136
+ let {
137
+ slug: _slug
138
+ } = _ref6;
139
+ return _slug === generalTemplateSlug;
140
+ });
141
+ const hasGeneralTemplate = existingTemplateSlugs === null || existingTemplateSlugs === void 0 ? void 0 : existingTemplateSlugs.includes(generalTemplateSlug);
142
+
143
+ const _needsUniqueIdentifier = needsUniqueIdentifier(labels, slug);
144
+
145
+ let menuItemTitle = sprintf( // translators: %s: Name of the post type e.g: "Post".
146
+ __('Single item: %s'), labels.singular_name);
147
+
148
+ if (_needsUniqueIdentifier) {
149
+ menuItemTitle = sprintf( // translators: %1s: Name of the post type e.g: "Post"; %2s: Slug of the post type e.g: "book".
150
+ __('Single item: %1$s (%2$s)'), labels.singular_name, slug);
151
+ }
152
+
153
+ const menuItem = defaultTemplateType ? { ...defaultTemplateType,
154
+ templatePrefix: templatePrefixes[slug]
155
+ } : {
156
+ slug: generalTemplateSlug,
157
+ title: menuItemTitle,
158
+ description: sprintf( // translators: %s: Name of the post type e.g: "Post".
159
+ __('Displays a single item: %s.'), labels.singular_name),
160
+ // `icon` is the `menu_icon` property of a post type. We
161
+ // only handle `dashicons` for now, even if the `menu_icon`
162
+ // also supports urls and svg as values.
163
+ icon: icon !== null && icon !== void 0 && icon.startsWith('dashicons-') ? icon.slice(10) : post,
164
+ templatePrefix: templatePrefixes[slug]
165
+ };
166
+ const hasEntities = postTypesInfo === null || postTypesInfo === void 0 ? void 0 : (_postTypesInfo$slug = postTypesInfo[slug]) === null || _postTypesInfo$slug === void 0 ? void 0 : _postTypesInfo$slug.hasEntities; // We have a different template creation flow only if they have entities.
167
+
168
+ if (hasEntities) {
169
+ menuItem.onClick = template => {
170
+ onClickMenuItem({
171
+ type: 'postType',
172
+ slug,
173
+ config: {
174
+ recordNamePath: 'title.rendered',
175
+ queryArgs: _ref7 => {
176
+ let {
177
+ search
178
+ } = _ref7;
179
+ return {
180
+ _fields: 'id,title,slug,link',
181
+ orderBy: search ? 'relevance' : 'modified',
182
+ exclude: postTypesInfo[slug].existingEntitiesIds
183
+ };
184
+ },
185
+ getSpecificTemplate: suggestion => {
186
+ let title = sprintf( // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the singular name of a post type and %2$s is the name of the post, e.g. "Page: Hello".
187
+ __('%1$s: %2$s'), labels.singular_name, suggestion.name);
188
+ const description = sprintf( // translators: Represents the description of a user's custom template in the Site Editor, e.g. "Template for Page: Hello"
189
+ __('Template for %1$s'), title);
190
+
191
+ if (_needsUniqueIdentifier) {
192
+ title = sprintf( // 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)"
193
+ __('%1$s (%2$s)'), title, slug);
194
+ }
195
+
196
+ return {
197
+ title,
198
+ description,
199
+ slug: `${templatePrefixes[slug]}-${suggestion.slug}`,
200
+ templatePrefix: templatePrefixes[slug]
201
+ };
202
+ }
203
+ },
204
+ labels,
205
+ hasGeneralTemplate,
206
+ template
207
+ });
208
+ };
209
+ } // We don't need to add the menu item if there are no
210
+ // entities and the general template exists.
211
+
212
+
213
+ if (!hasGeneralTemplate || hasEntities) {
214
+ accumulator.push(menuItem);
215
+ }
216
+
217
+ return accumulator;
218
+ }, []); // Split menu items into two groups: one for the default post types
219
+ // and one for the rest.
220
+
221
+ const postTypesMenuItems = useMemo(() => menuItems.reduce((accumulator, postType) => {
222
+ const {
223
+ slug
224
+ } = postType;
225
+ let key = 'postTypesMenuItems';
226
+
227
+ if (slug === 'page') {
228
+ key = 'defaultPostTypesMenuItems';
229
+ }
230
+
231
+ accumulator[key].push(postType);
232
+ return accumulator;
233
+ }, {
234
+ defaultPostTypesMenuItems: [],
235
+ postTypesMenuItems: []
236
+ }), [menuItems]);
237
+ return postTypesMenuItems;
238
+ };
239
+ export const useTaxonomiesMenuItems = onClickMenuItem => {
240
+ const publicTaxonomies = usePublicTaxonomies();
241
+ const existingTemplates = useExistingTemplates();
242
+ const defaultTemplateTypes = useDefaultTemplateTypes(); // `category` and `post_tag` are special cases in template hierarchy.
243
+
244
+ const templatePrefixes = useMemo(() => publicTaxonomies === null || publicTaxonomies === void 0 ? void 0 : publicTaxonomies.reduce((accumulator, _ref8) => {
245
+ let {
246
+ slug
247
+ } = _ref8;
248
+ let suffix = slug;
249
+
250
+ if (!['category', 'post_tag'].includes(slug)) {
251
+ suffix = `taxonomy-${suffix}`;
252
+ }
253
+
254
+ if (slug === 'post_tag') {
255
+ suffix = `tag`;
256
+ }
257
+
258
+ accumulator[slug] = suffix;
259
+ return accumulator;
260
+ }, {}), [publicTaxonomies]); // We need to keep track of naming conflicts. If a conflict
261
+ // occurs, we need to add slug.
262
+
263
+ const taxonomyLabels = publicTaxonomies === null || publicTaxonomies === void 0 ? void 0 : publicTaxonomies.reduce((accumulator, _ref9) => {
264
+ let {
265
+ labels
266
+ } = _ref9;
267
+ const singularName = labels.singular_name.toLowerCase();
268
+ accumulator[singularName] = (accumulator[singularName] || 0) + 1;
269
+ return accumulator;
270
+ }, {});
271
+
272
+ const needsUniqueIdentifier = (labels, slug) => {
273
+ if (['category', 'post_tag'].includes(slug)) {
274
+ return false;
275
+ }
276
+
277
+ const singularName = labels.singular_name.toLowerCase();
278
+ return taxonomyLabels[singularName] > 1 && singularName !== slug;
279
+ };
280
+
281
+ const taxonomiesInfo = useEntitiesInfo('taxonomy', templatePrefixes);
282
+ const existingTemplateSlugs = (existingTemplates || []).map(_ref10 => {
283
+ let {
284
+ slug
285
+ } = _ref10;
286
+ return slug;
287
+ });
288
+ const menuItems = (publicTaxonomies || []).reduce((accumulator, taxonomy) => {
289
+ var _taxonomiesInfo$slug;
290
+
291
+ const {
292
+ slug,
293
+ labels
294
+ } = taxonomy; // We need to check if the general template is part of the
295
+ // defaultTemplateTypes. If it is, just use that info and
296
+ // augment it with the specific template functionality.
297
+
298
+ const generalTemplateSlug = templatePrefixes[slug];
299
+ const defaultTemplateType = defaultTemplateTypes === null || defaultTemplateTypes === void 0 ? void 0 : defaultTemplateTypes.find(_ref11 => {
300
+ let {
301
+ slug: _slug
302
+ } = _ref11;
303
+ return _slug === generalTemplateSlug;
304
+ });
305
+ const hasGeneralTemplate = existingTemplateSlugs === null || existingTemplateSlugs === void 0 ? void 0 : existingTemplateSlugs.includes(generalTemplateSlug);
306
+
307
+ const _needsUniqueIdentifier = needsUniqueIdentifier(labels, slug);
308
+
309
+ let menuItemTitle = labels.singular_name;
310
+
311
+ if (_needsUniqueIdentifier) {
312
+ menuItemTitle = sprintf( // translators: %1s: Name of the taxonomy e.g: "Category"; %2s: Slug of the taxonomy e.g: "product_cat".
313
+ __('%1$s (%2$s)'), labels.singular_name, slug);
314
+ }
315
+
316
+ const menuItem = defaultTemplateType ? { ...defaultTemplateType,
317
+ templatePrefix: templatePrefixes[slug]
318
+ } : {
319
+ slug: generalTemplateSlug,
320
+ title: menuItemTitle,
321
+ description: sprintf( // translators: %s: Name of the taxonomy e.g: "Product Categories".
322
+ __('Displays taxonomy: %s.'), labels.singular_name),
323
+ icon: blockMeta,
324
+ templatePrefix: templatePrefixes[slug]
325
+ };
326
+ const hasEntities = taxonomiesInfo === null || taxonomiesInfo === void 0 ? void 0 : (_taxonomiesInfo$slug = taxonomiesInfo[slug]) === null || _taxonomiesInfo$slug === void 0 ? void 0 : _taxonomiesInfo$slug.hasEntities; // We have a different template creation flow only if they have entities.
327
+
328
+ if (hasEntities) {
329
+ menuItem.onClick = template => {
330
+ onClickMenuItem({
331
+ type: 'taxonomy',
332
+ slug,
333
+ config: {
334
+ queryArgs: _ref12 => {
335
+ let {
336
+ search
337
+ } = _ref12;
338
+ return {
339
+ _fields: 'id,name,slug,link',
340
+ orderBy: search ? 'name' : 'count',
341
+ exclude: taxonomiesInfo[slug].existingEntitiesIds
342
+ };
343
+ },
344
+ getSpecificTemplate: suggestion => {
345
+ let title = sprintf( // translators: Represents the title of a user's custom template in the Site Editor, where %1$s is the singular name of a taxonomy and %2$s is the name of the term, e.g. "Category: shoes".
346
+ __('%1$s: %2$s'), labels.singular_name, suggestion.name);
347
+ const description = sprintf( // translators: Represents the description of a user's custom template in the Site Editor, e.g. "Template for Category: shoes"
348
+ __('Template for %1$s'), title);
349
+
350
+ if (_needsUniqueIdentifier) {
351
+ title = sprintf( // 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)"
352
+ __('%1$s (%2$s)'), title, slug);
353
+ }
354
+
355
+ return {
356
+ title,
357
+ description,
358
+ slug: `${templatePrefixes[slug]}-${suggestion.slug}`,
359
+ templatePrefix: templatePrefixes[slug]
360
+ };
361
+ }
362
+ },
363
+ labels,
364
+ hasGeneralTemplate,
365
+ template
366
+ });
367
+ };
368
+ } // We don't need to add the menu item if there are no
369
+ // entities and the general template exists.
370
+
371
+
372
+ if (!hasGeneralTemplate || hasEntities) {
373
+ accumulator.push(menuItem);
374
+ }
375
+
376
+ return accumulator;
377
+ }, []); // Split menu items into two groups: one for the default taxonomies
378
+ // and one for the rest.
379
+
380
+ const taxonomiesMenuItems = useMemo(() => menuItems.reduce((accumulator, taxonomy) => {
381
+ const {
382
+ slug
383
+ } = taxonomy;
384
+ let key = 'taxonomiesMenuItems';
385
+
386
+ if (['category', 'tag'].includes(slug)) {
387
+ key = 'defaultTaxonomiesMenuItems';
388
+ }
389
+
390
+ accumulator[key].push(taxonomy);
391
+ return accumulator;
392
+ }, {
393
+ defaultTaxonomiesMenuItems: [],
394
+ taxonomiesMenuItems: []
395
+ }), [menuItems]);
396
+ return taxonomiesMenuItems;
397
+ };
398
+
399
+ function useAuthorNeedsUniqueIndentifier() {
400
+ const authors = useSelect(select => select(coreStore).getUsers({
401
+ who: 'authors',
402
+ per_page: -1
403
+ }), []);
404
+ const authorsCountByName = useMemo(() => {
405
+ return (authors || []).reduce((authorsCount, _ref13) => {
406
+ let {
407
+ name
408
+ } = _ref13;
409
+ authorsCount[name] = (authorsCount[name] || 0) + 1;
410
+ return authorsCount;
411
+ }, {});
412
+ }, [authors]);
413
+ return useCallback(name => {
414
+ return authorsCountByName[name] > 1;
415
+ }, [authorsCountByName]);
416
+ }
417
+
418
+ const USE_AUTHOR_MENU_ITEM_TEMPLATE_PREFIX = {
419
+ user: 'author'
420
+ };
421
+ const USE_AUTHOR_MENU_ITEM_QUERY_PARAMETERS = {
422
+ user: {
423
+ who: 'authors'
424
+ }
425
+ };
426
+ export function useAuthorMenuItem(onClickMenuItem) {
427
+ var _authorInfo$user, _authorInfo$user2;
428
+
429
+ const existingTemplates = useExistingTemplates();
430
+ const defaultTemplateTypes = useDefaultTemplateTypes();
431
+ const authorInfo = useEntitiesInfo('root', USE_AUTHOR_MENU_ITEM_TEMPLATE_PREFIX, USE_AUTHOR_MENU_ITEM_QUERY_PARAMETERS);
432
+ const authorNeedsUniqueId = useAuthorNeedsUniqueIndentifier();
433
+ let authorMenuItem = defaultTemplateTypes === null || defaultTemplateTypes === void 0 ? void 0 : defaultTemplateTypes.find(_ref14 => {
434
+ let {
435
+ slug
436
+ } = _ref14;
437
+ return slug === 'author';
438
+ });
439
+
440
+ if (!authorMenuItem) {
441
+ authorMenuItem = {
442
+ description: __('Displays latest posts written by a single author.'),
443
+ slug: 'author',
444
+ title: 'Author'
445
+ };
446
+ }
447
+
448
+ const hasGeneralTemplate = !!(existingTemplates !== null && existingTemplates !== void 0 && existingTemplates.find(_ref15 => {
449
+ let {
450
+ slug
451
+ } = _ref15;
452
+ return slug === 'author';
453
+ }));
454
+
455
+ if ((_authorInfo$user = authorInfo.user) !== null && _authorInfo$user !== void 0 && _authorInfo$user.hasEntities) {
456
+ authorMenuItem = { ...authorMenuItem,
457
+ templatePrefix: 'author'
458
+ };
459
+
460
+ authorMenuItem.onClick = template => {
461
+ onClickMenuItem({
462
+ type: 'root',
463
+ slug: 'user',
464
+ config: {
465
+ queryArgs: _ref16 => {
466
+ let {
467
+ search
468
+ } = _ref16;
469
+ return {
470
+ _fields: 'id,name,slug,link',
471
+ orderBy: search ? 'name' : 'registered_date',
472
+ exclude: authorInfo.user.existingEntitiesIds,
473
+ who: 'authors'
474
+ };
475
+ },
476
+ getSpecificTemplate: suggestion => {
477
+ const needsUniqueId = authorNeedsUniqueId(suggestion.name);
478
+ const title = needsUniqueId ? sprintf( // 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".
479
+ __('Author: %1$s (%2$s)'), suggestion.name, suggestion.slug) : sprintf( // translators: %s: Represents the name of an author e.g: "Jorge".
480
+ __('Author: %s'), suggestion.name);
481
+ const description = sprintf( // translators: %s: Represents the name of an author e.g: "Jorge".
482
+ __('Template for Author: %s'), suggestion.name);
483
+ return {
484
+ title,
485
+ description,
486
+ slug: `author-${suggestion.slug}`,
487
+ templatePrefix: 'author'
488
+ };
489
+ }
490
+ },
491
+ labels: {
492
+ singular_name: __('Author'),
493
+ search_items: __('Search Authors'),
494
+ not_found: __('No authors found.'),
495
+ all_items: __('All Authors')
496
+ },
497
+ hasGeneralTemplate,
498
+ template
499
+ });
500
+ };
501
+ }
502
+
503
+ if (!hasGeneralTemplate || (_authorInfo$user2 = authorInfo.user) !== null && _authorInfo$user2 !== void 0 && _authorInfo$user2.hasEntities) {
504
+ return authorMenuItem;
505
+ }
506
+ }
34
507
  /**
35
- * Helper hook that returns information about a post type having
36
- * posts that we can create a specific template for.
508
+ * Helper hook that filters all the existing templates by the given
509
+ * object with the entity's slug as key and the template prefix as value.
37
510
  *
38
- * First we need to find the existing posts with an associated template,
39
- * to query afterwards for any remaing post, by excluding them.
511
+ * Example:
512
+ * `existingTemplates` is: [ { slug: 'tag-apple' }, { slug: 'page-about' }, { slug: 'tag' } ]
513
+ * `templatePrefixes` is: { post_tag: 'tag' }
514
+ * It will return: { post_tag: ['apple'] }
40
515
  *
41
- * @param {string[]} existingTemplates The existing templates.
42
- * @return {Record<string,PostTypeEntitiesInfo>} An object with the postTypes as `keys` and PostTypeEntitiesInfo as values.
516
+ * Note: We append the `-` to the given template prefix in this function for our checks.
517
+ *
518
+ * @param {Record<string,string>} templatePrefixes An object with the entity's slug as key and the template prefix as value.
519
+ * @return {Record<string,string[]>} An object with the entity's slug as key and an array with the existing template slugs as value.
43
520
  */
44
521
 
45
- export const usePostTypesEntitiesInfo = existingTemplates => {
46
- const postTypes = usePostTypes();
47
- const slugsToExcludePerEntity = useMemo(() => {
48
- return postTypes === null || postTypes === void 0 ? void 0 : postTypes.reduce((accumulator, _postType) => {
522
+ const useExistingTemplateSlugs = templatePrefixes => {
523
+ const existingTemplates = useExistingTemplates();
524
+ const existingSlugs = useMemo(() => {
525
+ return Object.entries(templatePrefixes || {}).reduce((accumulator, _ref17) => {
526
+ let [slug, prefix] = _ref17;
49
527
  const slugsWithTemplates = (existingTemplates || []).reduce((_accumulator, existingTemplate) => {
50
- const prefix = `single-${_postType.slug}-`;
528
+ const _prefix = `${prefix}-`;
51
529
 
52
- if (existingTemplate.slug.startsWith(prefix)) {
53
- _accumulator.push(existingTemplate.slug.substring(prefix.length));
530
+ if (existingTemplate.slug.startsWith(_prefix)) {
531
+ _accumulator.push(existingTemplate.slug.substring(_prefix.length));
54
532
  }
55
533
 
56
534
  return _accumulator;
57
535
  }, []);
58
536
 
59
537
  if (slugsWithTemplates.length) {
60
- accumulator[_postType.slug] = slugsWithTemplates;
538
+ accumulator[slug] = slugsWithTemplates;
61
539
  }
62
540
 
63
541
  return accumulator;
64
542
  }, {});
65
- }, [postTypes, existingTemplates]);
66
- const postsToExcludePerEntity = useSelect(select => {
67
- if (!slugsToExcludePerEntity) {
68
- return;
69
- }
543
+ }, [templatePrefixes, existingTemplates]);
544
+ return existingSlugs;
545
+ };
546
+ /**
547
+ * Helper hook that finds the existing records with an associated template,
548
+ * as they need to be excluded from the template suggestions.
549
+ *
550
+ * @param {string} entityName The entity's name.
551
+ * @param {Record<string,string>} templatePrefixes An object with the entity's slug as key and the template prefix as value.
552
+ * @param {Record<string,Object>} additionalQueryParameters An object with the entity's slug as key and additional query parameters as value.
553
+ * @return {Record<string,EntitiesInfo>} An object with the entity's slug as key and the existing records as value.
554
+ */
70
555
 
71
- const postsToExclude = Object.entries(slugsToExcludePerEntity).reduce((accumulator, _ref2) => {
72
- let [slug, slugsWithTemplates] = _ref2;
73
- const postsWithTemplates = select(coreStore).getEntityRecords('postType', slug, {
556
+
557
+ const useTemplatesToExclude = function (entityName, templatePrefixes) {
558
+ let additionalQueryParameters = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
559
+ const slugsToExcludePerEntity = useExistingTemplateSlugs(templatePrefixes);
560
+ const recordsToExcludePerEntity = useSelect(select => {
561
+ return Object.entries(slugsToExcludePerEntity || {}).reduce((accumulator, _ref18) => {
562
+ let [slug, slugsWithTemplates] = _ref18;
563
+ const entitiesWithTemplates = select(coreStore).getEntityRecords(entityName, slug, {
74
564
  _fields: 'id',
75
565
  context: 'view',
76
- slug: slugsWithTemplates
566
+ slug: slugsWithTemplates,
567
+ ...additionalQueryParameters[slug]
77
568
  });
78
569
 
79
- if (postsWithTemplates !== null && postsWithTemplates !== void 0 && postsWithTemplates.length) {
80
- accumulator[slug] = postsWithTemplates;
570
+ if (entitiesWithTemplates !== null && entitiesWithTemplates !== void 0 && entitiesWithTemplates.length) {
571
+ accumulator[slug] = entitiesWithTemplates;
81
572
  }
82
573
 
83
574
  return accumulator;
84
575
  }, {});
85
- return postsToExclude;
86
576
  }, [slugsToExcludePerEntity]);
577
+ return recordsToExcludePerEntity;
578
+ };
579
+ /**
580
+ * Helper hook that returns information about an entity having
581
+ * records that we can create a specific template for.
582
+ *
583
+ * For example we can search for `terms` in `taxonomy` entity or
584
+ * `posts` in `postType` entity.
585
+ *
586
+ * First we need to find the existing records with an associated template,
587
+ * to query afterwards for any remaining record, by excluding them.
588
+ *
589
+ * @param {string} entityName The entity's name.
590
+ * @param {Record<string,string>} templatePrefixes An object with the entity's slug as key and the template prefix as value.
591
+ * @param {Record<string,Object>} additionalQueryParameters An object with the entity's slug as key and additional query parameters as value.
592
+ * @return {Record<string,EntitiesInfo>} An object with the entity's slug as key and the EntitiesInfo as value.
593
+ */
594
+
595
+
596
+ const useEntitiesInfo = function (entityName, templatePrefixes) {
597
+ let additionalQueryParameters = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
598
+ const recordsToExcludePerEntity = useTemplatesToExclude(entityName, templatePrefixes, additionalQueryParameters);
87
599
  const entitiesInfo = useSelect(select => {
88
- return postTypes === null || postTypes === void 0 ? void 0 : postTypes.reduce((accumulator, _ref3) => {
89
- var _postsToExcludePerEnt, _select$getEntityReco;
600
+ return Object.keys(templatePrefixes || {}).reduce((accumulator, slug) => {
601
+ var _recordsToExcludePerE, _select$getEntityReco;
90
602
 
91
- let {
92
- slug
93
- } = _ref3;
94
- const existingPosts = (postsToExcludePerEntity === null || postsToExcludePerEntity === void 0 ? void 0 : (_postsToExcludePerEnt = postsToExcludePerEntity[slug]) === null || _postsToExcludePerEnt === void 0 ? void 0 : _postsToExcludePerEnt.map(_ref4 => {
603
+ const existingEntitiesIds = (recordsToExcludePerEntity === null || recordsToExcludePerEntity === void 0 ? void 0 : (_recordsToExcludePerE = recordsToExcludePerEntity[slug]) === null || _recordsToExcludePerE === void 0 ? void 0 : _recordsToExcludePerE.map(_ref19 => {
95
604
  let {
96
605
  id
97
- } = _ref4;
606
+ } = _ref19;
98
607
  return id;
99
608
  })) || [];
100
609
  accumulator[slug] = {
101
- hasEntities: !!((_select$getEntityReco = select(coreStore).getEntityRecords('postType', slug, {
610
+ hasEntities: !!((_select$getEntityReco = select(coreStore).getEntityRecords(entityName, slug, {
102
611
  per_page: 1,
103
612
  _fields: 'id',
104
613
  context: 'view',
105
- exclude: existingPosts
614
+ exclude: existingEntitiesIds,
615
+ ...additionalQueryParameters[slug]
106
616
  })) !== null && _select$getEntityReco !== void 0 && _select$getEntityReco.length),
107
- existingPosts
617
+ existingEntitiesIds
108
618
  };
109
619
  return accumulator;
110
620
  }, {});
111
- }, [postTypes, postsToExcludePerEntity]);
621
+ }, [templatePrefixes, recordsToExcludePerEntity]);
112
622
  return entitiesInfo;
113
623
  };
114
- export const mapToIHasNameAndId = (entities, path) => {
115
- return (entities || []).map(entity => ({ ...entity,
116
- name: decodeEntities(get(entity, path))
117
- }));
118
- };
119
624
  //# sourceMappingURL=utils.js.map