@wordpress/block-library 7.1.1 → 7.2.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 (304) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/build/avatar/edit.js +205 -0
  3. package/build/avatar/edit.js.map +1 -0
  4. package/build/avatar/hooks.js +111 -0
  5. package/build/avatar/hooks.js.map +1 -0
  6. package/build/avatar/index.js +83 -0
  7. package/build/avatar/index.js.map +1 -0
  8. package/build/avatar/user-control.js +63 -0
  9. package/build/avatar/user-control.js.map +1 -0
  10. package/build/column/index.js +2 -1
  11. package/build/column/index.js.map +1 -1
  12. package/build/comment-author-avatar/index.js +1 -0
  13. package/build/comment-author-avatar/index.js.map +1 -1
  14. package/build/comment-date/edit.js +10 -25
  15. package/build/comment-date/edit.js.map +1 -1
  16. package/build/comment-template/edit.js +79 -52
  17. package/build/comment-template/edit.js.map +1 -1
  18. package/build/cover/edit.js +23 -33
  19. package/build/cover/edit.js.map +1 -1
  20. package/build/embed/variations.js +2 -2
  21. package/build/embed/variations.js.map +1 -1
  22. package/build/gallery/edit.js +18 -5
  23. package/build/gallery/edit.js.map +1 -1
  24. package/build/gallery/gallery.js +1 -1
  25. package/build/gallery/gallery.js.map +1 -1
  26. package/build/gallery/gap-styles.js +29 -0
  27. package/build/gallery/gap-styles.js.map +1 -0
  28. package/build/gallery/index.js +17 -1
  29. package/build/gallery/index.js.map +1 -1
  30. package/build/group/index.js +3 -1
  31. package/build/group/index.js.map +1 -1
  32. package/build/group/variations.js +1 -2
  33. package/build/group/variations.js.map +1 -1
  34. package/build/image/image.js +3 -1
  35. package/build/image/image.js.map +1 -1
  36. package/build/index.js +8 -2
  37. package/build/index.js.map +1 -1
  38. package/build/list/index.js +5 -7
  39. package/build/list/index.js.map +1 -1
  40. package/build/list/v2/edit.js +85 -0
  41. package/build/list/v2/edit.js.map +1 -0
  42. package/build/list/v2/index.js +33 -0
  43. package/build/list/v2/index.js.map +1 -0
  44. package/build/list/v2/save.js +34 -0
  45. package/build/list/v2/save.js.map +1 -0
  46. package/build/list/v2/transforms.js +121 -0
  47. package/build/list/v2/transforms.js.map +1 -0
  48. package/build/list-item/edit.js +47 -0
  49. package/build/list-item/edit.js.map +1 -0
  50. package/build/list-item/index.js +67 -0
  51. package/build/list-item/index.js.map +1 -0
  52. package/build/list-item/save.js +23 -0
  53. package/build/list-item/save.js.map +1 -0
  54. package/build/media-text/edit.js +3 -1
  55. package/build/media-text/edit.js.map +1 -1
  56. package/build/navigation/use-navigation-entities.js +26 -54
  57. package/build/navigation/use-navigation-entities.js.map +1 -1
  58. package/build/navigation-link/edit.js +88 -34
  59. package/build/navigation-link/edit.js.map +1 -1
  60. package/build/post-date/edit.js +31 -37
  61. package/build/post-date/edit.js.map +1 -1
  62. package/build/post-featured-image/edit.js +2 -1
  63. package/build/post-featured-image/edit.js.map +1 -1
  64. package/build/pullquote/edit.js +5 -3
  65. package/build/pullquote/edit.js.map +1 -1
  66. package/build/query/variations.js +4 -4
  67. package/build/query/variations.js.map +1 -1
  68. package/build/query-no-results/edit.js +28 -0
  69. package/build/query-no-results/edit.js.map +1 -0
  70. package/build/query-no-results/index.js +54 -0
  71. package/build/query-no-results/index.js.map +1 -0
  72. package/build/query-no-results/save.js +18 -0
  73. package/build/query-no-results/save.js.map +1 -0
  74. package/build/search/edit.js +2 -3
  75. package/build/search/edit.js.map +1 -1
  76. package/build/separator/deprecated.js +83 -0
  77. package/build/separator/deprecated.js.map +1 -0
  78. package/build/separator/edit.js +31 -23
  79. package/build/separator/edit.js.map +1 -1
  80. package/build/separator/index.js +17 -7
  81. package/build/separator/index.js.map +1 -1
  82. package/build/separator/save.js +18 -13
  83. package/build/separator/save.js.map +1 -1
  84. package/build/separator/use-deprecated-opacity.js +39 -0
  85. package/build/separator/use-deprecated-opacity.js.map +1 -0
  86. package/build/social-links/edit.js +14 -3
  87. package/build/social-links/edit.js.map +1 -1
  88. package/build/social-links/index.js +1 -1
  89. package/build/spacer/controls.js +6 -16
  90. package/build/spacer/controls.js.map +1 -1
  91. package/build/spacer/controls.native.js +3 -1
  92. package/build/spacer/controls.native.js.map +1 -1
  93. package/build/spacer/edit.js +4 -5
  94. package/build/spacer/edit.js.map +1 -1
  95. package/build/table/deprecated.js +1 -1
  96. package/build/table/deprecated.js.map +1 -1
  97. package/build/tag-cloud/edit.js +58 -2
  98. package/build/tag-cloud/edit.js.map +1 -1
  99. package/build/tag-cloud/index.js +8 -0
  100. package/build/tag-cloud/index.js.map +1 -1
  101. package/build-module/avatar/edit.js +190 -0
  102. package/build-module/avatar/edit.js.map +1 -0
  103. package/build-module/avatar/hooks.js +99 -0
  104. package/build-module/avatar/hooks.js.map +1 -0
  105. package/build-module/avatar/index.js +70 -0
  106. package/build-module/avatar/index.js.map +1 -0
  107. package/build-module/avatar/user-control.js +52 -0
  108. package/build-module/avatar/user-control.js.map +1 -0
  109. package/build-module/column/index.js +2 -1
  110. package/build-module/column/index.js.map +1 -1
  111. package/build-module/comment-author-avatar/index.js +1 -0
  112. package/build-module/comment-author-avatar/index.js.map +1 -1
  113. package/build-module/comment-date/edit.js +13 -30
  114. package/build-module/comment-date/edit.js.map +1 -1
  115. package/build-module/comment-template/edit.js +79 -52
  116. package/build-module/comment-template/edit.js.map +1 -1
  117. package/build-module/cover/edit.js +23 -34
  118. package/build-module/cover/edit.js.map +1 -1
  119. package/build-module/embed/variations.js +2 -2
  120. package/build-module/embed/variations.js.map +1 -1
  121. package/build-module/gallery/edit.js +17 -5
  122. package/build-module/gallery/edit.js.map +1 -1
  123. package/build-module/gallery/gallery.js +1 -1
  124. package/build-module/gallery/gallery.js.map +1 -1
  125. package/build-module/gallery/gap-styles.js +22 -0
  126. package/build-module/gallery/gap-styles.js.map +1 -0
  127. package/build-module/gallery/index.js +17 -1
  128. package/build-module/gallery/index.js.map +1 -1
  129. package/build-module/group/index.js +3 -1
  130. package/build-module/group/index.js.map +1 -1
  131. package/build-module/group/variations.js +1 -2
  132. package/build-module/group/variations.js.map +1 -1
  133. package/build-module/image/image.js +3 -1
  134. package/build-module/image/image.js.map +1 -1
  135. package/build-module/index.js +5 -2
  136. package/build-module/index.js.map +1 -1
  137. package/build-module/list/index.js +5 -1
  138. package/build-module/list/index.js.map +1 -1
  139. package/build-module/list/v2/edit.js +69 -0
  140. package/build-module/list/v2/edit.js.map +1 -0
  141. package/build-module/list/v2/index.js +19 -0
  142. package/build-module/list/v2/index.js.map +1 -0
  143. package/build-module/list/v2/save.js +23 -0
  144. package/build-module/list/v2/save.js.map +1 -0
  145. package/build-module/list/v2/transforms.js +111 -0
  146. package/build-module/list/v2/transforms.js.map +1 -0
  147. package/build-module/list-item/edit.js +37 -0
  148. package/build-module/list-item/edit.js.map +1 -0
  149. package/build-module/list-item/index.js +53 -0
  150. package/build-module/list-item/index.js.map +1 -0
  151. package/build-module/list-item/save.js +15 -0
  152. package/build-module/list-item/save.js.map +1 -0
  153. package/build-module/media-text/edit.js +3 -1
  154. package/build-module/media-text/edit.js.map +1 -1
  155. package/build-module/navigation/use-navigation-entities.js +27 -54
  156. package/build-module/navigation/use-navigation-entities.js.map +1 -1
  157. package/build-module/navigation-link/edit.js +88 -33
  158. package/build-module/navigation-link/edit.js.map +1 -1
  159. package/build-module/post-date/edit.js +32 -43
  160. package/build-module/post-date/edit.js.map +1 -1
  161. package/build-module/post-featured-image/edit.js +2 -1
  162. package/build-module/post-featured-image/edit.js.map +1 -1
  163. package/build-module/pullquote/edit.js +6 -3
  164. package/build-module/pullquote/edit.js.map +1 -1
  165. package/build-module/query/variations.js +4 -4
  166. package/build-module/query/variations.js.map +1 -1
  167. package/build-module/query-no-results/edit.js +18 -0
  168. package/build-module/query-no-results/edit.js.map +1 -0
  169. package/build-module/query-no-results/index.js +40 -0
  170. package/build-module/query-no-results/index.js.map +1 -0
  171. package/build-module/query-no-results/save.js +10 -0
  172. package/build-module/query-no-results/save.js.map +1 -0
  173. package/build-module/search/edit.js +2 -3
  174. package/build-module/search/edit.js.map +1 -1
  175. package/build-module/separator/deprecated.js +70 -0
  176. package/build-module/separator/deprecated.js.map +1 -0
  177. package/build-module/separator/edit.js +31 -22
  178. package/build-module/separator/edit.js.map +1 -1
  179. package/build-module/separator/index.js +16 -7
  180. package/build-module/separator/index.js.map +1 -1
  181. package/build-module/separator/save.js +19 -14
  182. package/build-module/separator/save.js.map +1 -1
  183. package/build-module/separator/use-deprecated-opacity.js +30 -0
  184. package/build-module/separator/use-deprecated-opacity.js.map +1 -0
  185. package/build-module/social-links/edit.js +15 -4
  186. package/build-module/social-links/edit.js.map +1 -1
  187. package/build-module/social-links/index.js +1 -1
  188. package/build-module/spacer/controls.js +8 -19
  189. package/build-module/spacer/controls.js.map +1 -1
  190. package/build-module/spacer/controls.native.js +2 -1
  191. package/build-module/spacer/controls.native.js.map +1 -1
  192. package/build-module/spacer/edit.js +3 -3
  193. package/build-module/spacer/edit.js.map +1 -1
  194. package/build-module/table/deprecated.js +1 -1
  195. package/build-module/table/deprecated.js.map +1 -1
  196. package/build-module/tag-cloud/edit.js +60 -4
  197. package/build-module/tag-cloud/edit.js.map +1 -1
  198. package/build-module/tag-cloud/index.js +8 -0
  199. package/build-module/tag-cloud/index.js.map +1 -1
  200. package/build-style/avatar/editor-rtl.css +79 -0
  201. package/build-style/avatar/editor.css +79 -0
  202. package/build-style/editor-rtl.css +23 -2
  203. package/build-style/editor.css +23 -2
  204. package/build-style/gallery/editor-rtl.css +0 -1
  205. package/build-style/gallery/editor.css +0 -1
  206. package/build-style/gallery/style-rtl.css +102 -169
  207. package/build-style/gallery/style.css +102 -169
  208. package/build-style/image/style-rtl.css +2 -0
  209. package/build-style/image/style.css +2 -0
  210. package/build-style/navigation-link/editor-rtl.css +13 -0
  211. package/build-style/navigation-link/editor.css +13 -0
  212. package/build-style/pullquote/style-rtl.css +0 -4
  213. package/build-style/pullquote/style.css +0 -4
  214. package/build-style/separator/editor-rtl.css +3 -0
  215. package/build-style/separator/editor.css +3 -0
  216. package/build-style/separator/theme-rtl.css +7 -1
  217. package/build-style/separator/theme.css +7 -1
  218. package/build-style/site-logo/editor-rtl.css +3 -1
  219. package/build-style/site-logo/editor.css +3 -1
  220. package/build-style/style-rtl.css +104 -173
  221. package/build-style/style.css +104 -173
  222. package/build-style/theme-rtl.css +7 -1
  223. package/build-style/theme.css +7 -1
  224. package/package.json +28 -28
  225. package/src/avatar/block.json +53 -0
  226. package/src/avatar/edit.js +222 -0
  227. package/src/avatar/editor.scss +3 -0
  228. package/src/avatar/hooks.js +96 -0
  229. package/src/avatar/index.js +18 -0
  230. package/src/avatar/index.php +146 -0
  231. package/src/avatar/user-control.js +56 -0
  232. package/src/column/block.json +2 -1
  233. package/src/comment-author-avatar/block.json +1 -0
  234. package/src/comment-date/edit.js +20 -30
  235. package/src/comment-template/edit.js +65 -44
  236. package/src/cover/edit.js +26 -31
  237. package/src/editor.scss +1 -0
  238. package/src/embed/variations.js +2 -2
  239. package/src/gallery/block.json +17 -1
  240. package/src/gallery/deprecated.scss +2 -2
  241. package/src/gallery/edit.js +15 -8
  242. package/src/gallery/editor.scss +0 -1
  243. package/src/gallery/gallery.js +8 -7
  244. package/src/gallery/gap-styles.js +21 -0
  245. package/src/gallery/index.php +42 -1
  246. package/src/gallery/style.scss +11 -44
  247. package/src/group/block.json +3 -1
  248. package/src/group/variations.js +1 -1
  249. package/src/image/image.js +4 -1
  250. package/src/image/style.scss +3 -0
  251. package/src/index.js +6 -1
  252. package/src/list/index.js +6 -1
  253. package/src/list/v2/edit.js +77 -0
  254. package/src/list/v2/index.js +20 -0
  255. package/src/list/v2/save.js +18 -0
  256. package/src/list/v2/transforms.js +116 -0
  257. package/src/list-item/block.json +26 -0
  258. package/src/list-item/edit.js +47 -0
  259. package/src/list-item/index.js +27 -0
  260. package/src/list-item/save.js +13 -0
  261. package/src/media-text/edit.js +1 -1
  262. package/src/navigation/index.php +22 -2
  263. package/src/navigation/use-navigation-entities.js +37 -73
  264. package/src/navigation-link/edit.js +145 -61
  265. package/src/navigation-link/editor.scss +11 -0
  266. package/src/post-date/edit.js +63 -52
  267. package/src/post-date/index.php +1 -1
  268. package/src/post-date/test/edit.js +17 -0
  269. package/src/post-featured-image/edit.js +9 -1
  270. package/src/post-featured-image/index.php +2 -1
  271. package/src/pullquote/edit.js +4 -3
  272. package/src/pullquote/style.scss +0 -5
  273. package/src/query/variations.js +4 -0
  274. package/src/query-no-results/block.json +20 -0
  275. package/src/query-no-results/edit.js +28 -0
  276. package/src/query-no-results/index.js +20 -0
  277. package/src/query-no-results/index.php +59 -0
  278. package/src/query-no-results/save.js +8 -0
  279. package/src/search/edit.js +1 -2
  280. package/src/separator/block.json +13 -6
  281. package/src/separator/deprecated.js +57 -0
  282. package/src/separator/deprecated.scss +6 -0
  283. package/src/separator/edit.js +36 -14
  284. package/src/separator/editor.scss +6 -0
  285. package/src/separator/index.js +2 -0
  286. package/src/separator/save.js +22 -14
  287. package/src/separator/test/edit.js +113 -0
  288. package/src/separator/theme.scss +7 -1
  289. package/src/separator/use-deprecated-opacity.js +41 -0
  290. package/src/site-logo/editor.scss +3 -1
  291. package/src/social-links/block.json +1 -1
  292. package/src/social-links/edit.js +15 -5
  293. package/src/spacer/controls.js +12 -18
  294. package/src/spacer/controls.native.js +2 -1
  295. package/src/spacer/edit.js +3 -6
  296. package/src/table/deprecated.js +5 -1
  297. package/src/tag-cloud/block.json +8 -0
  298. package/src/tag-cloud/edit.js +82 -2
  299. package/src/tag-cloud/index.php +6 -0
  300. package/build/separator/separator-settings.js +0 -36
  301. package/build/separator/separator-settings.js.map +0 -1
  302. package/build-module/separator/separator-settings.js +0 -27
  303. package/build-module/separator/separator-settings.js.map +0 -1
  304. package/src/separator/separator-settings.js +0 -24
@@ -1,11 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useSelect } from '@wordpress/data';
5
- import {
6
- store as coreStore,
7
- __experimentalUseEntityRecords as useEntityRecords,
8
- } from '@wordpress/core-data';
4
+ import { __experimentalUseEntityRecords as useEntityRecords } from '@wordpress/core-data';
9
5
 
10
6
  /**
11
7
  * @typedef {Object} NavigationEntitiesData
@@ -29,82 +25,50 @@ import {
29
25
  * @return { NavigationEntitiesData } the entity data.
30
26
  */
31
27
  export default function useNavigationEntities( menuId ) {
32
- return {
33
- ...usePageEntities(),
34
- ...useMenuEntities(),
35
- ...useMenuItemEntities( menuId ),
36
- };
37
- }
28
+ const {
29
+ records: menus,
30
+ isResolving: isResolvingMenus,
31
+ hasResolved: hasResolvedMenus,
32
+ } = useEntityRecords( 'root', 'menu', { per_page: -1, context: 'view' } );
38
33
 
39
- function useMenuEntities() {
40
- const { records, isResolving, hasResolved } = useEntityRecords(
34
+ const {
35
+ records: pages,
36
+ isResolving: isResolvingPages,
37
+ hasResolved: hasResolvedPages,
38
+ } = useEntityRecords( 'postType', 'page', {
39
+ parent: 0,
40
+ order: 'asc',
41
+ orderby: 'id',
42
+ per_page: -1,
43
+ context: 'view',
44
+ } );
45
+
46
+ const {
47
+ records: menuItems,
48
+ hasResolved: hasResolvedMenuItems,
49
+ } = useEntityRecords(
41
50
  'root',
42
- 'menu',
43
- { per_page: -1, context: 'view' }
51
+ 'menuItem',
52
+ {
53
+ menus: menuId,
54
+ per_page: -1,
55
+ context: 'view',
56
+ },
57
+ { enabled: !! menuId }
44
58
  );
45
59
 
46
60
  return {
47
- menus: records,
48
- isResolvingMenus: isResolving,
49
- hasResolvedMenus: hasResolved,
50
- hasMenus: !! ( hasResolved && records?.length ),
51
- };
52
- }
53
-
54
- function useMenuItemEntities( menuId ) {
55
- const { menuItems, hasResolvedMenuItems } = useSelect(
56
- ( select ) => {
57
- const { getMenuItems, hasFinishedResolution } = select( coreStore );
58
-
59
- const hasSelectedMenu = menuId !== undefined;
60
- const menuItemsParameters = hasSelectedMenu
61
- ? [
62
- {
63
- menus: menuId,
64
- per_page: -1,
65
- context: 'view',
66
- },
67
- ]
68
- : undefined;
61
+ pages,
62
+ isResolvingPages,
63
+ hasResolvedPages,
64
+ hasPages: !! ( hasResolvedPages && pages?.length ),
69
65
 
70
- return {
71
- menuItems: hasSelectedMenu
72
- ? getMenuItems( ...menuItemsParameters )
73
- : undefined,
74
- hasResolvedMenuItems: hasSelectedMenu
75
- ? hasFinishedResolution(
76
- 'getMenuItems',
77
- menuItemsParameters
78
- )
79
- : false,
80
- };
81
- },
82
- [ menuId ]
83
- );
66
+ menus,
67
+ isResolvingMenus,
68
+ hasResolvedMenus,
69
+ hasMenus: !! ( hasResolvedMenus && menus?.length ),
84
70
 
85
- return {
86
71
  menuItems,
87
72
  hasResolvedMenuItems,
88
73
  };
89
74
  }
90
-
91
- function usePageEntities() {
92
- const { records, isResolving, hasResolved } = useEntityRecords(
93
- 'postType',
94
- 'page',
95
- {
96
- parent: 0,
97
- order: 'asc',
98
- orderby: 'id',
99
- per_page: -1,
100
- context: 'view',
101
- }
102
- );
103
-
104
- return {
105
- pages: records,
106
- isResolvingPages: isResolving,
107
- hasResolvedPages: hasResolved,
108
- hasPages: !! ( hasResolved && records?.length ),
109
- };
110
- }
@@ -18,6 +18,7 @@ import {
18
18
  ToolbarButton,
19
19
  Tooltip,
20
20
  ToolbarGroup,
21
+ KeyboardShortcuts,
21
22
  } from '@wordpress/components';
22
23
  import { displayShortcut, isKeyboardEvent, ENTER } from '@wordpress/keycodes';
23
24
  import { __, sprintf } from '@wordpress/i18n';
@@ -265,6 +266,64 @@ export const updateNavigationLinkBlockAttributes = (
265
266
  } );
266
267
  };
267
268
 
269
+ const useIsInvalidLink = ( kind, type, id ) => {
270
+ const isPostType =
271
+ kind === 'post-type' || type === 'post' || type === 'page';
272
+ const hasId = Number.isInteger( id );
273
+ const postStatus = useSelect(
274
+ ( select ) => {
275
+ if ( ! isPostType ) {
276
+ return null;
277
+ }
278
+ const { getEntityRecord } = select( coreStore );
279
+ return getEntityRecord( 'postType', type, id )?.status;
280
+ },
281
+ [ isPostType, type, id ]
282
+ );
283
+
284
+ // Check Navigation Link validity if:
285
+ // 1. Link is 'post-type'.
286
+ // 2. It has an id.
287
+ // 3. It's neither null, nor undefined, as valid items might be either of those while loading.
288
+ // If those conditions are met, check if
289
+ // 1. The post status is published.
290
+ // 2. The Navigation Link item has no label.
291
+ // If either of those is true, invalidate.
292
+ const isInvalid =
293
+ isPostType && hasId && postStatus && 'trash' === postStatus;
294
+ const isDraft = 'draft' === postStatus;
295
+
296
+ return [ isInvalid, isDraft ];
297
+ };
298
+
299
+ const useMissingText = ( type ) => {
300
+ let missingText = '';
301
+
302
+ switch ( type ) {
303
+ case 'post':
304
+ /* translators: label for missing post in navigation link block */
305
+ missingText = __( 'Select post' );
306
+ break;
307
+ case 'page':
308
+ /* translators: label for missing page in navigation link block */
309
+ missingText = __( 'Select page' );
310
+ break;
311
+ case 'category':
312
+ /* translators: label for missing category in navigation link block */
313
+ missingText = __( 'Select category' );
314
+ break;
315
+ case 'tag':
316
+ /* translators: label for missing tag in navigation link block */
317
+ missingText = __( 'Select tag' );
318
+ break;
319
+ default:
320
+ /* translators: label for missing values in navigation link block */
321
+ missingText = __( 'Add link' );
322
+ }
323
+
324
+ return missingText;
325
+ };
326
+
268
327
  /**
269
328
  * Removes HTML from a given string.
270
329
  * Note the does not provide XSS protection or otherwise attempt
@@ -329,6 +388,7 @@ export default function NavigationLinkEdit( {
329
388
  clientId,
330
389
  } ) {
331
390
  const {
391
+ id,
332
392
  label,
333
393
  type,
334
394
  opensInNewTab,
@@ -339,6 +399,8 @@ export default function NavigationLinkEdit( {
339
399
  kind,
340
400
  } = attributes;
341
401
 
402
+ const [ isInvalid, isDraft ] = useIsInvalidLink( kind, type, id );
403
+
342
404
  const link = {
343
405
  url,
344
406
  opensInNewTab,
@@ -589,36 +651,23 @@ export default function NavigationLinkEdit( {
589
651
  onKeyDown,
590
652
  } );
591
653
 
592
- if ( ! url ) {
654
+ if ( ! url || isInvalid || isDraft ) {
593
655
  blockProps.onClick = () => setIsLinkOpen( true );
594
656
  }
595
657
 
596
658
  const classes = classnames( 'wp-block-navigation-item__content', {
597
- 'wp-block-navigation-link__placeholder': ! url,
659
+ 'wp-block-navigation-link__placeholder': ! url || isInvalid || isDraft,
598
660
  } );
599
661
 
600
- let missingText = '';
601
- switch ( type ) {
602
- case 'post':
603
- /* translators: label for missing post in navigation link block */
604
- missingText = __( 'Select post' );
605
- break;
606
- case 'page':
607
- /* translators: label for missing page in navigation link block */
608
- missingText = __( 'Select page' );
609
- break;
610
- case 'category':
611
- /* translators: label for missing category in navigation link block */
612
- missingText = __( 'Select category' );
613
- break;
614
- case 'tag':
615
- /* translators: label for missing tag in navigation link block */
616
- missingText = __( 'Select tag' );
617
- break;
618
- default:
619
- /* translators: label for missing values in navigation link block */
620
- missingText = __( 'Add link' );
621
- }
662
+ const missingText = useMissingText( type, isInvalid, isDraft );
663
+ /* translators: Whether the navigation link is Invalid or a Draft. */
664
+ const placeholderText = `(${
665
+ isInvalid ? __( 'Invalid' ) : __( 'Draft' )
666
+ })`;
667
+ const tooltipText =
668
+ isInvalid || isDraft
669
+ ? __( 'This item has been deleted, or is a draft' )
670
+ : __( 'This item is missing a link' );
622
671
 
623
672
  return (
624
673
  <Fragment>
@@ -677,46 +726,81 @@ export default function NavigationLinkEdit( {
677
726
  { /* eslint-enable */ }
678
727
  { ! url ? (
679
728
  <div className="wp-block-navigation-link__placeholder-text">
680
- <Tooltip
681
- position="top center"
682
- text={ __( 'This item is missing a link' ) }
683
- >
684
- <span>{ missingText }</span>
729
+ <Tooltip position="top center" text={ tooltipText }>
730
+ <>
731
+ <span>{ missingText }</span>
732
+ <span className="wp-block-navigation-link__missing_text-tooltip">
733
+ { tooltipText }
734
+ </span>
735
+ </>
685
736
  </Tooltip>
686
737
  </div>
687
738
  ) : (
688
- <RichText
689
- ref={ ref }
690
- identifier="label"
691
- className="wp-block-navigation-item__label"
692
- value={ label }
693
- onChange={ ( labelValue ) =>
694
- setAttributes( {
695
- label: labelValue,
696
- } )
697
- }
698
- onMerge={ mergeBlocks }
699
- onReplace={ onReplace }
700
- __unstableOnSplitAtEnd={ () =>
701
- insertBlocksAfter(
702
- createBlock( 'core/navigation-link' )
703
- )
704
- }
705
- aria-label={ __( 'Navigation link text' ) }
706
- placeholder={ itemLabelPlaceholder }
707
- withoutInteractiveFormatting
708
- allowedFormats={ [
709
- 'core/bold',
710
- 'core/italic',
711
- 'core/image',
712
- 'core/strikethrough',
713
- ] }
714
- onClick={ () => {
715
- if ( ! url ) {
716
- setIsLinkOpen( true );
717
- }
718
- } }
719
- />
739
+ <>
740
+ { ! isInvalid && ! isDraft && (
741
+ <RichText
742
+ ref={ ref }
743
+ identifier="label"
744
+ className="wp-block-navigation-item__label"
745
+ value={ label }
746
+ onChange={ ( labelValue ) =>
747
+ setAttributes( {
748
+ label: labelValue,
749
+ } )
750
+ }
751
+ onMerge={ mergeBlocks }
752
+ onReplace={ onReplace }
753
+ __unstableOnSplitAtEnd={ () =>
754
+ insertBlocksAfter(
755
+ createBlock(
756
+ 'core/navigation-link'
757
+ )
758
+ )
759
+ }
760
+ aria-label={ __( 'Navigation link text' ) }
761
+ placeholder={ itemLabelPlaceholder }
762
+ withoutInteractiveFormatting
763
+ allowedFormats={ [
764
+ 'core/bold',
765
+ 'core/italic',
766
+ 'core/image',
767
+ 'core/strikethrough',
768
+ ] }
769
+ onClick={ () => {
770
+ if ( ! url ) {
771
+ setIsLinkOpen( true );
772
+ }
773
+ } }
774
+ />
775
+ ) }
776
+ { ( isInvalid || isDraft ) && (
777
+ <div className="wp-block-navigation-link__placeholder-text wp-block-navigation-link__label">
778
+ <KeyboardShortcuts
779
+ shortcuts={ {
780
+ enter: () =>
781
+ isSelected &&
782
+ setIsLinkOpen( true ),
783
+ } }
784
+ />
785
+ <Tooltip
786
+ position="top center"
787
+ text={ tooltipText }
788
+ >
789
+ <>
790
+ <span>
791
+ {
792
+ /* Trim to avoid trailing white space when the placeholder text is not present */
793
+ `${ label } ${ placeholderText }`.trim()
794
+ }
795
+ </span>
796
+ <span className="wp-block-navigation-link__missing_text-tooltip">
797
+ { tooltipText }
798
+ </span>
799
+ </>
800
+ </Tooltip>
801
+ </div>
802
+ ) }
803
+ </>
720
804
  ) }
721
805
  { isLinkOpen && (
722
806
  <Popover
@@ -55,7 +55,18 @@
55
55
  }
56
56
  }
57
57
 
58
+ .wp-block-navigation-link__invalid-item {
59
+ color: #000;
60
+ }
58
61
 
62
+ .wp-block-navigation-link__missing_text-tooltip {
63
+ position: absolute;
64
+ width: 1px;
65
+ height: 1px;
66
+ padding: 0;
67
+ margin: -1px;
68
+ overflow: hidden;
69
+ }
59
70
  /**
60
71
  * Menu item setup state. Is shown when a menu item has no URL configured.
61
72
  */
@@ -6,14 +6,18 @@ import classnames from 'classnames';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { useEntityProp } from '@wordpress/core-data';
9
+ import { useEntityProp, store as coreStore } from '@wordpress/core-data';
10
10
  import { useRef } from '@wordpress/element';
11
- import { __experimentalGetSettings, dateI18n } from '@wordpress/date';
11
+ import {
12
+ dateI18n,
13
+ __experimentalGetSettings as getDateSettings,
14
+ } from '@wordpress/date';
12
15
  import {
13
16
  AlignmentControl,
14
17
  BlockControls,
15
18
  InspectorControls,
16
19
  useBlockProps,
20
+ __experimentalDateFormatPicker as DateFormatPicker,
17
21
  } from '@wordpress/block-editor';
18
22
  import {
19
23
  Dropdown,
@@ -22,58 +26,57 @@ import {
22
26
  ToggleControl,
23
27
  DateTimePicker,
24
28
  PanelBody,
25
- CustomSelectControl,
26
29
  } from '@wordpress/components';
27
30
  import { __, sprintf } from '@wordpress/i18n';
28
31
  import { edit } from '@wordpress/icons';
29
32
  import { DOWN } from '@wordpress/keycodes';
33
+ import { useSelect } from '@wordpress/data';
30
34
 
31
35
  export default function PostDateEdit( {
32
36
  attributes: { textAlign, format, isLink },
33
- context: { postId, postType, queryId },
37
+ context: { postId, postType: postTypeSlug, queryId },
34
38
  setAttributes,
35
39
  } ) {
40
+ const blockProps = useBlockProps( {
41
+ className: classnames( {
42
+ [ `has-text-align-${ textAlign }` ]: textAlign,
43
+ } ),
44
+ } );
45
+ const timeRef = useRef();
36
46
  const isDescendentOfQueryLoop = Number.isFinite( queryId );
37
- const [ siteFormat ] = useEntityProp( 'root', 'site', 'date_format' );
47
+ const dateSettings = getDateSettings();
48
+ const [ siteFormat = dateSettings.formats.date ] = useEntityProp(
49
+ 'root',
50
+ 'site',
51
+ 'date_format'
52
+ );
53
+ const [ siteTimeFormat = dateSettings.formats.time ] = useEntityProp(
54
+ 'root',
55
+ 'site',
56
+ 'time_format'
57
+ );
38
58
  const [ date, setDate ] = useEntityProp(
39
59
  'postType',
40
- postType,
60
+ postTypeSlug,
41
61
  'date',
42
62
  postId
43
63
  );
44
- const settings = __experimentalGetSettings();
45
- // To know if the current time format is a 12 hour time, look for "a".
46
- // Also make sure this "a" is not escaped by a "/".
47
- const is12Hour = /a(?!\\)/i.test(
48
- settings.formats.time
49
- .toLowerCase() // Test only for the lower case "a".
50
- .replace( /\\\\/g, '' ) // Replace "//" with empty strings.
51
- .split( '' )
52
- .reverse()
53
- .join( '' ) // Reverse the string and test for "a" not followed by a slash.
54
- );
55
- const formatOptions = Object.values( settings.formats ).map(
56
- ( formatOption ) => ( {
57
- key: formatOption,
58
- name: dateI18n( formatOption, date ),
59
- } )
64
+ const postType = useSelect(
65
+ ( select ) =>
66
+ postTypeSlug
67
+ ? select( coreStore ).getPostType( postTypeSlug )
68
+ : null,
69
+ [ postTypeSlug ]
60
70
  );
61
- const resolvedFormat = format || siteFormat || settings.formats.date;
62
- const blockProps = useBlockProps( {
63
- className: classnames( {
64
- [ `has-text-align-${ textAlign }` ]: textAlign,
65
- } ),
66
- } );
67
-
68
- const timeRef = useRef();
69
71
 
70
72
  let postDate = date ? (
71
73
  <time dateTime={ dateI18n( 'c', date ) } ref={ timeRef }>
72
- { dateI18n( resolvedFormat, date ) }
74
+ { dateI18n( format || siteFormat, date ) }
73
75
  </time>
74
76
  ) : (
75
77
  __( 'Post Date' )
76
78
  );
79
+
77
80
  if ( isLink && date ) {
78
81
  postDate = (
79
82
  <a
@@ -84,6 +87,7 @@ export default function PostDateEdit( {
84
87
  </a>
85
88
  );
86
89
  }
90
+
87
91
  return (
88
92
  <>
89
93
  <BlockControls group="block">
@@ -93,7 +97,6 @@ export default function PostDateEdit( {
93
97
  setAttributes( { textAlign: nextAlign } );
94
98
  } }
95
99
  />
96
-
97
100
  { date && ! isDescendentOfQueryLoop && (
98
101
  <ToolbarGroup>
99
102
  <Dropdown
@@ -102,7 +105,9 @@ export default function PostDateEdit( {
102
105
  <DateTimePicker
103
106
  currentDate={ date }
104
107
  onChange={ setDate }
105
- is12Hour={ is12Hour }
108
+ is12Hour={ is12HourFormat(
109
+ siteTimeFormat
110
+ ) }
106
111
  />
107
112
  ) }
108
113
  renderToggle={ ( { isOpen, onToggle } ) => {
@@ -128,34 +133,40 @@ export default function PostDateEdit( {
128
133
  </BlockControls>
129
134
 
130
135
  <InspectorControls>
131
- <PanelBody title={ __( 'Format settings' ) }>
132
- <CustomSelectControl
133
- hideLabelFromVision
134
- label={ __( 'Date Format' ) }
135
- options={ formatOptions }
136
- onChange={ ( { selectedItem } ) =>
137
- setAttributes( {
138
- format: selectedItem.key,
139
- } )
136
+ <PanelBody title={ __( 'Settings' ) }>
137
+ <DateFormatPicker
138
+ format={ format }
139
+ defaultFormat={ siteFormat }
140
+ onChange={ ( nextFormat ) =>
141
+ setAttributes( { format: nextFormat } )
140
142
  }
141
- value={ formatOptions.find(
142
- ( option ) => option.key === resolvedFormat
143
- ) }
144
143
  />
145
- </PanelBody>
146
- <PanelBody title={ __( 'Link settings' ) }>
147
144
  <ToggleControl
148
- label={ sprintf(
149
- // translators: %s: Name of the post type e.g: "post".
150
- __( 'Link to %s' ),
151
- postType
152
- ) }
145
+ label={
146
+ postType?.labels.singular_name
147
+ ? sprintf(
148
+ // translators: %s: Name of the post type e.g: "post".
149
+ __( 'Link to %s' ),
150
+ postType.labels.singular_name.toLowerCase()
151
+ )
152
+ : __( 'Link to post' )
153
+ }
153
154
  onChange={ () => setAttributes( { isLink: ! isLink } ) }
154
155
  checked={ isLink }
155
156
  />
156
157
  </PanelBody>
157
158
  </InspectorControls>
159
+
158
160
  <div { ...blockProps }>{ postDate }</div>
159
161
  </>
160
162
  );
161
163
  }
164
+
165
+ export function is12HourFormat( format ) {
166
+ // To know if the time format is a 12 hour time, look for any of the 12 hour
167
+ // format characters: 'a', 'A', 'g', and 'h'. The character must be
168
+ // unescaped, i.e. not preceded by a '\'. Coincidentally, 'aAgh' is how I
169
+ // feel when working with regular expressions.
170
+ // https://www.php.net/manual/en/datetime.format.php
171
+ return /(?:^|[^\\])[aAgh]/.test( format );
172
+ }
@@ -21,7 +21,7 @@ function render_block_core_post_date( $attributes, $content, $block ) {
21
21
  $post_ID = $block->context['postId'];
22
22
  $align_class_name = empty( $attributes['textAlign'] ) ? '' : "has-text-align-{$attributes['textAlign']}";
23
23
  $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $align_class_name ) );
24
- $formatted_date = get_the_date( isset( $attributes['format'] ) ? $attributes['format'] : '', $post_ID );
24
+ $formatted_date = get_the_date( empty( $attributes['format'] ) ? '' : $attributes['format'], $post_ID );
25
25
  if ( isset( $attributes['isLink'] ) && $attributes['isLink'] ) {
26
26
  $formatted_date = sprintf( '<a href="%1s">%2s</a>', get_the_permalink( $post_ID ), $formatted_date );
27
27
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { is12HourFormat } from '../edit';
5
+
6
+ describe( 'is12HourFormat', () => {
7
+ test.each( [
8
+ [ '', false ],
9
+ [ 'H:i', false ],
10
+ [ 'g:i A', true ],
11
+ [ 'g:i a', true ],
12
+ [ 'h:i', true ],
13
+ [ '\\g\\r\\e\\a\\t', false ],
14
+ ] )( 'is12HourFormat( %p ) = %p', ( format, expected ) => {
15
+ expect( is12HourFormat( format ) ).toBe( expected );
16
+ } );
17
+ } );
@@ -179,7 +179,15 @@ function PostFeaturedImageDisplay( {
179
179
  ) : (
180
180
  <img
181
181
  src={ mediaUrl }
182
- alt={ media.alt_text || __( 'Featured image' ) }
182
+ alt={
183
+ media.alt_text
184
+ ? sprintf(
185
+ // translators: %s: The image's alt text.
186
+ __( 'Featured image: %s' ),
187
+ media.alt_text
188
+ )
189
+ : __( 'Featured image' )
190
+ }
183
191
  style={ { height, objectFit: height && scale } }
184
192
  />
185
193
  );
@@ -20,7 +20,8 @@ function render_block_core_post_featured_image( $attributes, $content, $block )
20
20
  $post_ID = $block->context['postId'];
21
21
 
22
22
  $size_slug = isset( $attributes['sizeSlug'] ) ? $attributes['sizeSlug'] : 'post-thumbnail';
23
- $featured_image = get_the_post_thumbnail( $post_ID, $size_slug );
23
+ $post_title = trim( strip_tags( get_the_title( $post_ID ) ) );
24
+ $featured_image = get_the_post_thumbnail( $post_ID, $size_slug, array( 'alt' => $post_title ) );
24
25
  if ( ! $featured_image ) {
25
26
  return '';
26
27
  }
@@ -14,6 +14,7 @@ import {
14
14
  useBlockProps,
15
15
  } from '@wordpress/block-editor';
16
16
  import { createBlock } from '@wordpress/blocks';
17
+ import { Platform } from '@wordpress/element';
17
18
 
18
19
  /**
19
20
  * Internal dependencies
@@ -21,9 +22,7 @@ import { createBlock } from '@wordpress/blocks';
21
22
  import { Figure } from './figure';
22
23
  import { BlockQuote } from './blockquote';
23
24
 
24
- /**
25
- * Internal dependencies
26
- */
25
+ const isWebPlatform = Platform.OS === 'web';
27
26
 
28
27
  function PullQuoteEdit( {
29
28
  attributes,
@@ -70,6 +69,8 @@ function PullQuoteEdit( {
70
69
  { shouldShowCitation && (
71
70
  <RichText
72
71
  identifier="citation"
72
+ tagName={ isWebPlatform ? 'cite' : undefined }
73
+ style={ { display: 'block' } }
73
74
  value={ citation }
74
75
  aria-label={ __( 'Pullquote citation text' ) }
75
76
  placeholder={