WIP Migrate to stimulus.js and tailwind

This commit is contained in:
Björn Fromme
2021-08-20 18:24:42 +02:00
parent 2f0f8a8755
commit e4c4a5253e
13 changed files with 347 additions and 312 deletions
@@ -86,6 +86,15 @@ class AjaxSearchController extends ActionController
->searchResultService
->getResult($filterSettings, $excludedConceptUids);
// No results? First retry without date constraint if any
if ((!empty($filterSettings['dateFrom']) || !empty($filterSettings['dateTo'])) && 0 === $searchResult->getTotal()) {
$filterSettings['dateFrom'] = null;
$filterSettings['dateTo'] = null;
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
$organic = false;
}
// Still no results? Remove all constraints and search again with defaults
if (0 === $searchResult->getTotal()) {
$filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids);
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
@@ -136,9 +136,20 @@ class SearchController extends ActionController
$dateSelected = true;
}
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
$searchResult = $this
->searchResultService
->getResult($filterSettings, $excludedConceptUids);
if ($searchResult->getTotal() === 0) {
// No results? First retry without date constraint if any
if ((!empty($filterSettings['dateFrom']) || !empty($filterSettings['dateTo'])) && 0 === $searchResult->getTotal()) {
$filterSettings['dateFrom'] = null;
$filterSettings['dateTo'] = null;
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
$organic = false;
}
// Still no results? Remove all constraints and search again with defaults
if (0 === $searchResult->getTotal()) {
$filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids);
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
$organic = false;
@@ -33,7 +33,7 @@ use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class AjaxSearchbarController extends ActionController
class SearchbarController extends ActionController
{
use RequestArgumentTypeConversionTrait;
@@ -66,7 +66,6 @@ class AjaxSearchbarController extends ActionController
/**
* @param array $searchParams
* @return string
*/
public function indexAction(array $searchParams)
{
@@ -83,7 +82,6 @@ class AjaxSearchbarController extends ActionController
);
$this->view->assignMultiple([
'filterSettings' => $filterSettings,
'filterOptions' => $filterOptions,
'searchParams' => $searchParams,
'fixed' => $fixed,
@@ -156,9 +156,6 @@ tx_epproducts_ajax_json {
AjaxDate {
1 = dates
}
AjaxSearchbar {
1 = index
}
AjaxTable {
1 = datestable
2 = pricetable
@@ -25,12 +25,6 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\T
'description' => 'Importiert Schneehöhen',
];
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\Task\GroupsSwissTask::class] = [
'extension' => $_EXTKEY,
'title' => 'EP Groups Swiss Export',
'description' => 'Publiziert Verfügbarkeiten nach groups.swiss',
];
$GLOBALS['TYPO3_CONF_VARS']['LOG']['EP']['EpProducts']['Service']['GroupsSwissService']['writerConfiguration'] = [
\TYPO3\CMS\Core\Log\LogLevel::INFO => [
'TYPO3\\CMS\\Core\\Log\\Writer\\FileWriter' => [
@@ -50,10 +44,10 @@ $iconRegistry->registerIcon(
'EP.' . $_EXTKEY,
'searchbar',
[
'Search' => 'searchbar',
'Searchbar' => 'index',
],
[
'Search' => '',
'Searchbar' => 'index',
]
);
@@ -371,7 +365,6 @@ $iconRegistry->registerIcon(
[
'AjaxSearch' => 'searchresult',
'AjaxDate' => 'dates',
'AjaxSearchbar' => 'index',
'AjaxContingent' => 'list,rooms',
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
'AjaxCalendar' => 'range,contingents,availableRooms',
@@ -381,7 +374,6 @@ $iconRegistry->registerIcon(
[
'AjaxSearch' => 'searchresult',
'AjaxDate' => 'dates',
'AjaxSearchbar' => 'index',
'AjaxContingent' => 'list,rooms',
// 'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
'AjaxCalendar' => 'range,contingents,availableRooms',
@@ -4,11 +4,11 @@ lib.searchBar {
extensionName = EpProducts
pluginName = searchbar
vendorName = EP
controller = Search
action = searchbar
controller = Searchbar
action = index
switchableControllerActions {
Search {
1 = searchbar
Searchbar {
1 = index
}
}
settings =< plugin.tx_epproducts.settings
@@ -1,5 +1,5 @@
import { Controller } from 'stimulus'
import { useWindowResize, useDebounce } from 'stimulus-use'
import { useWindowResize, useDebounce, useClickOutside } from 'stimulus-use'
export default class extends Controller {
@@ -16,6 +16,7 @@ export default class extends Controller {
initialize() {
useWindowResize(this)
useDebounce(this)
useClickOutside(this)
}
connect() {
@@ -5,7 +5,11 @@ import { German } from 'flatpickr/dist/l10n/de'
export default class extends Controller {
static targets = [ 'field' ]
static targets = [
'field',
'dateFrom',
'dateTo',
]
static values = {
minDate: String,
@@ -23,13 +27,9 @@ export default class extends Controller {
minDate: this.minDateValue,
onChange: selectedDates => {
if (selectedDates.length === 2) {
let dateFrom = flatpickr.formatDate(selectedDates[0], 'Y-m-d')
let dateTo = flatpickr.formatDate(selectedDates[1], 'Y-m-d')
this.dispatch('range-select', {
from: dateFrom,
to: dateTo,
label: `${dateFrom} - ${dateTo}`
})
this.dateFromValue = flatpickr.formatDate(selectedDates[0], 'Y-m-d')
this.dateToValue = flatpickr.formatDate(selectedDates[1], 'Y-m-d')
this.update()
}
}
})
@@ -46,20 +46,42 @@ export default class extends Controller {
selectPreset(e) {
e.preventDefault()
const preset = JSON.parse(e.currentTarget.dataset.preset)
this.dateFromValue = preset.dateFrom
this.dateToValue = preset.dateTo
let dateFrom = new Date(preset.dateFrom)
let dateTo = new Date(preset.dateTo)
this.picker.setDate([ dateFrom, dateTo ])
this.dispatch('range-select', {
from: preset.dateFrom,
to: preset.dateTo,
label: `${flatpickr.formatDate(dateFrom, 'd.m.Y')} - ${flatpickr.formatDate(dateTo, 'd.m.Y')}`
})
this.update()
}
reset(e) {
e.preventDefault()
this.picker.clear()
this.picker.jumpToDate()
this.dispatch('range-select', { from: null, to: null, label: null })
this.dateFromValue = ''
this.dateToValue = ''
this.update()
}
update() {
// Set values to form fields if applicable
if (this.hasDateFromTarget) {
this.dateFromTarget.value = this.dateFromValue
}
if (this.hasDateToTarget) {
this.dateToTarget.value = this.dateToValue
}
// Dispatch event with selected values and formatted range label
let label = ''
if (this.dateFromValue && this.dateToValue) {
let dateFrom = new Date(this.dateFromValue)
let dateTo = new Date(this.dateToValue)
label = `${flatpickr.formatDate(dateFrom, 'd.m.Y')}-${flatpickr.formatDate(dateTo, 'd.m.Y')}`
}
this.dispatch('range-select', {
from: this.dateFromValue,
to: this.dateToValue,
label
})
}
}
@@ -1,56 +1,60 @@
import { Controller } from 'stimulus'
import { useFetch } from '../mixins/use_fetch'
import { useDispatch } from 'stimulus-use'
export default class extends Controller {
static targets = [
'container',
'form',
'destinationUid',
'destinationType',
'dateFrom',
'dateTo',
'nights',
'destinationUid',
'destinationType',
'pax',
'priceRange',
'dateRangeLabel'
'dateRangeLabel',
'paxLabel',
'destinationLabel',
'priceRangeLabel',
]
static values = {
uri: String,
loading: Boolean,
}
initialize() {
useFetch(this)
}
connect() {
}
load() {
this.loadingValue = true
let data = new URLSearchParams()
data.set('tx_epproducts_ajax[searchParams]', JSON.stringify({
dateFrom: this.dateFromTarget.value,
dateTo: this.dateToTarget.value,
}))
fetch(this.uriValue, { method: 'POST', body: data })
.then(this.checkStatus)
.then(this.parseHTML)
.then(html => {
this.containerTarget.innerHTML = html
})
.finally(() => {
this.loadingValue = false
})
useDispatch(this)
}
setDateRange(e) {
this.dateRangeLabelTarget.value = e.detail.label
this.dateFromTarget.value = e.detail.from
this.dateToTarget.value = e.detail.to
this.dateRangeLabelTarget.value = e.detail.label
this.load()
this.dispatch('input')
}
setPax(e) {
e.preventDefault()
let pax = e.currentTarget.dataset.option
this.paxTarget.value = pax
this.paxLabelTarget.value = pax
this.dispatch('input')
}
setDestination(e) {
e.preventDefault()
this.destinationUidTarget.value = e.currentTarget.dataset.optionUid
this.destinationTypeTarget.value = e.currentTarget.dataset.optionType
this.destinationLabelTarget.value = e.currentTarget.dataset.optionLabel
this.dispatch('input')
}
resetDestination(e) {
e.preventDefault()
this.destinationUidTarget.value = ''
this.destinationTypeTarget.value = ''
this.destinationLabelTarget.value = ''
this.dispatch('input')
}
setPriceRange(e) {
e.preventDefault()
this.priceRangeTarget.value = e.currentTarget.dataset.optionValue
this.priceRangeLabelTarget.value = e.currentTarget.dataset.labelValue
this.dispatch('input')
}
}
@@ -1,212 +0,0 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="searchbar {f:if(condition: fixed, then: ' sticky')}"
data-searchbar-target="container"
{f:if(condition: fixed, else: ' data-controller="sticky" data-sticky-offset-value="650" data-sticky-sticky-class="sticky"')}>
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
pageUid="{settings.defaultSearchPageUid}" data="{'searchbar-target': 'form'}">
<f:form.hidden property="destinationUid" data="{'searchbar-target': 'destinationUid'}"/>
<f:form.hidden property="destinationType" data="{'searchbar-target': 'destinationType'}"/>
<f:form.hidden property="dateFrom" data="{'searchbar-target': 'dateFrom'}"/>
<f:form.hidden property="dateTo" data="{'searchbar-target': 'dateTo'}"/>
<f:form.hidden property="nights" data="{'searchbar-target': 'nights'}"/>
<f:form.hidden property="pax" data="{'searchbar-target': 'pax'}"/>
<f:form.hidden property="priceRange" data="{'searchbar-target': 'priceRange'}"/>
<div class="sticky:max-w-screen-xl sticky:mx-auto flex flex-col sticky:flex-row sticky:items-center">
<div class="flex items-start justify-between sticky:flex-1 pt-8 px-8 sticky:py-4 sticky:px-0">
<div class="flex-1"
data-controller="collapse"
data-collapse-collapsed-value="true">
<div class="relative border-b border-r border-white mr-8">
<input type="text"
data-action="click->collapse#toggle"
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
placeholder="Zeitraum"
data-searchbar-target="dateRangeLabel"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
<use href="#icon-calendar"></use>
</svg>
<div class="hidden absolute z-20 top-100 mt-2 left-0 bg-white p-4 shadow-md"
data-collapse-target="container"
data-controller="datepicker"
<f:if condition="{filterSettings.dateFrom} && {filterSettings.dateTo}">
data-datepicker-date-from-value="{filterSettings.dateFrom}"
data-datepicker-date-to-value="{filterSettings.dateTo}"
</f:if>
data-datepicker-min-date-value="{filterOptions.dateFrom}"
data-datepicker-max-date-value="{filterOptions.dateTo}">
<f:if condition="{settings.datePickerPresets}">
<div class="flex items-center justify-between pb-2">
<f:for each="{settings.datePickerPresets}" as="preset">
<button class="daterange__preset"
data-preset='{preset -> f:format.json()}'
data-action="datepicker#selectPreset">
<span>
{preset.label}
</span>
</button>
</f:for>
</div>
</f:if>
<input type="hidden"
data-datepicker-target="field">
<div class="flex items-center justify-between pt-2">
<button class="daterange__button focus:outline-none"
data-action="collapse#close">
schließen
</button>
<button class="daterange__button focus:outline-none"
data-action="datepicker#reset">
zurücksetzen
</button>
</div>
</div>
</div>
</div>
<div class="w-24"
data-controller="collapse"
data-collapse-collapsed-value="true">
<div class="relative border-b border-r border-white mr-8">
<input type="text"
data-action="click->collapse#toggle"
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
<use href="#icon-user-group"></use>
</svg>
<div class="hidden absolute top-100 mt-2 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll"
data-collapse-target="container">
<ul class="m-0 p-0">
<f:for each="{filterOptions.pax}" as="option">
<li>
<a href="#" data-option-value="{option}">
{option}
</a>
</li>
</f:for>
<li>
<a href="{f:uri.typolink(parameter: settings.groupsPageUid)}">
&gt; 20
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="flex-1"
data-controller="collapse"
data-collapse-collapsed-value="true">
<div class="relative border-b border-r border-white mr-8">
<input type="text"
data-action="click->collapse#toggle"
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
placeholder="Reiseziel"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1 transform rotate-45">
<use href="#icon-airplane"></use>
</svg>
<div class="hidden absolute top-100 left-0 bg-white p-4 shadow-md min-w-full h-48 overflow-y-scroll"
data-collapse-target="container">
<f:for each="{filterOptions.destinations}" as="destination">
<div class="flex items-start justify-between">
<div class="w-1/2">
<a href="#">
<svg class="block w-8 h-6">
<use href="#icon-flag-{destination.country.code -> f:format.case(mode: 'lower')}"/>
</svg>
<strong>
{destination.country.label}
</strong>
</a>
</div>
<div class="w-1/2">
<ul class="plain-list mb-0">
<f:for each="{destination.regions}" as="region">
<li>
<a href="#">
{region.label}
</a>
</li>
</f:for>
</ul>
</div>
</div>
</f:for>
<div class="flex items-start justify-between">
<div class="w-1/2">
<strong>Konzept</strong>
</div>
<div class="w-1/2">
<ul class="plain-list mb-0">
<f:for each="{filterOptions.concepts}" as="concept">
<li>
<a href="#">
{concept.name}
</a>
</li>
</f:for>
</ul>
</div>
</div>
<f:if condition="{filterSettings.destinationUid} || {filterSettings.conceptUid}">
<button>
zurücksetzen
</button>
</f:if>
</div>
</div>
</div>
<div class="w-32"
data-controller="collapse"
data-collapse-collapsed-value="true">
<div class="relative border-r border-b border-white">
<input type="text"
data-action="click->collapse#toggle"
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
placeholder="Preis"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
<use href="#icon-euro"></use>
</svg>
<div class="hidden absolute top-100 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll"
data-collapse-target="container">
<ul class="m-0 p-0">
<li>
<a href="#">
beliebig
</a>
</li>
<f:for each="{filterOptions.priceRanges}" as="range" iteration="iteration">
<li>
<a href="#">
{range.formatted}
</a>
</li>
</f:for>
</ul>
</div>
</div>
</div>
</div>
<button type="submit" class="hidden sticky:block sticky:ml-4">
<svg class="w-8 h-8 text-white">
<use href="#icon-chevron-right"></use>
</svg>
</button>
<div class="flex justify-end mt-4 sticky:hidden">
<button type="submit" class="w-1/3 text-white uppercase py-2">
Detailsuche
</button>
<button type="submit" class="w-1/3 text-white bg-ep-primary-light uppercase py-2">
Alle anzeigen
</button>
</div>
</div>
</f:form>
</div>
</html>
@@ -1,11 +0,0 @@
<html data-namespace-typo3-fluid="true" lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<f:render partial="Search/Bar" arguments="{_all}"/>
</f:section>
</html>
@@ -1,15 +0,0 @@
#<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<div data-controller="searchbar"
data-action="datepicker:range-select->searchbar#setDateRange"
data-searchbar-uri-value="{ep:uri.ajax(controller: 'AjaxSearchbar', action: 'index', pageUid: settings.defaultAjaxUid, format: 'html')}">
<f:render partial="Search/Bar" arguments="{_all}"/>
</div>
</f:section>
</html>
@@ -0,0 +1,239 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<div data-controller="searchbar"
{f:if(condition: fixed, else: ' data-controller="sticky" data-sticky-offset-value="650" data-sticky-sticky-class="sticky"')}
data-action="datepicker:range-select->searchbar#setDateRange"
data-searchbar-uri-value="{ep:uri.ajax(controller: 'Searchbar', action: 'index', pageUid: settings.defaultAjaxUid, format: 'html')}">
<div class="searchbar {f:if(condition: fixed, then: ' sticky')}"
data-searchbar-target="container">
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
pageUid="{settings.defaultSearchPageUid}" data="{'searchbar-target': 'form'}">
<f:form.hidden property="dateFrom" data="{'searchbar-target': 'dateFrom'}"/>
<f:form.hidden property="dateTo" data="{'searchbar-target': 'dateTo'}"/>
<f:form.hidden property="pax" data="{'searchbar-target': 'pax'}"/>
<f:form.hidden property="destinationUid" data="{'searchbar-target': 'destinationUid'}"/>
<f:form.hidden property="destinationType" data="{'searchbar-target': 'destinationType'}"/>
<f:form.hidden property="priceRange" data="{'searchbar-target': 'priceRange'}"/>
<div class="sticky:max-w-screen-xl sticky:mx-auto flex flex-col sticky:flex-row sticky:items-center">
<div class="flex items-start justify-between sticky:flex-1 pt-8 px-8 sticky:py-4 sticky:px-0">
<div class="flex-1"
data-controller="collapse datepicker"
<f:if condition="{searchParams.dateFrom} && {searchParams.dateTo}">
data-datepicker-date-from-value="{searchParams.dateFrom}"
data-datepicker-date-to-value="{searchParams.dateTo}"
</f:if>
data-datepicker-min-date-value="{f:format.date(date: 'now', format: 'Y-m-d')}"
data-action="collapse:click:outside->collapse#close searchbar:input@window->collapse#close"
data-collapse-collapsed-value="true">
<div class="relative border-b border-r border-white mr-8">
<input type="text"
data-action="click->collapse#toggle"
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
placeholder="Zeitraum"
<f:if condition="{searchParams.dateFrom} && {searchParams.dateTo}">
value="{ep:dateRange(dateFrom: searchParams.dateFrom, dateTo: searchParams.dateTo, includeLabel: 0)}"
</f:if>
data-searchbar-target="dateRangeLabel"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1"
data-action="click->collapse#toggle">
<use href="#icon-calendar"></use>
</svg>
<div class="hidden absolute z-20 top-100 mt-2 left-0 bg-white p-4 shadow-md"
data-collapse-target="container">
<f:if condition="{settings.datePickerPresets}">
<div class="flex items-center justify-between pb-2">
<f:for each="{settings.datePickerPresets}" as="preset">
<button class="daterange__preset"
data-preset='{preset -> f:format.json()}'
data-action="datepicker#selectPreset">
<span>
{preset.label}
</span>
</button>
</f:for>
</div>
</f:if>
<input type="hidden"
data-datepicker-target="field">
<div class="flex items-center justify-between pt-2">
<button class="daterange__button focus:outline-none"
data-action="collapse#close">
schließen
</button>
<button class="daterange__button focus:outline-none"
data-action="datepicker#reset">
zurücksetzen
</button>
</div>
</div>
</div>
</div>
<div class="w-24"
data-controller="collapse"
data-action="collapse:click:outside->collapse#close searchbar:input@window->collapse#close"
data-collapse-collapsed-value="true">
<div class="relative border-b border-r border-white mr-8">
<input type="text"
data-action="click->collapse#toggle"
data-searchbar-target="paxLabel"
value="{searchParams.pax}"
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
<use href="#icon-user-group"></use>
</svg>
<div class="hidden absolute top-100 mt-2 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll"
data-collapse-target="container">
<ul class="m-0 p-0">
<f:for each="{filterOptions.pax}" as="option">
<li>
<button data-action="searchbar#setPax"
data-option="{option}">
{option}
</button>
</li>
</f:for>
<li>
<a href="{f:uri.typolink(parameter: settings.groupsPageUid)}">
&gt; 20
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="flex-1"
data-controller="collapse"
data-action="collapse:click:outside->collapse#close searchbar:input@window->collapse#close"
data-collapse-collapsed-value="true">
<div class="relative border-b border-r border-white mr-8">
<input type="text"
data-action="click->collapse#toggle"
data-searchbar-target="destinationLabel"
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
placeholder="Reiseziel"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1 transform rotate-45">
<use href="#icon-airplane"></use>
</svg>
<div class="hidden absolute top-100 left-0 bg-white p-4 shadow-md min-w-full h-48 overflow-y-scroll"
data-collapse-target="container">
<f:for each="{filterOptions.destinations}" as="destination">
<div class="flex items-start justify-between">
<div class="w-1/2">
<button data-action="searchbar#setDestination"
data-option-uid="{destination.country.uid}"
data-option-type="country"
data-option-label="{destination.country.label}">
<svg class="block w-8 h-6">
<use href="#icon-flag-{destination.country.code -> f:format.case(mode: 'lower')}"/>
</svg>
<strong>
{destination.country.label}
</strong>
</button>
</div>
<div class="w-1/2">
<ul class="plain-list mb-0">
<f:for each="{destination.regions}" as="region">
<li>
<button data-action="searchbar#setDestination"
data-option-uid="{region.uid}"
data-option-type="region"
data-option-label="{region.label}">
{region.label}
</button>
</li>
</f:for>
</ul>
</div>
</div>
</f:for>
<div class="flex items-start justify-between">
<div class="w-1/2">
<strong>Konzept</strong>
</div>
<div class="w-1/2">
<ul class="plain-list mb-0">
<f:for each="{filterOptions.concepts}" as="concept">
<li>
<button data-action="searchbar#setDestination"
data-option-uid="{concept.uid}"
data-option-type="concept"
data-option-label="{concept.name}">
{concept.name}
</button>
</li>
</f:for>
</ul>
</div>
</div>
<button data-action="searchbar#resetDestination">
zurücksetzen
</button>
</div>
</div>
</div>
<div class="w-32"
data-controller="collapse"
data-action="collapse:click:outside->collapse#close searchbar:input@window->collapse#close"
data-collapse-collapsed-value="true">
<div class="relative border-r border-b border-white">
<input type="text"
data-action="click->collapse#toggle"
data-searchbar-target="priceRangeLabel"
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
placeholder="Preis"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
<use href="#icon-euro"></use>
</svg>
<div class="hidden absolute top-100 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll"
data-collapse-target="container">
<ul class="m-0 p-0">
<li>
<button data-action="searchbar#setPriceRange"
data-option-value=""
data-label-value="beliebig">
beliebig
</button>
</li>
<f:for each="{filterOptions.priceRanges}" as="range" iteration="iteration">
<li>
<button data-action="searchbar#setPriceRange"
data-option-value="{range.uid}"
data-label-value="{range.formatted}">
{range.formatted}
</button>
</li>
</f:for>
</ul>
</div>
</div>
</div>
</div>
<button type="submit" class="hidden sticky:block sticky:ml-4">
<svg class="w-8 h-8 text-white">
<use href="#icon-chevron-right"></use>
</svg>
</button>
<div class="flex justify-end mt-4 sticky:hidden">
<button type="submit" class="w-1/3 text-white uppercase py-2">
Detailsuche
</button>
<button type="submit" class="w-1/3 text-white bg-ep-primary-light uppercase py-2">
Alle anzeigen
</button>
</div>
</f:form>
</div>
</div>
</f:section>
</html>