@wordpress/create-block-interactive-template 2.41.0 → 2.41.1-next.v.202603161435.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.
@@ -0,0 +1,13 @@
1
+ # Interactive Block — Client-Side Navigation
2
+
3
+ > **Note**
4
+ > Check the [Interactivity API Reference docs in the Block Editor handbook](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/) to learn more about the Interactivity API.
5
+
6
+ This block has been created with the `create-block-interactive-template` (`client-side-navigation` variant) and demonstrates client-side navigation powered by `@wordpress/interactivity-router`.
7
+
8
+ ## How it works
9
+
10
+ - **Server-rendered content** — `render.php` reads a `?quote=` query param and picks a quote from a hardcoded array, rendered inside a router region.
11
+ - **Client-side navigation** — Prev/next links change the query param and trigger navigation via `@wordpress/interactivity-router` instead of a full page reload.
12
+ - **State persistence proof** — A client-side stopwatch runs outside the navigated region, demonstrating that client state is preserved across navigations.
13
+ - **Loading indicator** — Visual feedback is shown while navigation is in progress.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { useBlockProps } from '@wordpress/block-editor';
6
+
7
+ /**
8
+ * The edit function describes the structure of your block in the context of the
9
+ * editor. This represents what the editor will render when the block is used.
10
+ *
11
+ * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
12
+ *
13
+ * @return {Element} Element to render.
14
+ */
15
+ export default function Edit() {
16
+ const blockProps = useBlockProps();
17
+
18
+ return (
19
+ <div { ...blockProps }>
20
+ <p>
21
+ { __(
22
+ '{{title}} — Client-Side Navigation block. Preview this block on the frontend to see the interactive quote navigator.',
23
+ '{{textdomain}}'
24
+ ) }
25
+ </p>
26
+ </div>
27
+ );
28
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The following styles get applied inside the editor only.
3
+ *
4
+ * Replace them with your own styles or remove the file completely.
5
+ */
6
+
7
+ .wp-block-{{namespace}}-{{slug}} p {
8
+ font-size: 0.9em;
9
+ color: #757575;
10
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Registers a new block provided a unique name and an object defining its behavior.
3
+ *
4
+ * @see https://developer.wordpress.org/block-editor/developers/block-api/#registering-a-block
5
+ */
6
+ import { registerBlockType } from '@wordpress/blocks';
7
+
8
+ /**
9
+ * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
10
+ * All files containing `style` keyword are bundled together. The code used
11
+ * gets applied both to the front of your site and to the editor. All other files
12
+ * get applied to the editor only.
13
+ *
14
+ * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
15
+ */
16
+ import './style.scss';
17
+ import './editor.scss';
18
+
19
+ /**
20
+ * Internal dependencies
21
+ */
22
+ import Edit from './edit';
23
+ import metadata from './block.json';
24
+
25
+ /**
26
+ * Every block starts by registering a new block type definition.
27
+ *
28
+ * @see https://developer.wordpress.org/block-editor/developers/block-api/#registering-a-block
29
+ */
30
+ registerBlockType( metadata.name, {
31
+ /**
32
+ * @see ./edit.js
33
+ */
34
+ edit: Edit,
35
+ } );
@@ -0,0 +1,112 @@
1
+ <?php
2
+ /**
3
+ * PHP file to use when rendering the block type on the server to show on the front end.
4
+ *
5
+ * The following variables are exposed to the file:
6
+ * $attributes (array): The block attributes.
7
+ * $content (string): The block default content.
8
+ * $block (WP_Block): The block instance.
9
+ *
10
+ * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
11
+ */
12
+
13
+ $quotes = array(
14
+ array(
15
+ 'text' => __( 'The best way to predict the future is to invent it.', '{{textdomain}}' ),
16
+ 'author' => 'Alan Kay',
17
+ ),
18
+ array(
19
+ 'text' => __( 'Talk is cheap. Show me the code.', '{{textdomain}}' ),
20
+ 'author' => 'Linus Torvalds',
21
+ ),
22
+ array(
23
+ 'text' => __( 'Simplicity is the soul of efficiency.', '{{textdomain}}' ),
24
+ 'author' => 'Austin Freeman',
25
+ ),
26
+ array(
27
+ 'text' => __( 'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.', '{{textdomain}}' ),
28
+ 'author' => 'Martin Fowler',
29
+ ),
30
+ array(
31
+ 'text' => __( 'First, solve the problem. Then, write the code.', '{{textdomain}}' ),
32
+ 'author' => 'John Johnson',
33
+ ),
34
+ );
35
+
36
+ $total = count( $quotes );
37
+ $quote_index = isset( $_GET['quote'] ) ? absint( $_GET['quote'] ) % $total : 0;
38
+ $quote = $quotes[ $quote_index ];
39
+ $prev_index = ( $quote_index - 1 + $total ) % $total;
40
+ $next_index = ( $quote_index + 1 ) % $total;
41
+
42
+ // Adds the global state.
43
+ wp_interactivity_state(
44
+ '{{namespace}}',
45
+ array(
46
+ 'timer' => '00:00.00',
47
+ 'isNavigating' => false,
48
+ 'artificialDelay' => false,
49
+ )
50
+ );
51
+ ?>
52
+
53
+ <div
54
+ <?php echo get_block_wrapper_attributes(); ?>
55
+ data-wp-interactive="{{namespace}}"
56
+ >
57
+ <div class="wp-block-{{namespace}}-{{slug}}__timer" data-wp-init="callbacks.startTimer">
58
+ <?php esc_html_e( 'Client timer:', '{{textdomain}}' ); ?>
59
+ <span data-wp-text="state.timer"></span>
60
+ </div>
61
+
62
+ <label class="wp-block-{{namespace}}-{{slug}}__delay">
63
+ <input
64
+ type="checkbox"
65
+ data-wp-bind--checked="state.artificialDelay"
66
+ data-wp-on--change="actions.toggleDelay"
67
+ />
68
+ <?php esc_html_e( 'Introduce artificial network delay', '{{textdomain}}' ); ?>
69
+ </label>
70
+
71
+ <div
72
+ class="wp-block-{{namespace}}-{{slug}}__content"
73
+ data-wp-interactive="{{namespace}}"
74
+ data-wp-router-region="{{namespace}}/quote"
75
+ >
76
+ <figure
77
+ class="wp-block-{{namespace}}-{{slug}}__quote"
78
+ data-wp-key="quote-<?php echo esc_attr( $quote_index ); ?>"
79
+ >
80
+ <blockquote>
81
+ <p><?php echo esc_html( $quote['text'] ); ?></p>
82
+ </blockquote>
83
+ <figcaption>
84
+ — <?php echo esc_html( $quote['author'] ); ?>
85
+ </figcaption>
86
+ </figure>
87
+
88
+ <nav class="wp-block-{{namespace}}-{{slug}}__nav">
89
+ <a
90
+ href="<?php echo esc_url( add_query_arg( 'quote', $prev_index ) ); ?>"
91
+ data-wp-on--click="actions.navigateTo"
92
+ data-wp-on--mouseenter="actions.prefetchTo"
93
+ >
94
+ ← <?php esc_html_e( 'Prev', '{{textdomain}}' ); ?>
95
+ </a>
96
+ <a
97
+ href="<?php echo esc_url( add_query_arg( 'quote', $next_index ) ); ?>"
98
+ data-wp-on--click="actions.navigateTo"
99
+ data-wp-on--mouseenter="actions.prefetchTo"
100
+ >
101
+ <?php esc_html_e( 'Next', '{{textdomain}}' ); ?> →
102
+ </a>
103
+ </nav>
104
+
105
+ <div
106
+ class="wp-block-{{namespace}}-{{slug}}__loading"
107
+ data-wp-bind--hidden="!state.isNavigating"
108
+ >
109
+ <?php esc_html_e( 'Loading…', '{{textdomain}}' ); ?>
110
+ </div>
111
+ </div>
112
+ </div>
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The following styles get applied both on the front of your site
3
+ * and in the editor.
4
+ *
5
+ * Replace them with your own styles or remove the file completely.
6
+ */
7
+
8
+ .wp-block-{{namespace}}-{{slug}} {
9
+ font-family: sans-serif;
10
+ max-width: 480px;
11
+ padding: 1.5em;
12
+ border: 1px solid #e0e0e0;
13
+ border-radius: 8px;
14
+
15
+ // Timer — sits outside the router region.
16
+ &__timer {
17
+ font-size: 0.85em;
18
+ color: #666;
19
+ padding-bottom: 1em;
20
+ margin-bottom: 1em;
21
+ border-bottom: 1px solid #eee;
22
+ font-variant-numeric: tabular-nums;
23
+
24
+ span {
25
+ font-weight: 600;
26
+ }
27
+ }
28
+
29
+ // Quote card.
30
+ &__quote {
31
+ margin: 0 0 1.5em;
32
+
33
+ blockquote {
34
+ margin: 0;
35
+ font-size: 1.15em;
36
+ line-height: 1.6;
37
+ }
38
+
39
+ figcaption {
40
+ margin-top: 0.75em;
41
+ font-size: 0.9em;
42
+ color: #555;
43
+ }
44
+ }
45
+
46
+ // Prev / Next navigation.
47
+ &__nav {
48
+ display: flex;
49
+ justify-content: space-between;
50
+ gap: 1em;
51
+
52
+ a {
53
+ padding: 0.4em 1em;
54
+ border: 1px solid #ccc;
55
+ border-radius: 4px;
56
+ text-decoration: none;
57
+ color: inherit;
58
+ font-size: 0.9em;
59
+
60
+ &:hover {
61
+ background: #f5f5f5;
62
+ }
63
+ }
64
+ }
65
+
66
+ // Artificial delay checkbox.
67
+ &__delay {
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 0.5em;
71
+ margin-bottom: 1em;
72
+ font-size: 0.85em;
73
+ color: #666;
74
+ cursor: pointer;
75
+ }
76
+
77
+ // Router region content wrapper.
78
+ &__content {
79
+ position: relative;
80
+ }
81
+
82
+ // Loading overlay — appears on top of the quote after 100ms.
83
+ &__loading {
84
+ position: absolute;
85
+ inset: 0;
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ background: rgba(255, 255, 255, 0.8);
90
+ font-size: 0.9em;
91
+ color: #666;
92
+ animation: wp-block-create-block-loading-fade-in 0s 100ms both;
93
+
94
+ &[hidden] {
95
+ display: none;
96
+ }
97
+ }
98
+ }
99
+
100
+ @keyframes wp-block-create-block-loading-fade-in {
101
+ from {
102
+ opacity: 0;
103
+ }
104
+
105
+ to {
106
+ opacity: 1;
107
+ }
108
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { getElement, store, withSyncEvent } from '@wordpress/interactivity';
5
+
6
+ const { state } = store( '{{namespace}}', {
7
+ state: {
8
+ // Elapsed time in centiseconds, used to derive the formatted timer.
9
+ elapsed: 0,
10
+ get timer() {
11
+ const cs = state.elapsed % 100;
12
+ const s = Math.floor( state.elapsed / 100 ) % 60;
13
+ const m = Math.floor( state.elapsed / 6000 );
14
+ const pad = ( n, len = 2 ) => String( n ).padStart( len, '0' );
15
+ return `${ pad( m ) }:${ pad( s ) }.${ pad( cs ) }`;
16
+ },
17
+ },
18
+ actions: {
19
+ /**
20
+ * Navigates to the clicked link using the Interactivity Router.
21
+ * The router swaps only the router-region content — no full
22
+ * page reload.
23
+ */
24
+ navigateTo: withSyncEvent( function* ( event ) {
25
+ event.preventDefault();
26
+ // getElement() must be called synchronously, before any yield.
27
+ const { attributes } = getElement();
28
+ state.isNavigating = true;
29
+ if ( state.artificialDelay ) {
30
+ yield new Promise( ( resolve ) => setTimeout( resolve, 1000 ) );
31
+ }
32
+ const { actions } = yield import(
33
+ '@wordpress/interactivity-router'
34
+ );
35
+ yield actions.navigate( attributes.href );
36
+ state.isNavigating = false;
37
+ } ),
38
+ *prefetchTo() {
39
+ const { attributes } = getElement();
40
+ const { actions } = yield import(
41
+ '@wordpress/interactivity-router'
42
+ );
43
+ yield actions.prefetch( attributes.href );
44
+ },
45
+ toggleDelay() {
46
+ state.artificialDelay = ! state.artificialDelay;
47
+ },
48
+ },
49
+ callbacks: {
50
+ /**
51
+ * Starts a stopwatch that ticks every 10 ms.
52
+ * Because it lives outside the router region, the timer state
53
+ * persists across client-side navigations — proving that no
54
+ * full page reload happened.
55
+ */
56
+ startTimer() {
57
+ const interval = setInterval( () => {
58
+ state.elapsed += 1;
59
+ }, 10 );
60
+ return () => clearInterval( interval );
61
+ },
62
+ },
63
+ } );
package/index.js CHANGED
@@ -32,6 +32,20 @@ module.exports = {
32
32
  'An interactive block with the Interactivity API using TypeScript.',
33
33
  viewScriptModule: 'file:./view.ts',
34
34
  },
35
+ 'client-side-navigation': {
36
+ slug: 'example-interactive-client-side-navigation',
37
+ title: 'Example Interactive Client-Side Navigation',
38
+ description:
39
+ 'An interactive block demonstrating client-side navigation with the Interactivity API Router.',
40
+ blockTemplatesPath: join(
41
+ __dirname,
42
+ 'block-templates-client-side-navigation'
43
+ ),
44
+ npmDependencies: [
45
+ '@wordpress/interactivity',
46
+ '@wordpress/interactivity-router',
47
+ ],
48
+ },
35
49
  },
36
50
  pluginTemplatesPath: join( __dirname, 'plugin-templates' ),
37
51
  blockTemplatesPath: join( __dirname, 'block-templates' ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/create-block-interactive-template",
3
- "version": "2.41.0",
3
+ "version": "2.41.1-next.v.202603161435.0+ab4981c4f",
4
4
  "description": "Template for @wordpress/create-block to create interactive blocks with the Interactivity API.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -26,5 +26,5 @@
26
26
  "publishConfig": {
27
27
  "access": "public"
28
28
  },
29
- "gitHead": "8bfc179b9aed74c0a6dd6e8edf7a49e40e4f87cc"
29
+ "gitHead": "748f4e4564fcc0e6ae90200d90bb993a3cef5828"
30
30
  }