Copied!
<?php namespace PHPFUI\Input; /** * AutoComplete allows you to have an autocomplete field for any * arbitrary data source. Based on jQuery-Autocomplete * * @link https://github.com/devbridge/jQuery-Autocomplete */ class AutoComplete extends \PHPFUI\Input\Input { use \PHPFUI\Traits\Page; protected string $className; protected \PHPFUI\Input\Hidden $hidden; protected bool $noFreeForm = false; /** @var array<string, mixed> */ protected array $options = []; /** * Construct a AutoComplete. * * **Required callback behavior:** * * The callback function must take an array and returns an * array. * * **Input Array:** * * If the input array contains an index named **'save'** then the * user has indicated they have selected text value passed in * the **'AutoComplete'** index. This generally means you should set * the value of the hidden field (set / getHiddenField) to the * value of **'AutoComplete'**. If save is not set, you must return * matches for the text in **'AutoComplete'** in the format * descriped below. * * **Return Array:** * * Has one index **'suggestions'** that contains an array of matches * in the form of `['value' => 'Text to display', 'data' => 123]`. * If **'save'** is specified, the **'suggestions'** value should be an * empty array. * * @param \PHPFUI\Page $page requires JS * @param callable $callback See above for correct callback * behavior * @param string $type of input field * @param string $name of field * @param string $label for field, optional * @param ?string $value initial value, optional * */ public function __construct(protected \PHPFUI\Page $page, protected $callback, string $type, string $name, ?string $label = null, ?string $value = null) { $this->hidden = new \PHPFUI\Input\Hidden($name, $value);	$name .= 'Text'; parent::__construct($type, $name, $label, $value); $this->hidden->setId($this->getId() . 'hidden'); $this->add($this->hidden); $this->className = \basename(\str_replace('\\', '/', self::class)); $this->page->addTailScript('jquery.autocomplete.js'); $this->addAttribute('autocomplete', 'off'); if (isset($_POST[$this->className]) && \PHPFUI\Session::checkCSRF() && $_POST['fieldName'] == $name)	{	$returnValue = \json_encode(\call_user_func($this->callback, $_POST), JSON_INVALID_UTF8_IGNORE); if ($returnValue)	{ $this->page->setRawResponse($returnValue);	}	}	$csrf = \PHPFUI\Session::csrf("'");	$csrfField = \PHPFUI\Session::csrfField();	$dollar = '$'; $this->options = [ 'minChars' => 3, 'type' => "'POST'", 'autoSelectFirst' => true, 'showNoSuggestionNotice' => true, 'paramName' => "'{$this->className}'", 'serviceUrl' => "'{$this->page->getBaseURL()}'", 'params' => ['fieldName' => "'{$name}'", $csrfField => $csrf], 'onSearchStart' => "function(){{$dollar}('#'+id+'hidden').val('').change()}", 'onSelect' => "function(suggestion){if(noFF){{$dollar}('#'+id).attr('placeholder',suggestion.value).attr('value','');};" . "{$dollar}('#'+id+'hidden').val(suggestion.data).change();" . "{$dollar}.ajax({type:'POST',traditional:true,data:{{$csrfField}:{$csrf},save:true,fieldName:'{$name}',{$this->className}:suggestion.data}})}", 'onInvalidateSelection' => "function(){{$dollar}('#'+id+'hidden').val('').change()}",	];	} /** * Add an option for jQuery-Autocomplete. * * @link https://github.com/devbridge/jQuery-Autocomplete */ public function addAutoCompleteOption(string $option, mixed $value) : static { $this->options[$option] = $value; return $this;	} /** * Get an option for jQuery-Autocomplete. * * @link https://github.com/devbridge/jQuery-Autocomplete * * @param string $option to retrieve */ public function getAutoCompleteOption(string $option) : mixed { return $this->options[$option] ?? null;	} /** * Returns the hidden field which is where the autocompleted * value will be stored. The hidden field name is the same name * as the AutoComplete field was constructed with. This should * generally be used to save the value the user has selected * when 'save' is passed to the callback. */ public function getHiddenField() : \PHPFUI\Input\Hidden { return $this->hidden;	} /** * Called recursively by Reveal to force fixed postion autocomplete hints. */ public function inReveal(bool $isInRevealModal = true) : static { return $this->addAutoCompleteOption('forceFixPosition', $isInRevealModal);	} /** * Remove an option for jQuery-Autocomplete. * * @link https://github.com/devbridge/jQuery-Autocomplete * * @param string $option to remove */ public function removeAutoCompleteOption(string $option) : static { unset($this->options[$option]); return $this;	} /** * Set an option for jQuery-Autocomplete. * * @link https://github.com/devbridge/jQuery-Autocomplete * * @param string $option name to add */ public function setAutoCompleteOption(string $option, mixed $value) : static { $this->options[$option] = $value; return $this;	} /** * If No Free Form is turned on, then the user can only pick * suggested values. It is off by default allowing the user to * specify any text and not just suggestions. */ public function setNoFreeForm(bool $on = true) : static { $this->noFreeForm = $on; if ($this->noFreeForm)	{ $this->addAttribute('placeholder', \str_replace("'", '&#39;', $this->value)); $this->value = '';	} else	{ $this->value = $this->getAttribute('placeholder'); $this->deleteAttribute('placeholder');	} return $this;	} protected function getEnd() : string {	$id = $this->getId();	$noFreeForm = (int)($this->noFreeForm); $this->page->addJavaScript("{$id}('{$id}','{$this->name}',{$noFreeForm})"); return parent::getEnd();	} protected function getStart() : string {	$id = $this->getId(); if ($this->required)	{ $this->setAutoCompleteRequired($this->page, $this);	}	$js = "function {$id}(id,fieldName,noFreeForm){var noFF=noFreeForm;";	$js .= '$("#"+id).devbridgeAutocomplete(' . \PHPFUI\TextHelper::arrayToJS($this->options) . ')}'; $this->page->addJavaScript($js); return parent::getStart();	}	} 
© 2025 Bruce Wells