@wordpress/block-library 6.0.15 → 6.0.18

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 (154) hide show
  1. package/build/columns/index.js +1 -2
  2. package/build/columns/index.js.map +1 -1
  3. package/build/group/edit.native.js +1 -1
  4. package/build/group/edit.native.js.map +1 -1
  5. package/build/group/index.js +3 -1
  6. package/build/group/index.js.map +1 -1
  7. package/build/navigation/deprecated.js +1 -3
  8. package/build/navigation/deprecated.js.map +1 -1
  9. package/build/navigation/edit/index.js +100 -44
  10. package/build/navigation/edit/index.js.map +1 -1
  11. package/build/navigation/edit/navigation-menu-selector.js +3 -2
  12. package/build/navigation/edit/navigation-menu-selector.js.map +1 -1
  13. package/build/navigation/edit/placeholder/index.js +15 -7
  14. package/build/navigation/edit/placeholder/index.js.map +1 -1
  15. package/build/navigation/edit/responsive-wrapper.js +9 -7
  16. package/build/navigation/edit/responsive-wrapper.js.map +1 -1
  17. package/build/navigation/edit/unsaved-inner-blocks.js +3 -11
  18. package/build/navigation/edit/unsaved-inner-blocks.js.map +1 -1
  19. package/build/navigation/edit/use-navigation-notice.js +54 -0
  20. package/build/navigation/edit/use-navigation-notice.js.map +1 -0
  21. package/build/navigation/index.js +2 -7
  22. package/build/navigation/index.js.map +1 -1
  23. package/build/navigation/use-navigation-menu.js +9 -2
  24. package/build/navigation/use-navigation-menu.js.map +1 -1
  25. package/build/navigation/view.js +12 -7
  26. package/build/navigation/view.js.map +1 -1
  27. package/build/post-author/edit.js +1 -1
  28. package/build/post-author/edit.js.map +1 -1
  29. package/build/post-content/edit.js +1 -1
  30. package/build/post-content/edit.js.map +1 -1
  31. package/build/post-date/edit.js +1 -1
  32. package/build/post-date/edit.js.map +1 -1
  33. package/build/post-excerpt/edit.js +1 -1
  34. package/build/post-excerpt/edit.js.map +1 -1
  35. package/build/post-featured-image/edit.js +1 -1
  36. package/build/post-featured-image/edit.js.map +1 -1
  37. package/build/post-template/edit.js +54 -13
  38. package/build/post-template/edit.js.map +1 -1
  39. package/build/post-title/edit.js +1 -1
  40. package/build/post-title/edit.js.map +1 -1
  41. package/build/site-logo/edit.js +117 -16
  42. package/build/site-logo/edit.js.map +1 -1
  43. package/build/site-logo/index.js +5 -4
  44. package/build/site-logo/index.js.map +1 -1
  45. package/build/template-part/edit/index.js +7 -1
  46. package/build/template-part/edit/index.js.map +1 -1
  47. package/build/template-part/index.js +25 -1
  48. package/build/template-part/index.js.map +1 -1
  49. package/build-module/columns/index.js +1 -2
  50. package/build-module/columns/index.js.map +1 -1
  51. package/build-module/group/edit.native.js +1 -1
  52. package/build-module/group/edit.native.js.map +1 -1
  53. package/build-module/group/index.js +3 -1
  54. package/build-module/group/index.js.map +1 -1
  55. package/build-module/navigation/deprecated.js +1 -3
  56. package/build-module/navigation/deprecated.js.map +1 -1
  57. package/build-module/navigation/edit/index.js +98 -44
  58. package/build-module/navigation/edit/index.js.map +1 -1
  59. package/build-module/navigation/edit/navigation-menu-selector.js +3 -2
  60. package/build-module/navigation/edit/navigation-menu-selector.js.map +1 -1
  61. package/build-module/navigation/edit/placeholder/index.js +15 -7
  62. package/build-module/navigation/edit/placeholder/index.js.map +1 -1
  63. package/build-module/navigation/edit/responsive-wrapper.js +9 -7
  64. package/build-module/navigation/edit/responsive-wrapper.js.map +1 -1
  65. package/build-module/navigation/edit/unsaved-inner-blocks.js +4 -13
  66. package/build-module/navigation/edit/unsaved-inner-blocks.js.map +1 -1
  67. package/build-module/navigation/edit/use-navigation-notice.js +44 -0
  68. package/build-module/navigation/edit/use-navigation-notice.js.map +1 -0
  69. package/build-module/navigation/index.js +2 -7
  70. package/build-module/navigation/index.js.map +1 -1
  71. package/build-module/navigation/use-navigation-menu.js +9 -2
  72. package/build-module/navigation/use-navigation-menu.js.map +1 -1
  73. package/build-module/navigation/view.js +12 -7
  74. package/build-module/navigation/view.js.map +1 -1
  75. package/build-module/post-author/edit.js +1 -1
  76. package/build-module/post-author/edit.js.map +1 -1
  77. package/build-module/post-content/edit.js +1 -1
  78. package/build-module/post-content/edit.js.map +1 -1
  79. package/build-module/post-date/edit.js +1 -1
  80. package/build-module/post-date/edit.js.map +1 -1
  81. package/build-module/post-excerpt/edit.js +1 -1
  82. package/build-module/post-excerpt/edit.js.map +1 -1
  83. package/build-module/post-featured-image/edit.js +1 -1
  84. package/build-module/post-featured-image/edit.js.map +1 -1
  85. package/build-module/post-template/edit.js +54 -15
  86. package/build-module/post-template/edit.js.map +1 -1
  87. package/build-module/post-title/edit.js +1 -1
  88. package/build-module/post-title/edit.js.map +1 -1
  89. package/build-module/site-logo/edit.js +118 -17
  90. package/build-module/site-logo/edit.js.map +1 -1
  91. package/build-module/site-logo/index.js +5 -4
  92. package/build-module/site-logo/index.js.map +1 -1
  93. package/build-module/template-part/edit/index.js +6 -1
  94. package/build-module/template-part/edit/index.js.map +1 -1
  95. package/build-module/template-part/index.js +25 -1
  96. package/build-module/template-part/index.js.map +1 -1
  97. package/build-style/common-rtl.css +10 -0
  98. package/build-style/common.css +10 -0
  99. package/build-style/editor-rtl.css +29 -17
  100. package/build-style/editor.css +29 -17
  101. package/build-style/navigation/editor-rtl.css +15 -6
  102. package/build-style/navigation/editor.css +15 -6
  103. package/build-style/navigation/style-rtl.css +55 -24
  104. package/build-style/navigation/style.css +55 -24
  105. package/build-style/post-comments/style-rtl.css +0 -1
  106. package/build-style/post-comments/style.css +0 -1
  107. package/build-style/site-logo/editor-rtl.css +1 -11
  108. package/build-style/site-logo/editor.css +1 -11
  109. package/build-style/site-logo/style-rtl.css +3 -1
  110. package/build-style/site-logo/style.css +3 -1
  111. package/build-style/style-rtl.css +68 -26
  112. package/build-style/style.css +68 -26
  113. package/package.json +14 -14
  114. package/src/columns/block.json +1 -2
  115. package/src/common.scss +12 -0
  116. package/src/editor.scss +15 -0
  117. package/src/gallery/index.php +1 -1
  118. package/src/group/block.json +3 -1
  119. package/src/group/edit.native.js +1 -1
  120. package/src/home-link/index.php +1 -1
  121. package/src/navigation/block.json +2 -13
  122. package/src/navigation/deprecated.js +0 -2
  123. package/src/navigation/edit/index.js +153 -55
  124. package/src/navigation/edit/navigation-menu-selector.js +19 -13
  125. package/src/navigation/edit/placeholder/index.js +40 -24
  126. package/src/navigation/edit/responsive-wrapper.js +10 -7
  127. package/src/navigation/edit/unsaved-inner-blocks.js +13 -25
  128. package/src/navigation/edit/use-navigation-notice.js +37 -0
  129. package/src/navigation/editor.scss +20 -8
  130. package/src/navigation/index.php +19 -8
  131. package/src/navigation/style.scss +81 -32
  132. package/src/navigation/use-navigation-menu.js +20 -0
  133. package/src/navigation/view.js +13 -7
  134. package/src/navigation-link/index.php +1 -1
  135. package/src/navigation-submenu/index.php +1 -1
  136. package/src/page-list/index.php +1 -1
  137. package/src/post-author/edit.js +1 -1
  138. package/src/post-comments/style.scss +0 -1
  139. package/src/post-content/edit.js +1 -1
  140. package/src/post-content/index.php +6 -1
  141. package/src/post-date/edit.js +1 -1
  142. package/src/post-excerpt/edit.js +1 -1
  143. package/src/post-excerpt/index.php +1 -1
  144. package/src/post-featured-image/edit.js +1 -1
  145. package/src/post-template/edit.js +54 -17
  146. package/src/post-title/edit.js +1 -1
  147. package/src/post-title/index.php +1 -1
  148. package/src/site-logo/block.json +5 -4
  149. package/src/site-logo/edit.js +130 -12
  150. package/src/site-logo/editor.scss +1 -15
  151. package/src/site-logo/index.php +17 -4
  152. package/src/site-logo/style.scss +3 -1
  153. package/src/template-part/edit/index.js +6 -1
  154. package/src/template-part/index.js +28 -0
@@ -25,7 +25,7 @@ function PostAuthorEdit( {
25
25
  attributes,
26
26
  setAttributes,
27
27
  } ) {
28
- const isDescendentOfQueryLoop = !! queryId;
28
+ const isDescendentOfQueryLoop = Number.isFinite( queryId );
29
29
  const { authorId, authorDetails, authors } = useSelect(
30
30
  ( select ) => {
31
31
  const { getEditedEntityRecord, getUser, getUsers } = select(
@@ -29,7 +29,6 @@
29
29
 
30
30
  .comment-author {
31
31
  line-height: 1.5;
32
- margin-left: -3.25em;
33
32
 
34
33
  .avatar {
35
34
  border-radius: 1.5em;
@@ -66,7 +66,7 @@ function EditableContent( { layout, context = {} } ) {
66
66
 
67
67
  function Content( props ) {
68
68
  const { context: { queryId, postType, postId } = {} } = props;
69
- const isDescendentOfQueryLoop = !! queryId;
69
+ const isDescendentOfQueryLoop = Number.isFinite( queryId );
70
70
  const userCanEdit = useCanEditEntity( 'postType', postType, postId );
71
71
  const isEditable = userCanEdit && ! isDescendentOfQueryLoop;
72
72
 
@@ -36,11 +36,16 @@ function render_block_core_post_content( $attributes, $content, $block ) {
36
36
 
37
37
  $seen_ids[ $post_id ] = true;
38
38
 
39
+ // Check is needed for backward compatibility with third-party plugins
40
+ // that might rely on the `in_the_loop` check; calling `the_post` sets it to true.
39
41
  if ( ! in_the_loop() && have_posts() ) {
40
42
  the_post();
41
43
  }
42
44
 
43
- $content = get_the_content( null, false, $post_id );
45
+ // When inside the main loop, we want to use queried object
46
+ // so that `the_preview` for the current post can apply.
47
+ // We force this behavior by omitting the third argument (post ID) from the `get_the_content`.
48
+ $content = get_the_content( null, false );
44
49
  /** This filter is documented in wp-includes/post-template.php */
45
50
  $content = apply_filters( 'the_content', str_replace( ']]>', ']]>', $content ) );
46
51
  unset( $seen_ids[ $post_id ] );
@@ -33,7 +33,7 @@ export default function PostDateEdit( {
33
33
  context: { postId, postType, queryId },
34
34
  setAttributes,
35
35
  } ) {
36
- const isDescendentOfQueryLoop = !! queryId;
36
+ const isDescendentOfQueryLoop = Number.isFinite( queryId );
37
37
  const [ siteFormat ] = useEntityProp( 'root', 'site', 'date_format' );
38
38
  const [ date, setDate ] = useEntityProp(
39
39
  'postType',
@@ -30,7 +30,7 @@ export default function PostExcerptEditor( {
30
30
  isSelected,
31
31
  context: { postId, postType, queryId },
32
32
  } ) {
33
- const isDescendentOfQueryLoop = !! queryId;
33
+ const isDescendentOfQueryLoop = Number.isFinite( queryId );
34
34
  const userCanEdit = useCanEditEntity( 'postType', postType, postId );
35
35
  const isEditable = userCanEdit && ! isDescendentOfQueryLoop;
36
36
  const [
@@ -18,7 +18,7 @@ function render_block_core_post_excerpt( $attributes, $content, $block ) {
18
18
  return '';
19
19
  }
20
20
 
21
- $excerpt = get_the_excerpt( $block->context['postId'] );
21
+ $excerpt = get_the_excerpt();
22
22
 
23
23
  if ( empty( $excerpt ) ) {
24
24
  return '';
@@ -50,7 +50,7 @@ function PostFeaturedImageDisplay( {
50
50
  setAttributes,
51
51
  context: { postId, postType, queryId },
52
52
  } ) {
53
- const isDescendentOfQueryLoop = !! queryId;
53
+ const isDescendentOfQueryLoop = Number.isFinite( queryId );
54
54
  const { isLink, height, width, scale } = attributes;
55
55
  const [ featuredImage, setFeaturedImage ] = useEntityProp(
56
56
  'postType',
@@ -6,12 +6,12 @@ import classnames from 'classnames';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { useState, useMemo } from '@wordpress/element';
9
+ import { memo, useMemo, useState } from '@wordpress/element';
10
10
  import { useSelect } from '@wordpress/data';
11
11
  import { __ } from '@wordpress/i18n';
12
12
  import {
13
13
  BlockContextProvider,
14
- BlockPreview,
14
+ __experimentalUseBlockPreview as useBlockPreview,
15
15
  useBlockProps,
16
16
  useInnerBlocksProps,
17
17
  store as blockEditorStore,
@@ -30,6 +30,39 @@ function PostTemplateInnerBlocks() {
30
30
  return <li { ...innerBlocksProps } />;
31
31
  }
32
32
 
33
+ function PostTemplateBlockPreview( {
34
+ blocks,
35
+ blockContextId,
36
+ isHidden,
37
+ setActiveBlockContextId,
38
+ } ) {
39
+ const blockPreviewProps = useBlockPreview( {
40
+ blocks,
41
+ } );
42
+
43
+ const handleOnClick = () => {
44
+ setActiveBlockContextId( blockContextId );
45
+ };
46
+
47
+ const style = {
48
+ display: isHidden ? 'none' : undefined,
49
+ };
50
+
51
+ return (
52
+ <li
53
+ { ...blockPreviewProps }
54
+ tabIndex={ 0 }
55
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role
56
+ role="button"
57
+ onClick={ handleOnClick }
58
+ onKeyPress={ handleOnClick }
59
+ style={ style }
60
+ />
61
+ );
62
+ }
63
+
64
+ const MemoizedPostTemplateBlockPreview = memo( PostTemplateBlockPreview );
65
+
33
66
  export default function PostTemplateEdit( {
34
67
  clientId,
35
68
  context: {
@@ -53,7 +86,7 @@ export default function PostTemplateEdit( {
53
86
  },
54
87
  } ) {
55
88
  const [ { page } ] = queryContext;
56
- const [ activeBlockContext, setActiveBlockContext ] = useState();
89
+ const [ activeBlockContextId, setActiveBlockContextId ] = useState();
57
90
 
58
91
  const { posts, blocks } = useSelect(
59
92
  ( select ) => {
@@ -115,7 +148,6 @@ export default function PostTemplateEdit( {
115
148
  templateSlug,
116
149
  ]
117
150
  );
118
-
119
151
  const blockContexts = useMemo(
120
152
  () =>
121
153
  posts?.map( ( post ) => ( {
@@ -144,6 +176,10 @@ export default function PostTemplateEdit( {
144
176
  return <p { ...blockProps }> { __( 'No results found.' ) }</p>;
145
177
  }
146
178
 
179
+ // To avoid flicker when switching active block contexts, a preview is rendered
180
+ // for each block context, but the preview for the active block context is hidden.
181
+ // This ensures that when it is displayed again, the cached rendering of the
182
+ // block preview is used, instead of having to re-render the preview from scratch.
147
183
  return (
148
184
  <ul { ...blockProps }>
149
185
  { blockContexts &&
@@ -152,20 +188,21 @@ export default function PostTemplateEdit( {
152
188
  key={ blockContext.postId }
153
189
  value={ blockContext }
154
190
  >
155
- { blockContext ===
156
- ( activeBlockContext || blockContexts[ 0 ] ) ? (
191
+ { blockContext.postId ===
192
+ ( activeBlockContextId ||
193
+ blockContexts[ 0 ]?.postId ) ? (
157
194
  <PostTemplateInnerBlocks />
158
- ) : (
159
- <li>
160
- <BlockPreview
161
- blocks={ blocks }
162
- __experimentalLive
163
- __experimentalOnClick={ () =>
164
- setActiveBlockContext( blockContext )
165
- }
166
- />
167
- </li>
168
- ) }
195
+ ) : null }
196
+ <MemoizedPostTemplateBlockPreview
197
+ blocks={ blocks }
198
+ blockContextId={ blockContext.postId }
199
+ setActiveBlockContextId={ setActiveBlockContextId }
200
+ isHidden={
201
+ blockContext.postId ===
202
+ ( activeBlockContextId ||
203
+ blockContexts[ 0 ]?.postId )
204
+ }
205
+ />
169
206
  </BlockContextProvider>
170
207
  ) ) }
171
208
  </ul>
@@ -30,7 +30,7 @@ export default function PostTitleEdit( {
30
30
  context: { postType, postId, queryId },
31
31
  } ) {
32
32
  const TagName = 0 === level ? 'p' : 'h' + level;
33
- const isDescendentOfQueryLoop = !! queryId;
33
+ const isDescendentOfQueryLoop = Number.isFinite( queryId );
34
34
  const userCanEdit = useCanEditEntity( 'postType', postType, postId );
35
35
  const [ rawTitle = '', setTitle, fullTitle ] = useEntityProp(
36
36
  'postType',
@@ -20,7 +20,7 @@ function render_block_core_post_title( $attributes, $content, $block ) {
20
20
  }
21
21
 
22
22
  $post_ID = $block->context['postId'];
23
- $title = get_the_title( $post_ID );
23
+ $title = get_the_title();
24
24
 
25
25
  if ( ! $title ) {
26
26
  return '';
@@ -6,9 +6,6 @@
6
6
  "description": "Display a graphic to represent this site. Update the block, and the changes apply everywhere it’s used. This is different than the site icon, which is the smaller image visible in your dashboard, browser tabs, etc used to help others recognize this site.",
7
7
  "textdomain": "default",
8
8
  "attributes": {
9
- "align": {
10
- "type": "string"
11
- },
12
9
  "width": {
13
10
  "type": "number"
14
11
  },
@@ -19,12 +16,16 @@
19
16
  "linkTarget": {
20
17
  "type": "string",
21
18
  "default": "_self"
19
+ },
20
+ "shouldSyncIcon": {
21
+ "type": "boolean"
22
22
  }
23
23
  },
24
24
  "example": {
25
25
  "viewportWidth": 500,
26
26
  "attributes": {
27
- "width": 350
27
+ "width": 350,
28
+ "className": "block-editor-block-types-list__site-logo-example"
28
29
  }
29
30
  },
30
31
  "supports": {
@@ -8,7 +8,12 @@ import { includes, pick } from 'lodash';
8
8
  * WordPress dependencies
9
9
  */
10
10
  import { isBlobURL } from '@wordpress/blob';
11
- import { useEffect, useState, useRef } from '@wordpress/element';
11
+ import {
12
+ createInterpolateElement,
13
+ useEffect,
14
+ useState,
15
+ useRef,
16
+ } from '@wordpress/element';
12
17
  import { __, isRTL } from '@wordpress/i18n';
13
18
  import {
14
19
  MenuItem,
@@ -53,7 +58,7 @@ const ACCEPT_MEDIA_STRING = 'image/*';
53
58
 
54
59
  const SiteLogo = ( {
55
60
  alt,
56
- attributes: { align, width, height, isLink, linkTarget },
61
+ attributes: { align, width, height, isLink, linkTarget, shouldSyncIcon },
57
62
  containerRef,
58
63
  isSelected,
59
64
  setAttributes,
@@ -61,6 +66,9 @@ const SiteLogo = ( {
61
66
  logoUrl,
62
67
  siteUrl,
63
68
  logoId,
69
+ iconId,
70
+ setIcon,
71
+ canUserEdit,
64
72
  } ) => {
65
73
  const clientWidth = useClientWidth( containerRef, [ align ] );
66
74
  const isLargeViewport = useViewportMatch( 'medium' );
@@ -84,6 +92,15 @@ const SiteLogo = ( {
84
92
  };
85
93
  }, [] );
86
94
 
95
+ useEffect( () => {
96
+ // Turn the `Use as site icon` toggle off if it is on but the logo and icon have
97
+ // fallen out of sync. This can happen if the toggle is saved in the `on` position,
98
+ // but changes are later made to the site icon in the Customizer.
99
+ if ( shouldSyncIcon && logoId !== iconId ) {
100
+ setAttributes( { shouldSyncIcon: false } );
101
+ }
102
+ }, [] );
103
+
87
104
  useEffect( () => {
88
105
  if ( ! isSelected ) {
89
106
  setIsEditingImage( false );
@@ -142,7 +159,11 @@ const SiteLogo = ( {
142
159
  return <div style={ { width, height } }>{ imgWrapper }</div>;
143
160
  }
144
161
 
145
- const currentWidth = width || imageWidthWithinContainer;
162
+ // Set the default width to a responsible size.
163
+ // Note that this width is also set in the attached frontend CSS file.
164
+ const defaultWidth = 120;
165
+
166
+ const currentWidth = width || defaultWidth;
146
167
  const ratio = naturalWidth / naturalHeight;
147
168
  const currentHeight = currentWidth / ratio;
148
169
  const minWidth = naturalWidth < naturalHeight ? MIN_SIZE : MIN_SIZE * ratio;
@@ -160,10 +181,6 @@ const SiteLogo = ( {
160
181
  // becomes available.
161
182
  const maxWidthBuffer = maxWidth * 2.5;
162
183
 
163
- // Set the default width to a responsible size.
164
- // Note that this width is also set in the attached CSS file.
165
- const defaultWidth = 120;
166
-
167
184
  let showRightHandle = false;
168
185
  let showLeftHandle = false;
169
186
 
@@ -250,6 +267,25 @@ const SiteLogo = ( {
250
267
  </ResizableBox>
251
268
  );
252
269
 
270
+ const syncSiteIconHelpText = createInterpolateElement(
271
+ __(
272
+ 'Site Icons are what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. To use a custom icon that is different from your site logo, use the <a>Site Icon settings</a>.'
273
+ ),
274
+ {
275
+ a: (
276
+ // eslint-disable-next-line jsx-a11y/anchor-has-content
277
+ <a
278
+ href={
279
+ siteUrl +
280
+ '/wp-admin/customize.php?autofocus[section]=title_tagline'
281
+ }
282
+ target="_blank"
283
+ rel="noopener noreferrer"
284
+ />
285
+ ),
286
+ }
287
+ );
288
+
253
289
  return (
254
290
  <>
255
291
  <InspectorControls>
@@ -286,6 +322,19 @@ const SiteLogo = ( {
286
322
  />
287
323
  </>
288
324
  ) }
325
+ { canUserEdit && (
326
+ <>
327
+ <ToggleControl
328
+ label={ __( 'Use as site icon' ) }
329
+ onChange={ ( value ) => {
330
+ setAttributes( { shouldSyncIcon: value } );
331
+ setIcon( value ? logoId : undefined );
332
+ } }
333
+ checked={ !! shouldSyncIcon }
334
+ help={ syncSiteIconHelpText }
335
+ />
336
+ </>
337
+ ) }
289
338
  </PanelBody>
290
339
  </InspectorControls>
291
340
  <BlockControls group="block">
@@ -308,7 +357,7 @@ export default function LogoEdit( {
308
357
  setAttributes,
309
358
  isSelected,
310
359
  } ) {
311
- const { width } = attributes;
360
+ const { className: styleClass, width, shouldSyncIcon } = attributes;
312
361
  const [ logoUrl, setLogoUrl ] = useState();
313
362
  const ref = useRef();
314
363
 
@@ -316,6 +365,7 @@ export default function LogoEdit( {
316
365
  siteLogoId,
317
366
  canUserEdit,
318
367
  url,
368
+ siteIconId,
319
369
  mediaItemData,
320
370
  isRequestingMediaItem,
321
371
  } = useSelect( ( select ) => {
@@ -328,6 +378,7 @@ export default function LogoEdit( {
328
378
  const _readOnlyLogo = siteData?.site_logo;
329
379
  const _canUserEdit = canUser( 'update', 'settings' );
330
380
  const _siteLogoId = _canUserEdit ? _siteLogo : _readOnlyLogo;
381
+ const _siteIconId = siteSettings?.site_icon;
331
382
  const mediaItem =
332
383
  _siteLogoId &&
333
384
  select( coreStore ).getMedia( _siteLogoId, {
@@ -339,6 +390,7 @@ export default function LogoEdit( {
339
390
  _siteLogoId,
340
391
  { context: 'view' },
341
392
  ] );
393
+
342
394
  return {
343
395
  siteLogoId: _siteLogoId,
344
396
  canUserEdit: _canUserEdit,
@@ -349,14 +401,59 @@ export default function LogoEdit( {
349
401
  alt: mediaItem.alt_text,
350
402
  },
351
403
  isRequestingMediaItem: _isRequestingMediaItem,
404
+ siteIconId: _siteIconId,
352
405
  };
353
406
  }, [] );
354
407
 
408
+ const { getGlobalBlockCount } = useSelect( blockEditorStore );
355
409
  const { editEntityRecord } = useDispatch( coreStore );
356
- const setLogo = ( newValue ) =>
410
+
411
+ useEffect( () => {
412
+ // Cleanup function to discard unsaved changes to the icon and logo when
413
+ // the block is removed.
414
+ return () => {
415
+ // Do nothing if the block is being rendered in the styles preview or the
416
+ // block inserter.
417
+ if (
418
+ styleClass?.includes(
419
+ 'block-editor-block-types-list__site-logo-example'
420
+ ) ||
421
+ styleClass?.includes(
422
+ 'block-editor-block-styles__block-preview-container'
423
+ )
424
+ ) {
425
+ return;
426
+ }
427
+
428
+ const logoBlockCount = getGlobalBlockCount( 'core/site-logo' );
429
+
430
+ // Only discard unsaved changes if we are removing the last Site Logo block
431
+ // on the page.
432
+ if ( logoBlockCount === 0 ) {
433
+ editEntityRecord( 'root', 'site', undefined, {
434
+ site_logo: undefined,
435
+ site_icon: undefined,
436
+ } );
437
+ }
438
+ };
439
+ }, [] );
440
+
441
+ const setLogo = ( newValue, shouldForceSync = false ) => {
442
+ // `shouldForceSync` is used to force syncing when the attribute
443
+ // may not have updated yet.
444
+ if ( shouldSyncIcon || shouldForceSync ) {
445
+ setIcon( newValue );
446
+ }
447
+
357
448
  editEntityRecord( 'root', 'site', undefined, {
358
449
  site_logo: newValue,
359
450
  } );
451
+ };
452
+
453
+ const setIcon = ( newValue ) =>
454
+ editEntityRecord( 'root', 'site', undefined, {
455
+ site_icon: newValue,
456
+ } );
360
457
 
361
458
  let alt = null;
362
459
  if ( mediaItemData ) {
@@ -365,7 +462,24 @@ export default function LogoEdit( {
365
462
  setLogoUrl( mediaItemData.url );
366
463
  }
367
464
  }
368
- const onSelectLogo = ( media ) => {
465
+
466
+ const onInitialSelectLogo = ( media ) => {
467
+ // Initialize the syncSiteIcon toggle. If we currently have no Site logo and no
468
+ // site icon, automatically sync the logo to the icon.
469
+ if ( shouldSyncIcon === undefined ) {
470
+ const shouldForceSync = ! siteIconId;
471
+ setAttributes( { shouldSyncIcon: shouldForceSync } );
472
+
473
+ // Because we cannot rely on the `shouldSyncIcon` attribute to have updated by
474
+ // the time `setLogo` is called, pass an argument to force the syncing.
475
+ onSelectLogo( media, shouldForceSync );
476
+ return;
477
+ }
478
+
479
+ onSelectLogo( media );
480
+ };
481
+
482
+ const onSelectLogo = ( media, shouldForceSync = false ) => {
369
483
  if ( ! media ) {
370
484
  return;
371
485
  }
@@ -377,12 +491,13 @@ export default function LogoEdit( {
377
491
  return;
378
492
  }
379
493
 
380
- setLogo( media.id );
494
+ setLogo( media.id, shouldForceSync );
381
495
  };
382
496
 
383
497
  const onRemoveLogo = () => {
384
498
  setLogo( null );
385
499
  setLogoUrl( undefined );
500
+ setAttributes( { width: undefined } );
386
501
  };
387
502
 
388
503
  const { createErrorNotice } = useDispatch( noticesStore );
@@ -422,6 +537,9 @@ export default function LogoEdit( {
422
537
  setLogo={ setLogo }
423
538
  logoId={ mediaItemData?.id || siteLogoId }
424
539
  siteUrl={ url }
540
+ setIcon={ setIcon }
541
+ iconId={ siteIconId }
542
+ canUserEdit={ canUserEdit }
425
543
  />
426
544
  );
427
545
  }
@@ -480,7 +598,7 @@ export default function LogoEdit( {
480
598
  ) }
481
599
  { ! logoUrl && canUserEdit && (
482
600
  <MediaPlaceholder
483
- onSelect={ onSelectLogo }
601
+ onSelect={ onInitialSelectLogo }
484
602
  accept={ ACCEPT_MEDIA_STRING }
485
603
  allowedTypes={ ALLOWED_MEDIA_TYPES }
486
604
  onError={ onUploadError }
@@ -1,7 +1,7 @@
1
1
  .wp-block[data-align="center"] > .wp-block-site-logo {
2
+ display: table;
2
3
  margin-left: auto;
3
4
  margin-right: auto;
4
- text-align: center;
5
5
  }
6
6
 
7
7
  .wp-block-site-logo {
@@ -10,20 +10,6 @@
10
10
  pointer-events: none;
11
11
  }
12
12
 
13
- &:not(.is-default-size) {
14
- display: table;
15
- }
16
-
17
- // Provide a sane starting point for the size.
18
- &.is-default-size {
19
- width: 120px;
20
-
21
- img {
22
- height: auto;
23
- width: 100%;
24
- }
25
- }
26
-
27
13
  .custom-logo-link {
28
14
  cursor: inherit;
29
15
 
@@ -48,10 +48,6 @@ function render_block_core_site_logo( $attributes ) {
48
48
  $classnames[] = $attributes['className'];
49
49
  }
50
50
 
51
- if ( ! empty( $attributes['align'] ) && in_array( $attributes['align'], array( 'center', 'left', 'right' ), true ) ) {
52
- $classnames[] = "align{$attributes['align']}";
53
- }
54
-
55
51
  if ( empty( $attributes['width'] ) ) {
56
52
  $classnames[] = 'is-default-size';
57
53
  }
@@ -80,6 +76,23 @@ function register_block_core_site_logo_setting() {
80
76
 
81
77
  add_action( 'rest_api_init', 'register_block_core_site_logo_setting', 10 );
82
78
 
79
+ /**
80
+ * Register a core site setting for a site icon
81
+ */
82
+ function register_block_core_site_icon_setting() {
83
+ register_setting(
84
+ 'general',
85
+ 'site_icon',
86
+ array(
87
+ 'show_in_rest' => true,
88
+ 'type' => 'integer',
89
+ 'description' => __( 'Site icon.' ),
90
+ )
91
+ );
92
+ }
93
+
94
+ add_action( 'rest_api_init', 'register_block_core_site_icon_setting', 10 );
95
+
83
96
  /**
84
97
  * Registers the `core/site-logo` block on the server.
85
98
  */
@@ -18,7 +18,9 @@
18
18
  }
19
19
 
20
20
  &.aligncenter {
21
- display: table;
21
+ margin-left: auto;
22
+ margin-right: auto;
23
+ text-align: center;
22
24
  }
23
25
 
24
26
  // Style variations
@@ -1,3 +1,8 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { isEmpty } from 'lodash';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
@@ -99,7 +104,7 @@ export default function TemplatePartEdit( {
99
104
  return {
100
105
  innerBlocks: getBlocks( clientId ),
101
106
  isResolved: hasResolvedEntity,
102
- isMissing: hasResolvedEntity && ! entityRecord,
107
+ isMissing: hasResolvedEntity && isEmpty( entityRecord ),
103
108
  defaultWrapper: defaultWrapperElement || 'div',
104
109
  area: _area,
105
110
  enableSelection: _enableSelection,
@@ -50,3 +50,31 @@ addFilter(
50
50
  'core/template-part',
51
51
  enhanceTemplatePartVariations
52
52
  );
53
+
54
+ // Prevent adding template parts inside post templates.
55
+ const DISALLOWED_PARENTS = [ 'core/post-template', 'core/post-content' ];
56
+ addFilter(
57
+ 'blockEditor.__unstableCanInsertBlockType',
58
+ 'removeTemplatePartsFromPostTemplates',
59
+ (
60
+ can,
61
+ blockType,
62
+ rootClientId,
63
+ { getBlock, getBlockParentsByBlockName }
64
+ ) => {
65
+ if ( blockType.name !== 'core/template-part' ) {
66
+ return can;
67
+ }
68
+
69
+ for ( const disallowedParentType of DISALLOWED_PARENTS ) {
70
+ const hasDisallowedParent =
71
+ getBlock( rootClientId )?.name === disallowedParentType ||
72
+ getBlockParentsByBlockName( rootClientId, disallowedParentType )
73
+ .length;
74
+ if ( hasDisallowedParent ) {
75
+ return false;
76
+ }
77
+ }
78
+ return true;
79
+ }
80
+ );