From a7248359793b32444799d56cf2fada01336a9e08 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Mon, 28 Jul 2025 16:32:16 +0200 Subject: [PATCH 1/2] Introduce `SuggestionElement` --- asset/css/compat.less | 5 ++ asset/css/suggestion-element.less | 19 ++++++ src/FormElement/SuggestionElement.php | 86 +++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 asset/css/suggestion-element.less create mode 100644 src/FormElement/SuggestionElement.php diff --git a/asset/css/compat.less b/asset/css/compat.less index 9f5482a55..f30cc2a5c 100644 --- a/asset/css/compat.less +++ b/asset/css/compat.less @@ -92,6 +92,11 @@ form.icinga-form .control-group { } } +// suggestion-element style +form.icinga-form .control-group span.suggestion-element-icon { + margin-right: 0; +} + .module-icingadb { // Icinga DB Web (legacy) table header layout (e.g. in group details) > .controls { diff --git a/asset/css/suggestion-element.less b/asset/css/suggestion-element.less new file mode 100644 index 000000000..7198c5e17 --- /dev/null +++ b/asset/css/suggestion-element.less @@ -0,0 +1,19 @@ +form { + input[type="text"].suggestion-element, + .suggestion-element-icon { + line-height: normal; + height: 2.25em; + padding: 0.5em; + background-color: var(--default-input-bg, @default-input-bg); + color: var(--default-text-color, @default-text-color); + } + + input[type="text"].suggestion-element { + border: none; + border-radius: 0 0.25em 0.25em 0; + } + + .suggestion-element-icon { + border-radius: 0.25em 0 0 0.25em; + } +} diff --git a/src/FormElement/SuggestionElement.php b/src/FormElement/SuggestionElement.php new file mode 100644 index 000000000..dcd067deb --- /dev/null +++ b/src/FormElement/SuggestionElement.php @@ -0,0 +1,86 @@ + 'off', + 'class' => 'suggestion-element', + 'data-enrichment-type' => 'completion' + ]; + + /** @var Url URL to fetch suggestions from */ + protected Url $suggestionsUrl; + + /** + * Create a new SuggestionElement + * + * @param string $name Name of the form element + * @param Url $suggestionsUrl URL to fetch suggestions from + * @param ?(array|Attributes) $attributes Attributes of the form element + */ + public function __construct(string $name, Url $suggestionsUrl, array|Attributes $attributes = null) + { + parent::__construct($name, $attributes); + + $this->setSuggestionsUrl($suggestionsUrl); + } + + /** + * Get the URL to fetch suggestions from + * + * @return Url + */ + public function getSuggestionsUrl(): Url + { + return $this->suggestionsUrl; + } + + /** + * Set the URL to fetch suggestions from + * + * @param Url $suggestionsUrl + * + * @return $this + */ + public function setSuggestionsUrl(Url $suggestionsUrl): static + { + $this->suggestionsUrl = $suggestionsUrl; + + return $this; + } + + /** + * @return string If not set, returns a default placeholder + */ + public function getPlaceholder(): string + { + return $this->placeholder ?? $this->translate('Start typing to see suggestions…'); + } + + protected function assemble(): void + { + $suggestionsId = uniqid('search-suggestions-'); + + $this->prependWrapper( + (new HtmlDocument()) + ->addHtml( + new HtmlElement('div', new Attributes(['id' => $suggestionsId, 'class' => 'search-suggestions'])), + new HtmlElement('span', new Attributes(['class' => 'suggestion-element-icon']), new Icon('search')) + ) + ); + + $this->getAttributes()->add([ + 'data-term-suggestions' => '#' . $suggestionsId, + 'data-suggest-url' => $this->getSuggestionsUrl() + ]); + } +} From ff14afd9e449ca2fe609247bc359c4e93987e230 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Wed, 6 Aug 2025 16:53:32 +0200 Subject: [PATCH 2/2] SelectElement: Outline the input and icon when input is focused --- asset/css/compat.less | 8 ++++++-- asset/css/suggestion-element.less | 16 +++++++++++++--- asset/css/variables.less | 6 ++++-- src/FormElement/SuggestionElement.php | 11 ++++++----- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/asset/css/compat.less b/asset/css/compat.less index f30cc2a5c..f29dc67b8 100644 --- a/asset/css/compat.less +++ b/asset/css/compat.less @@ -93,8 +93,12 @@ form.icinga-form .control-group { } // suggestion-element style -form.icinga-form .control-group span.suggestion-element-icon { - margin-right: 0; +form.icinga-form .suggestion-element-group { + flex: 1 1 auto; + + .suggestion-element { + border-radius: 0 0.25em 0.25em 0; + } } .module-icingadb { diff --git a/asset/css/suggestion-element.less b/asset/css/suggestion-element.less index 7198c5e17..db6b89fc4 100644 --- a/asset/css/suggestion-element.less +++ b/asset/css/suggestion-element.less @@ -1,5 +1,7 @@ -form { - input[type="text"].suggestion-element, +.suggestion-element-group { + display: inline-flex; + + .suggestion-element, .suggestion-element-icon { line-height: normal; height: 2.25em; @@ -8,12 +10,20 @@ form { color: var(--default-text-color, @default-text-color); } - input[type="text"].suggestion-element { + .suggestion-element { border: none; + outline: none; border-radius: 0 0.25em 0.25em 0; } .suggestion-element-icon { + padding-right: 0; border-radius: 0.25em 0 0 0.25em; } + + &:focus-within { + border-radius: 0.25em; + outline: 3px solid var(--default-input-outline-color, @default-input-outline-color); + outline-offset: 1px; + } } diff --git a/asset/css/variables.less b/asset/css/variables.less index b5d0e773f..01c2e68f6 100644 --- a/asset/css/variables.less +++ b/asset/css/variables.less @@ -42,6 +42,7 @@ @default-text-color-inverted: @default-bg; @default-input-bg: #404d72; @default-input-hover-bg: #434374; +@default-input-outline-color: @base-primary-light; @default-remove-bg: @state-critical; @default-remove-color: @default-text-color-inverted; @default-delete-bg: @state-critical; @@ -123,7 +124,7 @@ @schedule-element-fields-selected-bg: @primary-button-bg; @schedule-element-fields-selected-color: @default-text-color-inverted; @schedule-element-fields-hover-bg: @base-primary-light; -@schedule-element-fields-outline-color: fade(@base-primary-bg, 50%); +@schedule-element-fields-outline-color: @default-input-outline-color; @schedule-element-fields-selected-outline-color: fade(#fff, 50%); @schedule-element-fields-selected-hover-bg: @primary-button-hover-bg; @schedule-element-fields-disabled-color: @base-gray; @@ -152,6 +153,7 @@ --default-text-color-inverted: #F5F9FA; --default-input-bg: #DEECF1; --default-input-hover-bg: #C0CCCD; + --default-input-outline-color: @base-primary-light; --default-remove-bg: var(--base-remove-bg); --default-remove-color: var(--default-text-color-inverted); --default-delete-bg: var(--base-remove-bg); @@ -219,7 +221,7 @@ --schedule-element-fields-selected-bg: var(--primary-button-bg); --schedule-element-fields-selected-color: var(--default-text-color-inverted); --schedule-element-fields-hover-bg: @base-primary-light; - --schedule-element-fields-outline-color: fade(@base-primary-bg, 50%); + --schedule-element-fields-outline-color: @default-input-outline-color; --schedule-element-fields-selected-outline-color: fade(#fff, 50%); --schedule-element-fields-selected-hover-bg: var(--primary-button-hover-bg); --schedule-element-fields-disabled-color: var(--base-gray); diff --git a/src/FormElement/SuggestionElement.php b/src/FormElement/SuggestionElement.php index dcd067deb..83f3d45cc 100644 --- a/src/FormElement/SuggestionElement.php +++ b/src/FormElement/SuggestionElement.php @@ -71,11 +71,12 @@ protected function assemble(): void $suggestionsId = uniqid('search-suggestions-'); $this->prependWrapper( - (new HtmlDocument()) - ->addHtml( - new HtmlElement('div', new Attributes(['id' => $suggestionsId, 'class' => 'search-suggestions'])), - new HtmlElement('span', new Attributes(['class' => 'suggestion-element-icon']), new Icon('search')) - ) + new HtmlElement( + 'div', + new Attributes(['class' => 'suggestion-element-group']), + new HtmlElement('div', new Attributes(['id' => $suggestionsId, 'class' => 'search-suggestions'])), + new HtmlElement('span', new Attributes(['class' => 'suggestion-element-icon']), new Icon('search')) + ) ); $this->getAttributes()->add([