import { useState, useEffect } from '@wordpress/element';
import {
Button,
CheckboxControl,
SelectControl,
TextControl,
Spinner,
} from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';
const TYPE_OPTIONS = [
{ label: 'Text', value: 'CHAR' },
{ label: 'Number (integer)', value: 'NUMERIC' },
{ label: 'Number (decimal)', value: 'DECIMAL' },
{ label: 'Date', value: 'DATE' },
{ label: 'Date & Time', value: 'DATETIME' },
{ label: 'Time', value: 'TIME' },
{ label: 'Binary', value: 'BINARY' },
{ label: 'Unsigned integer', value: 'UNSIGNED' },
];
const COMPARE_OPTIONS = [
{ label: '= (equals)', value: '=' },
{ label: '!= (not equals)', value: '!=' },
{ label: '< (less than)', value: '<' },
{ label: '> (greater than)', value: '>' },
{ label: '<= (less or equal)', value: '<=' },
{ label: '>= (greater or equal)', value: '>=' },
{ label: 'LIKE (contains)', value: 'LIKE' },
{ label: 'NOT LIKE', value: 'NOT LIKE' },
{ label: 'IN (one of)', value: 'IN' },
{ label: 'NOT IN', value: 'NOT IN' },
{ label: 'BETWEEN', value: 'BETWEEN' },
{ label: 'NOT BETWEEN', value: 'NOT BETWEEN' },
{ label: 'EXISTS', value: 'EXISTS' },
{ label: 'NOT EXISTS', value: 'NOT EXISTS' },
];
export default function MetaKeySelector( { blockConfig, objectType, endpoint, onChange } ) {
const [ availableKeys, setAvailableKeys ] = useState( [] );
const [ loading, setLoading ] = useState( false );
const [ patternInput, setPatternInput ] = useState( '' );
const [ patternError, setPatternError ] = useState( '' );
const [ search, setSearch ] = useState( '' );
const metaKeys = blockConfig.meta_keys || {};
const metaObjectType = objectType || 'post';
const postType = blockConfig.post_type || '';
useEffect( () => {
setLoading( true );
let path;
if ( endpoint ) {
path = `wlconduit/v1/ext/${ endpoint }`;
} else {
path = `wlconduit/v1/meta-keys?object_type=${ metaObjectType }`;
if ( metaObjectType === 'post' && postType ) {
path += `&post_type=${ postType }`;
}
}
apiFetch( { path } )
.then( ( keys ) => setAvailableKeys( keys || [] ) )
.catch( () => setAvailableKeys( [] ) )
.finally( () => setLoading( false ) );
}, [ endpoint, metaObjectType, postType ] );
function toggleKey( key ) {
const updated = { ...metaKeys };
if ( updated[ key ] ) {
delete updated[ key ];
} else {
updated[ key ] = {
as_field: true,
as_filter: false,
type: 'CHAR',
compare: [ '=' ],
};
}
onChange( updated );
}
function updateKeyConfig( key, partial ) {
const updated = {
...metaKeys,
[ key ]: { ...metaKeys[ key ], ...partial },
};
onChange( updated );
}
function toggleCompare( key, op ) {
const current = metaKeys[ key ]?.compare || [ '=' ];
const updated = current.includes( op )
? current.filter( ( c ) => c !== op )
: [ ...current, op ];
updateKeyConfig( key, { compare: updated } );
}
function addPatternKey() {
const trimmed = patternInput.trim();
if ( ! trimmed ) {
return;
}
if ( ! trimmed.includes( '%' ) ) {
setPatternError( 'Pattern key must contain a % wildcard.' );
return;
}
if ( metaKeys[ trimmed ] ) {
setPatternError( 'This pattern key already exists.' );
return;
}
setPatternError( '' );
onChange( {
...metaKeys,
[ trimmed ]: { as_field: true },
} );
setPatternInput( '' );
}
function removePatternKey( key ) {
const updated = { ...metaKeys };
delete updated[ key ];
onChange( updated );
}
const patternKeys = Object.keys( metaKeys ).filter( ( k ) => k.includes( '%' ) );
function handleRefresh() {
setLoading( true );
let path;
if ( endpoint ) {
path = `wlconduit/v1/ext/${ endpoint }?refresh=1`;
} else {
path = `wlconduit/v1/meta-keys?object_type=${ metaObjectType }&refresh=1`;
if ( metaObjectType === 'post' && postType ) {
path += `&post_type=${ postType }`;
}
}
apiFetch( { path } )
.then( ( keys ) => setAvailableKeys( keys || [] ) )
.catch( () => {} )
.finally( () => setLoading( false ) );
}
if ( loading ) {
return
Select custom fields to include as data or expose as filters.
No meta keys found.
) : ( availableKeys.filter( ( item ) => { if ( ! search ) { return true; } const key = item.key || item; return key.toLowerCase().includes( search.toLowerCase() ); } ).map( ( item ) => { const key = item.key || item; const isInternal = item.internal || false; const isSerialized = item.serialized || false; const isSelected = !! metaKeys[ key ]; const config = metaKeys[ key ] || {}; return (Serialized value — filtering not available.
) : (Add pattern-based meta keys using % as wildcard (e.g., _course_completed_%).
{ patternError }
) } { patternKeys.map( ( key ) => (