@wordpress/block-library 9.7.1-next.1f6eadc42.0 → 9.8.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 (114) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/categories/edit.js +38 -17
  3. package/build/categories/edit.js.map +1 -1
  4. package/build/categories/index.js +10 -3
  5. package/build/categories/index.js.map +1 -1
  6. package/build/categories/variations.js +43 -0
  7. package/build/categories/variations.js.map +1 -0
  8. package/build/embed/edit.js +1 -0
  9. package/build/embed/edit.js.map +1 -1
  10. package/build/embed/edit.native.js +1 -0
  11. package/build/embed/edit.native.js.map +1 -1
  12. package/build/embed/embed-preview.js +80 -100
  13. package/build/embed/embed-preview.js.map +1 -1
  14. package/build/embed/embed-preview.native.js +1 -0
  15. package/build/embed/embed-preview.native.js.map +1 -1
  16. package/build/form/view.js +1 -0
  17. package/build/form/view.js.map +1 -1
  18. package/build/image/image.js +5 -2
  19. package/build/image/image.js.map +1 -1
  20. package/build/index.js +1 -0
  21. package/build/index.js.map +1 -1
  22. package/build/navigation-submenu/index.js +13 -0
  23. package/build/navigation-submenu/index.js.map +1 -1
  24. package/build/post-template/edit.js +6 -1
  25. package/build/post-template/edit.js.map +1 -1
  26. package/build/post-title/index.js +1 -4
  27. package/build/post-title/index.js.map +1 -1
  28. package/build/query/edit/inspector-controls/format-controls.js +102 -0
  29. package/build/query/edit/inspector-controls/format-controls.js.map +1 -0
  30. package/build/query/edit/inspector-controls/index.js +45 -9
  31. package/build/query/edit/inspector-controls/index.js.map +1 -1
  32. package/build/query/edit/query-content.js +26 -10
  33. package/build/query/edit/query-content.js.map +1 -1
  34. package/build/query/index.js +3 -1
  35. package/build/query/index.js.map +1 -1
  36. package/build/query/utils.js +12 -1
  37. package/build/query/utils.js.map +1 -1
  38. package/build/site-title/index.js +1 -4
  39. package/build/site-title/index.js.map +1 -1
  40. package/build/social-link/edit.js +13 -11
  41. package/build/social-link/edit.js.map +1 -1
  42. package/build-module/categories/edit.js +40 -19
  43. package/build-module/categories/edit.js.map +1 -1
  44. package/build-module/categories/index.js +10 -3
  45. package/build-module/categories/index.js.map +1 -1
  46. package/build-module/categories/variations.js +36 -0
  47. package/build-module/categories/variations.js.map +1 -0
  48. package/build-module/embed/edit.js +1 -0
  49. package/build-module/embed/edit.js.map +1 -1
  50. package/build-module/embed/edit.native.js +1 -0
  51. package/build-module/embed/edit.native.js.map +1 -1
  52. package/build-module/embed/embed-preview.js +80 -100
  53. package/build-module/embed/embed-preview.js.map +1 -1
  54. package/build-module/embed/embed-preview.native.js +1 -0
  55. package/build-module/embed/embed-preview.native.js.map +1 -1
  56. package/build-module/form/view.js +1 -0
  57. package/build-module/form/view.js.map +1 -1
  58. package/build-module/image/image.js +5 -2
  59. package/build-module/image/image.js.map +1 -1
  60. package/build-module/index.js +1 -0
  61. package/build-module/index.js.map +1 -1
  62. package/build-module/navigation-submenu/index.js +13 -0
  63. package/build-module/navigation-submenu/index.js.map +1 -1
  64. package/build-module/post-template/edit.js +6 -1
  65. package/build-module/post-template/edit.js.map +1 -1
  66. package/build-module/post-title/index.js +1 -4
  67. package/build-module/post-title/index.js.map +1 -1
  68. package/build-module/query/edit/inspector-controls/format-controls.js +96 -0
  69. package/build-module/query/edit/inspector-controls/format-controls.js.map +1 -0
  70. package/build-module/query/edit/inspector-controls/index.js +45 -9
  71. package/build-module/query/edit/inspector-controls/index.js.map +1 -1
  72. package/build-module/query/edit/query-content.js +27 -11
  73. package/build-module/query/edit/query-content.js.map +1 -1
  74. package/build-module/query/index.js +3 -1
  75. package/build-module/query/index.js.map +1 -1
  76. package/build-module/query/utils.js +12 -1
  77. package/build-module/query/utils.js.map +1 -1
  78. package/build-module/site-title/index.js +1 -4
  79. package/build-module/site-title/index.js.map +1 -1
  80. package/build-module/social-link/edit.js +14 -12
  81. package/build-module/social-link/edit.js.map +1 -1
  82. package/build-style/editor-rtl.css +1 -1
  83. package/build-style/editor.css +1 -1
  84. package/build-style/gallery/editor-rtl.css +1 -1
  85. package/build-style/gallery/editor.css +1 -1
  86. package/build-style/post-title/style-rtl.css +4 -18
  87. package/build-style/post-title/style.css +4 -18
  88. package/build-style/site-title/style-rtl.css +4 -18
  89. package/build-style/site-title/style.css +4 -18
  90. package/build-style/style-rtl.css +8 -36
  91. package/build-style/style.css +8 -36
  92. package/package.json +35 -35
  93. package/src/categories/block.json +7 -2
  94. package/src/categories/edit.js +69 -30
  95. package/src/categories/index.js +2 -0
  96. package/src/categories/index.php +21 -9
  97. package/src/categories/variations.js +40 -0
  98. package/src/comments-pagination-next/index.php +1 -1
  99. package/src/comments-pagination-previous/index.php +2 -1
  100. package/src/embed/embed-preview.js +96 -112
  101. package/src/image/image.js +2 -1
  102. package/src/navigation-submenu/block.json +13 -0
  103. package/src/post-template/edit.js +6 -0
  104. package/src/post-template/index.php +5 -0
  105. package/src/post-title/block.json +1 -4
  106. package/src/post-title/style.scss +4 -21
  107. package/src/query/block.json +3 -1
  108. package/src/query/edit/inspector-controls/format-controls.js +90 -0
  109. package/src/query/edit/inspector-controls/index.js +68 -9
  110. package/src/query/edit/query-content.js +38 -5
  111. package/src/query/utils.js +16 -1
  112. package/src/site-title/block.json +1 -4
  113. package/src/site-title/style.scss +4 -21
  114. package/src/social-link/edit.js +11 -7
@@ -14,7 +14,7 @@ import clsx from 'clsx';
14
14
  import { __, sprintf } from '@wordpress/i18n';
15
15
  import { Placeholder, SandBox } from '@wordpress/components';
16
16
  import { BlockIcon } from '@wordpress/block-editor';
17
- import { Component } from '@wordpress/element';
17
+ import { useState } from '@wordpress/element';
18
18
  import { getAuthority } from '@wordpress/url';
19
19
 
20
20
  /**
@@ -23,129 +23,113 @@ import { getAuthority } from '@wordpress/url';
23
23
  import WpEmbedPreview from './wp-embed-preview';
24
24
  import { Caption } from '../utils/caption';
25
25
 
26
- class EmbedPreview extends Component {
27
- constructor() {
28
- super( ...arguments );
29
- this.hideOverlay = this.hideOverlay.bind( this );
30
- this.state = {
31
- interactive: false,
32
- };
33
- }
34
-
35
- static getDerivedStateFromProps( nextProps, state ) {
36
- if ( ! nextProps.isSelected && state.interactive ) {
37
- // We only want to change this when the block is not selected, because changing it when
38
- // the block becomes selected makes the overlap disappear too early. Hiding the overlay
39
- // happens on mouseup when the overlay is clicked.
40
- return { interactive: false };
41
- }
26
+ export default function EmbedPreview( {
27
+ preview,
28
+ previewable,
29
+ url,
30
+ type,
31
+ isSelected,
32
+ className,
33
+ icon,
34
+ label,
35
+ insertBlocksAfter,
36
+ attributes,
37
+ setAttributes,
38
+ } ) {
39
+ const [ interactive, setInteractive ] = useState( false );
42
40
 
43
- return null;
41
+ if ( ! isSelected && interactive ) {
42
+ // We only want to change this when the block is not selected, because changing it when
43
+ // the block becomes selected makes the overlap disappear too early. Hiding the overlay
44
+ // happens on mouseup when the overlay is clicked.
45
+ setInteractive( false );
44
46
  }
45
47
 
46
- hideOverlay() {
48
+ const hideOverlay = () => {
47
49
  // This is called onMouseUp on the overlay. We can't respond to the `isSelected` prop
48
50
  // changing, because that happens on mouse down, and the overlay immediately disappears,
49
51
  // and the mouse event can end up in the preview content. We can't use onClick on
50
52
  // the overlay to hide it either, because then the editor misses the mouseup event, and
51
53
  // thinks we're multi-selecting blocks.
52
- this.setState( { interactive: true } );
53
- }
54
+ setInteractive( true );
55
+ };
54
56
 
55
- render() {
56
- const {
57
- preview,
58
- previewable,
59
- url,
60
- type,
61
- className,
62
- icon,
63
- label,
64
- insertBlocksAfter,
65
- attributes,
66
- setAttributes,
67
- isSelected,
68
- } = this.props;
69
- const { scripts } = preview;
70
- const { interactive } = this.state;
57
+ const { scripts } = preview;
71
58
 
72
- const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html;
73
- const embedSourceUrl = getAuthority( url );
74
- const iframeTitle = sprintf(
75
- // translators: %s: host providing embed content e.g: www.youtube.com
76
- __( 'Embedded content from %s' ),
77
- embedSourceUrl
78
- );
79
- const sandboxClassnames = clsx(
80
- type,
81
- className,
82
- 'wp-block-embed__wrapper'
83
- );
59
+ const html = 'photo' === type ? getPhotoHtml( preview ) : preview.html;
60
+ const embedSourceUrl = getAuthority( url );
61
+ const iframeTitle = sprintf(
62
+ // translators: %s: host providing embed content e.g: www.youtube.com
63
+ __( 'Embedded content from %s' ),
64
+ embedSourceUrl
65
+ );
66
+ const sandboxClassnames = clsx(
67
+ type,
68
+ className,
69
+ 'wp-block-embed__wrapper'
70
+ );
84
71
 
85
- // Disabled because the overlay div doesn't actually have a role or functionality
86
- // as far as the user is concerned. We're just catching the first click so that
87
- // the block can be selected without interacting with the embed preview that the overlay covers.
88
- /* eslint-disable jsx-a11y/no-static-element-interactions */
89
- const embedWrapper =
90
- 'wp-embed' === type ? (
91
- <WpEmbedPreview html={ html } />
92
- ) : (
93
- <div className="wp-block-embed__wrapper">
94
- <SandBox
95
- html={ html }
96
- scripts={ scripts }
97
- title={ iframeTitle }
98
- type={ sandboxClassnames }
99
- onFocus={ this.hideOverlay }
72
+ // Disabled because the overlay div doesn't actually have a role or functionality
73
+ // as far as the user is concerned. We're just catching the first click so that
74
+ // the block can be selected without interacting with the embed preview that the overlay covers.
75
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
76
+ const embedWrapper =
77
+ 'wp-embed' === type ? (
78
+ <WpEmbedPreview html={ html } />
79
+ ) : (
80
+ <div className="wp-block-embed__wrapper">
81
+ <SandBox
82
+ html={ html }
83
+ scripts={ scripts }
84
+ title={ iframeTitle }
85
+ type={ sandboxClassnames }
86
+ onFocus={ hideOverlay }
87
+ />
88
+ { ! interactive && (
89
+ <div
90
+ className="block-library-embed__interactive-overlay"
91
+ onMouseUp={ hideOverlay }
100
92
  />
101
- { ! interactive && (
102
- <div
103
- className="block-library-embed__interactive-overlay"
104
- onMouseUp={ this.hideOverlay }
105
- />
106
- ) }
107
- </div>
108
- );
109
- /* eslint-enable jsx-a11y/no-static-element-interactions */
110
-
111
- return (
112
- <figure
113
- className={ clsx( className, 'wp-block-embed', {
114
- 'is-type-video': 'video' === type,
115
- } ) }
116
- >
117
- { previewable ? (
118
- embedWrapper
119
- ) : (
120
- <Placeholder
121
- icon={ <BlockIcon icon={ icon } showColors /> }
122
- label={ label }
123
- >
124
- <p className="components-placeholder__error">
125
- <a href={ url }>{ url }</a>
126
- </p>
127
- <p className="components-placeholder__error">
128
- { sprintf(
129
- /* translators: %s: host providing embed content e.g: www.youtube.com */
130
- __(
131
- "Embedded content from %s can't be previewed in the editor."
132
- ),
133
- embedSourceUrl
134
- ) }
135
- </p>
136
- </Placeholder>
137
93
  ) }
138
- <Caption
139
- attributes={ attributes }
140
- setAttributes={ setAttributes }
141
- isSelected={ isSelected }
142
- insertBlocksAfter={ insertBlocksAfter }
143
- label={ __( 'Embed caption text' ) }
144
- showToolbarButton={ isSelected }
145
- />
146
- </figure>
94
+ </div>
147
95
  );
148
- }
149
- }
96
+ /* eslint-enable jsx-a11y/no-static-element-interactions */
150
97
 
151
- export default EmbedPreview;
98
+ return (
99
+ <figure
100
+ className={ clsx( className, 'wp-block-embed', {
101
+ 'is-type-video': 'video' === type,
102
+ } ) }
103
+ >
104
+ { previewable ? (
105
+ embedWrapper
106
+ ) : (
107
+ <Placeholder
108
+ icon={ <BlockIcon icon={ icon } showColors /> }
109
+ label={ label }
110
+ >
111
+ <p className="components-placeholder__error">
112
+ <a href={ url }>{ url }</a>
113
+ </p>
114
+ <p className="components-placeholder__error">
115
+ { sprintf(
116
+ /* translators: %s: host providing embed content e.g: www.youtube.com */
117
+ __(
118
+ "Embedded content from %s can't be previewed in the editor."
119
+ ),
120
+ embedSourceUrl
121
+ ) }
122
+ </p>
123
+ </Placeholder>
124
+ ) }
125
+ <Caption
126
+ attributes={ attributes }
127
+ setAttributes={ setAttributes }
128
+ isSelected={ isSelected }
129
+ insertBlocksAfter={ insertBlocksAfter }
130
+ label={ __( 'Embed caption text' ) }
131
+ showToolbarButton={ isSelected }
132
+ />
133
+ </figure>
134
+ );
135
+ }
@@ -559,7 +559,8 @@ export default function Image( {
559
559
  const mediaReplaceFlow = isSingleSelected &&
560
560
  ! isEditingImage &&
561
561
  ! lockUrlControls && (
562
- <BlockControls group="other">
562
+ // For contentOnly mode, put this button in its own area so it has borders around it.
563
+ <BlockControls group={ isContentOnlyMode ? 'inline' : 'other' }>
563
564
  <MediaReplaceFlow
564
565
  mediaId={ id }
565
566
  mediaURL={ url }
@@ -59,6 +59,19 @@
59
59
  "supports": {
60
60
  "reusable": false,
61
61
  "html": false,
62
+ "typography": {
63
+ "fontSize": true,
64
+ "lineHeight": true,
65
+ "__experimentalFontFamily": true,
66
+ "__experimentalFontWeight": true,
67
+ "__experimentalFontStyle": true,
68
+ "__experimentalTextTransform": true,
69
+ "__experimentalTextDecoration": true,
70
+ "__experimentalLetterSpacing": true,
71
+ "__experimentalDefaultControls": {
72
+ "fontSize": true
73
+ }
74
+ },
62
75
  "interactivity": {
63
76
  "clientNavigation": true
64
77
  }
@@ -90,6 +90,7 @@ export default function PostTemplateEdit( {
90
90
  taxQuery,
91
91
  parents,
92
92
  pages,
93
+ format,
93
94
  // We gather extra query args to pass to the REST API call.
94
95
  // This way extenders of Query Loop can add their own query args,
95
96
  // and have accurate previews in the editor.
@@ -163,6 +164,10 @@ export default function PostTemplateEdit( {
163
164
  if ( parents?.length ) {
164
165
  query.parent = parents;
165
166
  }
167
+ if ( format?.length ) {
168
+ query.format = format;
169
+ }
170
+
166
171
  // If sticky is not set, it will return all posts in the results.
167
172
  // If sticky is set to `only`, it will limit the results to sticky posts only.
168
173
  // If it is anything else, it will exclude sticky posts from results. For the record the value stored is `exclude`.
@@ -205,6 +210,7 @@ export default function PostTemplateEdit( {
205
210
  templateSlug,
206
211
  taxQuery,
207
212
  parents,
213
+ format,
208
214
  restQueryArgs,
209
215
  previewPostType,
210
216
  ]
@@ -64,6 +64,11 @@ function render_block_core_post_template( $attributes, $content, $block ) {
64
64
  if ( in_the_loop() ) {
65
65
  $query = clone $wp_query;
66
66
  $query->rewind_posts();
67
+
68
+ // If in a single post of any post type, default to the 'post' post type.
69
+ if ( is_singular() ) {
70
+ query_posts( array( 'post_type' => 'post' ) );
71
+ }
67
72
  } else {
68
73
  $query = $wp_query;
69
74
  }
@@ -80,8 +80,5 @@
80
80
  }
81
81
  }
82
82
  },
83
- "style": "wp-block-post-title",
84
- "selectors": {
85
- "typography": ".wp-block-post-title, .wp-block-post-title > a"
86
- }
83
+ "style": "wp-block-post-title"
87
84
  }
@@ -3,31 +3,14 @@
3
3
  // This block has customizable padding, border-box makes that more predictable.
4
4
  box-sizing: border-box;
5
5
 
6
- &[style*="font-weight"] :where(a) {
7
- font-weight: inherit;
8
- }
9
- &[class*="-font-family"] :where(a),
10
- &[style*="font-family"] :where(a) {
6
+ :where(a) {
7
+ display: inline-block;
11
8
  font-family: inherit;
12
- }
13
- &[class*="-font-size"] :where(a),
14
- &[style*="font-size"] :where(a) {
15
9
  font-size: inherit;
16
- }
17
- &[style*="line-height"] :where(a) {
18
- line-height: inherit;
19
- }
20
- &[style*="font-style"] :where(a) {
21
10
  font-style: inherit;
22
- }
23
- &[style*="letter-spacing"] :where(a) {
11
+ font-weight: inherit;
24
12
  letter-spacing: inherit;
25
- }
26
- &[style*="text-decoration"] :where(a) {
13
+ line-height: inherit;
27
14
  text-decoration: inherit;
28
15
  }
29
-
30
- a {
31
- display: inline-block;
32
- }
33
16
  }
@@ -25,7 +25,8 @@
25
25
  "sticky": "",
26
26
  "inherit": true,
27
27
  "taxQuery": null,
28
- "parents": []
28
+ "parents": [],
29
+ "format": []
29
30
  }
30
31
  },
31
32
  "tagName": {
@@ -40,6 +41,7 @@
40
41
  "default": false
41
42
  }
42
43
  },
44
+ "usesContext": [ "postType" ],
43
45
  "providesContext": {
44
46
  "queryId": "queryId",
45
47
  "query": "query",
@@ -0,0 +1,90 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { FormTokenField } from '@wordpress/components';
5
+ import { useSelect } from '@wordpress/data';
6
+ import { store as coreStore } from '@wordpress/core-data';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ // All WP post formats, sorted alphabetically by translated name.
10
+ // Value is the post format slug. Label is the name.
11
+ const POST_FORMATS = [
12
+ { value: 'aside', label: __( 'Aside' ) },
13
+ { value: 'audio', label: __( 'Audio' ) },
14
+ { value: 'chat', label: __( 'Chat' ) },
15
+ { value: 'gallery', label: __( 'Gallery' ) },
16
+ { value: 'image', label: __( 'Image' ) },
17
+ { value: 'link', label: __( 'Link' ) },
18
+ { value: 'quote', label: __( 'Quote' ) },
19
+ { value: 'standard', label: __( 'Standard' ) },
20
+ { value: 'status', label: __( 'Status' ) },
21
+ { value: 'video', label: __( 'Video' ) },
22
+ ].sort( ( a, b ) => {
23
+ const normalizedA = a.label.toUpperCase();
24
+ const normalizedB = b.label.toUpperCase();
25
+
26
+ if ( normalizedA < normalizedB ) {
27
+ return -1;
28
+ }
29
+ if ( normalizedA > normalizedB ) {
30
+ return 1;
31
+ }
32
+ return 0;
33
+ } );
34
+
35
+ // A helper function to convert translatable post format names into their static values.
36
+ function formatNamesToValues( names, formats ) {
37
+ return names
38
+ .map( ( name ) => {
39
+ return formats.find(
40
+ ( item ) =>
41
+ item.label.toLocaleLowerCase() === name.toLocaleLowerCase()
42
+ )?.value;
43
+ } )
44
+ .filter( Boolean );
45
+ }
46
+
47
+ export default function FormatControls( { onChange, query: { format } } ) {
48
+ // 'format' is expected to be an array. If it is not an array, for example
49
+ // if a user has manually entered an invalid value in the block markup,
50
+ // convert it to an array to prevent JavaScript errors.
51
+ const normalizedFormats = Array.isArray( format ) ? format : [ format ];
52
+
53
+ const { supportedFormats } = useSelect( ( select ) => {
54
+ const themeSupports = select( coreStore ).getThemeSupports();
55
+ return {
56
+ supportedFormats: themeSupports.formats,
57
+ };
58
+ }, [] );
59
+
60
+ const formats = POST_FORMATS.filter( ( item ) =>
61
+ supportedFormats.includes( item.value )
62
+ );
63
+
64
+ const values = normalizedFormats
65
+ .map(
66
+ ( name ) => formats.find( ( item ) => item.value === name )?.label
67
+ )
68
+ .filter( Boolean );
69
+
70
+ const suggestions = formats
71
+ .filter( ( item ) => ! format.includes( item.value ) )
72
+ .map( ( item ) => item.label );
73
+
74
+ return (
75
+ <FormTokenField
76
+ label={ __( 'Formats' ) }
77
+ value={ values }
78
+ suggestions={ suggestions }
79
+ onChange={ ( newValues ) => {
80
+ onChange( {
81
+ format: formatNamesToValues( newValues, formats ),
82
+ } );
83
+ } }
84
+ __experimentalShowHowTo={ false }
85
+ __experimentalExpandOnFocus
86
+ __nextHasNoMarginBottom
87
+ __next40pxDefaultSize
88
+ />
89
+ );
90
+ }
@@ -12,6 +12,8 @@ import {
12
12
  __experimentalToolsPanel as ToolsPanel,
13
13
  __experimentalToolsPanelItem as ToolsPanelItem,
14
14
  } from '@wordpress/components';
15
+ import { useSelect } from '@wordpress/data';
16
+ import { store as coreStore } from '@wordpress/core-data';
15
17
  import { __ } from '@wordpress/i18n';
16
18
  import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
17
19
  import { debounce } from '@wordpress/compose';
@@ -24,6 +26,7 @@ import OrderControl from './order-control';
24
26
  import AuthorControl from './author-control';
25
27
  import ParentControl from './parent-control';
26
28
  import { TaxonomyControls } from './taxonomy-controls';
29
+ import FormatControls from './format-controls';
27
30
  import StickyControl from './sticky-control';
28
31
  import CreateNewPostLink from './create-new-post-link';
29
32
  import PerPageControl from './per-page-control';
@@ -42,7 +45,7 @@ import { useToolsPanelDropdownMenuProps } from '../../../utils/hooks';
42
45
  const { BlockInfo } = unlock( blockEditorPrivateApis );
43
46
 
44
47
  export default function QueryInspectorControls( props ) {
45
- const { attributes, setQuery, setDisplayLayout } = props;
48
+ const { attributes, setQuery, setDisplayLayout, isTemplate } = props;
46
49
  const { query, displayLayout } = attributes;
47
50
  const {
48
51
  order,
@@ -56,10 +59,15 @@ export default function QueryInspectorControls( props ) {
56
59
  inherit,
57
60
  taxQuery,
58
61
  parents,
62
+ format,
59
63
  } = query;
60
64
  const allowedControls = useAllowedControls( attributes );
61
65
  const [ showSticky, setShowSticky ] = useState( postType === 'post' );
62
- const { postTypesTaxonomiesMap, postTypesSelectOptions } = usePostTypes();
66
+ const {
67
+ postTypesTaxonomiesMap,
68
+ postTypesSelectOptions,
69
+ postTypeFormatSupportMap,
70
+ } = usePostTypes();
63
71
  const taxonomies = useTaxonomies( postType );
64
72
  const isPostTypeHierarchical = useIsPostTypeHierarchical( postType );
65
73
  useEffect( () => {
@@ -88,6 +96,14 @@ export default function QueryInspectorControls( props ) {
88
96
  }
89
97
  // We need to reset `parents` because they are tied to each post type.
90
98
  updateQuery.parents = [];
99
+ // Post types can register post format support with `add_post_type_support`.
100
+ // But we need to reset the `format` property when switching to post types
101
+ // that do not support post formats.
102
+ const hasFormatSupport = postTypeFormatSupportMap[ newValue ];
103
+ if ( ! hasFormatSupport ) {
104
+ updateQuery.format = [];
105
+ }
106
+
91
107
  setQuery( updateQuery );
92
108
  };
93
109
  const [ querySearch, setQuerySearch ] = useState( query.search );
@@ -103,20 +119,25 @@ export default function QueryInspectorControls( props ) {
103
119
  onChangeDebounced();
104
120
  return onChangeDebounced.cancel;
105
121
  }, [ querySearch, onChangeDebounced ] );
106
- const showInheritControl = isControlAllowed( allowedControls, 'inherit' );
122
+
123
+ const showInheritControl =
124
+ isTemplate && isControlAllowed( allowedControls, 'inherit' );
107
125
  const showPostTypeControl =
108
- ! inherit && isControlAllowed( allowedControls, 'postType' );
126
+ ( ! inherit && isControlAllowed( allowedControls, 'postType' ) ) ||
127
+ ! isTemplate;
109
128
  const postTypeControlLabel = __( 'Post type' );
110
129
  const postTypeControlHelp = __(
111
130
  'Select the type of content to display: posts, pages, or custom post types.'
112
131
  );
113
132
  const showColumnsControl = false;
114
133
  const showOrderControl =
115
- ! inherit && isControlAllowed( allowedControls, 'order' );
134
+ ( ! inherit && isControlAllowed( allowedControls, 'order' ) ) ||
135
+ ! isTemplate;
116
136
  const showStickyControl =
117
- ! inherit &&
118
- showSticky &&
119
- isControlAllowed( allowedControls, 'sticky' );
137
+ ( ! inherit &&
138
+ showSticky &&
139
+ isControlAllowed( allowedControls, 'sticky' ) ) ||
140
+ ( showSticky && ! isTemplate );
120
141
  const showSettingsPanel =
121
142
  showInheritControl ||
122
143
  showPostTypeControl ||
@@ -132,11 +153,36 @@ export default function QueryInspectorControls( props ) {
132
153
  isControlAllowed( allowedControls, 'parents' ) &&
133
154
  isPostTypeHierarchical;
134
155
 
156
+ const postTypeHasFormatSupport = postTypeFormatSupportMap[ postType ];
157
+ const showFormatControl = useSelect(
158
+ ( select ) => {
159
+ // Check if the post type supports post formats and if the control is allowed.
160
+ if (
161
+ ! postTypeHasFormatSupport ||
162
+ ! isControlAllowed( allowedControls, 'format' )
163
+ ) {
164
+ return false;
165
+ }
166
+
167
+ const themeSupports = select( coreStore ).getThemeSupports();
168
+
169
+ // If there are no supported formats, getThemeSupports still includes the default 'standard' format,
170
+ // and in this case the control should not be shown since the user has no other formats to choose from.
171
+ return (
172
+ themeSupports.formats &&
173
+ themeSupports.formats.length > 0 &&
174
+ themeSupports.formats.some( ( type ) => type !== 'standard' )
175
+ );
176
+ },
177
+ [ allowedControls, postTypeHasFormatSupport ]
178
+ );
179
+
135
180
  const showFiltersPanel =
136
181
  showTaxControl ||
137
182
  showAuthorControl ||
138
183
  showSearchControl ||
139
- showParentControl;
184
+ showParentControl ||
185
+ showFormatControl;
140
186
  const dropdownMenuProps = useToolsPanelDropdownMenuProps();
141
187
 
142
188
  const showPostCountControl = isControlAllowed(
@@ -313,6 +359,7 @@ export default function QueryInspectorControls( props ) {
313
359
  parents: [],
314
360
  search: '',
315
361
  taxQuery: null,
362
+ format: [],
316
363
  } );
317
364
  setQuerySearch( '' );
318
365
  } }
@@ -374,6 +421,18 @@ export default function QueryInspectorControls( props ) {
374
421
  />
375
422
  </ToolsPanelItem>
376
423
  ) }
424
+ { showFormatControl && (
425
+ <ToolsPanelItem
426
+ hasValue={ () => !! format?.length }
427
+ label={ __( 'Formats' ) }
428
+ onDeselect={ () => setQuery( { format: [] } ) }
429
+ >
430
+ <FormatControls
431
+ onChange={ setQuery }
432
+ query={ query }
433
+ />
434
+ </ToolsPanelItem>
435
+ ) }
377
436
  </ToolsPanel>
378
437
  ) }
379
438
  </>