diff --git a/ACTIONS_AND_FILTERS.md b/ACTIONS_AND_FILTERS.md new file mode 100644 index 0000000..8f3c720 --- /dev/null +++ b/ACTIONS_AND_FILTERS.md @@ -0,0 +1,24 @@ +# Actions & Filters + +-> [Original documentation](https://www.wpgraphql.com/docs/customizing-wpgraphiql) + +_legend_ 🎉 = new + +## PHP Actions + +- `wpgraphqlide_enqueue_script` ([enqueue_graphiql_extension](https://www.wpgraphql.com/docs/customizing-wpgraphiql#enqueue_graphiql_extension)) + +## PHP Filters + +- `wpgraphqlide_capability_required` 🎉 +- `wpgraphqlide_context` 🎉 +- `wpgraphqlide_external_fragments` ([graphiql_external_fragments](https://www.wpgraphql.com/docs/customizing-wpgraphiql#graphiql_external_fragments)) + +## JavaScript Actions + +- `wpgraphqlide_destroyed` 🎉 +- `wpgraphqlide_rendered` ([graphiql_rendered](https://www.wpgraphql.com/docs/customizing-wpgraphiql#graphiql_rendered)) + +## JavaScript Filters + +... diff --git a/README.md b/README.md index e5ef51b..8fab46a 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,7 @@ GraphiQL IDE has intentionally been updated to read "GraphQL IDE", which feels m ``` Builds are only required for JS updates as the plugin's CSS is directly enqueued. + +## Custom Hooks + +See [ACTIONS_AND_FILTERS.md]. diff --git a/package-lock.json b/package-lock.json index da71d03..4851495 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "dependencies": { "@wordpress/element": "^5.23.0", + "@wordpress/hooks": "^3.49.0", "graphiql": "^3.0.10", "graphql-ws": "^5.14.2", "vaul": "^0.7.9" @@ -5546,10 +5547,9 @@ } }, "node_modules/@wordpress/hooks": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.46.0.tgz", - "integrity": "sha512-TTYNZwMZeATpkWmvAoShP43UONd/WPNTtsy1czMSyiqPzFhzGJbKD75CdJtPp5DqIAiuWQEuDmcxRAPcZ/1Qgw==", - "dev": true, + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.49.0.tgz", + "integrity": "sha512-GH546Jg8u/rw9I3fsvAhidwt8rUFNmkdXGByIPGsN3R6y+QwWMXPzsnoYdFmFOmDK9gOGCRDe5bXHikoWnaiKA==", "dependencies": { "@babel/runtime": "^7.16.0" }, diff --git a/package.json b/package.json index c73f6ca..972fb26 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@wordpress/element": "^5.23.0", + "@wordpress/hooks": "^3.49.0", "graphiql": "^3.0.10", "graphql-ws": "^5.14.2", "vaul": "^0.7.9" diff --git a/src/App.jsx b/src/App.jsx index 1e34dda..fa02afb 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,15 +1,45 @@ -import { useState } from '@wordpress/element'; +import { useState, useEffect } from '@wordpress/element'; import { EditorDrawer } from './components/EditorDrawer'; import { Editor } from './components/Editor'; +// Assuming wp.hooks is globally available through WordPress's script enqueueing mechanism. +const { doAction } = wp.hooks; + +/** + * The main application component. + * + * @returns {JSX.Element} The application component. + */ export function App() { - const [ drawerOpen, setDrawerOpen ] = useState( false ); - - return ( -
- - - -
- ); + const [drawerOpen, setDrawerOpen] = useState(false); + + useEffect(() => { + /** + * Perform actions on component mount. + * + * Triggers a custom action 'wpgraphqlide_rendered' when the App component mounts, + * allowing plugins or themes to hook into this event. The action passes + * the current state of `drawerOpen` to any listeners, providing context + * about the application's UI state. + */ + doAction('wpgraphqlide_rendered', drawerOpen); + + /** + * Cleanup action on component unmount. + * + * Returns a cleanup function that triggers the 'wpgraphqlide_destroyed' action, + * signaling that the App component is about to unmount. This allows for + * any necessary cleanup or teardown operations in response to the App + * component's lifecycle. + */ + return () => doAction('wpgraphqlide_destroyed'); + }, [drawerOpen]); + + return ( +
+ + + +
+ ); } diff --git a/src/index.js b/src/index.js index 4732918..59db144 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,7 @@ /* global WPGRAPHQL_IDE_DATA */ import { createRoot } from '@wordpress/element'; +import { createHooks } from '@wordpress/hooks'; + import { App } from './App'; /** @@ -18,5 +20,8 @@ if ( rootElement ) { root.render( ); } +// Initialize hook system. +App.hooks = createHooks(); + // Expose app as a global variable to utilize in gutenberg. window.WPGraphQLIDE = App; diff --git a/wpgraphql-ide.php b/wpgraphql-ide.php index d66d1d3..f2f1d58 100644 --- a/wpgraphql-ide.php +++ b/wpgraphql-ide.php @@ -121,7 +121,9 @@ function enqueue_react_app_with_styles(): void { 'wp-i18n', ]; - $version = plugin_version(); + $app_context = get_app_context(); + + $version = get_plugin_header( 'Version' ); wp_enqueue_script( 'wpgraphql-ide-app', @@ -138,28 +140,52 @@ function enqueue_react_app_with_styles(): void { 'nonce' => wp_create_nonce( 'wp_rest' ), 'graphqlEndpoint' => trailingslashit( site_url() ) . 'index.php?' . \WPGraphQL\Router::$route, 'rootElementId' => WPGRAPHQL_IDE_ROOT_ELEMENT_ID, + 'context' => $app_context, ] ); wp_enqueue_style( 'wpgraphql-ide-app', plugins_url( 'build/index.css', __FILE__ ), [], $version ); // Avoid running custom styles through a build process for an improved developer experience. wp_enqueue_style( 'wpgraphql-ide', plugins_url( 'styles/wpgraphql-ide.css', __FILE__ ), [], $version ); + + // Extensions looking to extend GraphiQL can hook in here, + // after the window object is established, but before the App renders + do_action( 'wpgraphqlide_enqueue_script', $app_context ); } add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\\enqueue_react_app_with_styles' ); add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\\enqueue_react_app_with_styles' ); /** - * Retrieves the version of the current plugin. + * Retrieves the specific header of this plugin. * - * @return string The version number of the plugin. Returns an empty string if the version is not found. + * @param string The plugin data key. + * @return string|null The version number of the plugin. Returns an empty string if the version is not found. */ -function plugin_version(): string { +function get_plugin_header( $key = '' ): ?string { if ( ! function_exists( 'get_plugin_data' ) ) { require_once( ABSPATH . 'wp-admin/includes/plugin.php' ); } + if ( empty( $key ) ) { + return null; + } + $plugin_data = get_plugin_data( __FILE__ ); - $version = $plugin_data['Version']; - return $version; + return $plugin_data[ $key ] ?? null; +} + +/** + * Retrieves app context. + * + * @return array The possibly filtered app context array. + */ +function get_app_context() { + $context = apply_filters( 'wpgraphqlide_context', [ + 'pluginVersion' => get_plugin_header( 'Version' ), + 'pluginName' => get_plugin_header( 'Name' ), + 'externalFragments' => apply_filters( 'wpgraphqlide_external_fragments', [] ) + ]); + + return $context; }