import React, { Fragment, useState, useRef, useEffect, useMemo } from 'react'; import { useSelect } from '@wordpress/data'; import { sprintf, __ } from '@wordpress/i18n'; import { store } from '../../data'; import { Field } from '../../types'; import SelectOperator from './select-operator'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from '@/components/ui/select'; type RuleInputProps = { disabled: boolean, currentField: Field, name: string, operator: string, value: string, onChange( e: React.ChangeEvent ): void, } /** * Rule input component. */ const RuleInput = ( props: RuleInputProps ) => { const { disabled, currentField, name, operator, value, onChange, } = props; // Get the currently available values for the field. const options = currentField.data?.map( option => ( { value: option.value, label: ( option.label ? `${ option.label } (${ option.value })` : option.value ) + String( option.percent ? ` ~${ option.percent }%` : '' ), count: Number( option?.count ) || 0, percent: Number( option?.percent ) || 0, } ) ) || []; // Sort options by their size/hits. const topHits = options.filter( option => option.percent ).slice( 0, 10 ); topHits.sort( ( a, b ) => b.percent - a.percent ); // Check if the filter doesn't allow free text. const allowFreeText = ! currentField?.options?.disable_free_text; // Get input refs to manage focus. const inputEl = useRef( null ); // Default to display of the dropdown if the value is in existing data or empty. const defaultDropdownState = value === '' || !! options.find( option => option.value === value ); const [ showDropdown, setShowDropdown ] = useState( defaultDropdownState ); useEffect( () => { if ( ! showDropdown ) { inputEl && inputEl.current && inputEl.current.focus(); } }, [ showDropdown ] ); // Set intial dropdown state and update if the field has changed. const [ prevField, setPrevField ] = useState( null ); if ( currentField.name !== prevField ) { setShowDropdown( defaultDropdownState ); setPrevField( currentField.name ); } // Placeholder for empty value (Radix Select doesn't allow empty string values) const EMPTY_VALUE = '__empty__'; // Create a synthetic event to maintain compatibility with existing handlers const handleValueChange = ( newValue: string ) => { if ( newValue === '___' ) { setShowDropdown( false ); } else { // Convert placeholder back to empty string const actualValue = newValue === EMPTY_VALUE ? '' : newValue; onChange( { target: { value: actualValue }, } as React.ChangeEvent ); } }; // Convert empty value to placeholder for Select component const selectValue = value === '' ? EMPTY_VALUE : value; switch ( currentField.type ) { case 'number': return ( ); case 'string': default: switch ( operator ) { case '=': case '!=': case '*=': case '!*': case '^=': return ( { ! showDropdown && (
{ // If the value is empty or in the field data convert back to dropdown on blur. setShowDropdown( defaultDropdownState ); } } onChange={ onChange } />
) } { showDropdown && ( ) }
); default: return null; } } }; interface Props { canRemove: boolean, field: string, namePrefix: string, operator: string, readOnly?: boolean, value: string, onChange( e: { field?: string, operator?: string, value?: string } ): void, onRemove(): void, } const DEFAULT_FIELD : Field = { label: '', name: '', type: 'string', data: [], options: {}, stats: undefined, }; /** * The rule editor input component. */ export default function Rule( props: Props ) { const { canRemove, field, namePrefix, operator, readOnly, value, onChange, onRemove, } = props; const fields = useSelect( select => select( store ).getFields(), [] ); const currentField = useMemo( () => { return fields.find( fieldData => fieldData.name === field ); }, [ fields, field ] ); const persistentFields = fields.filter( fieldData => fieldData.name.match( /^endpoint\./ ) ); const pointInTimeFields = fields.filter( fieldData => ! fieldData.name.match( /^endpoint\./ ) ); // Create a synthetic event to maintain compatibility with existing handlers const handleFieldChange = ( newValue: string ) => { onChange( { field: newValue, value: '', } ); }; return (
onChange( { operator: e.target.value } ) } /> onChange( { value: e.target.value } ) } /> { canRemove && ( ) } { currentField?.options?.description && (

{ currentField.options.description }

) }
); }