Cree una página de configuración usando los componentes del editor de bloques de WordPress (Gutenberg)
Anteriormente, analizamos las opciones de almacenamiento y la configuración mediante el Editor de bloques de WordPress (Gutenberg) y la expansión del Script de creación de bloques para permitir puntos finales adicionales. En esta guía, los juntaremos todos para crear una página de configuración utilizando componentes de Gutenberg.
La página de configuración que estamos a punto de construir
Pero primero, crédito donde se debe, la inspiración para esta guía se encuentra en el artículo Code in WP de Hardeep Asrani: Making a "Plugin Options Page" With Gutenberg Components.
requisitos previos
- Haber seguido la guía Crear un complemento para el editor de bloques de WordPress (Gutenberg)
- Haber seguido la guía Usar opciones para almacenar datos en el editor de bloques de WordPress (Gutenberg)
- Haber seguido la guía Agregar puntos de entrada a la guía Crear script de bloque de WordPress
Crear la página de configuración en PHP
Siguiendo las guías en los requisitos previos, abra el archivo PHP raíz del complemento (en este caso wholesome-plugin.php) y agregue lo siguiente:
Registrar la configuración
Como en las opciones de uso para almacenar datos, agregue la siguiente configuración al archivo:
function wholesomecode_wholesome_plugin_register_settings() {
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_select',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text_2',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text_3',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_toggle',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
}
add_action( 'init', 'wholesomecode_wholesome_plugin_register_settings', 10 );
Registre la configuración a la que accederemos en la página de configuración. Asegúrese de configurar show_in_restpara truecada uno, de modo que se pueda acceder a ellos a través de Gutenberg.
Registrar la página de configuración
Agregue el bloque de código para registrar la página de configuración:
function wholesomecode_wholesome_plugin_settings_page() {
add_options_page(
__( 'Wholesome Plugin Settings', 'wholesome-plugin' ),
__( 'Wholesome Plugin Settings', 'wholesome-plugin' ),
'manage_options',
'wholesome_plugin_settings',
function() {
?>
<div id="wholesome-plugin-settings"></div>
<?php
},
);
}
add_action( 'admin_menu', 'wholesomecode_wholesome_plugin_settings_page', 10 );
El código anterior agrega una nueva página al menú de configuración. Tenga en cuenta que todo lo que hace es generar un <div>, esto es lo que usaremos para representar los componentes de Gutenberg basados en React.
Elemento del menú de configuración
Poner en cola activos de administración
Para el siguiente bloque de código, debemos haber seguido todos los pasos de la guía Agregar puntos de entrada a la guía Crear script de bloque . Asegúrese de seguir todos los pasos de esa guía antes de este paso y vuelva y siga el resto de esta guía.
function wholesomecode_wholesome_plugin_admin_scripts() {
$dir = __DIR__;
$script_asset_path = "$dir/build/admin.asset.php";
if (! file_exists( $script_asset_path)) {
throw new Error(
'You need to run `npm start` or `npm run build` for the "wholesomecode/wholesome-plugin" block first.'
);
}
$admin_js = 'build/admin.js';
$script_asset = require( $script_asset_path );
wp_enqueue_script(
'wholesomecode-wholesome-plugin-admin-editor',
plugins_url( $admin_js, __FILE__ ),
$script_asset['dependencies'],
$script_asset['version']
);
wp_set_script_translations( 'wholesomecode-wholesome-plugin-block-editor', 'wholesome-plugin' );
$admin_css = 'build/admin.css';
wp_enqueue_style(
'wholesomecode-wholesome-plugin-admin',
plugins_url( $admin_css, __FILE__ ),
['wp-components'],
filemtime( "$dir/$admin_css") );
}
add_action( 'admin_enqueue_scripts', 'wholesomecode_wholesome_plugin_admin_scripts', 10 );
Cree la página de administración en JavaScript
Si ha seguido todos los pasos en Agregar puntos de entrada a la guía Crear script de bloque, debe tener un /src/admin.jsarchivo. Abra ese archivo y elimine su contenido.
Renderizar el componente
Recuerde ejecutar npm starten su terminal según la guía de creación de complementos y agregue lo siguiente a su /src/admin.jsarchivo.
import './admin.scss';
import { Icon } from '@wordpress/components';
import {
Fragment,
render,
Component,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
class App extends Component {
constructor() {
super( ...arguments );
}
render() {
return (<Fragment>
<div className="wholesome-plugin__header">
<div className="wholesome-plugin__container">
<div className="wholesome-plugin__title">
<h1>{ __( 'Wholesome Plugin Settings', 'wholesome-plugin') } <Icon icon="admin-plugins" /></h1>
</div>
</div>
</div>
<div className="wholesome-plugin__main"></div>
</Fragment>) }
}
document.addEventListener( 'DOMContentLoaded',() => {
const htmlOutput = document.getElementById( 'wholesome-plugin-settings' );
if (htmlOutput) {
render(
<App />,
htmlOutput
);
}
});
Si va a su página de configuración en el navegador, ahora debería ver lo siguiente:
Representación de componentes en la pantalla de configuración
Agregar los campos de configuración
¿Recuerda los pasos adicionales en la guía ‘Uso de opciones para almacenar datos’? Bueno, prácticamente vamos a pegarlos palabra por palabra en este componente, por lo que debería terminar con un código que se parece un poco a esto:
import './admin.scss';
import api from '@wordpress/api';
import {
Button,
Icon,
Panel,
PanelBody,
PanelRow,
Placeholder,
SelectControl,
Spinner,
TextControl,
ToggleControl,
} from '@wordpress/components';
import {
Fragment,
render,
Component,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
class App extends Component {
constructor() {
super( ...arguments );
this.state = {
exampleSelect: '',
exampleText: '',
exampleText2: '',
exampleText3: '',
exampleToggle: false,
isAPILoaded: false,
};
}
componentDidMount() {
api.loadPromise.then(() => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleSelect: response[ 'wholesomecode_wholesome_plugin_example_select' ],
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
exampleText2: response[ 'wholesomecode_wholesome_plugin_example_text_2' ],
exampleText3: response[ 'wholesomecode_wholesome_plugin_example_text_3' ],
exampleToggle: Boolean( response[ 'wholesomecode_wholesome_plugin_example_toggle' ] ),
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Fragment>
<div className="wholesome-plugin__header">
<div className="wholesome-plugin__container">
<div className="wholesome-plugin__title">
<h1>{ __( 'Wholesome Plugin Settings', 'wholesome-plugin') } <Icon icon="admin-plugins" /></h1>
</div>
</div>
</div>
<div className="wholesome-plugin__main">
<Panel>
<PanelBody
title={ __( 'Panel Body One', 'wholesome-plugin') }
icon="admin-plugins"
>
<SelectControl
help={ __( 'An example dropdown field.', 'wholesome-plugin') }
label={ __( 'Example Select', 'wholesome-plugin') }
onChange={ (exampleSelect) => this.setState( { exampleSelect }) }
options={ [
{
label: __( 'Please Select...', 'wholesome-plugin' ),
value: '',
},
{
label: __( 'Option 1', 'wholesome-plugin' ),
value: 'option-1',
},
{
label: __( 'Option 2', 'wholesome-plugin' ),
value: 'option-2',
},
] }
value={ exampleSelect }
/>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Two', 'wholesome-plugin') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Three', 'wholesome-plugin') }
icon="admin-plugins"
>
<PanelRow>
<TextControl
help={ __( 'Use PanelRow to place controls inline.', 'wholesome-plugin') }
label={ __( 'Example Text 2', 'wholesome-plugin') }
onChange={ (exampleText2) => this.setState( { exampleText2 }) }
value={ exampleText2 }
/>
<TextControl
help={ __( 'This control is inline.', 'wholesome-plugin') }
label={ __( 'Example Text 3', 'wholesome-plugin') }
onChange={ (exampleText3) => this.setState( { exampleText3 }) }
value={ exampleText3 }
/>
</PanelRow>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Four', 'wholesome-plugin') }
icon="admin-plugins"
>
<ToggleControl
checked={ exampleToggle }
help={ __( 'An example toggle.', 'wholesome-plugin') }
label={ __( 'Example Toggle', 'wholesome-plugin') }
onChange={ (exampleToggle) => this.setState( { exampleToggle }) }
/>
</PanelBody>
<Button
isPrimary
isLarge
onClick={ () => {}}
>
{ __( 'Save', 'wholesome-plugin') }
</Button>
</Panel>
</div>
</Fragment>) }
}
document.addEventListener( 'DOMContentLoaded', () => {
const htmlOutput = document.getElementById( 'wholesome-plugin-settings' );
if (htmlOutput) {
render(
<App />,
htmlOutput
);
}
});
Aparte de la eliminación del subscribein componentDidMountque usamos anteriormente para guardar en esa guía, y la adición del botón, el código es básicamente copiar y pegar.
Si todo está bien, nuestra página de configuración ahora debería verse un poco así:
Representación de los campos de configuración
No se preocupe, limpiaremos los estilos en la sección 4 de esta guía.
Manejar el guardado
En el onClickcontrolador del <button>componente agregue el siguiente código:
<Button
isPrimary
isLarge
onClick={ () => {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
} = this.state;
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_select' ]: exampleSelect,
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
[ 'wholesomecode_wholesome_plugin_example_text_2' ]: exampleText2,
[ 'wholesomecode_wholesome_plugin_example_text_3' ]: exampleText3,
[ 'wholesomecode_wholesome_plugin_example_toggle' ]: exampleToggle? 'true': '',
} );
settings.save();
}}
>
{ __( 'Save', 'wholesome-plugin') }
</Button>
Esto guardará nuestras opciones y configuraciones cuando se haga clic en el botón. Sin embargo, no hay ninguna indicación de que las opciones se hayan guardado de forma predeterminada.
Crear la Notificación
Para dar a nuestro usuario algunos comentarios de que las opciones y configuraciones se han guardado, implementemos una notificación de ‘snackbar’. Esta es la misma notificación que se usa en la pantalla principal del editor de publicaciones que usa el editor de bloques cuando se guarda la publicación.
Para agregar esto, necesitamos portar un componente principal de Gutenberg a nuestra compilación, ya que la lista de notificaciones no está disponible con las declaraciones de importación habituales.
Tendremos que agregar el siguiente código en el archivo:
import { SnackbarList } from '@wordpress/components';
import {
dispatch,
useDispatch,
useSelect,
} from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
const Notices = () => {
const notices = useSelect( (select) =>
select( noticesStore) .getNotices()
.filter( (notice) => notice.type === 'snackbar' ),
[]
);
const { removeNotice } = useDispatch( noticesStore );
return (<SnackbarList
className="edit-site-notices"
notices={ notices }
onRemove={ removeNotice }
/>
);
};
Luego, en el renderizado principal del <App>componente, agregue lo siguiente antes del cierre </Fragment>:
<div className="wholesome-plugin__notices">
<Notices/>
</div>
Finalmente agregue lo siguiente al onClickcontrolador del botón:
dispatch('core/notices').createNotice(
'success',
__( 'Settings Saved', 'wholesome-plugin' ),
{
type: 'snackbar',
isDismissible: true,
}
);
Esto creará una pequeña ventana emergente ‘snackbar’ cada vez que se guarden las configuraciones.
Notificación de snackbar en acción
Lo sé, lo sé, todavía tenemos que arreglar esos estilos.
El /src/admin.jsarchivo completo
Como referencia, aquí está el /src/admin.jscódigo completo del archivo:
import './admin.scss';
import api from '@wordpress/api';
import {
Button,
Icon,
Panel,
PanelBody,
PanelRow,
Placeholder,
SelectControl,
SnackbarList,
Spinner,
TextControl,
ToggleControl,
} from '@wordpress/components';
import {
dispatch,
useDispatch,
useSelect,
} from '@wordpress/data';
import {
Fragment,
render,
Component,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
const Notices = () => {
const notices = useSelect( (select) =>
select( noticesStore) .getNotices()
.filter( (notice) => notice.type === 'snackbar' ),
[]
);
const { removeNotice } = useDispatch( noticesStore );
return (<SnackbarList
className="edit-site-notices"
notices={ notices }
onRemove={ removeNotice }
/>
);
};
class App extends Component {
constructor() {
super( ...arguments );
this.state = {
exampleSelect: '',
exampleText: '',
exampleText2: '',
exampleText3: '',
exampleToggle: false,
isAPILoaded: false,
};
}
componentDidMount() {
api.loadPromise.then( () => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleSelect: response[ 'wholesomecode_wholesome_plugin_example_select' ],
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
exampleText2: response[ 'wholesomecode_wholesome_plugin_example_text_2' ],
exampleText3: response[ 'wholesomecode_wholesome_plugin_example_text_3' ],
exampleToggle: Boolean( response[ 'wholesomecode_wholesome_plugin_example_toggle' ] ),
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Fragment>
<div className="wholesome-plugin__header">
<div className="wholesome-plugin__container">
<div className="wholesome-plugin__title">
<h1>{ __( 'Wholesome Plugin Settings', 'wholesome-plugin') } <Icon icon="admin-plugins" /></h1>
</div>
</div>
</div>
<div className="wholesome-plugin__main">
<Panel>
<PanelBody
title={ __( 'Panel Body One', 'wholesome-plugin') }
icon="admin-plugins"
>
<SelectControl
help={ __( 'An example dropdown field.', 'wholesome-plugin') }
label={ __( 'Example Select', 'wholesome-plugin') }
onChange={ (exampleSelect) => this.setState( { exampleSelect }) }
options={ [
{
label: __( 'Please Select...', 'wholesome-plugin' ),
value: '',
},
{
label: __( 'Option 1', 'wholesome-plugin' ),
value: 'option-1',
},
{
label: __( 'Option 2', 'wholesome-plugin' ),
value: 'option-2',
},
] }
value={ exampleSelect }
/>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Two', 'wholesome-plugin') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Three', 'wholesome-plugin') }
icon="admin-plugins"
>
<PanelRow>
<TextControl
help={ __( 'Use PanelRow to place controls inline.', 'wholesome-plugin') }
label={ __( 'Example Text 2', 'wholesome-plugin') }
onChange={ (exampleText2) => this.setState( { exampleText2 }) }
value={ exampleText2 }
/>
<TextControl
help={ __( 'This control is inline.', 'wholesome-plugin') }
label={ __( 'Example Text 3', 'wholesome-plugin') }
onChange={ (exampleText3) => this.setState( { exampleText3 }) }
value={ exampleText3 }
/>
</PanelRow>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Four', 'wholesome-plugin') }
icon="admin-plugins"
>
<ToggleControl
checked={ exampleToggle }
help={ __( 'An example toggle.', 'wholesome-plugin') }
label={ __( 'Example Toggle', 'wholesome-plugin') }
onChange={ (exampleToggle) => this.setState( { exampleToggle }) }
/>
</PanelBody>
<Button
isPrimary
isLarge
onClick={ () => {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
} = this.state;
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_select' ]: exampleSelect,
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
[ 'wholesomecode_wholesome_plugin_example_text_2' ]: exampleText2,
[ 'wholesomecode_wholesome_plugin_example_text_3' ]: exampleText3,
[ 'wholesomecode_wholesome_plugin_example_toggle' ]: exampleToggle? 'true': '',
} );
settings.save();
dispatch('core/notices').createNotice(
'success',
__( 'Settings Saved', 'wholesome-plugin' ),
{
type: 'snackbar',
isDismissible: true,
}
);
}}
>
{ __( 'Save', 'wholesome-plugin') }
</Button>
</Panel>
</div>
<div className="wholesome-plugin__notices">
<Notices/>
</div>
</Fragment>) }
}
document.addEventListener( 'DOMContentLoaded', () => {
const htmlOutput = document.getElementById( 'wholesome-plugin-settings' );
if (htmlOutput) {
render(
<App />,
htmlOutput
);
}
});
Agregar el SCSS
Es hora de arreglar esos estilos. Simplemente agregue lo siguiente en el /src/admin.scssarchivo (que habría creado en la guía Agregar puntos de entrada a la guía Crear script de bloque) .
#wholesome-plugin-settings {
.components-placeholder {
background: #f1f1f1;
}
.wholesome-plugin__header {
background-color: #ffffff;
box-shadow: 0 1px 0 rgba(213, 213, 213, .5), 0 1px 2px #eeeeee;
margin-left: -2em;
padding: 20px 10px;
.wholesome-plugin__container {
margin: 0 auto;
max-width: 750px;
.wholesome-plugin__title {
align-items: center;
display: flex;
justify-content: center;
.dashicon {
color: #757575;
}
}
}
}
.wholesome-plugin__main {
margin-left: auto;
margin-right: auto;
max-width: 750px;
.components-panel {
background: none;
border: none;
}
.components-panel__body {
background: #ffffff;
border: 1px solid #e2e4e7;
margin: 1rem 0;
}
}
.components-base-control__help {
margin-top: .5rem;
}
.components-panel__row {
> div {
flex-grow: 1;
margin-right: 1rem;
&:last-of-type {
margin-right: 0;
}
}
}
.wholesome-plugin__notices {
.components-snackbar {
bottom: .5rem;
position: fixed;
}
}
}
Visualización de la página de configuración
Aquí esta el resultado final:
La página de configuración
Algunos complementos tienen un enlace de ‘configuración’ en la página de complementos como este:
Enlace de configuración en el panel de la página de configuración del complemento
Para lograr esto, agregue el siguiente bloque de código a la raíz de su archivo de complemento (en este caso wholesome-plugin.php):
function wholesomecode_wholesome_plugin_settings_link( $links ): array {
$label = esc_html__( 'Settings', 'wholesome-plugin' );
$slug = 'wholesome_plugin_settings';
array_unshift( $links, "<a href='options-general.php?page=$slug'>$label</a>" );
return $links;
}
add_action( 'plugin_action_links_'. plugin_basename( __FILE__ ), 'wholesomecode_wholesome_plugin_settings_link', 10 );
- Eche un vistazo a la creación de bloques secundarios anidados con el
InnerBlockscomponente - Eche un vistazo al uso de metacampos de publicación en bloques de Gutenberg
- Eche un vistazo a la creación de un panel de personalización con componentes de Gutenberg