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