Skip to content

Commit

Permalink
Add class SimpleSearchField and simpleSuggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
sukhwinder33445 authored and nilmerg committed May 3, 2024
1 parent 20a7a22 commit 5ce928a
Show file tree
Hide file tree
Showing 2 changed files with 366 additions and 0 deletions.
170 changes: 170 additions & 0 deletions src/Control/SimpleSearchField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<?php

namespace ipl\Web\Control;

use ipl\Html\Attributes;
use ipl\Html\Form;
use ipl\Html\FormElement\InputElement;
use ipl\Html\FormElement\SubmitElement;
use ipl\Html\HtmlElement;
use ipl\Web\Url;
use ipl\Web\Widget\Icon;

use function ipl\I18n\t;

/**
* Form for simple value based search
*/
class SimpleSearchField extends Form
{
protected $defaultAttributes = [
'class' => 'search-field',
'name' => 'search-field',
'role' => 'search'
];

/** @var string The term separator */
public const TERM_SEPARATOR = ',';

/** @var string The default search parameter */
public const DEFAULT_SEARCH_PARAM = 'q';

/** @var string The search parameter */
protected $searchParameter;

/** @var Url The suggestion url */
protected $suggestionUrl;

/** @var string Submit label */
protected $submitLabel;

/**
* Set the search parameter
*
* @param string $name
*
* @return $this
*/
public function setSearchParameter(string $name): self
{
$this->searchParameter = $name;

return $this;
}

/**
* Get the search parameter
*
* @return string
*/
public function getSearchParameter(): string
{
return $this->searchParameter ?: self::DEFAULT_SEARCH_PARAM;
}

/**
* Set the suggestion url
*
* @param Url $url
*
* @return $this
*/
public function setSuggestionUrl(Url $url): self
{
$this->suggestionUrl = $url;

return $this;
}

/**
* Get the suggestion url
*
* @return Url
*/
public function getSuggestionUrl(): Url
{
return $this->suggestionUrl;
}

/**
* Set submit label
*
* @param string $label
*
* @return $this
*/
public function setSubmitLabel(string $label): self
{
$this->submitLabel = $label;

return $this;
}

/**
* Get submit label
*
* @return string
*/
public function getSubmitLabel(): string
{
return $this->submitLabel ?? t('Submit');
}

public function assemble()
{
$filterInput = new InputElement(null, [
'type' => 'text',
'placeholder' => t('Type to search'),
'class' => 'search-field',
'id' => 'search-filed',
'autocomplete' => 'off',
'required' => true,
'data-no-auto-submit' => true,
'data-no-js-placeholder' => true,
'data-enrichment-type' => 'terms',
'data-term-separator' => self::TERM_SEPARATOR,
'data-term-mode' => 'read-only',
'data-term-direction' => 'vertical',
'data-data-input' => '#data-input',
'data-term-input' => '#term-input',
'data-term-container' => '#term-container',
'data-term-suggestions' => '#term-suggestions',
'data-suggest-url' => $this->getSuggestionUrl()
]);

$dataInput = new InputElement('data', ['type' => 'hidden', 'id' => 'data-input']);

$termInput = new InputElement($this->getSearchParameter(), ['type' => 'hidden', 'id' => 'term-input']);
$this->registerElement($termInput);

$termContainer = new HtmlElement(
'div',
Attributes::create(['id' => 'term-container', 'class' => 'term-container'])
);

$termSuggestions = new HtmlElement(
'div',
Attributes::create(['id' => 'term-suggestions', 'class' => 'search-suggestions'])
);

$submitButton = new SubmitElement('submit', ['label' => $this->getSubmitLabel()]);

$this->registerElement($submitButton);

$this->add([
HtmlElement::create(
'div',
null,
[
new Icon('search', ['class' => 'search-icon']),
$filterInput,
$termSuggestions,
$dataInput,
$termInput,
$submitButton
]
),
$termContainer
]);
}
}
196 changes: 196 additions & 0 deletions src/Control/SimpleSuggestions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<?php

namespace ipl\Web\Control;

use ArrayIterator;
use ipl\Html\Attributes;
use ipl\Html\BaseHtmlElement;
use ipl\Html\FormattedString;
use ipl\Html\FormElement\ButtonElement;
use ipl\Html\FormElement\InputElement;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use IteratorIterator;
use LimitIterator;
use Psr\Http\Message\ServerRequestInterface;

abstract class SimpleSuggestions extends BaseHtmlElement
{
/** @var int Suggestions limit */
public const DEFAULT_LIMIT = 10;

/** @var string Class name for suggestion title */
public const SUGGESTION_TITLE_CLASS = 'suggestion-title';

protected $tag = 'ul';

/** @var string The given input for search */
protected $searchTerm;

/** @var mixed Fetched data for given input */
protected $data;

/** @var string Default first suggestion in the suggestion list */
protected $default;

/**
* Set the search term
*
* @param string $searchTerm
*
* @return $this
*/
public function setSearchTerm(string $searchTerm): self
{
$this->searchTerm = $searchTerm;

return $this;
}

/**
* Set the fetched data
*
* @param mixed $data
*
* @return $this
*/
public function setData($data): self
{
$this->data = $data;

return $this;
}

/**
* Set the default suggestion
*
* @param string $default
*
* @return $this
*/
public function setDefault(string $default): self
{
$this->default = trim($default, '"\'');

return $this;
}

/**
* Fetch suggestions according to the input in the search field
*
* @param string $searchTerm The given input in the search field
* @param array $exclude Already added terms to be excluded from the suggestion list
*
* @return mixed
*/
abstract protected function fetchSuggestions(string $searchTerm, array $exclude = []);

protected function assembleDefault(): void
{
if ($this->default === null) {
return;
}

$attributes = [
'type' => 'button',
'tabindex' => -1,
'data-label' => $this->default,
'value' => $this->default,
];

$button = new ButtonElement(null, $attributes);
$button->addHtml(FormattedString::create(
t('Add %s'),
new HtmlElement('em', null, Text::create($this->default))
));

$this->prependHtml(new HtmlElement('li', Attributes::create(['class' => 'default']), $button));
}

protected function assemble()
{
if ($this->data === null) {
$data = [];
} else {
$data = $this->data;
if (is_array($data)) {
$data = new ArrayIterator($data);
}

$data = new LimitIterator(new IteratorIterator($data), 0, self::DEFAULT_LIMIT);
}

foreach ($data as $term => $label) {
if (is_int($term)) {
$term = $label;
}

$attributes = [
'type' => 'button',
'tabindex' => -1,
'data-search' => $term
];

$attributes['value'] = $label;
$attributes['data-label'] = $label;

$this->addHtml(new HtmlElement('li', null, new InputElement(null, $attributes)));
}

$showDefault = true;
if ($this->searchTerm && $this->count() === 1) {
// The default option is not shown if the user's input result in an exact match
$input = $this->getFirst('li')->getFirst('input');
$showDefault = $input->getValue() != $this->searchTerm
&& $input->getAttributes()->get('data-search')->getValue() != $this->searchTerm;
}

if ($showDefault) {
$this->assembleDefault();
}
}

/**
* Load suggestions as requested by the client
*
* @param ServerRequestInterface $request
*
* @return $this
*/
public function forRequest(ServerRequestInterface $request): self
{
if ($request->getMethod() !== 'POST') {
return $this;
}

$requestData = json_decode($request->getBody()->read(8192), true);
if (empty($requestData)) {
return $this;
}

$search = $requestData['term']['search'];
$label = $requestData['term']['label'];
$exclude = $requestData['exclude'];

$this->setSearchTerm($search);

$this->setData($this->fetchSuggestions($label, $exclude));

if (! empty($search)) {
$this->setDefault($search);
}

return $this;
}

public function renderUnwrapped()
{
$this->ensureAssembled();

if ($this->isEmpty()) {
return '';
}

return parent::renderUnwrapped();
}
}

0 comments on commit 5ce928a

Please sign in to comment.