├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── build ├── post-type │ ├── block.json │ ├── index.asset.php │ ├── index.js │ ├── index.js.map │ └── render.php └── taxonomy │ ├── block.json │ ├── index-rtl.css │ ├── index.asset.php │ ├── index.css │ ├── index.css.map │ ├── index.js │ ├── index.js.map │ ├── render.php │ ├── view.asset.php │ ├── view.js │ └── view.js.map ├── composer.json ├── inc └── namespace.php ├── package-lock.json ├── package.json ├── query-filter.php └── src ├── post-type ├── block.json ├── edit.js ├── index.js └── render.php └── taxonomy ├── block.json ├── edit.js ├── index.js ├── render.php ├── style-index.css └── view.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | /vendor/ 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing. 2 | 3 | ## Building assets 4 | 5 | Run `npm run build` to build production assets, or `npm run start` whilst actively working on the plugin., 6 | 7 | There is currently no automated process to build assets when releasing, so please commit updated build files as part of any pull request. This is something we can revisit later if it becomes an issue. 8 | 9 | ## Releasing a new version 10 | 11 | [Releases are managed using Github.](https://github.com/humanmade/query-filter/releases). 12 | 13 | Create a new release setting the tag to the desired version number (Follow semver for major/minor releases). Target should be main and release notes can be auto-generated. Use the version number as the release title. 14 | 15 | Once a release is created, it will be published automatically on Packagist. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Query Loop Filters 2 | 3 | ![image](https://github.com/user-attachments/assets/85358de8-0929-47fe-85f5-b53a59fb522e) 4 | 5 | This plugin allows you to easily add filters to any query loop block. 6 | 7 | Provides 2 new blocks that can be added within a query loop block to allow filtering by either post type or a taxonomy. Also supports using the core search block to allow you to search. 8 | 9 | Compatible with both the core query loop block and the [Advanced query loop plugin](https://wordpress.org/plugins/advanced-query-loop/) (In fact, in order to use post type filters, use of the Advanced Query Loop plugin is required). 10 | 11 | Easy to use and lightweight, built using the WordPress Interactivity API. 12 | 13 | ## Usage 14 | 15 | * Add a query block. This can anyhere that the query block is supported e.g. page, template, or pattern. 16 | * Add one of the filter blocks and configure as required: 17 | * Taxonomy filter. Select which taxonomy to to use, customise the label (and whether it's shown), and customise the text used when none is selected. 18 | * Post type filter. Customise the label (and whether it's shown), as well as the text used when no filter is applied. 19 | * Search block. No extra options. 20 | 21 | ![image](https://github.com/user-attachments/assets/e2f9b62d-91f7-4c22-87ac-078b4d031a60) 22 | 23 | ## Installation 24 | 25 | 1. Download the plugin from the [GitHub repository](https://github.com/humanmade/query-filter). 26 | 2. Upload the plugin to your site's `wp-content/plugins` directory. 27 | 3. Activate the plugin from the WordPress admin. 28 | -------------------------------------------------------------------------------- /build/post-type/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schemas.wp.org/trunk/block.json", 3 | "apiVersion": 2, 4 | "name": "query-filter/post-type", 5 | "version": "0.1.0", 6 | "title": "Post Type Filter", 7 | "category": "theme", 8 | "icon": "filter", 9 | "description": "Allows users to filter by post type when placed wihin a query loop block", 10 | "ancestor": [ 11 | "core/query" 12 | ], 13 | "usesContext": [ 14 | "queryId", 15 | "query" 16 | ], 17 | "supports": { 18 | "html": false, 19 | "className": true, 20 | "customClassName": true, 21 | "color": { 22 | "background": true, 23 | "text": true 24 | }, 25 | "typography": { 26 | "fontSize": true, 27 | "textAlign": true, 28 | "lineHeight": true, 29 | "__experimentalFontFamily": true, 30 | "__experimentalFontWeight": true, 31 | "__experimentalFontStyle": true, 32 | "__experimentalTextTransform": true, 33 | "__experimentalTextDecoration": true, 34 | "__experimentalLetterSpacing": true, 35 | "__experimentalDefaultControls": { 36 | "fontSize": true 37 | } 38 | }, 39 | "spacing": { 40 | "margin": true, 41 | "padding": true, 42 | "blockGap": true 43 | }, 44 | "interactivity": { 45 | "clientNavigation": true 46 | } 47 | }, 48 | "attributes": { 49 | "emptyLabel": { 50 | "type": "string", 51 | "default": "" 52 | }, 53 | "label": { 54 | "type": "string" 55 | }, 56 | "showLabel": { 57 | "type": "boolean", 58 | "default": true 59 | } 60 | }, 61 | "textdomain": "query-filter", 62 | "editorScript": "file:./index.js", 63 | "viewScriptModule": "query-filter-taxonomy-view-script-module", 64 | "style": "query-filter-view", 65 | "render": "file:./render.php" 66 | } -------------------------------------------------------------------------------- /build/post-type/index.asset.php: -------------------------------------------------------------------------------- 1 | array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-i18n'), 'version' => 'ad8227c21a432607ccaf'); 2 | -------------------------------------------------------------------------------- /build/post-type/index.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";const e=window.wp.blocks,l=window.wp.i18n,t=window.wp.blockEditor,r=window.wp.components,o=window.wp.data,s=window.ReactJSXRuntime,i=JSON.parse('{"UU":"query-filter/post-type"}');(0,e.registerBlockType)(i.UU,{edit:function({attributes:e,setAttributes:i,context:n}){const{emptyLabel:a,label:p,showLabel:c}=e,u=(0,o.useSelect)((e=>(e("core").getPostTypes({per_page:100})||[]).filter((e=>e.viewable))||[]),[]);let y=(n.query.postType||"").split(",").map((e=>e.trim()));Array.isArray(n.query.multiple_posts)&&(y=y.concat(n.query.multiple_posts));const _=y.map((e=>u.find((l=>l.slug===e))||{slug:e,name:e}));return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.InspectorControls,{children:(0,s.jsxs)(r.PanelBody,{title:(0,l.__)("Post Type Settings","query-filter"),children:[(0,s.jsx)(r.TextControl,{label:(0,l.__)("Label","query-filter"),value:p,defaultValue:(0,l.__)("Content Type","query-filter"),help:(0,l.__)("If empty then no label will be shown","query-filter"),onChange:e=>i({label:e})}),(0,s.jsx)(r.ToggleControl,{label:(0,l.__)("Show Label","query-filter"),checked:c,onChange:e=>i({showLabel:e})}),(0,s.jsx)(r.TextControl,{label:(0,l.__)("Empty Choice Label","query-filter"),value:a,placeholder:(0,l.__)("All","query-filter"),onChange:e=>i({emptyLabel:e})})]})}),(0,s.jsxs)("div",{...(0,t.useBlockProps)({className:"wp-block-query-filter"}),children:[c&&(0,s.jsx)("label",{className:"wp-block-query-filter-post-type__label wp-block-query-filter__label",children:p||(0,l.__)("Content Type","query-filter")}),(0,s.jsxs)("select",{className:"wp-block-query-filter-post-type__select wp-block-query-filter__select",inert:!0,children:[(0,s.jsx)("option",{children:a||(0,l.__)("All","query-filter")}),_.map((e=>(0,s.jsx)("option",{children:e.name},e.slug)))]})]})]})}})})(); -------------------------------------------------------------------------------- /build/post-type/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"post-type/index.js","mappings":"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","sources":["webpack://query-loop-filter/./src/post-type/edit.js","webpack://query-loop-filter/external window \"ReactJSXRuntime\"","webpack://query-loop-filter/external window [\"wp\",\"blockEditor\"]","webpack://query-loop-filter/external window [\"wp\",\"blocks\"]","webpack://query-loop-filter/external window [\"wp\",\"components\"]","webpack://query-loop-filter/external window [\"wp\",\"data\"]","webpack://query-loop-filter/external window [\"wp\",\"i18n\"]","webpack://query-loop-filter/webpack/bootstrap","webpack://query-loop-filter/webpack/runtime/compat get default export","webpack://query-loop-filter/webpack/runtime/define property getters","webpack://query-loop-filter/webpack/runtime/hasOwnProperty shorthand","webpack://query-loop-filter/webpack/runtime/make namespace object","webpack://query-loop-filter/./src/post-type/index.js"],"sourcesContent":["import { __ } from '@wordpress/i18n';\nimport { useBlockProps, InspectorControls } from '@wordpress/block-editor';\nimport { PanelBody, TextControl, ToggleControl } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\n\nexport default function Edit( { attributes, setAttributes, context } ) {\n\tconst { emptyLabel, label, showLabel } = attributes;\n\n\tconst allPostTypes = useSelect( ( select ) => {\n\t\treturn (\n\t\t\t( select( 'core' ).getPostTypes( { per_page: 100 } ) || [] ).filter(\n\t\t\t\t( type ) => type.viewable\n\t\t\t) || []\n\t\t);\n\t}, [] );\n\n\tlet contextPostTypes = ( context.query.postType || '' )\n\t\t.split( ',' )\n\t\t.map( ( type ) => type.trim() );\n\n\t// Support for enhanced query loop block plugin.\n\tif ( Array.isArray( context.query.multiple_posts ) ) {\n\t\tcontextPostTypes = contextPostTypes.concat(\n\t\t\tcontext.query.multiple_posts\n\t\t);\n\t}\n\n\tconst postTypes = contextPostTypes.map( ( postType ) => {\n\t\treturn (\n\t\t\tallPostTypes.find( ( type ) => type.slug === postType ) || {\n\t\t\t\tslug: postType,\n\t\t\t\tname: postType,\n\t\t\t}\n\t\t);\n\t} );\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t setAttributes( { label } ) }\n\t\t\t\t\t/>\n\t\t\t\t\t\n\t\t\t\t\t\t\tsetAttributes( { showLabel } )\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t\n\t\t\t\t\t\t\tsetAttributes( { emptyLabel } )\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\t
\n\t\t\t\t{ showLabel && (\n\t\t\t\t\t\n\t\t\t\t) }\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{ postTypes.map( ( type ) => (\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","module.exports = window[\"ReactJSXRuntime\"];","module.exports = window[\"wp\"][\"blockEditor\"];","module.exports = window[\"wp\"][\"blocks\"];","module.exports = window[\"wp\"][\"components\"];","module.exports = window[\"wp\"][\"data\"];","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 { registerBlockType } from '@wordpress/blocks';\nimport Edit from './edit';\nimport metadata from './block.json';\n\nregisterBlockType( metadata.name, {\n\t/**\n\t * @see ./edit.js\n\t */\n\tedit: Edit,\n} );\n"],"names":["__","useBlockProps","InspectorControls","PanelBody","TextControl","ToggleControl","useSelect","jsx","_jsx","jsxs","_jsxs","Fragment","_Fragment","Edit","attributes","setAttributes","context","emptyLabel","label","showLabel","allPostTypes","select","getPostTypes","per_page","filter","type","viewable","contextPostTypes","query","postType","split","map","trim","Array","isArray","multiple_posts","concat","postTypes","find","slug","name","children","title","value","defaultValue","help","onChange","checked","placeholder","className","inert","registerBlockType","metadata","edit"],"sourceRoot":""} -------------------------------------------------------------------------------- /build/post-type/render.php: -------------------------------------------------------------------------------- 1 | context['query']['inherit'] ) { 7 | $query_var = 'query-post_type'; 8 | $page_var = 'page'; 9 | $base_url = str_replace( '/page/' . get_query_var( 'paged' ), '', remove_query_arg( [ $query_var, $page_var ] ) ); 10 | } else { 11 | $query_id = $block->context['queryId'] ?? 0; 12 | $query_var = sprintf( 'query-%d-post_type', $query_id ); 13 | $page_var = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page'; 14 | $base_url = remove_query_arg( [ $query_var, $page_var ] ); 15 | } 16 | 17 | $post_types = array_map( 'trim', explode( ',', $block->context['query']['postType'] ?? 'post' ) ); 18 | 19 | // Support for enhanced query block. 20 | if ( isset( $block->context['query']['multiple_posts'] ) && is_array( $block->context['query']['multiple_posts'] ) ) { 21 | $post_types = array_merge( $post_types, $block->context['query']['multiple_posts'] ); 22 | } 23 | 24 | // Fill in inherited query types. 25 | if ( $block->context['query']['inherit'] ) { 26 | $inherited_post_types = $wp_query->get( 'query-filter-post_type' ) === 'any' 27 | ? get_post_types( [ 'public' => true, 'exclude_from_search' => false ] ) 28 | : (array) $wp_query->get( 'query-filter-post_type' ); 29 | 30 | $post_types = array_merge( $post_types, $inherited_post_types ); 31 | if ( ! get_option( 'wp_attachment_pages_enabled' ) ) { 32 | $post_types = array_diff( $post_types, [ 'attachment' ] ); 33 | } 34 | } 35 | 36 | $post_types = array_unique( $post_types ); 37 | $post_types = array_map( 'get_post_type_object', $post_types ); 38 | 39 | if ( empty( $post_types ) ) { 40 | return; 41 | } 42 | ?> 43 | 44 |
'wp-block-query-filter' ] ); ?> data-wp-interactive="query-filter" data-wp-context="{}"> 45 | 48 | 54 |
55 | -------------------------------------------------------------------------------- /build/taxonomy/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schemas.wp.org/trunk/block.json", 3 | "apiVersion": 3, 4 | "name": "query-filter/taxonomy", 5 | "version": "0.1.0", 6 | "title": "Taxonomy Filter", 7 | "category": "theme", 8 | "icon": "filter", 9 | "description": "Allows users to filter by taxonomy terms when placed wihin a query loop block", 10 | "ancestor": [ 11 | "core/query" 12 | ], 13 | "usesContext": [ 14 | "queryId", 15 | "query" 16 | ], 17 | "supports": { 18 | "html": false, 19 | "className": true, 20 | "customClassName": true, 21 | "color": { 22 | "background": true, 23 | "text": true 24 | }, 25 | "typography": { 26 | "fontSize": true, 27 | "textAlign": true, 28 | "lineHeight": true, 29 | "__experimentalFontFamily": true, 30 | "__experimentalFontWeight": true, 31 | "__experimentalFontStyle": true, 32 | "__experimentalTextTransform": true, 33 | "__experimentalTextDecoration": true, 34 | "__experimentalLetterSpacing": true, 35 | "__experimentalDefaultControls": { 36 | "fontSize": true 37 | } 38 | }, 39 | "spacing": { 40 | "margin": true, 41 | "padding": true, 42 | "blockGap": true 43 | }, 44 | "interactivity": { 45 | "clientNavigation": true 46 | } 47 | }, 48 | "attributes": { 49 | "taxonomy": { 50 | "type": "string" 51 | }, 52 | "emptyLabel": { 53 | "type": "string", 54 | "default": "" 55 | }, 56 | "label": { 57 | "type": "string" 58 | }, 59 | "showLabel": { 60 | "type": "boolean", 61 | "default": true 62 | } 63 | }, 64 | "textdomain": "query-filter", 65 | "editorScript": "file:./index.js", 66 | "style": "query-filter-view", 67 | "viewScriptModule": "file:./view.js", 68 | "render": "file:./render.php" 69 | } -------------------------------------------------------------------------------- /build/taxonomy/index-rtl.css: -------------------------------------------------------------------------------- 1 | @view-transition{navigation:auto}.wp-block-query-filter{display:flex;flex-direction:column;justify-content:stretch} 2 | -------------------------------------------------------------------------------- /build/taxonomy/index.asset.php: -------------------------------------------------------------------------------- 1 | array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-i18n'), 'version' => 'f1456d24ac8e3da497aa'); 2 | -------------------------------------------------------------------------------- /build/taxonomy/index.css: -------------------------------------------------------------------------------- 1 | @view-transition{navigation:auto}.wp-block-query-filter{display:flex;flex-direction:column;justify-content:stretch} 2 | -------------------------------------------------------------------------------- /build/taxonomy/index.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"taxonomy/index.css","mappings":";;;AAAA;CACC,gBAAgB;AACjB;;AAEA;CACC,aAAa;CACb,sBAAsB;CACtB,wBAAwB;AACzB","sources":["webpack://query-loop-filter/./src/taxonomy/style-index.css"],"sourcesContent":["@view-transition {\n\tnavigation: auto;\n}\n\n.wp-block-query-filter {\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: stretch;\n}\n"],"names":[],"sourceRoot":""} -------------------------------------------------------------------------------- /build/taxonomy/index.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";const e=window.wp.blocks,l=window.wp.i18n,t=window.wp.blockEditor,o=window.wp.components,n=window.wp.data,r=window.ReactJSXRuntime,a=JSON.parse('{"UU":"query-filter/taxonomy"}');(0,e.registerBlockType)(a.UU,{edit:function({attributes:e,setAttributes:a}){const{taxonomy:i,emptyLabel:s,label:c,showLabel:u}=e,b=(0,n.useSelect)((e=>{const l=(e("core").getTaxonomies({per_page:100})||[]).filter((e=>e.visibility.publicly_queryable));return l&&l.length>0&&!i&&a({taxonomy:l[0].slug,label:l[0].name}),l}),[i]),y=(0,n.useSelect)((e=>e("core").getEntityRecords("taxonomy",i,{number:50})||[]),[i]);return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(t.InspectorControls,{children:(0,r.jsxs)(o.PanelBody,{title:(0,l.__)("Taxonomy Settings","query-filter"),children:[(0,r.jsx)(o.SelectControl,{label:(0,l.__)("Select Taxonomy","query-filter"),value:i,options:(b||[]).map((e=>({label:e.name,value:e.slug}))),onChange:e=>a({taxonomy:e,label:b.find((l=>l.slug===e)).name})}),(0,r.jsx)(o.TextControl,{label:(0,l.__)("Label","query-filter"),value:c,help:(0,l.__)("If empty then no label will be shown","query-filter"),onChange:e=>a({label:e})}),(0,r.jsx)(o.ToggleControl,{label:(0,l.__)("Show Label","query-filter"),checked:u,onChange:e=>a({showLabel:e})}),(0,r.jsx)(o.TextControl,{label:(0,l.__)("Empty Choice Label","query-filter"),value:s,placeholder:(0,l.__)("All","query-filter"),onChange:e=>a({emptyLabel:e})})]})}),(0,r.jsxs)("div",{...(0,t.useBlockProps)({className:"wp-block-query-filter"}),children:[u&&(0,r.jsx)("label",{className:"wp-block-query-filter-taxonomy__label wp-block-query-filter__label",children:c}),(0,r.jsxs)("select",{className:"wp-block-query-filter-taxonomy__select wp-block-query-filter__select",inert:!0,children:[(0,r.jsx)("option",{children:s||(0,l.__)("All","query-filter")}),y.map((e=>(0,r.jsx)("option",{children:e.name},e.slug)))]})]})]})}})})(); -------------------------------------------------------------------------------- /build/taxonomy/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"taxonomy/index.js","mappings":"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","sources":["webpack://query-loop-filter/./src/taxonomy/edit.js","webpack://query-loop-filter/./src/taxonomy/style-index.css?27a5","webpack://query-loop-filter/external window \"ReactJSXRuntime\"","webpack://query-loop-filter/external window [\"wp\",\"blockEditor\"]","webpack://query-loop-filter/external window [\"wp\",\"blocks\"]","webpack://query-loop-filter/external window [\"wp\",\"components\"]","webpack://query-loop-filter/external window [\"wp\",\"data\"]","webpack://query-loop-filter/external window [\"wp\",\"i18n\"]","webpack://query-loop-filter/webpack/bootstrap","webpack://query-loop-filter/webpack/runtime/compat get default export","webpack://query-loop-filter/webpack/runtime/define property getters","webpack://query-loop-filter/webpack/runtime/hasOwnProperty shorthand","webpack://query-loop-filter/webpack/runtime/make namespace object","webpack://query-loop-filter/./src/taxonomy/index.js"],"sourcesContent":["import { __ } from '@wordpress/i18n';\nimport { useBlockProps, InspectorControls } from '@wordpress/block-editor';\nimport {\n\tPanelBody,\n\tSelectControl,\n\tTextControl,\n\tToggleControl,\n} from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\n\nexport default function Edit( { attributes, setAttributes } ) {\n\tconst { taxonomy, emptyLabel, label, showLabel } = attributes;\n\n\tconst taxonomies = useSelect(\n\t\t( select ) => {\n\t\t\tconst results = (\n\t\t\t\tselect( 'core' ).getTaxonomies( { per_page: 100 } ) || []\n\t\t\t).filter( ( taxonomy ) => taxonomy.visibility.public );\n\n\t\t\tif ( results && results.length > 0 && ! taxonomy ) {\n\t\t\t\tsetAttributes( {\n\t\t\t\t\ttaxonomy: results[ 0 ].slug,\n\t\t\t\t\tlabel: results[ 0 ].name,\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn results;\n\t\t},\n\t\t[ taxonomy ]\n\t);\n\n\tconst terms = useSelect(\n\t\t( select ) => {\n\t\t\treturn (\n\t\t\t\tselect( 'core' ).getEntityRecords( 'taxonomy', taxonomy, {\n\t\t\t\t\tnumber: 50,\n\t\t\t\t} ) || []\n\t\t\t);\n\t\t},\n\t\t[ taxonomy ]\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\t\t\tlabel: taxonomy.name,\n\t\t\t\t\t\t\tvalue: taxonomy.slug,\n\t\t\t\t\t\t} ) ) }\n\t\t\t\t\t\tonChange={ ( taxonomy ) =>\n\t\t\t\t\t\t\tsetAttributes( {\n\t\t\t\t\t\t\t\ttaxonomy,\n\t\t\t\t\t\t\t\tlabel: taxonomies.find(\n\t\t\t\t\t\t\t\t\t( tax ) => tax.slug === taxonomy\n\t\t\t\t\t\t\t\t).name,\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t setAttributes( { label } ) }\n\t\t\t\t\t/>\n\t\t\t\t\t\n\t\t\t\t\t\t\tsetAttributes( { showLabel } )\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t\n\t\t\t\t\t\t\tsetAttributes( { emptyLabel } )\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\t
\n\t\t\t\t{ showLabel && (\n\t\t\t\t\t\n\t\t\t\t) }\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{ terms.map( ( term ) => (\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","// extracted by mini-css-extract-plugin\nexport {};","module.exports = window[\"ReactJSXRuntime\"];","module.exports = window[\"wp\"][\"blockEditor\"];","module.exports = window[\"wp\"][\"blocks\"];","module.exports = window[\"wp\"][\"components\"];","module.exports = window[\"wp\"][\"data\"];","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 { registerBlockType } from '@wordpress/blocks';\nimport Edit from './edit';\nimport metadata from './block.json';\nimport './style-index.css';\n\nregisterBlockType( metadata.name, {\n\t/**\n\t * @see ./edit.js\n\t */\n\tedit: Edit,\n} );\n"],"names":["__","useBlockProps","InspectorControls","PanelBody","SelectControl","TextControl","ToggleControl","useSelect","jsx","_jsx","jsxs","_jsxs","Fragment","_Fragment","Edit","attributes","setAttributes","taxonomy","emptyLabel","label","showLabel","taxonomies","select","results","getTaxonomies","per_page","filter","visibility","public","length","slug","name","terms","getEntityRecords","number","children","title","value","options","map","onChange","find","tax","help","checked","placeholder","className","inert","term","registerBlockType","metadata","edit"],"sourceRoot":""} -------------------------------------------------------------------------------- /build/taxonomy/render.php: -------------------------------------------------------------------------------- 1 | context['query']['inherit'] ) { 11 | $query_var = sprintf( 'query-%s', $attributes['taxonomy'] ); 12 | $page_var = 'page'; 13 | $base_url = str_replace( '/page/' . get_query_var( 'paged' ), '', remove_query_arg( [ $query_var, $page_var ] ) ); 14 | } else { 15 | $query_id = $block->context['queryId'] ?? 0; 16 | $query_var = sprintf( 'query-%d-%s', $query_id, $attributes['taxonomy'] ); 17 | $page_var = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page'; 18 | $base_url = remove_query_arg( [ $query_var, $page_var ] ); 19 | } 20 | 21 | $terms = get_terms( [ 22 | 'hide_empty' => true, 23 | 'taxonomy' => $attributes['taxonomy'], 24 | 'number' => 100, 25 | ] ); 26 | 27 | if ( is_wp_error( $terms ) || empty( $terms ) ) { 28 | return; 29 | } 30 | ?> 31 | 32 |
'wp-block-query-filter' ] ); ?> data-wp-interactive="query-filter" data-wp-context="{}"> 33 | 36 | 42 |
43 | -------------------------------------------------------------------------------- /build/taxonomy/view.asset.php: -------------------------------------------------------------------------------- 1 | array('@wordpress/interactivity', array('id' => '@wordpress/interactivity-router', 'import' => 'dynamic')), 'version' => 'e7ba522c0e73d74a56f1', 'type' => 'module'); 2 | -------------------------------------------------------------------------------- /build/taxonomy/view.js: -------------------------------------------------------------------------------- 1 | import*as e from"@wordpress/interactivity";var t={438:e=>{e.exports=import("@wordpress/interactivity-router")}},r={};function a(e){var o=r[e];if(void 0!==o)return o.exports;var s=r[e]={exports:{}};return t[e](s,s.exports,a),s.exports}a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);const o=(n={getElement:()=>e.getElement,store:()=>e.store},i={},a.d(i,n),i),{state:s}=(0,o.store)("query-filter",{actions:{*navigate(e){e.preventDefault();const{actions:t}=yield Promise.resolve().then(a.bind(a,438));yield t.navigate(e.target.value)},*search(e){e.preventDefault();const{ref:t}=(0,o.getElement)();let r,n,i;if("FORM"===t.tagName){const e=t.querySelector('input[type="search"]');r=t.action,n=e.name,i=e.value}else r=t.closest("form").action,n=t.name,i=t.value;i!==s.searchValue&&(s.searchValue=i,yield(async(e,t,r)=>{const o=new URL(e);t||"s"===r?o.searchParams.set(r,t):o.searchParams.delete(r);const{actions:s}=await Promise.resolve().then(a.bind(a,438));await s.navigate(o.toString())})(r,i,n))}}});var n,i; -------------------------------------------------------------------------------- /build/taxonomy/view.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"taxonomy/view.js","mappings":";;;;;;;;;AAAA;;;;;;;;;;ACAA;;;;;;SCAA;SACA;;SAEA;SACA;SACA;SACA;SACA;SACA;SACA;SACA;SACA;SACA;SACA;SACA;SACA;;SAEA;SACA;;SAEA;SACA;SACA;;;;;UCtBA;UACA;UACA;UACA,uDAAuD,iBAAiB;UACxE;UACA,gDAAgD,aAAa;UAC7D;;;;;;;;;;ACN6D;AAE7D,MAAME,SAAS,GAAG,MAAAA,CAAQC,MAAM,EAAEC,KAAK,EAAEC,IAAI,KAAM;EAClD,MAAMC,GAAG,GAAG,IAAIC,GAAG,CAAEJ,MAAO,CAAC;EAC7B,IAAKC,KAAK,IAAIC,IAAI,KAAK,GAAG,EAAG;IAC5BC,GAAG,CAACE,YAAY,CAACC,GAAG,CAAEJ,IAAI,EAAED,KAAM,CAAC;EACpC,CAAC,MAAM;IACNE,GAAG,CAACE,YAAY,CAACE,MAAM,CAAEL,IAAK,CAAC;EAChC;EACA,MAAM;IAAEM;EAAQ,CAAC,GAAG,MAAM,8JAA2C;EACrE,MAAMA,OAAO,CAACC,QAAQ,CAAEN,GAAG,CAACO,QAAQ,CAAC,CAAE,CAAC;AACzC,CAAC;AAED,MAAM;EAAEC;AAAM,CAAC,GAAGd,+DAAK,CAAE,cAAc,EAAE;EACxCW,OAAO,EAAE;IACR,CAACC,QAAQA,CAAEG,CAAC,EAAG;MACdA,CAAC,CAACC,cAAc,CAAC,CAAC;MAClB,MAAM;QAAEL;MAAQ,CAAC,GAAG,MAAM,8JAEzB;MACD,MAAMA,OAAO,CAACC,QAAQ,CAAEG,CAAC,CAACE,MAAM,CAACb,KAAM,CAAC;IACzC,CAAC;IACD,CAACc,MAAMA,CAAEH,CAAC,EAAG;MACZA,CAAC,CAACC,cAAc,CAAC,CAAC;MAClB,MAAM;QAAEG;MAAI,CAAC,GAAGlB,oEAAU,CAAC,CAAC;MAC5B,IAAIE,MAAM,EAAEE,IAAI,EAAED,KAAK;MACvB,IAAKe,GAAG,CAACC,OAAO,KAAK,MAAM,EAAG;QAC7B,MAAMC,KAAK,GAAGF,GAAG,CAACG,aAAa,CAAE,sBAAuB,CAAC;QACzDnB,MAAM,GAAGgB,GAAG,CAAChB,MAAM;QACnBE,IAAI,GAAGgB,KAAK,CAAChB,IAAI;QACjBD,KAAK,GAAGiB,KAAK,CAACjB,KAAK;MACpB,CAAC,MAAM;QACND,MAAM,GAAGgB,GAAG,CAACI,OAAO,CAAE,MAAO,CAAC,CAACpB,MAAM;QACrCE,IAAI,GAAGc,GAAG,CAACd,IAAI;QACfD,KAAK,GAAGe,GAAG,CAACf,KAAK;MAClB;;MAEA;MACA,IAAKA,KAAK,KAAKU,KAAK,CAACU,WAAW,EAAG;MAEnCV,KAAK,CAACU,WAAW,GAAGpB,KAAK;MAEzB,MAAMF,SAAS,CAAEC,MAAM,EAAEC,KAAK,EAAEC,IAAK,CAAC;IACvC;EACD;AACD,CAAE,CAAC,C","sources":["webpack://query-loop-filter/external import \"@wordpress/interactivity-router\"","webpack://query-loop-filter/external module \"@wordpress/interactivity\"","webpack://query-loop-filter/webpack/bootstrap","webpack://query-loop-filter/webpack/runtime/make namespace object","webpack://query-loop-filter/./src/taxonomy/view.js"],"sourcesContent":["module.exports = import(\"@wordpress/interactivity-router\");;","module.exports = __WEBPACK_EXTERNAL_MODULE__wordpress_interactivity_8e89b257__;","// 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","// 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 { store, getElement } from '@wordpress/interactivity';\n\nconst updateURL = async ( action, value, name ) => {\n\tconst url = new URL( action );\n\tif ( value || name === 's' ) {\n\t\turl.searchParams.set( name, value );\n\t} else {\n\t\turl.searchParams.delete( name );\n\t}\n\tconst { actions } = await import( '@wordpress/interactivity-router' );\n\tawait actions.navigate( url.toString() );\n};\n\nconst { state } = store( 'query-filter', {\n\tactions: {\n\t\t*navigate( e ) {\n\t\t\te.preventDefault();\n\t\t\tconst { actions } = yield import(\n\t\t\t\t'@wordpress/interactivity-router'\n\t\t\t);\n\t\t\tyield actions.navigate( e.target.value );\n\t\t},\n\t\t*search( e ) {\n\t\t\te.preventDefault();\n\t\t\tconst { ref } = getElement();\n\t\t\tlet action, name, value;\n\t\t\tif ( ref.tagName === 'FORM' ) {\n\t\t\t\tconst input = ref.querySelector( 'input[type=\"search\"]' );\n\t\t\t\taction = ref.action;\n\t\t\t\tname = input.name;\n\t\t\t\tvalue = input.value;\n\t\t\t} else {\n\t\t\t\taction = ref.closest( 'form' ).action;\n\t\t\t\tname = ref.name;\n\t\t\t\tvalue = ref.value;\n\t\t\t}\n\n\t\t\t// Don't navigate if the search didn't really change.\n\t\t\tif ( value === state.searchValue ) return;\n\n\t\t\tstate.searchValue = value;\n\n\t\t\tyield updateURL( action, value, name );\n\t\t},\n\t},\n} );\n"],"names":["store","getElement","updateURL","action","value","name","url","URL","searchParams","set","delete","actions","navigate","toString","state","e","preventDefault","target","search","ref","tagName","input","querySelector","closest","searchValue"],"sourceRoot":""} -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "humanmade/query-filter", 3 | "description": "Query Loop Block filters", 4 | "type": "wordpress-plugin", 5 | "require": { 6 | "composer/installers": "^1 || ^2" 7 | }, 8 | "license": "GPL-2.0-or-later", 9 | "authors": [ 10 | { 11 | "name": "Human Made Limited", 12 | "email": "hello@humanmade.com" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /inc/namespace.php: -------------------------------------------------------------------------------- 1 | WP_Query as parsed by the block context. 61 | * @param \WP_Block $block Block instance. 62 | * @param int $page Current query's page. 63 | * @return array Array containing parameters for WP_Query as parsed by the block context. 64 | */ 65 | function filter_query_loop_block_query_vars( array $query, \WP_Block $block, int $page ) : array { 66 | if ( isset( $block->context['queryId'] ) ) { 67 | $query['query_id'] = $block->context['queryId']; 68 | } 69 | 70 | return $query; 71 | } 72 | 73 | /** 74 | * Fires after the query variable object is created, but before the actual query is run. 75 | * 76 | * @param WP_Query $query The WP_Query instance (passed by reference). 77 | */ 78 | function pre_get_posts_transpose_query_vars( WP_Query $query ) : void { 79 | $query_id = $query->get( 'query_id', null ); 80 | 81 | if ( ! $query->is_main_query() && is_null( $query_id ) ) { 82 | return; 83 | } 84 | 85 | $prefix = $query->is_main_query() ? 'query-' : "query-{$query_id}-"; 86 | $tax_query = []; 87 | $valid_keys = [ 88 | 'post_type' => $query->is_search() ? 'any' : 'post', 89 | 's' => '', 90 | ]; 91 | 92 | // Preserve valid params for later retrieval. 93 | foreach ( $valid_keys as $key => $default ) { 94 | $query->set( 95 | "query-filter-$key", 96 | $query->get( $key, $default ) 97 | ); 98 | } 99 | 100 | // Map get params to this query. 101 | foreach ( $_GET as $key => $value ) { 102 | if ( strpos( $key, $prefix ) === 0 ) { 103 | $key = str_replace( $prefix, '', $key ); 104 | $value = sanitize_text_field( urldecode( wp_unslash( $value ) ) ); 105 | 106 | // Handle taxonomies specifically. 107 | if ( get_taxonomy( $key ) ) { 108 | $tax_query['relation'] = 'AND'; 109 | $tax_query[] = [ 110 | 'taxonomy' => $key, 111 | 'terms' => [ $value ], 112 | 'field' => 'slug', 113 | ]; 114 | } else { 115 | // Other options should map directly to query vars. 116 | $key = sanitize_key( $key ); 117 | 118 | if ( ! in_array( $key, array_keys( $valid_keys ), true ) ) { 119 | continue; 120 | } 121 | 122 | $query->set( 123 | $key, 124 | $value 125 | ); 126 | } 127 | } 128 | } 129 | 130 | if ( ! empty( $tax_query ) ) { 131 | $existing_query = $query->get( 'tax_query', [] ); 132 | 133 | if ( ! empty( $existing_query ) ) { 134 | $tax_query = [ 135 | 'relation' => 'AND', 136 | [ $existing_query ], 137 | $tax_query, 138 | ]; 139 | } 140 | 141 | $query->set( 'tax_query', $tax_query ); 142 | } 143 | } 144 | 145 | /** 146 | * Filters the settings determined from the block type metadata. 147 | * 148 | * @param array $metadata Metadata provided for registering a block type. 149 | * @return array Array of metadata for registering a block type. 150 | */ 151 | function filter_block_type_metadata( array $metadata ) : array { 152 | // Add query context to search block. 153 | if ( $metadata['name'] === 'core/search' ) { 154 | $metadata['usesContext'] = array_merge( $metadata['usesContext'] ?? [], [ 'queryId', 'query' ] ); 155 | } 156 | 157 | return $metadata; 158 | } 159 | 160 | /** 161 | * Filters the content of a single block. 162 | * 163 | * @param string $block_content The block content. 164 | * @param array $block The full block, including name and attributes. 165 | * @param \WP_Block $instance The block instance. 166 | * @return string The block content. 167 | */ 168 | function render_block_search( string $block_content, array $block, \WP_Block $instance ) : string { 169 | if ( empty( $instance->context['query'] ) ) { 170 | return $block_content; 171 | } 172 | 173 | wp_enqueue_script_module( 'query-filter-taxonomy-view-script-module' ); 174 | 175 | $query_var = empty( $instance->context['query']['inherit'] ) 176 | ? sprintf( 'query-%d-s', $instance->context['queryId'] ?? 0 ) 177 | : 's'; 178 | 179 | $action = str_replace( '/page/'. get_query_var( 'paged', 1 ), '', add_query_arg( [ $query_var => '' ] ) ); 180 | 181 | // Note sanitize_text_field trims whitespace from start/end of string causing unexpected behaviour. 182 | $value = wp_unslash( $_GET[ $query_var ] ?? '' ); 183 | $value = urldecode( $value ); 184 | $value = wp_check_invalid_utf8( $value ); 185 | $value = wp_pre_kses_less_than( $value ); 186 | $value = strip_tags( $value ); 187 | 188 | wp_interactivity_state( 'query-filter', [ 189 | 'searchValue' => $value, 190 | ] ); 191 | 192 | $block_content = new WP_HTML_Tag_Processor( $block_content ); 193 | $block_content->next_tag( [ 'tag_name' => 'form' ] ); 194 | $block_content->set_attribute( 'action', $action ); 195 | $block_content->set_attribute( 'data-wp-interactive', 'query-filter' ); 196 | $block_content->set_attribute( 'data-wp-on--submit', 'actions.search' ); 197 | $block_content->set_attribute( 'data-wp-context', '{searchValue:""}' ); 198 | $block_content->next_tag( [ 'tag_name' => 'input', 'class_name' => 'wp-block-search__input' ] ); 199 | $block_content->set_attribute( 'name', $query_var ); 200 | $block_content->set_attribute( 'inputmode', 'search' ); 201 | $block_content->set_attribute( 'value', $value ); 202 | $block_content->set_attribute( 'data-wp-bind--value', 'state.searchValue' ); 203 | $block_content->set_attribute( 'data-wp-on--input', 'actions.search' ); 204 | 205 | return (string) $block_content; 206 | } 207 | 208 | /** 209 | * Add data attributes to the query block to describe the block query. 210 | * 211 | * @param string $block_content Default query content. 212 | * @param array $block Parsed block. 213 | * @return string 214 | */ 215 | function render_block_query( $block_content, $block ) { 216 | $block_content = new WP_HTML_Tag_Processor( $block_content ); 217 | $block_content->next_tag(); 218 | 219 | // Always allow region updates on interactivity, use standard core region naming. 220 | $block_content->set_attribute( 'data-wp-router-region', 'query-' . ( $block['attrs']['queryId'] ?? 0 ) ); 221 | 222 | return (string) $block_content; 223 | } 224 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "query-loop-filter", 3 | "private": true, 4 | "description": "Filter blocks for query loops", 5 | "scripts": { 6 | "build": "wp-scripts build --experimental-modules", 7 | "start": "wp-scripts start --experimental-modules", 8 | "format": "wp-scripts format", 9 | "lint:css": "wp-scripts lint-style --fix", 10 | "lint:js": "wp-scripts lint-js", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "author": "Human Made Limited", 14 | "license": "GPL-2.0-or-later", 15 | "dependencies": { 16 | "@wordpress/scripts": "^28.6.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /query-filter.php: -------------------------------------------------------------------------------- 1 | { 10 | return ( 11 | ( select( 'core' ).getPostTypes( { per_page: 100 } ) || [] ).filter( 12 | ( type ) => type.viewable 13 | ) || [] 14 | ); 15 | }, [] ); 16 | 17 | let contextPostTypes = ( context.query.postType || '' ) 18 | .split( ',' ) 19 | .map( ( type ) => type.trim() ); 20 | 21 | // Support for enhanced query loop block plugin. 22 | if ( Array.isArray( context.query.multiple_posts ) ) { 23 | contextPostTypes = contextPostTypes.concat( 24 | context.query.multiple_posts 25 | ); 26 | } 27 | 28 | const postTypes = contextPostTypes.map( ( postType ) => { 29 | return ( 30 | allPostTypes.find( ( type ) => type.slug === postType ) || { 31 | slug: postType, 32 | name: postType, 33 | } 34 | ); 35 | } ); 36 | 37 | return ( 38 | <> 39 | 40 | 41 | setAttributes( { label } ) } 50 | /> 51 | 55 | setAttributes( { showLabel } ) 56 | } 57 | /> 58 | 63 | setAttributes( { emptyLabel } ) 64 | } 65 | /> 66 | 67 | 68 |
69 | { showLabel && ( 70 | 73 | ) } 74 | 85 |
86 | 87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /src/post-type/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from '@wordpress/blocks'; 2 | import Edit from './edit'; 3 | import metadata from './block.json'; 4 | 5 | registerBlockType( metadata.name, { 6 | /** 7 | * @see ./edit.js 8 | */ 9 | edit: Edit, 10 | } ); 11 | -------------------------------------------------------------------------------- /src/post-type/render.php: -------------------------------------------------------------------------------- 1 | context['query']['inherit'] ) { 7 | $query_var = 'query-post_type'; 8 | $page_var = 'page'; 9 | $base_url = str_replace( '/page/' . get_query_var( 'paged' ), '', remove_query_arg( [ $query_var, $page_var ] ) ); 10 | } else { 11 | $query_id = $block->context['queryId'] ?? 0; 12 | $query_var = sprintf( 'query-%d-post_type', $query_id ); 13 | $page_var = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page'; 14 | $base_url = remove_query_arg( [ $query_var, $page_var ] ); 15 | } 16 | 17 | $post_types = array_map( 'trim', explode( ',', $block->context['query']['postType'] ?? 'post' ) ); 18 | 19 | // Support for enhanced query block. 20 | if ( isset( $block->context['query']['multiple_posts'] ) && is_array( $block->context['query']['multiple_posts'] ) ) { 21 | $post_types = array_merge( $post_types, $block->context['query']['multiple_posts'] ); 22 | } 23 | 24 | // Fill in inherited query types. 25 | if ( $block->context['query']['inherit'] ) { 26 | $inherited_post_types = $wp_query->get( 'query-filter-post_type' ) === 'any' 27 | ? get_post_types( [ 'public' => true, 'exclude_from_search' => false ] ) 28 | : (array) $wp_query->get( 'query-filter-post_type' ); 29 | 30 | $post_types = array_merge( $post_types, $inherited_post_types ); 31 | if ( ! get_option( 'wp_attachment_pages_enabled' ) ) { 32 | $post_types = array_diff( $post_types, [ 'attachment' ] ); 33 | } 34 | } 35 | 36 | $post_types = array_unique( $post_types ); 37 | $post_types = array_map( 'get_post_type_object', $post_types ); 38 | 39 | if ( empty( $post_types ) ) { 40 | return; 41 | } 42 | ?> 43 | 44 |
'wp-block-query-filter' ] ); ?> data-wp-interactive="query-filter" data-wp-context="{}"> 45 | 48 | 54 |
55 | -------------------------------------------------------------------------------- /src/taxonomy/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schemas.wp.org/trunk/block.json", 3 | "apiVersion": 3, 4 | "name": "query-filter/taxonomy", 5 | "version": "0.1.0", 6 | "title": "Taxonomy Filter", 7 | "category": "theme", 8 | "icon": "filter", 9 | "description": "Allows users to filter by taxonomy terms when placed wihin a query loop block", 10 | "ancestor": [ "core/query" ], 11 | "usesContext": [ "queryId", "query" ], 12 | "supports": { 13 | "html": false, 14 | "className": true, 15 | "customClassName": true, 16 | "color": { 17 | "background": true, 18 | "text": true 19 | }, 20 | "typography": { 21 | "fontSize": true, 22 | "textAlign": true, 23 | "lineHeight": true, 24 | "__experimentalFontFamily": true, 25 | "__experimentalFontWeight": true, 26 | "__experimentalFontStyle": true, 27 | "__experimentalTextTransform": true, 28 | "__experimentalTextDecoration": true, 29 | "__experimentalLetterSpacing": true, 30 | "__experimentalDefaultControls": { 31 | "fontSize": true 32 | } 33 | }, 34 | "spacing": { 35 | "margin": true, 36 | "padding": true, 37 | "blockGap": true 38 | }, 39 | "interactivity": { 40 | "clientNavigation": true 41 | } 42 | }, 43 | "attributes": { 44 | "taxonomy": { 45 | "type": "string" 46 | }, 47 | "emptyLabel": { 48 | "type": "string", 49 | "default": "" 50 | }, 51 | "label": { 52 | "type": "string" 53 | }, 54 | "showLabel": { 55 | "type": "boolean", 56 | "default": true 57 | } 58 | }, 59 | "textdomain": "query-filter", 60 | "editorScript": "file:./index.js", 61 | "style": "query-filter-view", 62 | "viewScriptModule": "file:./view.js", 63 | "render": "file:./render.php" 64 | } 65 | -------------------------------------------------------------------------------- /src/taxonomy/edit.js: -------------------------------------------------------------------------------- 1 | import { __ } from '@wordpress/i18n'; 2 | import { useBlockProps, InspectorControls } from '@wordpress/block-editor'; 3 | import { 4 | PanelBody, 5 | SelectControl, 6 | TextControl, 7 | ToggleControl, 8 | } from '@wordpress/components'; 9 | import { useSelect } from '@wordpress/data'; 10 | 11 | export default function Edit( { attributes, setAttributes } ) { 12 | const { taxonomy, emptyLabel, label, showLabel } = attributes; 13 | 14 | const taxonomies = useSelect( 15 | ( select ) => { 16 | const results = ( 17 | select( 'core' ).getTaxonomies( { per_page: 100 } ) || [] 18 | ).filter( ( taxonomy ) => taxonomy.visibility.publicly_queryable ); 19 | 20 | if ( results && results.length > 0 && ! taxonomy ) { 21 | setAttributes( { 22 | taxonomy: results[ 0 ].slug, 23 | label: results[ 0 ].name, 24 | } ); 25 | } 26 | 27 | return results; 28 | }, 29 | [ taxonomy ] 30 | ); 31 | 32 | const terms = useSelect( 33 | ( select ) => { 34 | return ( 35 | select( 'core' ).getEntityRecords( 'taxonomy', taxonomy, { 36 | number: 50, 37 | } ) || [] 38 | ); 39 | }, 40 | [ taxonomy ] 41 | ); 42 | 43 | return ( 44 | <> 45 | 46 | 47 | ( { 51 | label: taxonomy.name, 52 | value: taxonomy.slug, 53 | } ) ) } 54 | onChange={ ( taxonomy ) => 55 | setAttributes( { 56 | taxonomy, 57 | label: taxonomies.find( 58 | ( tax ) => tax.slug === taxonomy 59 | ).name, 60 | } ) 61 | } 62 | /> 63 | setAttributes( { label } ) } 71 | /> 72 | 76 | setAttributes( { showLabel } ) 77 | } 78 | /> 79 | 84 | setAttributes( { emptyLabel } ) 85 | } 86 | /> 87 | 88 | 89 |
90 | { showLabel && ( 91 | 94 | ) } 95 | 106 |
107 | 108 | ); 109 | } 110 | -------------------------------------------------------------------------------- /src/taxonomy/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from '@wordpress/blocks'; 2 | import Edit from './edit'; 3 | import metadata from './block.json'; 4 | import './style-index.css'; 5 | 6 | registerBlockType( metadata.name, { 7 | /** 8 | * @see ./edit.js 9 | */ 10 | edit: Edit, 11 | } ); 12 | -------------------------------------------------------------------------------- /src/taxonomy/render.php: -------------------------------------------------------------------------------- 1 | context['query']['inherit'] ) ) { 11 | $query_id = $block->context['queryId'] ?? 0; 12 | $query_var = sprintf( 'query-%d-%s', $query_id, $attributes['taxonomy'] ); 13 | $page_var = isset( $block->context['queryId'] ) ? 'query-' . $block->context['queryId'] . '-page' : 'query-page'; 14 | $base_url = remove_query_arg( [ $query_var, $page_var ] ); 15 | } else { 16 | $query_var = sprintf( 'query-%s', $attributes['taxonomy'] ); 17 | $page_var = 'page'; 18 | $base_url = str_replace( '/page/' . get_query_var( 'paged' ), '', remove_query_arg( [ $query_var, $page_var ] ) ); 19 | } 20 | 21 | $terms = get_terms( [ 22 | 'hide_empty' => true, 23 | 'taxonomy' => $attributes['taxonomy'], 24 | 'number' => 100, 25 | ] ); 26 | 27 | if ( is_wp_error( $terms ) || empty( $terms ) ) { 28 | return; 29 | } 30 | ?> 31 | 32 |
'wp-block-query-filter' ] ); ?> data-wp-interactive="query-filter" data-wp-context="{}"> 33 | 36 | 42 |
43 | -------------------------------------------------------------------------------- /src/taxonomy/style-index.css: -------------------------------------------------------------------------------- 1 | @view-transition { 2 | navigation: auto; 3 | } 4 | 5 | .wp-block-query-filter { 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: stretch; 9 | } 10 | -------------------------------------------------------------------------------- /src/taxonomy/view.js: -------------------------------------------------------------------------------- 1 | import { store, getElement } from '@wordpress/interactivity'; 2 | 3 | const updateURL = async ( action, value, name ) => { 4 | const url = new URL( action ); 5 | if ( value || name === 's' ) { 6 | url.searchParams.set( name, value ); 7 | } else { 8 | url.searchParams.delete( name ); 9 | } 10 | const { actions } = await import( '@wordpress/interactivity-router' ); 11 | await actions.navigate( url.toString() ); 12 | }; 13 | 14 | const { state } = store( 'query-filter', { 15 | actions: { 16 | *navigate( e ) { 17 | e.preventDefault(); 18 | const { actions } = yield import( 19 | '@wordpress/interactivity-router' 20 | ); 21 | yield actions.navigate( e.target.value ); 22 | }, 23 | *search( e ) { 24 | e.preventDefault(); 25 | const { ref } = getElement(); 26 | let action, name, value; 27 | if ( ref.tagName === 'FORM' ) { 28 | const input = ref.querySelector( 'input[type="search"]' ); 29 | action = ref.action; 30 | name = input.name; 31 | value = input.value; 32 | } else { 33 | action = ref.closest( 'form' ).action; 34 | name = ref.name; 35 | value = ref.value; 36 | } 37 | 38 | // Don't navigate if the search didn't really change. 39 | if ( value === state.searchValue ) return; 40 | 41 | state.searchValue = value; 42 | 43 | yield updateURL( action, value, name ); 44 | }, 45 | }, 46 | } ); 47 | --------------------------------------------------------------------------------