import { _x } from '@wordpress/i18n';
import {
BaseControl,
RadioControl,
ToggleControl,
} from '@wordpress/components';
import Select from './select';
import { useEffect, useState } from '@wordpress/element';
export default function FieldOptions( props ) {
const { editField, onChange } = props;
const [ option, setOption ] = useState( editField.options || {} );
const prepareDataSources = function( data ) {
return data.map( ( source ) => {
return {
text: source.label,
value: source.id,
};
} );
};
const prepareFacets = function( facetDistribution ) {
const result = {};
Object.entries( facetDistribution ).map( ( [ key, value ] ) => {
result[ key ] = Object.entries( value ).map( ( [ key, value ] ) => {
return {
text: key,
value: key,
};
} );
} );
return result;
};
const prepareFacetsOptions = function( source ) {
if ( ! source ) {
return [];
}
const sourceName = source.split( '/' ).slice( -1 )[ 0 ];
return facetsOptions[ sourceName ];
};
const dataSources = Object.values( findstr.settingsIndexableFields ) || [];
const [ facets, setFacets ] = useState( [] );
const [ facetsOptions, setFacetsOptions ] = useState( {} );
useEffect( () => {
//call meilisearch
findstr.index.search( '', { facets: [ '*' ] } ).then( ( response ) => {
setFacetsOptions( prepareFacets( response.facetDistribution ) );
const facets = Object.entries( response.facetDistribution ).map(
( [ key, value ] ) => {
return key;
}
);
setFacets( facets );
} );
}, [] );
/**
* Filters the field options.
*
* Structure:
* - allFields: All fields available for all field types.
* - [fieldType]: The field type specific options. (checkbox, dropdown, toggle, etc.)
* - [OptionKey]: This is the option key.
* - [type]: How the option should be rendered. (select, radio, toggle)
* - [label]: The label of the option.
* - [default]: The default value of the option.
* - [data]: The data used to render the option.
* - [conditions]: The conditions that need to be met for the option to be rendered.
*
*
* @hook findstr.fieldOptions
* @param {Object} options All available options.
* @param {Object} editField The field being edited.
* @param {Object} option The current options.
*
* @return {Object} The field options.
*/
const options = findstr.hooks.applyFilters(
'fieldOptions',
{
allFields: {
sourceName: {
type: 'select',
data: prepareDataSources(
dataSources.filter( ( source ) => {
return source.filterable;
} )
),
label: _x( 'Source', 'edit field form', 'findstr' ),
conditions: {
fieldType: [ 'checkbox', 'dropdown', 'toggle' ],
},
},
excludeOptions: {
type: 'select',
conditions: {
fieldType: [ 'checkbox', 'dropdown' ],
},
isMulti: true,
data: prepareFacetsOptions( editField.options.sourceName ),
label: _x( 'Exclude options', 'edit field form', 'findstr' ),
},
},
search: {
hideMagnifier: {
type: 'toggle',
label: _x( 'Hide magnifier', 'edit field form', 'findstr' ),
default: false,
},
disableSearchAsYouType: {
type: 'toggle',
label: _x(
'Disable search as you type',
'edit field form',
'findstr'
),
default: false,
},
},
checkbox: {
hideFieldTitle: {
type: 'toggle',
label: _x( 'Hide field title', 'edit field form', 'findstr' ),
default: false,
},
multiselect: {
type: 'toggle',
label: _x( 'Multi select', 'edit field form', 'findstr' ),
default: false,
},
multiselectLogic: {
conditions: { multiselect: true },
type: 'radio',
label: _x(
'Multiple values Behavior',
'edit field form',
'findstr'
),
default: 'and',
data: [
{ label: 'AND', value: 'AND' },
{ label: 'OR', value: 'OR' },
],
},
hideEmpty: {
type: 'toggle',
label: _x(
'Hide options with zero results',
'edit field form',
'findstr'
),
default: false,
conditions: {
multiselect: true,
},
},
showCount: {
type: 'toggle',
label: _x( 'Show count', 'edit field form', 'findstr' ),
default: false,
},
disableCountUpdate: {
type: 'toggle',
label: _x(
'Disable count update',
'edit field form',
'findstr'
),
default: false,
},
},
dropdown: {
multiselect: {
type: 'toggle',
label: _x( 'Multi select', 'edit field form', 'findstr' ),
default: false,
},
hideEmpty: {
type: 'toggle',
label: _x(
'Hide options with zero results',
'edit field form',
'findstr'
),
default: false,
conditions: {
multiselect: true,
},
},
showCount: {
type: 'toggle',
label: _x( 'Show count', 'edit field form', 'findstr' ),
default: false,
},
multiselectLogic: {
conditions: { multiselect: true },
type: 'radio',
label: _x(
'Multiple values Behavior',
'edit field form',
'findstr'
),
default: 'and',
data: [
{ label: 'AND', value: 'AND' },
{ label: 'OR', value: 'OR' },
],
},
},
sort: {
sortableFields: {
type: 'select',
isMulti: true,
data: prepareDataSources(
dataSources.filter( ( source ) => source.sortable )
),
label: _x( 'Sortable sources', 'edit field form', 'findstr' ),
},
},
pagination: {
infiniteScroll: {
type: 'toggle',
label: _x( 'Infinite scroll', 'edit field form', 'findstr' ),
default: false,
},
},
loadMore: {
infiniteScroll: {
type: 'toggle',
label: _x( 'Infinite scroll', 'edit field form', 'findstr' ),
default: false,
},
showProgressBar: {
type: 'toggle',
label: _x(
'Show progress bar',
'edit field form',
'findstr'
),
default: false,
},
showPagesNumber: {
type: 'toggle',
label: _x(
'Show pages number',
'edit field form',
'findstr'
),
default: false,
},
},
toggle: {
targetValue: {
type: 'select',
isMulti: true,
tomselect: {
create: true,
persist: true,
},
data: [],
label: _x( 'Target values', 'edit field form', 'findstr' ),
},
},
datepicker: {
sourceName: {
type: 'select',
data: facets,
label: _x( 'Source', 'edit field form', 'findstr' ),
},
inline: {
type: 'toggle',
label: _x( 'Inline', 'edit field form', 'findstr' ),
default: false,
},
showEvents: {
type: 'toggle',
label: _x( 'Show events', 'edit field form', 'findstr' ),
default: false,
},
displayButtons: {
type: 'select',
label: _x( 'Display buttons', 'edit field form', 'findstr' ),
default: [],
data: [
{ text: 'Today', value: 'today' },
{ text: 'Clear', value: 'clear' },
],
isMulti: true,
},
dateType: {
type: 'select',
data: [
{ text: 'Single date', value: 'single' },
{ text: 'Date range', value: 'range' },
],
tomselect: {},
label: _x( 'Behavior', 'edit field form', 'findstr' ),
default: 'single',
},
singleDateBehavior: {
conditions: { dateType: 'single' },
type: 'radio',
label: _x( 'Behavior', 'edit field form', 'findstr' ),
data: [
{
label: _x(
'Filter at the date',
'datepicker option field',
'findstr'
),
value: 'current',
},
{
label: _x(
'Filter after date',
'datepicker option field',
'findstr'
),
value: 'after',
},
{
label: _x(
'Filter before date',
'datepicker option field',
'findstr'
),
value: 'before',
},
],
default: 'current',
},
},
reset: {
autoHide: {
type: 'toggle',
label: _x(
'Hide button when no reset is needed',
'edit field form',
'findstr'
),
default: false,
},
},
},
editField,
option
);
function fieldMeetConditions( editField, conditions ) {
if ( conditions ) {
for ( const [ condition_key, condition_value ] of Object.entries(
conditions
) ) {
//if field exists
if (
editField[ condition_key ] &&
condition_value.includes( editField[ condition_key ] )
) {
return true;
}
if (
editField?.options[ condition_key ] &&
editField.options[ condition_key ] === condition_value
) {
return true;
}
if (
! editField?.options[ condition_key ] &&
options?.[ editField.fieldType ]?.[ condition_key ]?.default &&
options?.[ editField.fieldType ]?.[ condition_key ]?.default ===
condition_value
) {
return true;
}
if ( editField.options[ condition_key ] !== condition_value ) {
return false;
}
}
}
return true;
}
useEffect( () => {
if ( typeof onChange === 'function' ) {
onChange( option );
}
}, [ option ] );
function renderOptions( key, value ) {
if ( fieldMeetConditions( editField, value.conditions ) ) {
switch ( value.type ) {
case 'radio':
return (
<li key={ key }>
<RadioControl
label={ value.label }
selected={
editField.options[ key ] ?? value.default
}
options={ value.data }
onChange={ ( value ) => {
setOption( { ...option, [ key ]: value } );
} }
/>
</li>
);
case 'toggle':
return (
<li key={ key }>
<ToggleControl
label={ value.label }
checked={
editField.options[ key ] ?? value.default
}
onChange={ ( value ) => {
setOption( { ...option, [ key ]: value } );
} }
/>
</li>
);
case 'select':
const plugins = [ 'drag_drop' ];
if ( value.isMulti ) {
plugins.push( 'remove_button' );
}
return (
<li key={ key }>
<BaseControl id={ key } label={ value.label }>
<Select
id={ key }
tomselect={ value.tomselect }
options={ value.data }
value={
editField.options[ key ] ?? value.default
}
isMulti={ value.isMulti ?? false }
plugins={ plugins }
name={ key }
onChange={ ( value ) => {
setOption( { ...option, [ key ]: value } );
} }
/>
</BaseControl>
</li>
);
case 'number':
return (
<li key={ key }>
<BaseControl id={ key } label={ value.label }>
<input
type="number"
id={ key }
value={ ( editField.options[ key ] ?? value.default ) || 0 }
min={ value.min ?? undefined }
max={ value.max ?? undefined }
step={ value.step ?? undefined }
onChange={ ( event ) => {
setOption( {
...option,
[ key ]: parseFloat( event.target.value ),
} );
} }
/>
</BaseControl>
</li>
);
}
}
}
let fieldOptions = Object.entries( options.allFields );
if ( options[ editField.fieldType ] ) {
fieldOptions = [
...fieldOptions,
...Object.entries( options[ editField.fieldType ] ),
];
}
return (
<div className="options">
<ul>
{ fieldOptions.map( ( [ key, value ] ) => {
return renderOptions( key, value );
} ) }
</ul>
</div>
);
}