@wordpress/block-library 8.17.1 → 8.18.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 (101) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/cover/edit/index.js +3 -1
  3. package/build/cover/edit/index.js.map +1 -1
  4. package/build/cover/shared.js +26 -21
  5. package/build/cover/shared.js.map +1 -1
  6. package/build/embed/embed-link-settings.native.js +0 -1
  7. package/build/embed/embed-link-settings.native.js.map +1 -1
  8. package/build/post-content/edit.js +1 -1
  9. package/build/post-content/edit.js.map +1 -1
  10. package/build/post-template/index.js +1 -1
  11. package/build/query/edit/inspector-controls/index.js +28 -3
  12. package/build/query/edit/inspector-controls/index.js.map +1 -1
  13. package/build/query/edit/query-content.js +2 -1
  14. package/build/query/edit/query-content.js.map +1 -1
  15. package/build/query/index.js +9 -2
  16. package/build/query/index.js.map +1 -1
  17. package/build/query/view.js +75 -0
  18. package/build/query/view.js.map +1 -0
  19. package/build/query-pagination-next/index.js +1 -1
  20. package/build/query-pagination-numbers/edit.js +50 -5
  21. package/build/query-pagination-numbers/edit.js.map +1 -1
  22. package/build/query-pagination-numbers/index.js +7 -1
  23. package/build/query-pagination-numbers/index.js.map +1 -1
  24. package/build/query-pagination-previous/index.js +1 -1
  25. package/build/search/edit.js +3 -1
  26. package/build/search/edit.js.map +1 -1
  27. package/build/search/utils.js +2 -4
  28. package/build/search/utils.js.map +1 -1
  29. package/build/social-link/edit.native.js +1 -3
  30. package/build/social-link/edit.native.js.map +1 -1
  31. package/build-module/cover/edit/index.js +3 -1
  32. package/build-module/cover/edit/index.js.map +1 -1
  33. package/build-module/cover/shared.js +26 -21
  34. package/build-module/cover/shared.js.map +1 -1
  35. package/build-module/embed/embed-link-settings.native.js +0 -1
  36. package/build-module/embed/embed-link-settings.native.js.map +1 -1
  37. package/build-module/post-content/edit.js +1 -1
  38. package/build-module/post-content/edit.js.map +1 -1
  39. package/build-module/post-template/index.js +1 -1
  40. package/build-module/query/edit/inspector-controls/index.js +29 -4
  41. package/build-module/query/edit/inspector-controls/index.js.map +1 -1
  42. package/build-module/query/edit/query-content.js +2 -1
  43. package/build-module/query/edit/query-content.js.map +1 -1
  44. package/build-module/query/index.js +9 -2
  45. package/build-module/query/index.js.map +1 -1
  46. package/build-module/query/view.js +72 -0
  47. package/build-module/query/view.js.map +1 -0
  48. package/build-module/query-pagination-next/index.js +1 -1
  49. package/build-module/query-pagination-numbers/edit.js +51 -6
  50. package/build-module/query-pagination-numbers/edit.js.map +1 -1
  51. package/build-module/query-pagination-numbers/index.js +7 -1
  52. package/build-module/query-pagination-numbers/index.js.map +1 -1
  53. package/build-module/query-pagination-previous/index.js +1 -1
  54. package/build-module/search/edit.js +4 -2
  55. package/build-module/search/edit.js.map +1 -1
  56. package/build-module/search/utils.js +0 -1
  57. package/build-module/search/utils.js.map +1 -1
  58. package/build-module/social-link/edit.native.js +1 -3
  59. package/build-module/social-link/edit.native.js.map +1 -1
  60. package/build-style/query/style-rtl.css +140 -0
  61. package/build-style/query/style.css +140 -0
  62. package/build-style/site-logo/style-rtl.css +1 -0
  63. package/build-style/site-logo/style.css +1 -0
  64. package/build-style/style-rtl.css +1 -0
  65. package/build-style/style.css +1 -0
  66. package/package.json +32 -32
  67. package/src/audio/test/__snapshots__/edit.native.js.snap +106 -78
  68. package/src/cover/edit/index.js +3 -1
  69. package/src/cover/shared.js +25 -20
  70. package/src/cover/test/edit.native.js +1 -5
  71. package/src/embed/embed-link-settings.native.js +0 -1
  72. package/src/embed/test/index.native.js +9 -9
  73. package/src/file/test/__snapshots__/edit.native.js.snap +218 -162
  74. package/src/image/index.php +5 -0
  75. package/src/post-content/edit.js +2 -2
  76. package/src/post-content/index.php +0 -6
  77. package/src/post-featured-image/index.php +0 -6
  78. package/src/post-template/block.json +2 -1
  79. package/src/post-template/index.php +19 -4
  80. package/src/query/block.json +9 -2
  81. package/src/query/edit/inspector-controls/index.js +46 -3
  82. package/src/query/edit/query-content.js +1 -0
  83. package/src/query/index.php +94 -1
  84. package/src/query/style.scss +63 -0
  85. package/src/query/view.js +82 -0
  86. package/src/query-no-results/index.php +1 -5
  87. package/src/query-pagination-next/block.json +7 -1
  88. package/src/query-pagination-next/index.php +20 -3
  89. package/src/query-pagination-numbers/block.json +7 -1
  90. package/src/query-pagination-numbers/edit.js +64 -16
  91. package/src/query-pagination-numbers/index.php +27 -4
  92. package/src/query-pagination-previous/block.json +7 -1
  93. package/src/query-pagination-previous/index.php +19 -2
  94. package/src/search/edit.js +8 -2
  95. package/src/search/test/__snapshots__/edit.native.js.snap +363 -265
  96. package/src/search/utils.js +0 -1
  97. package/src/site-logo/style.scss +1 -0
  98. package/src/social-link/edit.native.js +1 -1
  99. package/src/social-link/editor.native.scss +0 -4
  100. package/src/social-links/test/edit.native.js +30 -0
  101. package/src/social-link/test/index.native.js +0 -132
@@ -17,7 +17,8 @@ import {
17
17
  privateApis as blockEditorPrivateApis,
18
18
  } from '@wordpress/block-editor';
19
19
  import { debounce } from '@wordpress/compose';
20
- import { useEffect, useState, useCallback } from '@wordpress/element';
20
+ import { useEffect, useState, useCallback, useRef } from '@wordpress/element';
21
+ import { speak } from '@wordpress/a11y';
21
22
 
22
23
  /**
23
24
  * Internal dependencies
@@ -40,8 +41,8 @@ import {
40
41
  const { BlockInfo } = unlock( blockEditorPrivateApis );
41
42
 
42
43
  export default function QueryInspectorControls( props ) {
43
- const { attributes, setQuery, setDisplayLayout } = props;
44
- const { query, displayLayout } = attributes;
44
+ const { attributes, setQuery, setDisplayLayout, setAttributes } = props;
45
+ const { query, displayLayout, enhancedPagination } = attributes;
45
46
  const {
46
47
  order,
47
48
  orderBy,
@@ -123,6 +124,18 @@ export default function QueryInspectorControls( props ) {
123
124
  isControlAllowed( allowedControls, 'parents' ) &&
124
125
  isPostTypeHierarchical;
125
126
 
127
+ const enhancedPaginationNotice = __(
128
+ 'Enhanced Pagination might cause interactive blocks within the Post Template to stop working. Disable it if you experience any issues.'
129
+ );
130
+
131
+ const isFirstRender = useRef( true ); // Don't speak on first render.
132
+ useEffect( () => {
133
+ if ( ! isFirstRender.current && enhancedPagination ) {
134
+ speak( enhancedPaginationNotice );
135
+ }
136
+ isFirstRender.current = false;
137
+ }, [ enhancedPagination, enhancedPaginationNotice ] );
138
+
126
139
  const showFiltersPanel =
127
140
  showTaxControl ||
128
141
  showAuthorControl ||
@@ -280,6 +293,36 @@ export default function QueryInspectorControls( props ) {
280
293
  </ToolsPanel>
281
294
  </InspectorControls>
282
295
  ) }
296
+ <InspectorControls>
297
+ <PanelBody
298
+ title={ __( 'User Experience' ) }
299
+ initialOpen={ false }
300
+ >
301
+ <ToggleControl
302
+ label={ __( 'Enhanced pagination' ) }
303
+ help={ __(
304
+ "Don't refresh the page when paginating to another page."
305
+ ) }
306
+ checked={ !! enhancedPagination }
307
+ onChange={ ( value ) =>
308
+ setAttributes( {
309
+ enhancedPagination: !! value,
310
+ } )
311
+ }
312
+ />
313
+ { enhancedPagination && (
314
+ <div>
315
+ <Notice
316
+ spokenMessage={ null }
317
+ status="warning"
318
+ isDismissible={ false }
319
+ >
320
+ { enhancedPaginationNotice }
321
+ </Notice>
322
+ </div>
323
+ ) }
324
+ </PanelBody>
325
+ </InspectorControls>
283
326
  </>
284
327
  );
285
328
  }
@@ -109,6 +109,7 @@ export default function QueryContent( {
109
109
  attributes={ attributes }
110
110
  setQuery={ updateQuery }
111
111
  setDisplayLayout={ updateDisplayLayout }
112
+ setAttributes={ setAttributes }
112
113
  />
113
114
  <BlockControls>
114
115
  <QueryToolbar
@@ -5,12 +5,105 @@
5
5
  * @package WordPress
6
6
  */
7
7
 
8
+ /**
9
+ * Modifies the static `core/query` block on the server.
10
+ *
11
+ * @since X.X.X
12
+ *
13
+ * @param array $attributes Block attributes.
14
+ * @param string $content Block default content.
15
+ * @param string $block Block instance.
16
+ *
17
+ * @return string Returns the modified output of the query block.
18
+ */
19
+ function render_block_core_query( $attributes, $content, $block ) {
20
+ if ( $attributes['enhancedPagination'] ) {
21
+ $p = new WP_HTML_Tag_Processor( $content );
22
+ if ( $p->next_tag() ) {
23
+ // Add the necessary directives.
24
+ $p->set_attribute( 'data-wp-interactive', true );
25
+ $p->set_attribute( 'data-wp-navigation-id', 'query-' . $attributes['queryId'] );
26
+ $p->set_attribute(
27
+ 'data-wp-context',
28
+ wp_json_encode( array( 'core' => array( 'query' => (object) array() ) ) )
29
+ );
30
+ $content = $p->get_updated_html();
31
+
32
+ // Mark the block as interactive.
33
+ $block->block_type->supports['interactivity'] = true;
34
+
35
+ // Add a div to announce messages using `aria-live`.
36
+ $last_div_position = strripos( $content, '</div>' );
37
+ $content = substr_replace(
38
+ $content,
39
+ '<div
40
+ class="wp-block-query__enhanced-pagination-navigation-announce"
41
+ aria-live="polite"
42
+ data-wp-text="context.core.query.message"
43
+ ></div>
44
+ <div
45
+ class="wp-block-query__enhanced-pagination-animation"
46
+ data-wp-class--start-animation="selectors.core.query.startAnimation"
47
+ data-wp-class--finish-animation="selectors.core.query.finishAnimation"
48
+ ></div>',
49
+ $last_div_position,
50
+ 0
51
+ );
52
+
53
+ // Use state to send translated strings.
54
+ wp_store(
55
+ array(
56
+ 'state' => array(
57
+ 'core' => array(
58
+ 'query' => array(
59
+ 'loadingText' => __( 'Loading page, please wait.' ),
60
+ 'loadedText' => __( 'Page Loaded.' ),
61
+ ),
62
+ ),
63
+ ),
64
+ )
65
+ );
66
+ }
67
+ }
68
+
69
+ $view_asset = 'wp-block-query-view';
70
+ if ( ! wp_script_is( $view_asset ) ) {
71
+ $script_handles = $block->block_type->view_script_handles;
72
+ // If the script is not needed, and it is still in the `view_script_handles`, remove it.
73
+ if ( ! $attributes['enhancedPagination'] && in_array( $view_asset, $script_handles, true ) ) {
74
+ $block->block_type->view_script_handles = array_diff( $script_handles, array( $view_asset ) );
75
+ }
76
+ // If the script is needed, but it was previously removed, add it again.
77
+ if ( $attributes['enhancedPagination'] && ! in_array( $view_asset, $script_handles, true ) ) {
78
+ $block->block_type->view_script_handles = array_merge( $script_handles, array( $view_asset ) );
79
+ }
80
+ }
81
+
82
+ $style_asset = 'wp-block-query';
83
+ if ( ! wp_style_is( $style_asset ) ) {
84
+ $style_handles = $block->block_type->style_handles;
85
+ // If the styles are not needed, and they are still in the `style_handles`, remove them.
86
+ if ( ! $attributes['enhancedPagination'] && in_array( $style_asset, $style_handles, true ) ) {
87
+ $block->block_type->style_handles = array_diff( $style_handles, array( $style_asset ) );
88
+ }
89
+ // If the styles are needed, but they were previously removed, add them again.
90
+ if ( $attributes['enhancedPagination'] && ! in_array( $style_asset, $style_handles, true ) ) {
91
+ $block->block_type->style_handles = array_merge( $style_handles, array( $style_asset ) );
92
+ }
93
+ }
94
+
95
+ return $content;
96
+ }
97
+
8
98
  /**
9
99
  * Registers the `core/query` block on the server.
10
100
  */
11
101
  function register_block_core_query() {
12
102
  register_block_type_from_metadata(
13
- __DIR__ . '/query'
103
+ __DIR__ . '/query',
104
+ array(
105
+ 'render_callback' => 'render_block_core_query',
106
+ )
14
107
  );
15
108
  }
16
109
  add_action( 'init', 'register_block_core_query' );
@@ -0,0 +1,63 @@
1
+ .wp-block-query__enhanced-pagination-animation {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ margin: 0;
6
+ padding: 0;
7
+ width: 100vw;
8
+ max-width: 100vw !important;
9
+ height: 4px;
10
+ background-color: var(--wp--preset--color--primary, #000);
11
+ opacity: 0;
12
+
13
+ &.start-animation {
14
+ animation:
15
+ wp-block-query__enhanced-pagination-start-animation
16
+ 30s
17
+ cubic-bezier(0, 1, 0, 1)
18
+ infinite;
19
+ }
20
+
21
+ &.finish-animation {
22
+ animation:
23
+ wp-block-query__enhanced-pagination-finish-animation
24
+ 300ms
25
+ ease-in;
26
+ }
27
+ }
28
+
29
+ @keyframes wp-block-query__enhanced-pagination-start-animation {
30
+ 0% {
31
+ transform: scaleX(0);
32
+ transform-origin: 0% 0%;
33
+ opacity: 1;
34
+ }
35
+ 100% {
36
+ transform: scaleX(1);
37
+ transform-origin: 0% 0%;
38
+ opacity: 1;
39
+ }
40
+ }
41
+
42
+ @keyframes wp-block-query__enhanced-pagination-finish-animation {
43
+ 0% {
44
+ opacity: 1;
45
+ }
46
+ 50% {
47
+ opacity: 1;
48
+ }
49
+ 100% {
50
+ opacity: 0;
51
+ }
52
+ }
53
+
54
+ .wp-block-query__enhanced-pagination-navigation-announce {
55
+ position: absolute;
56
+ clip: rect(0, 0, 0, 0);
57
+ width: 1px;
58
+ height: 1px;
59
+ padding: 0;
60
+ margin: -1px;
61
+ overflow: hidden;
62
+ border: 0;
63
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { store, navigate, prefetch } from '@wordpress/interactivity';
5
+
6
+ const isValidLink = ( ref ) =>
7
+ ref &&
8
+ ref instanceof window.HTMLAnchorElement &&
9
+ ref.href &&
10
+ ( ! ref.target || ref.target === '_self' ) &&
11
+ ref.origin === window.location.origin;
12
+
13
+ const isValidEvent = ( event ) =>
14
+ event.button === 0 && // left clicks only
15
+ ! event.metaKey && // open in new tab (mac)
16
+ ! event.ctrlKey && // open in new tab (windows)
17
+ ! event.altKey && // download
18
+ ! event.shiftKey &&
19
+ ! event.defaultPrevented;
20
+
21
+ store( {
22
+ selectors: {
23
+ core: {
24
+ query: {
25
+ startAnimation: ( { context } ) =>
26
+ context.core.query.animation === 'start',
27
+ finishAnimation: ( { context } ) =>
28
+ context.core.query.animation === 'finish',
29
+ },
30
+ },
31
+ },
32
+ actions: {
33
+ core: {
34
+ query: {
35
+ navigate: async ( { event, ref, context, state } ) => {
36
+ if ( isValidLink( ref ) && isValidEvent( event ) ) {
37
+ event.preventDefault();
38
+
39
+ const id = ref.closest( '[data-wp-navigation-id]' )
40
+ .dataset.wpNavigationId;
41
+
42
+ // Don't announce the navigation immediately, wait 300 ms.
43
+ const timeout = setTimeout( () => {
44
+ context.core.query.message =
45
+ state.core.query.loadingText;
46
+ context.core.query.animation = 'start';
47
+ }, 300 );
48
+
49
+ await navigate( ref.href );
50
+
51
+ // Dismiss loading message if it hasn't been added yet.
52
+ clearTimeout( timeout );
53
+
54
+ // Announce that the page has been loaded. If the message is the
55
+ // same, we use a no-break space similar to the @wordpress/a11y
56
+ // package: https://github.com/WordPress/gutenberg/blob/c395242b8e6ee20f8b06c199e4fc2920d7018af1/packages/a11y/src/filter-message.js#L20-L26
57
+ context.core.query.message =
58
+ state.core.query.loadedText +
59
+ ( context.core.query.message ===
60
+ state.core.query.loadedText
61
+ ? '\u00A0'
62
+ : '' );
63
+
64
+ context.core.query.animation = 'finish';
65
+
66
+ // Focus the first anchor of the Query block.
67
+ document
68
+ .querySelector(
69
+ `[data-wp-navigation-id=${ id }] a[href]`
70
+ )
71
+ ?.focus();
72
+ }
73
+ },
74
+ prefetch: async ( { ref } ) => {
75
+ if ( isValidLink( ref ) ) {
76
+ await prefetch( ref.href );
77
+ }
78
+ },
79
+ },
80
+ },
81
+ },
82
+ } );
@@ -32,14 +32,10 @@ function render_block_core_query_no_results( $attributes, $content, $block ) {
32
32
  $query = new WP_Query( $query_args );
33
33
  }
34
34
 
35
- if ( $query->have_posts() ) {
35
+ if ( $query->post_count > 0 ) {
36
36
  return '';
37
37
  }
38
38
 
39
- if ( ! $use_global_query ) {
40
- wp_reset_postdata();
41
- }
42
-
43
39
  $classes = ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) ? 'has-link-color' : '';
44
40
  $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classes ) );
45
41
  return sprintf(
@@ -12,7 +12,13 @@
12
12
  "type": "string"
13
13
  }
14
14
  },
15
- "usesContext": [ "queryId", "query", "paginationArrow", "showLabel" ],
15
+ "usesContext": [
16
+ "queryId",
17
+ "query",
18
+ "paginationArrow",
19
+ "showLabel",
20
+ "enhancedPagination"
21
+ ],
16
22
  "supports": {
17
23
  "reusable": false,
18
24
  "html": false,
@@ -15,9 +15,10 @@
15
15
  * @return string Returns the next posts link for the query pagination.
16
16
  */
17
17
  function render_block_core_query_pagination_next( $attributes, $content, $block ) {
18
- $page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
19
- $page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
20
- $max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0;
18
+ $page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
19
+ $enhanced_pagination = isset( $block->context['enhancedPagination'] ) && $block->context['enhancedPagination'];
20
+ $page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
21
+ $max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0;
21
22
 
22
23
  $wrapper_attributes = get_block_wrapper_attributes();
23
24
  $show_label = isset( $block->context['showLabel'] ) ? (bool) $block->context['showLabel'] : true;
@@ -61,6 +62,22 @@ function render_block_core_query_pagination_next( $attributes, $content, $block
61
62
  }
62
63
  wp_reset_postdata(); // Restore original Post Data.
63
64
  }
65
+
66
+ if ( $enhanced_pagination ) {
67
+ $p = new WP_HTML_Tag_Processor( $content );
68
+ if ( $p->next_tag(
69
+ array(
70
+ 'tag_name' => 'a',
71
+ 'class_name' => 'wp-block-query-pagination-next',
72
+ )
73
+ ) ) {
74
+ $p->set_attribute( 'data-wp-key', 'query-pagination-next' );
75
+ $p->set_attribute( 'data-wp-on--click', 'actions.core.query.navigate' );
76
+ $p->set_attribute( 'data-wp-on--mouseenter', 'actions.core.query.prefetch' );
77
+ $content = $p->get_updated_html();
78
+ }
79
+ }
80
+
64
81
  return $content;
65
82
  }
66
83
 
@@ -7,7 +7,13 @@
7
7
  "parent": [ "core/query-pagination" ],
8
8
  "description": "Displays a list of page numbers for pagination",
9
9
  "textdomain": "default",
10
- "usesContext": [ "queryId", "query" ],
10
+ "attributes": {
11
+ "midSize": {
12
+ "type": "number",
13
+ "default": 2
14
+ }
15
+ },
16
+ "usesContext": [ "queryId", "query", "enhancedPagination" ],
11
17
  "supports": {
12
18
  "reusable": false,
13
19
  "html": false,
@@ -1,25 +1,73 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useBlockProps } from '@wordpress/block-editor';
4
+ import { __ } from '@wordpress/i18n';
5
+ import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
6
+ import { PanelBody, RangeControl } from '@wordpress/components';
5
7
 
6
8
  const createPaginationItem = ( content, Tag = 'a', extraClass = '' ) => (
7
- <Tag className={ `page-numbers ${ extraClass }` }>{ content }</Tag>
9
+ <Tag key={ content } className={ `page-numbers ${ extraClass }` }>
10
+ { content }
11
+ </Tag>
8
12
  );
9
13
 
10
- const previewPaginationNumbers = () => (
11
- <>
12
- { createPaginationItem( 1 ) }
13
- { createPaginationItem( 2 ) }
14
- { createPaginationItem( 3, 'span', 'current' ) }
15
- { createPaginationItem( 4 ) }
16
- { createPaginationItem( 5 ) }
17
- { createPaginationItem( '...', 'span', 'dots' ) }
18
- { createPaginationItem( 8 ) }
19
- </>
20
- );
14
+ const previewPaginationNumbers = ( midSize ) => {
15
+ const paginationItems = [];
16
+
17
+ // First set of pagination items.
18
+ for ( let i = 1; i <= midSize; i++ ) {
19
+ paginationItems.push( createPaginationItem( i ) );
20
+ }
21
+
22
+ // Current pagination item.
23
+ paginationItems.push(
24
+ createPaginationItem( midSize + 1, 'span', 'current' )
25
+ );
26
+
27
+ // Second set of pagination items.
28
+ for ( let i = 1; i <= midSize; i++ ) {
29
+ paginationItems.push( createPaginationItem( midSize + 1 + i ) );
30
+ }
31
+
32
+ // Dots.
33
+ paginationItems.push( createPaginationItem( '...', 'span', 'dots' ) );
34
+
35
+ // Last pagination item.
36
+ paginationItems.push( createPaginationItem( midSize * 2 + 3 ) );
37
+
38
+ return <>{ paginationItems }</>;
39
+ };
21
40
 
22
- export default function QueryPaginationNumbersEdit() {
23
- const paginationNumbers = previewPaginationNumbers();
24
- return <div { ...useBlockProps() }>{ paginationNumbers }</div>;
41
+ export default function QueryPaginationNumbersEdit( {
42
+ attributes,
43
+ setAttributes,
44
+ } ) {
45
+ const { midSize } = attributes;
46
+ const paginationNumbers = previewPaginationNumbers(
47
+ parseInt( midSize, 10 )
48
+ );
49
+ return (
50
+ <>
51
+ <InspectorControls>
52
+ <PanelBody title={ __( 'Settings' ) }>
53
+ <RangeControl
54
+ label={ __( 'Number of links' ) }
55
+ help={ __(
56
+ 'Specify how many links can appear before and after the current page number. Links to the first, current and last page are always visible.'
57
+ ) }
58
+ value={ midSize }
59
+ onChange={ ( value ) => {
60
+ setAttributes( {
61
+ midSize: parseInt( value, 10 ),
62
+ } );
63
+ } }
64
+ min={ 0 }
65
+ max={ 5 }
66
+ withInputField={ false }
67
+ />
68
+ </PanelBody>
69
+ </InspectorControls>
70
+ <div { ...useBlockProps() }>{ paginationNumbers }</div>
71
+ </>
72
+ );
25
73
  }
@@ -15,13 +15,15 @@
15
15
  * @return string Returns the pagination numbers for the Query.
16
16
  */
17
17
  function render_block_core_query_pagination_numbers( $attributes, $content, $block ) {
18
- $page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
19
- $page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
20
- $max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0;
18
+ $page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
19
+ $enhanced_pagination = isset( $block->context['enhancedPagination'] ) && $block->context['enhancedPagination'];
20
+ $page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
21
+ $max_page = isset( $block->context['query']['pages'] ) ? (int) $block->context['query']['pages'] : 0;
21
22
 
22
23
  $wrapper_attributes = get_block_wrapper_attributes();
23
24
  $content = '';
24
25
  global $wp_query;
26
+ $mid_size = isset( $block->attributes['midSize'] ) ? (int) $block->attributes['midSize'] : null;
25
27
  if ( isset( $block->context['query']['inherit'] ) && $block->context['query']['inherit'] ) {
26
28
  // Take into account if we have set a bigger `max page`
27
29
  // than what the query has.
@@ -30,7 +32,10 @@ function render_block_core_query_pagination_numbers( $attributes, $content, $blo
30
32
  'prev_next' => false,
31
33
  'total' => $total,
32
34
  );
33
- $content = paginate_links( $paginate_args );
35
+ if ( null !== $mid_size ) {
36
+ $paginate_args['mid_size'] = $mid_size;
37
+ }
38
+ $content = paginate_links( $paginate_args );
34
39
  } else {
35
40
  $block_query = new WP_Query( build_query_vars_from_query_block( $block, $page ) );
36
41
  // `paginate_links` works with the global $wp_query, so we have to
@@ -45,6 +50,9 @@ function render_block_core_query_pagination_numbers( $attributes, $content, $blo
45
50
  'total' => $total,
46
51
  'prev_next' => false,
47
52
  );
53
+ if ( null !== $mid_size ) {
54
+ $paginate_args['mid_size'] = $mid_size;
55
+ }
48
56
  if ( 1 !== $page ) {
49
57
  /**
50
58
  * `paginate_links` doesn't use the provided `format` when the page is `1`.
@@ -77,9 +85,24 @@ function render_block_core_query_pagination_numbers( $attributes, $content, $blo
77
85
  wp_reset_postdata(); // Restore original Post Data.
78
86
  $wp_query = $prev_wp_query;
79
87
  }
88
+
80
89
  if ( empty( $content ) ) {
81
90
  return '';
82
91
  }
92
+
93
+ if ( $enhanced_pagination ) {
94
+ $p = new WP_HTML_Tag_Processor( $content );
95
+ while ( $p->next_tag(
96
+ array(
97
+ 'tag_name' => 'a',
98
+ 'class_name' => 'page-numbers',
99
+ )
100
+ ) ) {
101
+ $p->set_attribute( 'data-wp-on--click', 'actions.core.query.navigate' );
102
+ }
103
+ $content = $p->get_updated_html();
104
+ }
105
+
83
106
  return sprintf(
84
107
  '<div %1$s>%2$s</div>',
85
108
  $wrapper_attributes,
@@ -12,7 +12,13 @@
12
12
  "type": "string"
13
13
  }
14
14
  },
15
- "usesContext": [ "queryId", "query", "paginationArrow", "showLabel" ],
15
+ "usesContext": [
16
+ "queryId",
17
+ "query",
18
+ "paginationArrow",
19
+ "showLabel",
20
+ "enhancedPagination"
21
+ ],
16
22
  "supports": {
17
23
  "reusable": false,
18
24
  "html": false,
@@ -15,8 +15,9 @@
15
15
  * @return string Returns the previous posts link for the query.
16
16
  */
17
17
  function render_block_core_query_pagination_previous( $attributes, $content, $block ) {
18
- $page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
19
- $page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
18
+ $page_key = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page';
19
+ $enhanced_pagination = isset( $block->context['enhancedPagination'] ) && $block->context['enhancedPagination'];
20
+ $page = empty( $_GET[ $page_key ] ) ? 1 : (int) $_GET[ $page_key ];
20
21
 
21
22
  $wrapper_attributes = get_block_wrapper_attributes();
22
23
  $show_label = isset( $block->context['showLabel'] ) ? (bool) $block->context['showLabel'] : true;
@@ -49,6 +50,22 @@ function render_block_core_query_pagination_previous( $attributes, $content, $bl
49
50
  $label
50
51
  );
51
52
  }
53
+
54
+ if ( $enhanced_pagination ) {
55
+ $p = new WP_HTML_Tag_Processor( $content );
56
+ if ( $p->next_tag(
57
+ array(
58
+ 'tag_name' => 'a',
59
+ 'class_name' => 'wp-block-query-pagination-previous',
60
+ )
61
+ ) ) {
62
+ $p->set_attribute( 'data-wp-key', 'query-pagination-previous' );
63
+ $p->set_attribute( 'data-wp-on--click', 'actions.core.query.navigate' );
64
+ $p->set_attribute( 'data-wp-on--mouseenter', 'actions.core.query.prefetch' );
65
+ $content = $p->get_updated_html();
66
+ }
67
+ }
68
+
52
69
  return $content;
53
70
  }
54
71
 
@@ -52,7 +52,7 @@ import {
52
52
  PC_WIDTH_DEFAULT,
53
53
  PX_WIDTH_DEFAULT,
54
54
  MIN_WIDTH,
55
- MIN_WIDTH_UNIT,
55
+ isPercentageUnit,
56
56
  } from './utils.js';
57
57
 
58
58
  // Used to calculate border radius adjustment to avoid "fat" corners when
@@ -405,7 +405,13 @@ export default function SearchEdit( {
405
405
  >
406
406
  <UnitControl
407
407
  id={ unitControlInputId }
408
- min={ `${ MIN_WIDTH }${ MIN_WIDTH_UNIT }` }
408
+ min={
409
+ isPercentageUnit( widthUnit ) ? 0 : MIN_WIDTH
410
+ }
411
+ max={
412
+ isPercentageUnit( widthUnit ) ? 100 : undefined
413
+ }
414
+ step={ 1 }
409
415
  onChange={ ( newWidth ) => {
410
416
  const filteredWidth =
411
417
  widthUnit === '%' &&