@wordpress/block-library 9.28.0 → 9.28.1-next.0f6f9d12c.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 (163) hide show
  1. package/build/accordion-header/edit.js +118 -0
  2. package/build/accordion-header/edit.js.map +1 -0
  3. package/build/accordion-header/index.js +124 -0
  4. package/build/accordion-header/index.js.map +1 -0
  5. package/build/accordion-header/init.js +12 -0
  6. package/build/accordion-header/init.js.map +1 -0
  7. package/build/accordion-header/save.js +81 -0
  8. package/build/accordion-header/save.js.map +1 -0
  9. package/build/accordion-item/edit.js +129 -0
  10. package/build/accordion-item/edit.js.map +1 -0
  11. package/build/accordion-item/icons.js +30 -0
  12. package/build/accordion-item/icons.js.map +1 -0
  13. package/build/accordion-item/index.js +79 -0
  14. package/build/accordion-item/index.js.map +1 -0
  15. package/build/accordion-item/init.js +12 -0
  16. package/build/accordion-item/init.js.map +1 -0
  17. package/build/accordion-item/save.js +37 -0
  18. package/build/accordion-item/save.js.map +1 -0
  19. package/build/accordion-panel/edit.js +59 -0
  20. package/build/accordion-panel/edit.js.map +1 -0
  21. package/build/accordion-panel/index.js +108 -0
  22. package/build/accordion-panel/index.js.map +1 -0
  23. package/build/accordion-panel/init.js +12 -0
  24. package/build/accordion-panel/init.js.map +1 -0
  25. package/build/accordion-panel/save.js +46 -0
  26. package/build/accordion-panel/save.js.map +1 -0
  27. package/build/accordions/edit.js +123 -0
  28. package/build/accordions/edit.js.map +1 -0
  29. package/build/accordions/index.js +103 -0
  30. package/build/accordions/index.js.map +1 -0
  31. package/build/accordions/init.js +12 -0
  32. package/build/accordions/init.js.map +1 -0
  33. package/build/accordions/save.js +36 -0
  34. package/build/accordions/save.js.map +1 -0
  35. package/build/accordions/view.js +49 -0
  36. package/build/accordions/view.js.map +1 -0
  37. package/build/cover/index.js +2 -1
  38. package/build/cover/index.js.map +1 -1
  39. package/build/freeform/edit.js +6 -0
  40. package/build/freeform/edit.js.map +1 -1
  41. package/build/index.js +10 -0
  42. package/build/index.js.map +1 -1
  43. package/build/media-text/media-container.js +2 -1
  44. package/build/media-text/media-container.js.map +1 -1
  45. package/build/media-text/transforms.js +7 -3
  46. package/build/media-text/transforms.js.map +1 -1
  47. package/build/query-title/edit.js +52 -2
  48. package/build/query-title/edit.js.map +1 -1
  49. package/build/query-title/index.js +1 -0
  50. package/build/query-title/index.js.map +1 -1
  51. package/build/query-title/use-post-type-label.js +42 -0
  52. package/build/query-title/use-post-type-label.js.map +1 -0
  53. package/build/query-title/variations.js +10 -0
  54. package/build/query-title/variations.js.map +1 -1
  55. package/build/site-logo/edit.js +56 -41
  56. package/build/site-logo/edit.js.map +1 -1
  57. package/build/site-title/edit.js +9 -3
  58. package/build/site-title/edit.js.map +1 -1
  59. package/build-module/accordion-header/edit.js +108 -0
  60. package/build-module/accordion-header/edit.js.map +1 -0
  61. package/build-module/accordion-header/index.js +116 -0
  62. package/build-module/accordion-header/index.js.map +1 -0
  63. package/build-module/accordion-header/init.js +6 -0
  64. package/build-module/accordion-header/init.js.map +1 -0
  65. package/build-module/accordion-header/save.js +71 -0
  66. package/build-module/accordion-header/save.js.map +1 -0
  67. package/build-module/accordion-item/edit.js +120 -0
  68. package/build-module/accordion-item/edit.js.map +1 -0
  69. package/build-module/accordion-item/icons.js +22 -0
  70. package/build-module/accordion-item/icons.js.map +1 -0
  71. package/build-module/accordion-item/index.js +71 -0
  72. package/build-module/accordion-item/index.js.map +1 -0
  73. package/build-module/accordion-item/init.js +6 -0
  74. package/build-module/accordion-item/init.js.map +1 -0
  75. package/build-module/accordion-item/save.js +28 -0
  76. package/build-module/accordion-item/save.js.map +1 -0
  77. package/build-module/accordion-panel/edit.js +50 -0
  78. package/build-module/accordion-panel/edit.js.map +1 -0
  79. package/build-module/accordion-panel/index.js +100 -0
  80. package/build-module/accordion-panel/index.js.map +1 -0
  81. package/build-module/accordion-panel/init.js +6 -0
  82. package/build-module/accordion-panel/init.js.map +1 -0
  83. package/build-module/accordion-panel/save.js +37 -0
  84. package/build-module/accordion-panel/save.js.map +1 -0
  85. package/build-module/accordions/edit.js +116 -0
  86. package/build-module/accordions/edit.js.map +1 -0
  87. package/build-module/accordions/index.js +95 -0
  88. package/build-module/accordions/index.js.map +1 -0
  89. package/build-module/accordions/init.js +6 -0
  90. package/build-module/accordions/init.js.map +1 -0
  91. package/build-module/accordions/save.js +27 -0
  92. package/build-module/accordions/save.js.map +1 -0
  93. package/build-module/accordions/view.js +46 -0
  94. package/build-module/accordions/view.js.map +1 -0
  95. package/build-module/cover/index.js +2 -1
  96. package/build-module/cover/index.js.map +1 -1
  97. package/build-module/freeform/edit.js +6 -0
  98. package/build-module/freeform/edit.js.map +1 -1
  99. package/build-module/index.js +10 -0
  100. package/build-module/index.js.map +1 -1
  101. package/build-module/media-text/media-container.js +2 -1
  102. package/build-module/media-text/media-container.js.map +1 -1
  103. package/build-module/media-text/transforms.js +7 -3
  104. package/build-module/media-text/transforms.js.map +1 -1
  105. package/build-module/query-title/edit.js +52 -2
  106. package/build-module/query-title/edit.js.map +1 -1
  107. package/build-module/query-title/index.js +1 -0
  108. package/build-module/query-title/index.js.map +1 -1
  109. package/build-module/query-title/use-post-type-label.js +36 -0
  110. package/build-module/query-title/use-post-type-label.js.map +1 -0
  111. package/build-module/query-title/variations.js +10 -0
  112. package/build-module/query-title/variations.js.map +1 -1
  113. package/build-module/site-logo/edit.js +57 -42
  114. package/build-module/site-logo/edit.js.map +1 -1
  115. package/build-module/site-title/edit.js +10 -4
  116. package/build-module/site-title/edit.js.map +1 -1
  117. package/build-style/accordions/style-rtl.css +223 -0
  118. package/build-style/accordions/style.css +223 -0
  119. package/build-style/style-rtl.css +88 -0
  120. package/build-style/style.css +88 -0
  121. package/package.json +36 -35
  122. package/src/accordion-header/block.json +93 -0
  123. package/src/accordion-header/edit.js +128 -0
  124. package/src/accordion-header/index.js +24 -0
  125. package/src/accordion-header/init.js +6 -0
  126. package/src/accordion-header/save.js +79 -0
  127. package/src/accordion-item/block.json +45 -0
  128. package/src/accordion-item/edit.js +149 -0
  129. package/src/accordion-item/icons.js +23 -0
  130. package/src/accordion-item/index.js +24 -0
  131. package/src/accordion-item/index.php +73 -0
  132. package/src/accordion-item/init.js +6 -0
  133. package/src/accordion-item/save.js +25 -0
  134. package/src/accordion-panel/block.json +74 -0
  135. package/src/accordion-panel/edit.js +61 -0
  136. package/src/accordion-panel/index.js +24 -0
  137. package/src/accordion-panel/init.js +6 -0
  138. package/src/accordion-panel/save.js +51 -0
  139. package/src/accordions/block.json +69 -0
  140. package/src/accordions/edit.js +133 -0
  141. package/src/accordions/index.js +24 -0
  142. package/src/accordions/index.php +61 -0
  143. package/src/accordions/init.js +6 -0
  144. package/src/accordions/save.js +23 -0
  145. package/src/accordions/style.scss +91 -0
  146. package/src/accordions/view.js +38 -0
  147. package/src/block/index.php +16 -14
  148. package/src/cover/block.json +2 -1
  149. package/src/file/index.php +28 -22
  150. package/src/freeform/edit.js +7 -0
  151. package/src/index.js +12 -0
  152. package/src/media-text/media-container.js +1 -0
  153. package/src/media-text/transforms.js +5 -1
  154. package/src/post-excerpt/index.php +16 -12
  155. package/src/query-title/block.json +1 -0
  156. package/src/query-title/edit.js +58 -1
  157. package/src/query-title/index.php +30 -5
  158. package/src/query-title/use-post-type-label.js +34 -0
  159. package/src/query-title/variations.js +13 -0
  160. package/src/site-logo/edit.js +33 -12
  161. package/src/site-title/edit.js +24 -16
  162. package/src/social-link/index.php +1 -1
  163. package/src/style.scss +1 -0
@@ -0,0 +1,91 @@
1
+ .wp-block-accordion-item {
2
+ display: grid;
3
+ grid-template-rows: max-content 0fr;
4
+ }
5
+
6
+ .wp-block-accordion-item.is-open {
7
+ grid-template-rows: max-content 1fr;
8
+ }
9
+
10
+ .accordion-item__heading {
11
+ margin-block-start: 0;
12
+ margin-block-end: 0;
13
+ }
14
+
15
+ .accordion-item__toggle {
16
+ font-family: inherit;
17
+ font-size: inherit;
18
+ font-weight: inherit;
19
+ line-height: inherit;
20
+ letter-spacing: inherit;
21
+ text-transform: inherit;
22
+ text-decoration: inherit;
23
+ word-spacing: inherit;
24
+ background: none;
25
+ border: none;
26
+ color: inherit;
27
+ padding: var(--wp--preset--spacing--20, 1em) 0;
28
+ cursor: pointer;
29
+ outline: none;
30
+ display: flex;
31
+ align-items: center;
32
+ text-align: inherit;
33
+ position: relative;
34
+ width: 100%;
35
+ }
36
+
37
+ .accordion-item__toggle > span {
38
+ width: 100%;
39
+ }
40
+
41
+ .is-layout-flow > .wp-block-accordion-panel,
42
+ .wp-block-accordion-panel {
43
+ overflow: hidden;
44
+ margin: 0;
45
+ }
46
+
47
+ .accordion-panel__wrapper {
48
+ padding-bottom: var(--wp--preset--spacing--20, 1em);
49
+ }
50
+
51
+ /* No icon block style */
52
+ .is-style-no-icon .accordion-item__toggle-icon {
53
+ background-color: unset;
54
+ }
55
+
56
+ .wp-block-accordion-header.icon-position-left .accordion-item__toggle {
57
+ /* stylelint-disable-next-line declaration-property-value-allowed-list -- This should be refactored to not use the row-reverse value. */
58
+ flex-direction: row-reverse;
59
+ }
60
+
61
+ /* RTL language support */
62
+ [dir="rtl"] .wp-block-accordion-header:not(.icon-position-left) .accordion-item__toggle {
63
+ /* stylelint-disable-next-line declaration-property-value-allowed-list -- Automatically adjust icon position for RTL languages. */
64
+ flex-direction: row-reverse;
65
+ }
66
+
67
+ [dir="rtl"] .wp-block-accordion-header.icon-position-left .accordion-item__toggle {
68
+ flex-direction: row;
69
+ }
70
+
71
+ .accordion-item__toggle:focus-visible {
72
+ outline: 2px solid -webkit-focus-ring-color;
73
+ outline-offset: 2px;
74
+ }
75
+
76
+ /* Add transitions only for users who do not prefer reduced motion */
77
+ @media (prefers-reduced-motion: no-preference) {
78
+ .wp-block-accordion-item .accordion-item__toggle-icon {
79
+ transition: transform 0.2s ease-in-out;
80
+ }
81
+
82
+ .wp-block-accordion-item {
83
+ transition: grid-template-rows 0.3s ease-out;
84
+ }
85
+ }
86
+
87
+ .is-open {
88
+ .accordion-item__toggle-icon.has-icon-plus {
89
+ transform: rotate(45deg);
90
+ }
91
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { store, getContext } from '@wordpress/interactivity';
5
+
6
+ const { state } = store( 'core/accordion', {
7
+ state: {
8
+ get isOpen() {
9
+ const { isOpen, id } = getContext();
10
+ return isOpen.includes( id );
11
+ },
12
+ },
13
+ actions: {
14
+ toggle: () => {
15
+ const context = getContext();
16
+ const { id, autoclose } = context;
17
+
18
+ if ( autoclose ) {
19
+ context.isOpen = state.isOpen ? [] : [ id ];
20
+ } else if ( state.isOpen ) {
21
+ context.isOpen = context.isOpen.filter(
22
+ ( item ) => item !== id
23
+ );
24
+ } else {
25
+ context.isOpen.push( id );
26
+ }
27
+ },
28
+ },
29
+ callbacks: {
30
+ initIsOpen: () => {
31
+ const context = getContext();
32
+ const { id, openByDefault } = context;
33
+ if ( openByDefault ) {
34
+ context.isOpen.push( id );
35
+ }
36
+ },
37
+ },
38
+ } );
@@ -88,20 +88,22 @@ function render_block_core_block( $attributes, $content, $block_instance ) {
88
88
  $block_instance->refresh_context_dependents();
89
89
  } else {
90
90
  // This branch can be removed once Gutenberg requires WordPress 6.8 or later.
91
- // phpcs:ignore Gutenberg.Commenting.SinceTag.MissingClassSinceTag
92
- class WP_Block_Cloner extends WP_Block {
93
- /**
94
- * Static methods of subclasses have access to protected properties
95
- * of instances of the parent class.
96
- * In this case, this gives us access to `available_context` and `registry`.
97
- */
98
- // phpcs:ignore Gutenberg.Commenting.SinceTag.MissingMethodSinceTag
99
- public static function clone_instance( $instance ) {
100
- return new WP_Block(
101
- $instance->parsed_block,
102
- $instance->available_context,
103
- $instance->registry
104
- );
91
+ if ( ! class_exists( 'WP_Block_Cloner' ) ) {
92
+ // phpcs:ignore Gutenberg.Commenting.SinceTag.MissingClassSinceTag
93
+ class WP_Block_Cloner extends WP_Block {
94
+ /**
95
+ * Static methods of subclasses have access to protected properties
96
+ * of instances of the parent class.
97
+ * In this case, this gives us access to `available_context` and `registry`.
98
+ */
99
+ // phpcs:ignore Gutenberg.Commenting.SinceTag.MissingMethodSinceTag
100
+ public static function clone_instance( $instance ) {
101
+ return new WP_Block(
102
+ $instance->parsed_block,
103
+ $instance->available_context,
104
+ $instance->registry
105
+ );
106
+ }
105
107
  }
106
108
  }
107
109
  $block_instance = WP_Block_Cloner::clone_instance( $block_instance );
@@ -8,7 +8,8 @@
8
8
  "textdomain": "default",
9
9
  "attributes": {
10
10
  "url": {
11
- "type": "string"
11
+ "type": "string",
12
+ "role": "content"
12
13
  },
13
14
  "useFeaturedImage": {
14
15
  "type": "boolean",
@@ -16,33 +16,39 @@
16
16
  * @return string Returns the block content.
17
17
  */
18
18
  function render_block_core_file( $attributes, $content ) {
19
+ if ( empty( $attributes['displayPreview'] ) ) {
20
+ return $content;
21
+ }
22
+
19
23
  // If it's interactive, enqueue the script module and add the directives.
20
- if ( ! empty( $attributes['displayPreview'] ) ) {
21
- wp_enqueue_script_module( '@wordpress/block-library/file/view' );
24
+ wp_enqueue_script_module( '@wordpress/block-library/file/view' );
22
25
 
23
- $processor = new WP_HTML_Tag_Processor( $content );
24
- $processor->next_tag();
26
+ $processor = new WP_HTML_Tag_Processor( $content );
27
+ if ( $processor->next_tag() ) {
25
28
  $processor->set_attribute( 'data-wp-interactive', 'core/file' );
26
- $processor->next_tag( 'object' );
27
- $processor->set_attribute( 'data-wp-bind--hidden', '!state.hasPdfPreview' );
28
- $processor->set_attribute( 'hidden', true );
29
-
30
- $filename = $processor->get_attribute( 'aria-label' );
31
- $has_filename = ! empty( $filename ) && 'PDF embed' !== $filename;
32
- $label = $has_filename ? sprintf(
33
- /* translators: %s: filename. */
34
- __( 'Embed of %s.' ),
35
- $filename
36
- ) : __( 'PDF embed' );
37
-
38
- // Update object's aria-label attribute if present in block HTML.
39
- // Match an aria-label attribute from an object tag.
40
- $processor->set_attribute( 'aria-label', $label );
41
-
42
- return $processor->get_updated_html();
43
29
  }
44
30
 
45
- return $content;
31
+ // If there are no OBJECT elements, something might have already modified the block.
32
+ if ( ! $processor->next_tag( 'OBJECT' ) ) {
33
+ return $content;
34
+ }
35
+
36
+ $processor->set_attribute( 'data-wp-bind--hidden', '!state.hasPdfPreview' );
37
+ $processor->set_attribute( 'hidden', true );
38
+
39
+ $filename = $processor->get_attribute( 'aria-label' );
40
+ $has_filename = is_string( $filename ) && ! empty( $filename ) && 'PDF embed' !== $filename;
41
+ $label = $has_filename ? sprintf(
42
+ /* translators: %s: filename. */
43
+ __( 'Embed of %s.' ),
44
+ $filename
45
+ ) : __( 'PDF embed' );
46
+
47
+ // Update object's aria-label attribute if present in block HTML.
48
+ // Match an aria-label attribute from an object tag.
49
+ $processor->set_attribute( 'aria-label', $label );
50
+
51
+ return $processor->get_updated_html();
46
52
  }
47
53
 
48
54
  /**
@@ -188,6 +188,13 @@ function ClassicEdit( {
188
188
  }
189
189
  } );
190
190
 
191
+ editor.on( 'paste', ( event ) => {
192
+ // TinyMCE selection isn’t synced with the block editor selection store.
193
+ // This event handler prevents paste from bubbling so the useClipboardHandler
194
+ // won’t replace the block.
195
+ event.stopPropagation();
196
+ } );
197
+
191
198
  editor.on( 'init', () => {
192
199
  const rootNode = editor.getBody();
193
200
 
package/src/index.js CHANGED
@@ -20,6 +20,10 @@ import {
20
20
  // production build to make the final bundle smaller.
21
21
  //
22
22
  // See https://github.com/WordPress/gutenberg/pull/40655 for more context.
23
+ import * as accordions from './accordions';
24
+ import * as accordionItem from './accordion-item';
25
+ import * as accordionHeader from './accordion-header';
26
+ import * as accordionPanel from './accordion-panel';
23
27
  import * as archives from './archives';
24
28
  import * as avatar from './avatar';
25
29
  import * as audio from './audio';
@@ -234,6 +238,14 @@ const getAllBlocks = () => {
234
238
  queryTitle,
235
239
  postAuthorBiography,
236
240
  ];
241
+
242
+ if ( window?.__experimentalEnableBlockExperiments ) {
243
+ blocks.push( accordions );
244
+ blocks.push( accordionItem );
245
+ blocks.push( accordionHeader );
246
+ blocks.push( accordionPanel );
247
+ }
248
+
237
249
  if ( window?.__experimentalEnableFormBlocks ) {
238
250
  blocks.push( form );
239
251
  blocks.push( formInput );
@@ -200,6 +200,7 @@ function MediaContainer( props, ref ) {
200
200
  }
201
201
  mediaId={ mediaId }
202
202
  toggleUseFeaturedImage={ toggleUseFeaturedImage }
203
+ useFeaturedImage={ useFeaturedImage }
203
204
  />
204
205
  { ( mediaTypeRenderers[ mediaType ] || noop )() }
205
206
  { isTemporaryMedia && <Spinner /> }
@@ -45,6 +45,7 @@ const transforms = {
45
45
  style,
46
46
  textColor,
47
47
  url,
48
+ useFeaturedImage,
48
49
  },
49
50
  innerBlocks
50
51
  ) => {
@@ -90,6 +91,7 @@ const transforms = {
90
91
  mediaType: backgroundType,
91
92
  mediaUrl: url,
92
93
  textColor,
94
+ useFeaturedImage,
93
95
  ...additionalAttributes,
94
96
  },
95
97
  innerBlocks
@@ -143,6 +145,7 @@ const transforms = {
143
145
  mediaUrl,
144
146
  style,
145
147
  textColor,
148
+ useFeaturedImage,
146
149
  },
147
150
  innerBlocks
148
151
  ) => {
@@ -169,13 +172,14 @@ const transforms = {
169
172
  alt: mediaAlt,
170
173
  anchor,
171
174
  backgroundType: mediaType,
172
- dimRatio: !! mediaUrl ? 50 : 100,
175
+ dimRatio: !! mediaUrl || useFeaturedImage ? 50 : 100,
173
176
  focalPoint,
174
177
  gradient,
175
178
  id: mediaId,
176
179
  overlayColor: backgroundColor,
177
180
  textColor,
178
181
  url: mediaUrl,
182
+ useFeaturedImage,
179
183
  ...additionalAttributes,
180
184
  };
181
185
 
@@ -20,18 +20,6 @@ function render_block_core_post_excerpt( $attributes, $content, $block ) {
20
20
  return '';
21
21
  }
22
22
 
23
- /*
24
- * The purpose of the excerpt length setting is to limit the length of both
25
- * automatically generated and user-created excerpts.
26
- * Because the excerpt_length filter only applies to auto generated excerpts,
27
- * wp_trim_words is used instead.
28
- */
29
- $excerpt_length = $attributes['excerptLength'];
30
- $excerpt = get_the_excerpt( $block->context['postId'] );
31
- if ( isset( $excerpt_length ) ) {
32
- $excerpt = wp_trim_words( $excerpt, $excerpt_length );
33
- }
34
-
35
23
  $more_text = ! empty( $attributes['moreText'] ) ? '<a class="wp-block-post-excerpt__more-link" href="' . esc_url( get_the_permalink( $block->context['postId'] ) ) . '">' . wp_kses_post( $attributes['moreText'] ) . '</a>' : '';
36
24
  $filter_excerpt_more = static function ( $more ) use ( $more_text ) {
37
25
  return empty( $more_text ) ? $more : '';
@@ -44,8 +32,24 @@ function render_block_core_post_excerpt( $attributes, $content, $block ) {
44
32
  * So if the block's attribute is not empty override the
45
33
  * `excerpt_more` filter and return nothing. This will
46
34
  * result in showing only one `read more` link at a time.
35
+ *
36
+ * This hook needs to be applied before the excerpt is retrieved with get_the_excerpt.
37
+ * Otherwise, the read more link filter from the theme is not removed.
47
38
  */
48
39
  add_filter( 'excerpt_more', $filter_excerpt_more );
40
+
41
+ /*
42
+ * The purpose of the excerpt length setting is to limit the length of both
43
+ * automatically generated and user-created excerpts.
44
+ * Because the excerpt_length filter only applies to auto generated excerpts,
45
+ * wp_trim_words is used instead.
46
+ */
47
+ $excerpt_length = $attributes['excerptLength'];
48
+ $excerpt = get_the_excerpt( $block->context['postId'] );
49
+ if ( isset( $excerpt_length ) ) {
50
+ $excerpt = wp_trim_words( $excerpt, $excerpt_length );
51
+ }
52
+
49
53
  $classes = array();
50
54
  if ( isset( $attributes['textAlign'] ) ) {
51
55
  $classes[] = 'has-text-align-' . $attributes['textAlign'];
@@ -34,6 +34,7 @@
34
34
  "type": "search"
35
35
  }
36
36
  },
37
+ "usesContext": [ "query" ],
37
38
  "supports": {
38
39
  "align": [ "wide", "full" ],
39
40
  "html": false,
@@ -25,9 +25,10 @@ import { __, _x, sprintf } from '@wordpress/i18n';
25
25
  * Internal dependencies
26
26
  */
27
27
  import { useArchiveLabel } from './use-archive-label';
28
+ import { usePostTypeLabel } from './use-post-type-label';
28
29
  import { useToolsPanelDropdownMenuProps } from '../utils/hooks';
29
30
 
30
- const SUPPORTED_TYPES = [ 'archive', 'search' ];
31
+ const SUPPORTED_TYPES = [ 'archive', 'search', 'post-type' ];
31
32
 
32
33
  export default function QueryTitleEdit( {
33
34
  attributes: {
@@ -39,8 +40,10 @@ export default function QueryTitleEdit( {
39
40
  showSearchTerm,
40
41
  },
41
42
  setAttributes,
43
+ context: { query },
42
44
  } ) {
43
45
  const { archiveTypeLabel, archiveNameLabel } = useArchiveLabel();
46
+ const { postTypeLabel } = usePostTypeLabel( query?.postType );
44
47
  const dropdownMenuProps = useToolsPanelDropdownMenuProps();
45
48
 
46
49
  const TagName = `h${ level }`;
@@ -174,6 +177,60 @@ export default function QueryTitleEdit( {
174
177
  );
175
178
  }
176
179
 
180
+ if ( type === 'post-type' ) {
181
+ let title;
182
+ if ( postTypeLabel ) {
183
+ if ( showPrefix ) {
184
+ title = sprintf(
185
+ /* translators: %s: Singular post type name of the queried object */
186
+ __( 'Post Type: "%s"' ),
187
+ postTypeLabel
188
+ );
189
+ } else {
190
+ title = postTypeLabel;
191
+ }
192
+ } else {
193
+ title = showPrefix ? __( 'Post Type: Name' ) : __( 'Name' );
194
+ }
195
+
196
+ titleElement = (
197
+ <>
198
+ <InspectorControls>
199
+ <ToolsPanel
200
+ label={ __( 'Settings' ) }
201
+ resetAll={ () =>
202
+ setAttributes( {
203
+ showPrefix: true,
204
+ } )
205
+ }
206
+ dropdownMenuProps={ dropdownMenuProps }
207
+ >
208
+ <ToolsPanelItem
209
+ hasValue={ () => ! showPrefix }
210
+ label={ __( 'Show post type label' ) }
211
+ onDeselect={ () =>
212
+ setAttributes( { showPrefix: true } )
213
+ }
214
+ isShownByDefault
215
+ >
216
+ <ToggleControl
217
+ __nextHasNoMarginBottom
218
+ label={ __( 'Show post type label' ) }
219
+ onChange={ () =>
220
+ setAttributes( {
221
+ showPrefix: ! showPrefix,
222
+ } )
223
+ }
224
+ checked={ showPrefix }
225
+ />
226
+ </ToolsPanelItem>
227
+ </ToolsPanel>
228
+ </InspectorControls>
229
+ <TagName { ...blockProps }>{ title }</TagName>
230
+ </>
231
+ );
232
+ }
233
+
177
234
  return (
178
235
  <>
179
236
  <BlockControls group="block">
@@ -7,23 +7,28 @@
7
7
 
8
8
  /**
9
9
  * Renders the `core/query-title` block on the server.
10
- * For now it only supports Archive title,
10
+ * For now it supports Archive title, Search title, and Post Type Label,
11
11
  * using queried object information
12
12
  *
13
13
  * @since 5.8.0
14
14
  *
15
- * @param array $attributes Block attributes.
15
+ * @param array $attributes Block attributes.
16
+ * @param array $_content Block content.
17
+ * @param object $block Block instance.
16
18
  *
17
19
  * @return string Returns the query title based on the queried object.
18
20
  */
19
- function render_block_core_query_title( $attributes ) {
21
+ function render_block_core_query_title( $attributes, $content, $block ) {
20
22
  $type = isset( $attributes['type'] ) ? $attributes['type'] : null;
21
23
  $is_archive = is_archive();
22
24
  $is_search = is_search();
25
+ $post_type = isset( $block->context['query']['postType'] ) ? $block->context['query']['postType'] : get_post_type();
26
+
23
27
  if ( ! $type ||
24
28
  ( 'archive' === $type && ! $is_archive ) ||
25
- ( 'search' === $type && ! $is_search )
26
- ) {
29
+ ( 'search' === $type && ! $is_search ) ||
30
+ ( 'post-type' === $type && ! $post_type )
31
+ ) {
27
32
  return '';
28
33
  }
29
34
  $title = '';
@@ -48,6 +53,26 @@ function render_block_core_query_title( $attributes ) {
48
53
  );
49
54
  }
50
55
  }
56
+ if ( 'post-type' === $type ) {
57
+ $post_type_object = get_post_type_object( $post_type );
58
+
59
+ if ( ! $post_type_object ) {
60
+ return '';
61
+ }
62
+
63
+ $post_type_name = $post_type_object->labels->singular_name;
64
+ $show_prefix = isset( $attributes['showPrefix'] ) ? $attributes['showPrefix'] : true;
65
+
66
+ if ( $show_prefix ) {
67
+ $title = sprintf(
68
+ /* translators: %s is the post type name. */
69
+ __( 'Post Type: "%s"' ),
70
+ $post_type_name
71
+ );
72
+ } else {
73
+ $title = $post_type_name;
74
+ }
75
+ }
51
76
 
52
77
  $tag_name = isset( $attributes['level'] ) ? 'h' . (int) $attributes['level'] : 'h1';
53
78
  $align_class_name = empty( $attributes['textAlign'] ) ? '' : "has-text-align-{$attributes['textAlign']}";
@@ -0,0 +1,34 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { store as coreStore } from '@wordpress/core-data';
5
+ import { useSelect } from '@wordpress/data';
6
+
7
+ /**
8
+ * Hook to fetch the singular label for the current post type.
9
+ *
10
+ * @param {string} contextPostType Context provided post type.
11
+ */
12
+ export function usePostTypeLabel( contextPostType ) {
13
+ const currentPostType = useSelect( ( select ) => {
14
+ // Access core/editor by string to avoid @wordpress/editor dependency.
15
+ // eslint-disable-next-line @wordpress/data-no-store-string-literals
16
+ const { getCurrentPostType } = select( 'core/editor' );
17
+ return getCurrentPostType();
18
+ }, [] );
19
+
20
+ // Fetch the post type label from the core data store
21
+ return useSelect(
22
+ ( select ) => {
23
+ const { getPostType } = select( coreStore );
24
+ const postTypeSlug = contextPostType || currentPostType;
25
+ const postType = getPostType( postTypeSlug );
26
+
27
+ // Return the singular name of the post type
28
+ return {
29
+ postTypeLabel: postType ? postType.labels.singular_name : '',
30
+ };
31
+ },
32
+ [ contextPostType, currentPostType ]
33
+ );
34
+ }
@@ -30,6 +30,19 @@ const variations = [
30
30
  },
31
31
  scope: [ 'inserter' ],
32
32
  },
33
+ {
34
+ isDefault: false,
35
+ name: 'post-type-label',
36
+ title: __( 'Post Type Label' ),
37
+ description: __(
38
+ 'Display the post type label based on the queried object.'
39
+ ),
40
+ icon: title,
41
+ attributes: {
42
+ type: 'post-type',
43
+ },
44
+ scope: [ 'inserter' ],
45
+ },
33
46
  ];
34
47
 
35
48
  /**
@@ -38,6 +38,7 @@ import {
38
38
  useBlockProps,
39
39
  store as blockEditorStore,
40
40
  __experimentalImageEditor as ImageEditor,
41
+ useBlockEditingMode,
41
42
  } from '@wordpress/block-editor';
42
43
  import { useSelect, useDispatch } from '@wordpress/data';
43
44
  import { store as coreStore } from '@wordpress/core-data';
@@ -73,6 +74,16 @@ const SiteLogo = ( {
73
74
  const [ isEditingImage, setIsEditingImage ] = useState( false );
74
75
  const { toggleSelection } = useDispatch( blockEditorStore );
75
76
  const dropdownMenuProps = useToolsPanelDropdownMenuProps();
77
+
78
+ // Check if we're in contentOnly mode
79
+ const blockEditingMode = useBlockEditingMode();
80
+ const isNavigationMode = useSelect(
81
+ ( select ) => select( blockEditorStore ).isNavigationMode(),
82
+ []
83
+ );
84
+ const isContentOnlyMode = blockEditingMode === 'contentOnly';
85
+ const isContentOnlyWriteMode = isNavigationMode && isContentOnlyMode;
86
+
76
87
  const { imageEditing, maxWidth, title } = useSelect( ( select ) => {
77
88
  const settings = select( blockEditorStore ).getSettings();
78
89
  const siteEntities = select( coreStore ).getEntityRecord(
@@ -205,8 +216,12 @@ const SiteLogo = ( {
205
216
  const canEditImage =
206
217
  logoId && naturalWidth && naturalHeight && imageEditing;
207
218
 
208
- const imgEdit =
209
- canEditImage && isEditingImage ? (
219
+ // Hide crop and dimensions editing in write mode
220
+ const shouldShowCropAndDimensions = ! isContentOnlyWriteMode;
221
+
222
+ let imgEdit;
223
+ if ( canEditImage && isEditingImage ) {
224
+ imgEdit = (
210
225
  <ImageEditor
211
226
  id={ logoId }
212
227
  url={ logoUrl }
@@ -221,13 +236,16 @@ const SiteLogo = ( {
221
236
  setIsEditingImage( false );
222
237
  } }
223
238
  />
224
- ) : (
239
+ );
240
+ } else {
241
+ // Always render ResizableBox but disable resize functionality in contentOnly mode
242
+ imgEdit = (
225
243
  <ResizableBox
226
244
  size={ {
227
245
  width: currentWidth,
228
246
  height: currentHeight,
229
247
  } }
230
- showHandle={ isSelected }
248
+ showHandle={ isSelected && shouldShowCropAndDimensions }
231
249
  minWidth={ minWidth }
232
250
  maxWidth={ maxWidthBuffer }
233
251
  minHeight={ minHeight }
@@ -251,6 +269,7 @@ const SiteLogo = ( {
251
269
  { imgWrapper }
252
270
  </ResizableBox>
253
271
  );
272
+ }
254
273
 
255
274
  // Support the previous location for the Site Icon settings. To be removed
256
275
  // when the required WP core version for Gutenberg is >= 6.5.0.
@@ -371,15 +390,17 @@ const SiteLogo = ( {
371
390
  ) }
372
391
  </ToolsPanel>
373
392
  </InspectorControls>
374
- <BlockControls group="block">
375
- { canEditImage && ! isEditingImage && (
376
- <ToolbarButton
377
- onClick={ () => setIsEditingImage( true ) }
378
- icon={ crop }
379
- label={ __( 'Crop' ) }
380
- />
393
+ { canEditImage &&
394
+ ! isEditingImage &&
395
+ shouldShowCropAndDimensions && (
396
+ <BlockControls group="block">
397
+ <ToolbarButton
398
+ onClick={ () => setIsEditingImage( true ) }
399
+ icon={ crop }
400
+ label={ __( 'Crop' ) }
401
+ />
402
+ </BlockControls>
381
403
  ) }
382
- </BlockControls>
383
404
  { imgEdit }
384
405
  </>
385
406
  );