Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,22 @@ export default class OptionFilterFactory {

/**
* @param {HTMLElement} inputFieldWithOptionFilter
* @param {string} optionsDataSource
* @param {string} optionsDataSourceToken
* @param {string} optionsDataSourceDisplayValueToken
* @param {number} optionsDataSourceSuggestionStart
* @param {null|string} selectedValue
* @return {OptionFilter}
* @throws {Error} if the input was already initialized.
*/
init(inputFieldWithOptionFilter) {
init(
inputFieldWithOptionFilter,
optionsDataSource = '',
optionsDataSourceToken = 'term',
optionsDataSourceDisplayValueToken = 'display_value',
optionsDataSourceSuggestionStart = 3,
selectedValue = null,
) {
if (inputFieldWithOptionFilter === undefined) {
throw new TypeError('During init of an InputHasOptionFilter an undefined element was passed to the factory.');
}
Expand All @@ -42,7 +54,9 @@ export default class OptionFilterFactory {
const itemList = inputFieldContext.querySelector('.c-field--has-option-filter__list');
const items = itemList.querySelectorAll('.c-field--has-option-filter__item');
const messageNoMatch = inputFieldContext.querySelector('.message-no-match');
const messageAsyncStartSearch = inputFieldContext.querySelector('.message-async-start-search');
const resultCountDisplay = inputFieldContext.querySelector('.c-input--has-option-filter__synopsis [role="status"]');
const loaderAnimation = inputFieldContext.querySelector('.c-input--has-option-filter__loader');

/* Buttons */
const clearFilterButton = inputFieldContext.querySelector('.c-input--has-option-filter__clear-search');
Expand All @@ -52,12 +66,19 @@ export default class OptionFilterFactory {

const instance = new OptionFilter(
inputFieldWithOptionFilter,
optionsDataSource === '' ? null : optionsDataSource,
optionsDataSourceToken,
optionsDataSourceDisplayValueToken,
optionsDataSourceSuggestionStart,
JSON.parse(selectedValue),
scrollContainer,
searchbar,
listType,
itemList,
items,
messageNoMatch,
messageAsyncStartSearch,
loaderAnimation,
clearFilterButton,
engageDisengageToggle,
toggleExpandText,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,16 @@
*/
interface HasOptionFilter extends FormInput
{
public const int OPTIONS_DATA_SOURCE_SUGGESTION_START = 3;

/**
* Adds a collapsed view and a searchbar to the field.
*/
public function withHasOptionFilter(bool $has_option_filter = true): static;
public function withHasOptionFilter(
bool $has_option_filter = true,
string $options_data_source = '',
string $options_data_source_token = 'term',
string $options_data_source_display_value_token = 'display_values',
int $options_data_source_suggestion_start = self::OPTIONS_DATA_SOURCE_SUGGESTION_START
): static;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,41 @@

namespace ILIAS\UI\Implementation\Component\Input\Field;

use Exception;

/**
* @see HasOptionFilterInternal for interface definition
*/
trait HasOptionFilter
{
protected bool $has_option_filter = false;
protected string $options_data_source = '';
protected string $options_data_source_token = 'term';
protected string $options_data_source_display_value_token = 'display_values';
protected int $options_data_source_suggestion_start = self::OPTIONS_DATA_SOURCE_SUGGESTION_START;

/** @var array<string, string> (value => label) */
protected array $options = [];

public function withHasOptionFilter(bool $has_option_filter = true): static
{
/**
* @throws Exception
*/
public function withHasOptionFilter(
bool $has_option_filter = true,
string $options_data_source = '',
string $options_data_source_token = 'term',
string $options_data_source_display_value_token = 'display_values',
int $options_data_source_suggestion_start = self::OPTIONS_DATA_SOURCE_SUGGESTION_START
): static {
if ($this->getOptions() !== [] && $options_data_source) {
throw new Exception('Input should not have data source when using options');
}
$clone = clone $this;
$clone->has_option_filter = $has_option_filter;
$clone->options_data_source = $options_data_source;
$clone->options_data_source_token = $options_data_source_token;
$clone->options_data_source_display_value_token = $options_data_source_display_value_token;
$clone->options_data_source_suggestion_start = $options_data_source_suggestion_start;
return $clone;
}

Expand All @@ -42,6 +63,26 @@ public function hasOptionFilter(): bool
return $this->has_option_filter;
}

public function getOptionsDataSource(): string
{
return $this->options_data_source;
}

public function getOptionsDataSourceToken(): string
{
return $this->options_data_source_token;
}

public function getOptionsDataSourceDisplayValueToken(): string
{
return $this->options_data_source_display_value_token;
}

public function getOptionsDataSourceSuggestionStart(): int
{
return $this->options_data_source_suggestion_start;
}

public function getOptions(): array
{
return $this->options;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,12 @@ interface HasOptionFilterInternal extends C\Field\HasOptionFilter
* @return array<string, string> (value => label)
*/
public function getOptions(): array;

public function getOptionsDataSource(): string;

public function getOptionsDataSourceToken(): string;

public function getOptionsDataSourceDisplayValueToken(): string;

public function getOptionsDataSourceSuggestionStart(): int;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

namespace ILIAS\UI\Implementation\Component\Input\Field;

use Exception;
use ILIAS\UI\Component as C;
use ILIAS\Data\Factory as DataFactory;
use ILIAS\Refinery\Constraint;
Expand All @@ -34,6 +35,7 @@ class MultiSelect extends FormInput implements C\Input\Field\MultiSelect, HasOpt

/**
* @param array<string, string> $options
* @throws Exception
*/
public function __construct(
DataFactory $data_factory,
Expand All @@ -43,6 +45,11 @@ public function __construct(
?string $byline
) {
parent::__construct($data_factory, $refinery, $label, $byline);

if ($options !== [] && $this->getOptionsDataSource()) {
throw new Exception('Input should not have options when using data source');
}

$this->options = $options;
}

Expand All @@ -55,6 +62,10 @@ protected function isClientSideValueOk($value): bool
return true;
}
if (is_array($value)) {
if ($this->getOptionsDataSource()) {
return true;
}

foreach ($value as $v) {
if (!array_key_exists($v, $this->options)) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

namespace ILIAS\UI\Implementation\Component\Input\Field;

use Exception;
use ILIAS\UI\Component as C;
use ILIAS\UI\Component\Input\InputData;
use ILIAS\UI\Implementation\Component\JavaScriptBindable;
Expand Down Expand Up @@ -47,7 +48,15 @@ class Radio extends FormInput implements C\Input\Field\Radio, HasOptionFilterInt
*/
protected function isClientSideValueOk($value): bool
{
return ($value === null || array_key_exists($value, $this->getOptions()));
if ($value === null) {
return true;
}

if ($this->getOptionsDataSource()) {
return true;
}

return array_key_exists($value, $this->getOptions());
}

/**
Expand All @@ -66,9 +75,14 @@ protected function getConstraintForRequirement(): ?Constraint

/**
* @inheritdoc
* @throws Exception
*/
public function withOption(string $value, string $label, ?string $byline = null): C\Input\Field\Radio
{
if ($this->getOptionsDataSource()) {
throw new Exception('Input should not have options when using data source');
}

$clone = clone $this;
$clone->options[$value] = $label;
if (!is_null($byline)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1334,8 +1334,16 @@ protected function renderOptionFilter(string $input_html, F\HasOptionFilterInter
$option_filter_template->setVariable('SEARCH_LABEL', $this->txt("ui_field_option_filter_search_in"));
$option_filter_template->setVariable('SCREEN_READER_HINT', $this->txt('ui_field_option_filter_screen_reader_hint'));
$option_filter_template->setVariable('NO_MATCH', $this->txt('ui_field_option_filter_no_match'));
$option_filter_template->setVariable('ASYNC_START_SEARCH', sprintf(
$this->txt('ui_field_option_filter_async_start_search'),
$component->getOptionsDataSourceSuggestionStart()
));
$option_filter_template->setVariable('OPTIONS_SHOWN', $this->txt('ui_field_option_filter_options_shown'));

if (!$component->getOptionsDataSource()) {
$option_filter_template->touchBlock("not_async");
}

$expand_icon = $default_renderer->render($this->getUIFactory()->symbol()->glyph()->expand());
$option_filter_template->setVariable('EXPAND_TEXT', $expand_icon . $this->txt('ui_field_option_filter_show_all_options'));

Expand All @@ -1345,8 +1353,17 @@ protected function renderOptionFilter(string $input_html, F\HasOptionFilterInter
$remove_icon = $default_renderer->render($this->getUIFactory()->symbol()->glyph()->remove());
$option_filter_template->setVariable('CLEAR_SEARCH_BTN', $remove_icon . $this->txt('ui_field_option_filter_clear_search'));

$json_encoded_value = json_encode($component->getValue(), JSON_THROW_ON_ERROR);

$component = $component->withAdditionalOnLoadCode(
static fn($id): string => "il.UI.Input.optionFilter.init(document.getElementById('$id'));",
static fn($id): string => "il.UI.Input.optionFilter.init(
document.getElementById('$id'),
'{$component->getOptionsDataSource()}',
'{$component->getOptionsDataSourceToken()}',
'{$component->getOptionsDataSourceDisplayValueToken()}',
{$component->getOptionsDataSourceSuggestionStart()},
'$json_encoded_value'
);",
);

return [$option_filter_template->get(), $component];
Expand Down
Loading