├── .editorconfig ├── .gitignore ├── build ├── index.asset.php ├── index.js └── index.js.map ├── example-query-loop-button.php ├── package-lock.json ├── package.json ├── readme.md ├── readme.txt └── src └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # https://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | root = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = tab 15 | 16 | [*.{yml,yaml}] 17 | indent_style = space 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Coverage directory used by tools like istanbul 9 | coverage 10 | 11 | # Compiled binary addons (https://nodejs.org/api/addons.html) 12 | build/Release 13 | 14 | # Dependency directories 15 | node_modules/ 16 | 17 | # Optional npm cache directory 18 | .npm 19 | 20 | # Optional eslint cache 21 | .eslintcache 22 | 23 | # Output of `npm pack` 24 | *.tgz 25 | 26 | # Output of `wp-scripts plugin-zip` 27 | *.zip 28 | 29 | # dotenv environment variables file 30 | .env 31 | -------------------------------------------------------------------------------- /build/index.asset.php: -------------------------------------------------------------------------------- 1 | array('react', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-hooks', 'wp-i18n'), 'version' => 'e103e445eb33673d07a1'); 2 | -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | /******/ (() => { 2 | // webpackBootstrap 3 | /******/ "use strict"; 4 | /******/ var __webpack_modules__ = { 5 | /***/ react: 6 | /*!************************!*\ 7 | !*** external "React" ***! 8 | \************************/ 9 | /***/ (module) => { 10 | module.exports = window["React"]; 11 | 12 | /***/ 13 | }, 14 | 15 | /***/ "@wordpress/block-editor": 16 | /*!*************************************!*\ 17 | !*** external ["wp","blockEditor"] ***! 18 | \*************************************/ 19 | /***/ (module) => { 20 | module.exports = window["wp"]["blockEditor"]; 21 | 22 | /***/ 23 | }, 24 | 25 | /***/ "@wordpress/components": 26 | /*!************************************!*\ 27 | !*** external ["wp","components"] ***! 28 | \************************************/ 29 | /***/ (module) => { 30 | module.exports = window["wp"]["components"]; 31 | 32 | /***/ 33 | }, 34 | 35 | /***/ "@wordpress/compose": 36 | /*!*********************************!*\ 37 | !*** external ["wp","compose"] ***! 38 | \*********************************/ 39 | /***/ (module) => { 40 | module.exports = window["wp"]["compose"]; 41 | 42 | /***/ 43 | }, 44 | 45 | /***/ "@wordpress/hooks": 46 | /*!*******************************!*\ 47 | !*** external ["wp","hooks"] ***! 48 | \*******************************/ 49 | /***/ (module) => { 50 | module.exports = window["wp"]["hooks"]; 51 | 52 | /***/ 53 | }, 54 | 55 | /***/ "@wordpress/i18n": 56 | /*!******************************!*\ 57 | !*** external ["wp","i18n"] ***! 58 | \******************************/ 59 | /***/ (module) => { 60 | module.exports = window["wp"]["i18n"]; 61 | 62 | /***/ 63 | }, 64 | 65 | /******/ 66 | }; 67 | /************************************************************************/ 68 | /******/ // The module cache 69 | /******/ var __webpack_module_cache__ = {}; 70 | /******/ 71 | /******/ // The require function 72 | /******/ function __webpack_require__(moduleId) { 73 | /******/ // Check if module is in cache 74 | /******/ var cachedModule = __webpack_module_cache__[moduleId]; 75 | /******/ if (cachedModule !== undefined) { 76 | /******/ return cachedModule.exports; 77 | /******/ 78 | } 79 | /******/ // Create a new module (and put it into the cache) 80 | /******/ var module = (__webpack_module_cache__[moduleId] = { 81 | /******/ // no module.id needed 82 | /******/ // no module.loaded needed 83 | /******/ exports: {}, 84 | /******/ 85 | }); 86 | /******/ 87 | /******/ // Execute the module function 88 | /******/ __webpack_modules__[moduleId]( 89 | module, 90 | module.exports, 91 | __webpack_require__ 92 | ); 93 | /******/ 94 | /******/ // Return the exports of the module 95 | /******/ return module.exports; 96 | /******/ 97 | } 98 | /******/ 99 | /************************************************************************/ 100 | /******/ /* webpack/runtime/compat get default export */ 101 | /******/ (() => { 102 | /******/ // getDefaultExport function for compatibility with non-harmony modules 103 | /******/ __webpack_require__.n = (module) => { 104 | /******/ var getter = 105 | module && module.__esModule 106 | ? /******/ () => module["default"] 107 | : /******/ () => module; 108 | /******/ __webpack_require__.d(getter, { a: getter }); 109 | /******/ return getter; 110 | /******/ 111 | }; 112 | /******/ 113 | })(); 114 | /******/ 115 | /******/ /* webpack/runtime/define property getters */ 116 | /******/ (() => { 117 | /******/ // define getter functions for harmony exports 118 | /******/ __webpack_require__.d = (exports, definition) => { 119 | /******/ for (var key in definition) { 120 | /******/ if ( 121 | __webpack_require__.o(definition, key) && 122 | !__webpack_require__.o(exports, key) 123 | ) { 124 | /******/ Object.defineProperty(exports, key, { 125 | enumerable: true, 126 | get: definition[key], 127 | }); 128 | /******/ 129 | } 130 | /******/ 131 | } 132 | /******/ 133 | }; 134 | /******/ 135 | })(); 136 | /******/ 137 | /******/ /* webpack/runtime/hasOwnProperty shorthand */ 138 | /******/ (() => { 139 | /******/ __webpack_require__.o = (obj, prop) => 140 | Object.prototype.hasOwnProperty.call(obj, prop); 141 | /******/ 142 | })(); 143 | /******/ 144 | /******/ /* webpack/runtime/make namespace object */ 145 | /******/ (() => { 146 | /******/ // define __esModule on exports 147 | /******/ __webpack_require__.r = (exports) => { 148 | /******/ if (typeof Symbol !== "undefined" && Symbol.toStringTag) { 149 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { 150 | value: "Module", 151 | }); 152 | /******/ 153 | } 154 | /******/ Object.defineProperty(exports, "__esModule", { value: true }); 155 | /******/ 156 | }; 157 | /******/ 158 | })(); 159 | /******/ 160 | /************************************************************************/ 161 | var __webpack_exports__ = {}; 162 | // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. 163 | (() => { 164 | /*!**********************!*\ 165 | !*** ./src/index.js ***! 166 | \**********************/ 167 | __webpack_require__.r(__webpack_exports__); 168 | /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = 169 | __webpack_require__(/*! react */ "react"); 170 | /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = 171 | /*#__PURE__*/ __webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 172 | /* harmony import */ var _wordpress_hooks__WEBPACK_IMPORTED_MODULE_1__ = 173 | __webpack_require__(/*! @wordpress/hooks */ "@wordpress/hooks"); 174 | /* harmony import */ var _wordpress_hooks__WEBPACK_IMPORTED_MODULE_1___default = 175 | /*#__PURE__*/ __webpack_require__.n( 176 | _wordpress_hooks__WEBPACK_IMPORTED_MODULE_1__ 177 | ); 178 | /* harmony import */ var _wordpress_compose__WEBPACK_IMPORTED_MODULE_2__ = 179 | __webpack_require__(/*! @wordpress/compose */ "@wordpress/compose"); 180 | /* harmony import */ var _wordpress_compose__WEBPACK_IMPORTED_MODULE_2___default = 181 | /*#__PURE__*/ __webpack_require__.n( 182 | _wordpress_compose__WEBPACK_IMPORTED_MODULE_2__ 183 | ); 184 | /* harmony import */ var _wordpress_block_editor__WEBPACK_IMPORTED_MODULE_3__ = 185 | __webpack_require__( 186 | /*! @wordpress/block-editor */ "@wordpress/block-editor" 187 | ); 188 | /* harmony import */ var _wordpress_block_editor__WEBPACK_IMPORTED_MODULE_3___default = 189 | /*#__PURE__*/ __webpack_require__.n( 190 | _wordpress_block_editor__WEBPACK_IMPORTED_MODULE_3__ 191 | ); 192 | /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_4__ = 193 | __webpack_require__(/*! @wordpress/components */ "@wordpress/components"); 194 | /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_4___default = 195 | /*#__PURE__*/ __webpack_require__.n( 196 | _wordpress_components__WEBPACK_IMPORTED_MODULE_4__ 197 | ); 198 | /* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__ = 199 | __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); 200 | /* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5___default = 201 | /*#__PURE__*/ __webpack_require__.n( 202 | _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__ 203 | ); 204 | 205 | /** 206 | * Add the attribute to the block. 207 | * This is the attribute that will be saved to the database. 208 | * 209 | * @param {object} settings block settings 210 | * @param {string} name block name 211 | * @returns {object} modified settings 212 | * 213 | * @see https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/#blocks-registerblocktype 214 | */ 215 | (0, _wordpress_hooks__WEBPACK_IMPORTED_MODULE_1__.addFilter)( 216 | "blocks.registerBlockType", 217 | "wpdev/is-post-link", 218 | function (settings, name) { 219 | if (name !== "core/button") { 220 | return settings; 221 | } 222 | return { 223 | ...settings, 224 | attributes: { 225 | ...settings.attributes, 226 | isPostLink: { 227 | type: "string", 228 | default: "", 229 | }, 230 | customFieldName: { 231 | type: "string", 232 | default: "", 233 | }, 234 | }, 235 | }; 236 | } 237 | ); 238 | 239 | /** 240 | * Edit component for the block. 241 | * 242 | * @param {object} props block props 243 | * @returns {JSX} 244 | */ 245 | function Edit(props) { 246 | const setIsPostLink = (value) => { 247 | props.setAttributes({ 248 | isPostLink: value, 249 | }); 250 | }; 251 | const setCustomFieldName = (value) => { 252 | props.setAttributes({ 253 | customFieldName: value, 254 | }); 255 | }; 256 | return (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)( 257 | _wordpress_block_editor__WEBPACK_IMPORTED_MODULE_3__.InspectorControls, 258 | null, 259 | (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)( 260 | _wordpress_components__WEBPACK_IMPORTED_MODULE_4__.PanelBody, 261 | { 262 | title: (0, _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)( 263 | "Post Permalink" 264 | ), 265 | }, 266 | (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)( 267 | _wordpress_components__WEBPACK_IMPORTED_MODULE_4__.PanelRow, 268 | null, 269 | (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)( 270 | _wordpress_components__WEBPACK_IMPORTED_MODULE_4__.SelectControl, 271 | { 272 | label: (0, _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)( 273 | "Link this button to a post value?" 274 | ), 275 | value: props.attributes.isPostLink, 276 | options: [ 277 | { 278 | label: (0, _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)( 279 | "No" 280 | ), 281 | value: "", 282 | }, 283 | { 284 | label: (0, _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)( 285 | "Yes, to the permalink" 286 | ), 287 | value: "permalink", 288 | }, 289 | { 290 | label: (0, _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)( 291 | "Yes, to a custom field" 292 | ), 293 | value: "custom_field", 294 | }, 295 | ], 296 | onChange: setIsPostLink, 297 | help: (0, _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)( 298 | "This is mainly used for buttons inside query loops." 299 | ), 300 | } 301 | ) 302 | ), 303 | "custom_field" === props.attributes.isPostLink && 304 | (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)( 305 | _wordpress_components__WEBPACK_IMPORTED_MODULE_4__.PanelRow, 306 | null, 307 | (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)( 308 | _wordpress_components__WEBPACK_IMPORTED_MODULE_4__.TextControl, 309 | { 310 | label: (0, _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)( 311 | "Custom field name" 312 | ), 313 | value: props.attributes.customFieldName, 314 | onChange: setCustomFieldName, 315 | help: (0, _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)( 316 | "The name of the custom field to link to." 317 | ), 318 | } 319 | ) 320 | ) 321 | ) 322 | ); 323 | } 324 | 325 | /** 326 | * Add the edit component to the block. 327 | * This is the component that will be rendered in the editor. 328 | * It will be rendered after the original block edit component. 329 | * 330 | * @param {function} BlockEdit Original component 331 | * @returns {function} Wrapped component 332 | * 333 | * @see https://developer.wordpress.org/block-editor/developers/filters/block-filters/#editor-blockedit 334 | */ 335 | (0, _wordpress_hooks__WEBPACK_IMPORTED_MODULE_1__.addFilter)( 336 | "editor.BlockEdit", 337 | "wpdev/is-post-link", 338 | (0, 339 | _wordpress_compose__WEBPACK_IMPORTED_MODULE_2__.createHigherOrderComponent)( 340 | (BlockEdit) => { 341 | return (props) => { 342 | if (props.name !== "core/button") { 343 | return (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)( 344 | BlockEdit, 345 | { 346 | ...props, 347 | } 348 | ); 349 | } 350 | return (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)( 351 | react__WEBPACK_IMPORTED_MODULE_0__.Fragment, 352 | null, 353 | (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)(BlockEdit, { 354 | ...props, 355 | }), 356 | (0, react__WEBPACK_IMPORTED_MODULE_0__.createElement)(Edit, { 357 | ...props, 358 | }) 359 | ); 360 | }; 361 | } 362 | ) 363 | ); 364 | })(); 365 | 366 | /******/ 367 | })(); 368 | //# sourceMappingURL=index.js.map 369 | -------------------------------------------------------------------------------- /build/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","mappings":"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","sources":["webpack://example-query-loop-button/external window \"React\"","webpack://example-query-loop-button/external window [\"wp\",\"blockEditor\"]","webpack://example-query-loop-button/external window [\"wp\",\"components\"]","webpack://example-query-loop-button/external window [\"wp\",\"compose\"]","webpack://example-query-loop-button/external window [\"wp\",\"hooks\"]","webpack://example-query-loop-button/external window [\"wp\",\"i18n\"]","webpack://example-query-loop-button/webpack/bootstrap","webpack://example-query-loop-button/webpack/runtime/compat get default export","webpack://example-query-loop-button/webpack/runtime/define property getters","webpack://example-query-loop-button/webpack/runtime/hasOwnProperty shorthand","webpack://example-query-loop-button/webpack/runtime/make namespace object","webpack://example-query-loop-button/./src/index.js"],"sourcesContent":["module.exports = window[\"React\"];","module.exports = window[\"wp\"][\"blockEditor\"];","module.exports = window[\"wp\"][\"components\"];","module.exports = window[\"wp\"][\"compose\"];","module.exports = window[\"wp\"][\"hooks\"];","module.exports = window[\"wp\"][\"i18n\"];","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { addFilter } from \"@wordpress/hooks\";\nimport { createHigherOrderComponent } from \"@wordpress/compose\";\nimport { InspectorControls } from \"@wordpress/block-editor\";\nimport {\n\tSelectControl,\n\tTextControl,\n\tPanelBody,\n\tPanelRow,\n} from \"@wordpress/components\";\nimport { __ } from \"@wordpress/i18n\";\n\n/**\n * Add the attribute to the block.\n * This is the attribute that will be saved to the database.\n *\n * @param {object} settings block settings\n * @param {string} name block name\n * @returns {object} modified settings\n *\n * @see https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/#blocks-registerblocktype\n */\naddFilter(\n\t\"blocks.registerBlockType\",\n\t\"wpdev/is-post-link\",\n\tfunction (settings, name) {\n\t\tif (name !== \"core/button\") {\n\t\t\treturn settings;\n\t\t}\n\n\t\treturn {\n\t\t\t...settings,\n\t\t\tattributes: {\n\t\t\t\t...settings.attributes,\n\t\t\t\tisPostLink: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdefault: \"\",\n\t\t\t\t},\n\t\t\t\tcustomFieldName: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdefault: \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t},\n);\n\n/**\n * Edit component for the block.\n *\n * @param {object} props block props\n * @returns {JSX}\n */\nfunction Edit(props) {\n\tconst setIsPostLink = (value) => {\n\t\tprops.setAttributes({ isPostLink: value });\n\t};\n\n\tconst setCustomFieldName = (value) => {\n\t\tprops.setAttributes({ customFieldName: value });\n\t};\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t{\"custom_field\" === props.attributes.isPostLink && (\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t)}\n\t\t\t\n\t\t\n\t);\n}\n\n/**\n * Add the edit component to the block.\n * This is the component that will be rendered in the editor.\n * It will be rendered after the original block edit component.\n *\n * @param {function} BlockEdit Original component\n * @returns {function} Wrapped component\n *\n * @see https://developer.wordpress.org/block-editor/developers/filters/block-filters/#editor-blockedit\n */\naddFilter(\n\t\"editor.BlockEdit\",\n\t\"wpdev/is-post-link\",\n\tcreateHigherOrderComponent((BlockEdit) => {\n\t\treturn (props) => {\n\t\t\tif (props.name !== \"core/button\") {\n\t\t\t\treturn ;\n\t\t\t}\n\n\t\t\treturn (\n\t\t\t\t<>\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t);\n\t\t};\n\t}),\n);\n"],"names":["addFilter","createHigherOrderComponent","InspectorControls","SelectControl","TextControl","PanelBody","PanelRow","__","settings","name","attributes","isPostLink","type","default","customFieldName","Edit","props","setIsPostLink","value","setAttributes","setCustomFieldName","createElement","title","label","options","onChange","help","BlockEdit","Fragment"],"sourceRoot":""} 2 | -------------------------------------------------------------------------------- /example-query-loop-button.php: -------------------------------------------------------------------------------- 1 | next_tag( 'a' ) ) { 47 | $p->set_attribute( 'href', get_permalink() ); 48 | $p->set_attribute( 'rel', 'bookmark' ); 49 | $block_content = $p->get_updated_html(); 50 | } 51 | } 52 | if ( isset( $block['attrs']['isPostLink'] ) && 'custom_field' === $block['attrs']['isPostLink'] && isset( $block['attrs']['customFieldName'] ) ) { 53 | $p = new WP_HTML_Tag_Processor( $block_content ); 54 | if ( $p->next_tag( 'a' ) ) { 55 | $p->set_attribute( 'href', get_post_meta( get_the_ID(), $block['attrs']['customFieldName'], true ) ); 56 | $block_content = $p->get_updated_html(); 57 | } 58 | } 59 | 60 | return $block_content; 61 | } 62 | add_filter( 'render_block_core/button', __NAMESPACE__ . '\filter_button_block_render_block', 10, 2 ); 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-query-loop-button", 3 | "version": "0.1.0", 4 | "description": "Example block extension for the WordPress core/button block.", 5 | "author": "The WordPress Contributors", 6 | "license": "GPL-2.0-or-later", 7 | "main": "build/index.js", 8 | "scripts": { 9 | "build": "wp-scripts build", 10 | "format": "wp-scripts format", 11 | "lint:css": "wp-scripts lint-style", 12 | "lint:js": "wp-scripts lint-js", 13 | "packages-update": "wp-scripts packages-update", 14 | "plugin-zip": "wp-scripts plugin-zip", 15 | "start": "wp-scripts start" 16 | }, 17 | "devDependencies": { 18 | "@wordpress/scripts": "^26.17.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Example Query Loop Button Extension 2 | 3 | Extends the button block to allow you to populate the `href` value automatically with the permalink or to a custom meta field. This is especially useful in the query loop, where you may want a custom button for your read more link. 4 | 5 | [🎥 The full tutorial](https://www.briancoords.com/adding-custom-fields-attributes-to-core-blocks/). 6 | 7 | Xnapper-2023-11-28-21 31 38 8 | 9 | This repo is part of a tutorial that teaches you how to: 10 | 11 | 1. Add custom attributes (aka custom fields) to a core block, and 12 | 2. Modify the final frontend markup of the block based on the attribute values 13 | 14 | 15 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Example Query Loop Button === 2 | Contributors: bacoords 3 | Tags: block 4 | Tested up to: 6.4.1 5 | Stable tag: 0.1.0 6 | License: GPL-2.0-or-later 7 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 8 | 9 | Example block extension for the WordPress core/button block 10 | 11 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { addFilter } from "@wordpress/hooks"; 2 | import { createHigherOrderComponent } from "@wordpress/compose"; 3 | import { InspectorControls } from "@wordpress/block-editor"; 4 | import { 5 | SelectControl, 6 | TextControl, 7 | PanelBody, 8 | PanelRow, 9 | } from "@wordpress/components"; 10 | import { __ } from "@wordpress/i18n"; 11 | 12 | /** 13 | * Add the attribute to the block. 14 | * This is the attribute that will be saved to the database. 15 | * 16 | * @param {object} settings block settings 17 | * @param {string} name block name 18 | * @returns {object} modified settings 19 | * 20 | * @see https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/#blocks-registerblocktype 21 | */ 22 | addFilter( 23 | "blocks.registerBlockType", 24 | "wpdev/is-post-link", 25 | function (settings, name) { 26 | if (name !== "core/button") { 27 | return settings; 28 | } 29 | 30 | return { 31 | ...settings, 32 | attributes: { 33 | ...settings.attributes, 34 | isPostLink: { 35 | type: "string", 36 | default: "", 37 | }, 38 | customFieldName: { 39 | type: "string", 40 | default: "", 41 | }, 42 | }, 43 | }; 44 | } 45 | ); 46 | 47 | /** 48 | * Edit component for the block. 49 | * 50 | * @param {object} props block props 51 | * @returns {JSX} 52 | */ 53 | function Edit(props) { 54 | const setIsPostLink = (value) => { 55 | props.setAttributes({ isPostLink: value }); 56 | }; 57 | 58 | const setCustomFieldName = (value) => { 59 | props.setAttributes({ customFieldName: value }); 60 | }; 61 | 62 | return ( 63 | 64 | 65 | 66 | 77 | 78 | {"custom_field" === props.attributes.isPostLink && ( 79 | 80 | 86 | 87 | )} 88 | 89 | 90 | ); 91 | } 92 | 93 | /** 94 | * Add the edit component to the block. 95 | * This is the component that will be rendered in the editor. 96 | * It will be rendered after the original block edit component. 97 | * 98 | * @param {function} BlockEdit Original component 99 | * @returns {function} Wrapped component 100 | * 101 | * @see https://developer.wordpress.org/block-editor/developers/filters/block-filters/#editor-blockedit 102 | */ 103 | addFilter( 104 | "editor.BlockEdit", 105 | "wpdev/is-post-link", 106 | createHigherOrderComponent((BlockEdit) => { 107 | return (props) => { 108 | if (props.name !== "core/button") { 109 | return ; 110 | } 111 | 112 | return ( 113 | <> 114 | 115 | 116 | 117 | ); 118 | }; 119 | }) 120 | ); 121 | --------------------------------------------------------------------------------