@wordpress/block-library
Version:
Block library for the WordPress editor.
126 lines (121 loc) • 3.85 kB
JavaScript
/**
* WordPress dependencies
*/
import { FormTokenField } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
/**
* Internal dependencies
*/
import { getEntitiesInfo, useTaxonomies } from '../../utils';
import { MAX_FETCHED_TERMS } from '../../constants';
// Helper function to get the term id based on user input in terms `FormTokenField`.
const getTermIdByTermValue = ( termsMappedByName, termValue ) => {
// First we check for exact match by `term.id` or case sensitive `term.name` match.
const termId = termValue?.id || termsMappedByName[ termValue ]?.id;
if ( termId ) return termId;
/**
* Here we make an extra check for entered terms in a non case sensitive way,
* to match user expectations, due to `FormTokenField` behaviour that shows
* suggestions which are case insensitive.
*
* Although WP tries to discourage users to add terms with the same name (case insensitive),
* it's still possible if you manually change the name, as long as the terms have different slugs.
* In this edge case we always apply the first match from the terms list.
*/
const termValueLower = termValue.toLocaleLowerCase();
for ( const term in termsMappedByName ) {
if ( term.toLocaleLowerCase() === termValueLower ) {
return termsMappedByName[ term ].id;
}
}
};
export const useTaxonomiesInfo = ( postType ) => {
const taxonomies = useTaxonomies( postType );
const taxonomiesInfo = useSelect(
( select ) => {
const { getEntityRecords } = select( coreStore );
const termsQuery = { context: 'view', per_page: MAX_FETCHED_TERMS };
const _taxonomiesInfo = taxonomies?.map( ( { slug, name } ) => {
const _terms = getEntityRecords( 'taxonomy', slug, termsQuery );
return {
slug,
name,
terms: getEntitiesInfo( _terms ),
};
} );
return _taxonomiesInfo;
},
[ taxonomies ]
);
return taxonomiesInfo;
};
export function TaxonomyControls( { onChange, query } ) {
const taxonomiesInfo = useTaxonomiesInfo( query.postType );
const onTermsChange = ( taxonomySlug ) => ( newTermValues ) => {
const taxonomyInfo = taxonomiesInfo.find(
( { slug } ) => slug === taxonomySlug
);
if ( ! taxonomyInfo ) return;
const termIds = Array.from(
newTermValues.reduce( ( accumulator, termValue ) => {
const termId = getTermIdByTermValue(
taxonomyInfo.terms.mapByName,
termValue
);
if ( termId ) accumulator.add( termId );
return accumulator;
}, new Set() )
);
const newTaxQuery = {
...query.taxQuery,
[ taxonomySlug ]: termIds,
};
onChange( { taxQuery: newTaxQuery } );
};
// Returns only the existing term ids in proper format to be
// used in `FormTokenField`. This prevents the component from
// crashing in the editor, when non existing term ids were provided.
const getExistingTaxQueryValue = ( taxonomySlug ) => {
const taxonomyInfo = taxonomiesInfo.find(
( { slug } ) => slug === taxonomySlug
);
if ( ! taxonomyInfo ) return [];
return ( query.taxQuery?.[ taxonomySlug ] || [] ).reduce(
( accumulator, termId ) => {
const term = taxonomyInfo.terms.mapById[ termId ];
if ( term ) {
accumulator.push( {
id: termId,
value: term.name,
} );
}
return accumulator;
},
[]
);
};
return (
<>
{ !! taxonomiesInfo?.length &&
taxonomiesInfo.map( ( { slug, name, terms } ) => {
if ( ! terms?.names?.length ) {
return null;
}
return (
<div
key={ slug }
className="block-library-query-inspector__taxonomy-control"
>
<FormTokenField
label={ name }
value={ getExistingTaxQueryValue( slug ) }
suggestions={ terms.names }
onChange={ onTermsChange( slug ) }
/>
</div>
);
} ) }
</>
);
}