8889841ccontext.ts000064400000001307150537663550006620 0ustar00/** * External dependencies */ import { createContext, useContext } from '@wordpress/element'; /** * Context consumed by inner blocks. */ export type FilterContextProps = { wrapper?: React.RefObject< HTMLDivElement >; }; export const FilterBlockContext: React.Context< FilterContextProps > = createContext< FilterContextProps >( {} ); export const useFilterBlockContext = (): FilterContextProps => { return useContext( FilterBlockContext ); }; export const useSetWraperVisibility = () => { const { wrapper } = useFilterBlockContext(); return ( isVisible: boolean ) => { if ( ! wrapper ) { return; } if ( wrapper.current ) { wrapper.current.hidden = isVisible ? false : true; } }; }; frontend.ts000064400000000665150537663550006761 0ustar00/** * External dependencies */ import { renderParentBlock } from '@woocommerce/atomic-utils'; import { getRegisteredBlockComponents } from '@woocommerce/blocks-registry'; /** * Internal dependencies */ import Block from './block'; renderParentBlock( { blockName: 'woocommerce/filter-wrapper', selector: '.wp-block-woocommerce-filter-wrapper', Block, blockMap: getRegisteredBlockComponents( 'woocommerce/filter-wrapper' ), } ); register-components.ts000064400000002632150537663550011145 0ustar00/** * External dependencies */ import { registerBlockComponent } from '@woocommerce/blocks-registry'; import { lazy } from '@wordpress/element'; import { WC_BLOCKS_BUILD_URL } from '@woocommerce/block-settings'; // Modify webpack publicPath at runtime based on location of WordPress Plugin. // eslint-disable-next-line no-undef,camelcase __webpack_public_path__ = WC_BLOCKS_BUILD_URL; registerBlockComponent( { blockName: 'woocommerce/active-filters', component: lazy( () => import( /* webpackChunkName: "active-filters-wrapper" */ '../active-filters/block-wrapper' ) ), } ); registerBlockComponent( { blockName: 'woocommerce/price-filter', component: lazy( () => import( /* webpackChunkName: "price-filter-wrapper" */ '../price-filter/block-wrapper' ) ), } ); registerBlockComponent( { blockName: 'woocommerce/stock-filter', component: lazy( () => import( /* webpackChunkName: "stock-filter-wrapper" */ '../stock-filter/block-wrapper' ) ), } ); registerBlockComponent( { blockName: 'woocommerce/attribute-filter', component: lazy( () => import( /* webpackChunkName: "attribute-filter-wrapper" */ '../attribute-filter/block-wrapper' ) ), } ); registerBlockComponent( { blockName: 'woocommerce/rating-filter', component: lazy( () => import( /* webpackChunkName: "rating-filter-wrapper" */ '../rating-filter/block-wrapper' ) ), } ); block.json000064400000000543150537663550006552 0ustar00{ "name": "woocommerce/filter-wrapper", "version": "1.0.0", "title": "Filter Block", "category": "woocommerce", "keywords": [ "WooCommerce" ], "attributes": { "filterType": { "type": "string" }, "heading": { "type": "string" } }, "textdomain": "woocommerce", "apiVersion": 2, "$schema": "https://schemas.wp.org/trunk/block.json" } edit.tsx000064400000001343150537663550006251 0ustar00/** * External dependencies */ import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; import type { BlockEditProps } from '@wordpress/blocks'; /** * Internal dependencies */ import { Attributes } from './types'; const Edit = ( { attributes }: BlockEditProps< Attributes > ) => { const blockProps = useBlockProps(); return (
); }; export default Edit; types.ts000064400000000107150537663550006275 0ustar00export interface Attributes { heading: string; filterType: string; } upgrade.tsx000064400000004131150537663550006751 0ustar00/** * External dependencies */ import { __ } from '@wordpress/i18n'; import { createBlock } from '@wordpress/blocks'; import type { BlockInstance } from '@wordpress/blocks'; import { useDispatch, useSelect } from '@wordpress/data'; import { Button } from '@wordpress/components'; import { Warning } from '@wordpress/block-editor'; interface UpgradeNoticeProps { clientId: string; setAttributes: ( attributes: Record< string, unknown > ) => void; attributes: Record< string, unknown >; filterType: undefined | string; } export const UpgradeNotice = ( { clientId, setAttributes, filterType, attributes, }: UpgradeNoticeProps ) => { const { replaceBlock } = useDispatch( 'core/block-editor' ); const { heading, headingLevel } = attributes; const isInsideFilterWrapper = useSelect( ( select ) => { const { getBlockParentsByBlockName } = select( 'core/block-editor' ); return ( getBlockParentsByBlockName( clientId, 'woocommerce/filter-wrapper' ).length > 0 ); }, [ clientId ] ); const upgradeFilterBlockHandler = () => { const filterWrapperInnerBlocks: BlockInstance[] = [ createBlock( `woocommerce/${ filterType }`, { ...attributes, heading: '', } ), ]; if ( heading && heading !== '' ) { filterWrapperInnerBlocks.unshift( createBlock( 'core/heading', { content: heading, level: headingLevel ?? 2, } ) ); } replaceBlock( clientId, createBlock( 'woocommerce/filter-wrapper', { heading, filterType, }, [ ...filterWrapperInnerBlocks ] ) ); setAttributes( { heading: '', lock: { remove: true, }, } ); }; if ( isInsideFilterWrapper || ! filterType ) { return null; } const actions = [ , ]; return ( { __( 'Filter block: We have improved this block to make styling easier. Upgrade it using the button below.', 'woo-gutenberg-products-block' ) } ); }; index.tsx000064400000014606150537663550006441 0ustar00/** * External dependencies */ import { __ } from '@wordpress/i18n'; import { createBlock, registerBlockType } from '@wordpress/blocks'; import type { BlockInstance } from '@wordpress/blocks'; import { toggle } from '@woocommerce/icons'; import { useBlockProps, InnerBlocks } from '@wordpress/block-editor'; import { Icon, category, currencyDollar, box, starEmpty, } from '@wordpress/icons'; /** * Internal dependencies */ import edit from './edit'; import metadata from './block.json'; const filterBlocksWidgets = [ { widgetId: 'woocommerce_layered_nav_filters', name: 'active-filters', heading: __( 'Active filters', 'woo-gutenberg-products-block' ), }, { widgetId: 'woocommerce_price_filter', name: 'price-filter', heading: __( 'Filter by price', 'woo-gutenberg-products-block' ), }, { widgetId: 'woocommerce_layered_nav', name: 'attribute-filter', heading: __( 'Filter by attribute', 'woo-gutenberg-products-block' ), }, { widgetId: 'woocommerce_rating_filter', name: 'rating-filter', heading: __( 'Filter by rating', 'woo-gutenberg-products-block' ), }, ]; const getTransformAttributes = ( instance, filterType: string ) => { switch ( filterType ) { case 'attribute-filter': return { attributeId: 0, showCounts: true, queryType: instance?.raw?.query_type || 'or', heading: '', displayStyle: instance?.raw?.display_type || 'list', showFilterButton: false, selectType: instance?.raw?.select_type || 'multiple', isPreview: false, }; case 'active-filters': return { displayStyle: 'list', heading: '', }; case 'price-filter': return { heading: '', showInputFields: false, showFilterButton: true, inlineInput: false, }; default: return {}; } }; const isFilterWidget = ( widgetId: string ) => filterBlocksWidgets.some( ( item ) => item.widgetId === widgetId ); const getFilterBlockObject = ( widgetId: string ) => { const filterBlock = filterBlocksWidgets.find( ( item ) => item.widgetId === widgetId ); return filterBlock; }; const transformFilterBlock = ( filterType: string, attributes: Record< string, unknown >, title: string ) => { const filterWrapperInnerBlocks: BlockInstance[] = [ createBlock( `woocommerce/${ filterType }`, attributes ), ]; filterWrapperInnerBlocks.unshift( createBlock( 'core/heading', { content: title, level: 3, } ) ); return createBlock( 'woocommerce/filter-wrapper', { filterType, }, filterWrapperInnerBlocks ); }; registerBlockType( metadata, { edit, save() { return (
); }, variations: [ { name: 'active-filters', title: __( 'Active Filters', 'woo-gutenberg-products-block' ), description: __( 'Display the currently active filters.', 'woo-gutenberg-products-block' ), /** * We need to handle the isActive function differently for this * variation. The `attributes` is empty for default variation. So we * set this variation as active if `filterType` is not passed. */ isActive: ( attributes ) => attributes.filterType === 'active-filters' || ! attributes.filterType, attributes: { heading: __( 'Active filters', 'woo-gutenberg-products-block' ), filterType: 'active-filters', }, icon: { src: ( ), }, isDefault: true, }, { name: 'price-filter', title: __( 'Filter by Price', 'woo-gutenberg-products-block' ), description: __( 'Enable customers to filter the product grid by choosing a price range.', 'woo-gutenberg-products-block' ), isActive: ( attributes ) => attributes.filterType === 'price-filter', attributes: { filterType: 'price-filter', heading: __( 'Filter by price', 'woo-gutenberg-products-block' ), }, icon: { src: ( ), }, }, { name: 'stock-filter', title: __( 'Filter by Stock', 'woo-gutenberg-products-block' ), description: __( 'Enable customers to filter the product grid by stock status.', 'woo-gutenberg-products-block' ), isActive: ( attributes ) => attributes.filterType === 'stock-filter', attributes: { filterType: 'stock-filter', heading: __( 'Filter by stock status', 'woo-gutenberg-products-block' ), }, icon: { src: ( ), }, }, { name: 'attribute-filter', title: __( 'Filter by Attribute', 'woo-gutenberg-products-block' ), description: __( 'Enable customers to filter the product grid by selecting one or more attributes, such as color.', 'woo-gutenberg-products-block' ), isActive: ( attributes ) => attributes.filterType === 'attribute-filter', attributes: { filterType: 'attribute-filter', heading: __( 'Filter by attribute', 'woo-gutenberg-products-block' ), }, icon: { src: ( ), }, }, { name: 'rating-filter', title: __( 'Filter by Rating', 'woo-gutenberg-products-block' ), description: __( 'Enable customers to filter the product grid by rating.', 'woo-gutenberg-products-block' ), isActive: ( attributes ) => attributes.filterType === 'rating-filter', attributes: { filterType: 'rating-filter', heading: __( 'Filter by rating', 'woo-gutenberg-products-block' ), }, icon: { src: ( ), }, }, ], transforms: { from: [ { type: 'block', blocks: [ 'core/legacy-widget' ], // We can't transform if raw instance isn't shown in the REST API. isMatch: ( { idBase, instance } ) => isFilterWidget( idBase ) && !! instance?.raw, transform: ( { idBase, instance } ) => { const filterBlockObject = getFilterBlockObject( idBase ); if ( ! filterBlockObject ) return null; return transformFilterBlock( filterBlockObject.name, getTransformAttributes( instance, filterBlockObject.name ), instance?.raw?.title || filterBlockObject.heading ); }, }, ], }, } ); block.tsx000064400000001037150537663550006416 0ustar00/** * External dependencies */ import { useRef } from '@wordpress/element'; /** * Internal dependencies */ import './register-components'; import { FilterBlockContext } from './context'; type Props = { children: JSX.Element[]; }; const Block = ( { children }: Props ): JSX.Element => { const wrapper = useRef( null ); return (
{ children }
); }; export default Block;