WIP Migrate to stimulus.js
This commit is contained in:
@@ -14,8 +14,6 @@ import Alpine from 'alpinejs'
|
||||
import parseAppConfig from './components/appconfig'
|
||||
import initSliders from './components/sliders'
|
||||
import appendPaCode from './components/pacode'
|
||||
import watchlistToggle from './components/watchlist-toggle'
|
||||
import watchlistButton from './components/watchlist-button'
|
||||
import watchlist from './components/watchlist'
|
||||
import productDetail from './components/product-detail'
|
||||
import daytripDateSelect from './components/daytrip-date-select'
|
||||
@@ -23,30 +21,10 @@ import searchBar from './components/search-bar'
|
||||
import calendar from './components/calendar'
|
||||
import contactForm from './components/contact-form'
|
||||
|
||||
import defaultFilterSettings from './_filtersettings'
|
||||
import defaultFilterOptions from './_filteroptions'
|
||||
|
||||
const appConfig = parseAppConfig()
|
||||
const storedWatchlist = JSON.parse(sessionStorage.getItem('myep-watchlist')) || []
|
||||
appendPaCode(appConfig)
|
||||
initSliders()
|
||||
Alpine.store('appconfig', appConfig)
|
||||
Alpine.store('show', {
|
||||
searchBox: false,
|
||||
mobileNav: false,
|
||||
modal: null,
|
||||
toggleMobileNav() {
|
||||
this.mobileNav = !this.mobileNav
|
||||
},
|
||||
toggleSearchBox() {
|
||||
this.searchBox = !this.searchBox
|
||||
}
|
||||
})
|
||||
Alpine.store('watchlist', storedWatchlist)
|
||||
Alpine.store('filterSettings', defaultFilterSettings)
|
||||
Alpine.store('filterOptions', defaultFilterOptions)
|
||||
Alpine.data('watchlistToggle', watchlistToggle)
|
||||
Alpine.data('watchlistButton', watchlistButton)
|
||||
Alpine.data('watchlist', watchlist)
|
||||
Alpine.data('productDetail', productDetail)
|
||||
Alpine.data('daytripDateSelect', daytripDateSelect)
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
export default () => ({
|
||||
show() {
|
||||
return this.$store.watchlist.length > 0
|
||||
},
|
||||
label() {
|
||||
return `Merkliste (${this.$store.watchlist.length})`
|
||||
}
|
||||
})
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
export default uid => ({
|
||||
uid,
|
||||
trigger: {
|
||||
['@click.prevent']() {
|
||||
const watchlistIndex = this.$store.watchlist.indexOf(this.uid)
|
||||
if (-1 !== watchlistIndex) {
|
||||
this.$store.watchlist.splice(watchlistIndex, 1)
|
||||
} else {
|
||||
this.$store.watchlist.push(this.uid)
|
||||
}
|
||||
sessionStorage.setItem('myep-watchlist', JSON.stringify(this.$store.watchlist))
|
||||
}
|
||||
},
|
||||
onList() {
|
||||
return -1 !== this.$store.watchlist.indexOf(this.uid)
|
||||
},
|
||||
label() {
|
||||
return this.onList() ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen'
|
||||
},
|
||||
})
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useDispatch } from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static values = {
|
||||
id: String,
|
||||
show: Boolean
|
||||
}
|
||||
|
||||
static targets = [ 'overlay' ]
|
||||
|
||||
initialize() {
|
||||
useDispatch(this)
|
||||
}
|
||||
|
||||
invokeToggle() {
|
||||
this.dispatch('toggle', this.idValue)
|
||||
}
|
||||
|
||||
toggle(e) {
|
||||
if (e.detail === this.idValue) {
|
||||
this.showValue = !this.showValue
|
||||
}
|
||||
}
|
||||
|
||||
showValueChanged() {
|
||||
if (this.hasOverlayTarget) {
|
||||
this.overlayTarget.classList.toggle('hidden', this.showValue === false)
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-6
@@ -23,9 +23,19 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
filter() {
|
||||
this.load({
|
||||
method: 'POST',
|
||||
body: new FormData(this.formTarget)
|
||||
})
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.load({ method: 'POST' })
|
||||
}
|
||||
|
||||
load(options) {
|
||||
this.loadingValue = true
|
||||
let data = new FormData(this.formTarget)
|
||||
fetch(this.uriValue, { method: 'POST', body: data })
|
||||
fetch(this.uriValue, options)
|
||||
.then(this.checkStatus)
|
||||
.then(this.parseHTML)
|
||||
.then(html => {
|
||||
@@ -37,10 +47,6 @@ export default class extends Controller {
|
||||
})
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.filter()
|
||||
}
|
||||
|
||||
selectDateRange(e) {
|
||||
this.dateFromTarget.value = e.detail.from
|
||||
this.dateToTarget.value = e.detail.to
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { Controller } from 'stimulus'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static values = { count: Number }
|
||||
|
||||
static targets = [ 'label' ]
|
||||
|
||||
connect() {
|
||||
this.watchlist = JSON.parse(sessionStorage.getItem('myep-watchlist')) || []
|
||||
this.countValue = this.watchlist.length
|
||||
}
|
||||
|
||||
update(e) {
|
||||
let key = e.detail.key
|
||||
let index = this.watchlist.indexOf(key)
|
||||
if (-1 !== index) {
|
||||
this.watchlist.splice(index, 1)
|
||||
} else {
|
||||
this.watchlist.push(key)
|
||||
}
|
||||
this.countValue = this.watchlist.length
|
||||
sessionStorage.setItem('myep-watchlist', JSON.stringify(this.watchlist))
|
||||
}
|
||||
|
||||
countValueChanged() {
|
||||
if (0 === this.countValue) {
|
||||
this.element.classList.add('hidden')
|
||||
} else {
|
||||
this.element.classList.remove('hidden')
|
||||
this.labelTarget.innerText = `Merkliste (${this.watchlist.length})`
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useDispatch } from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static values = {
|
||||
key: String,
|
||||
onList: Boolean
|
||||
}
|
||||
|
||||
static targets = [ 'label', 'icon' ]
|
||||
|
||||
initialize() {
|
||||
useDispatch(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
let watchlist = JSON.parse(sessionStorage.getItem('myep-watchlist')) || []
|
||||
this.onListValue = -1 !== watchlist.indexOf(this.keyValue)
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.onListValue = !this.onListValue
|
||||
this.dispatch('toggle', {
|
||||
key: this.keyValue,
|
||||
onList: this.onListValue,
|
||||
})
|
||||
}
|
||||
|
||||
onListValueChanged() {
|
||||
this.labelTarget.setAttribute('aria-label', true === this.onListValue ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen')
|
||||
this.iconTarget.setAttribute('href', true === this.onListValue ? '#icon-minus-circle' : '#icon-plus-circle')
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
<f:render partial="ContactBar" arguments="{_all}"/>
|
||||
<f:render partial="Topnav" arguments="{_all}"/>
|
||||
<f:render partial="Mobilenav" arguments="{_all}"/>
|
||||
<f:cObject typoscriptObjectPath="lib.searchBox"/>
|
||||
<f:render partial="Config" arguments="{_all}" />
|
||||
<f:render partial="Icons" arguments="{_all}"/>
|
||||
</html>
|
||||
|
||||
@@ -6,15 +6,13 @@
|
||||
<a href="{f:uri.typolink(parameter: settings.watchlistPageUid)}"
|
||||
class="contact-bar__item"
|
||||
title="Merkliste"
|
||||
x-data="watchlistButton"
|
||||
x-show="show"
|
||||
x-transition
|
||||
x-cloak
|
||||
data-controller="watchlist-label"
|
||||
data-action="watchlist-toggle:toggle@window->watchlist-label#update"
|
||||
rel="nofollow">
|
||||
<svg class="contact-bar__icon">
|
||||
<use href="#icon-heart"></use>
|
||||
</svg>
|
||||
<div class="contact-bar__label" x-text="label"></div>
|
||||
<div class="contact-bar__label" data-watchlist-label-target="label"></div>
|
||||
</a>
|
||||
<a href="tel:{settings.phoneNumberLink}"
|
||||
class="contact-bar__item"
|
||||
|
||||
@@ -18,14 +18,20 @@
|
||||
<div class="flex lg:hidden items-center space-x-4 pb-4" x-data>
|
||||
<f:if condition="{data.tx_eptheme_hide_searchbar}">
|
||||
<f:else>
|
||||
<button x-on:click.prevent="$store.show.toggleSearchBox()" class="focus:outline-none text-gray-800">
|
||||
<button class="focus:outline-none text-gray-800"
|
||||
data-controller="overlay-toggle"
|
||||
data-overlay-toggle-id-value="search"
|
||||
data-action="overlay-toggle#invokeToggle">
|
||||
<svg class="w-8 h-8 text-gray-800">
|
||||
<use href="#icon-search"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</f:else>
|
||||
</f:if>
|
||||
<button x-on:click.prevent="$store.show.toggleMobileNav()" class="focus:outline-none text-gray-800">
|
||||
<button class="focus:outline-none text-gray-800"
|
||||
data-controller="overlay-toggle"
|
||||
data-overlay-toggle-id-value="mobilenav"
|
||||
data-action="overlay-toggle#invokeToggle">
|
||||
<svg class="w-8 h-8 text-gray-800">
|
||||
<use href="#icon-menu"></use>
|
||||
</svg>
|
||||
|
||||
@@ -3,19 +3,14 @@
|
||||
xmlns:v="http://typo3.org/ns/FluidTYPO3/Vhs/ViewHelpers"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div class="fixed lg:hidden top-0 left-0 inset-0 w-screen h-screen z-100 overflow-y-scroll bg-cover bg-gray-700 transform"
|
||||
x-data
|
||||
x-cloak
|
||||
x-show="$store.show.mobileNav"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="-translate-y-full"
|
||||
x-transition:enter-end="translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="translate-y-0"
|
||||
x-transition:leave-end="-translate-y-full">
|
||||
<div class="fixed lg:hidden top-0 left-0 inset-0 w-screen h-screen z-100 overflow-y-scroll bg-cover bg-gray-700 transform hidden"
|
||||
data-controller="overlay-toggle"
|
||||
data-overlay-toggle-id-value="mobilenav"
|
||||
data-overlay-toggle-target="overlay"
|
||||
data-action="overlay-toggle:toggle@window->overlay-toggle#toggle">
|
||||
<div class="relative bg-ep-primary">
|
||||
<div class="fixed top-0 left-0 inset-x-0 px-8 py-2 flex justify-end bg-ep-primary">
|
||||
<button class="focus:outline-none" x-on:click="$store.show.toggleMobileNav()">
|
||||
<button class="focus:outline-none" data-action="overlay-toggle#invokeToggle">
|
||||
<svg class="w-10 h-10 text-white">
|
||||
<use href="#icon-close"></use>
|
||||
</svg>
|
||||
|
||||
@@ -142,12 +142,18 @@
|
||||
</f:link.action>
|
||||
</div>
|
||||
</div>
|
||||
<button class="teaserbox-watchlist" x-data="watchlistToggle('{item.key}')" x-bind="trigger">
|
||||
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
|
||||
<svg class="w-5 h-5">
|
||||
<use x-bind:href="onList() ? '#icon-minus-circle' : '#icon-plus-circle'"></use>
|
||||
</svg>
|
||||
</span>
|
||||
<button class="teaserbox-watchlist"
|
||||
data-controller="watchlist-toggle"
|
||||
data-watchlist-toggle-key-value="{item.key}"
|
||||
data-action="watchlist-toggle#toggle">
|
||||
<span data-microtip-position="top"
|
||||
role="tooltip"
|
||||
aria-label="auf die Merkliste setzen"
|
||||
data-watchlist-toggle-target="label">
|
||||
<svg class="w-5 h-5 text-ep-secondary">
|
||||
<use href="#icon-plus-circle" data-watchlist-toggle-target="icon"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</f:for>
|
||||
|
||||
@@ -23,15 +23,19 @@
|
||||
<div class="teaserbox teaserbox--landscape teaserbox--searchresult searchresult-item">
|
||||
<div class="teaserbox__image">
|
||||
<img src="{item.country.flag}" class="teaserbox__flag" alt="{item.country.name}"/>
|
||||
<f:link.action action="detail" controller="Product" extensionName="epproducts" pluginName="product_detail"
|
||||
<f:link.action action="detail" controller="Product" extensionName="epproducts"
|
||||
pluginName="product_detail"
|
||||
arguments="{product: item.product.uid, hotel: item.hotel.uid}"
|
||||
additionalParams="{ref: referrer}"
|
||||
pageUid="{item.product.detailPage}" absolute="1"
|
||||
rel="nofollow" title="Details & Buchen">
|
||||
<picture class="searchresult-item__image">
|
||||
<source media="(min-width: 1201px)" data-srcset="{item.hotel.images.resized.m.0.url} 260w"/>
|
||||
<source media="(max-width: 1200px)" data-srcset="{item.hotel.images.resized.l.0.url} 280w"/>
|
||||
<source media="(max-width: 768px)" data-srcset="{item.hotel.images.resized.s.0.url} 220w"/>
|
||||
<source media="(min-width: 1201px)"
|
||||
data-srcset="{item.hotel.images.resized.m.0.url} 260w"/>
|
||||
<source media="(max-width: 1200px)"
|
||||
data-srcset="{item.hotel.images.resized.l.0.url} 280w"/>
|
||||
<source media="(max-width: 768px)"
|
||||
data-srcset="{item.hotel.images.resized.s.0.url} 220w"/>
|
||||
<img class="scale lazyload" data-src="{item.hotel.images.resized.s[0].url}"
|
||||
title="{item.hotel.images.resized.s.0.title}"
|
||||
alt="{item.hotel.images.resized.s.0.alt}">
|
||||
@@ -44,23 +48,23 @@
|
||||
</div>
|
||||
<div class="teaserbox__main">
|
||||
<div class="teaserbox__header">
|
||||
<span class="searchresult-item__headline">
|
||||
{item.region.name}
|
||||
<f:if condition="{section.concept.code} == 'evt'">
|
||||
<strong>
|
||||
<span class="block">
|
||||
{item.product.name}
|
||||
</span>
|
||||
</strong>
|
||||
</f:if>
|
||||
</span>
|
||||
<span class="searchresult-item__subline">
|
||||
{item.hotel.name}
|
||||
</span>
|
||||
<f:if condition="{item.hotel.category}">
|
||||
<span class="searchresult-item__category">
|
||||
Kategorie {item.hotel.category}
|
||||
<span class="searchresult-item__headline">
|
||||
{item.region.name}
|
||||
<f:if condition="{section.concept.code} == 'evt'">
|
||||
<strong>
|
||||
<span class="block">
|
||||
{item.product.name}
|
||||
</span>
|
||||
</strong>
|
||||
</f:if>
|
||||
</span>
|
||||
<span class="searchresult-item__subline">
|
||||
{item.hotel.name}
|
||||
</span>
|
||||
<f:if condition="{item.hotel.category}">
|
||||
<span class="searchresult-item__category">
|
||||
Kategorie {item.hotel.category}
|
||||
</span>
|
||||
</f:if>
|
||||
</div>
|
||||
<ul class="check-list searchresult-item__checklist">
|
||||
@@ -93,35 +97,37 @@
|
||||
</div>
|
||||
<div class="teaserbox__aside">
|
||||
<div class="teaserbox__aside__top">
|
||||
<span class="searchresult-item__label">
|
||||
{f:if(condition: '{item.dates -> f:count()} > 1', then: 'Zeitraum', else: 'Termin')}
|
||||
</span>
|
||||
<span class="searchresult-item__label">
|
||||
{f:if(condition: '{item.dates -> f:count()} > 1', then: 'Zeitraum', else: 'Termin')}
|
||||
</span>
|
||||
<span class="searchresult-item__daterange">
|
||||
{ep:dateRange(dateFrom: item.minDateStart, dateTo: item.maxDateEnd, includeLabel: 0)}
|
||||
</span>
|
||||
{ep:dateRange(dateFrom: item.minDateStart, dateTo: item.maxDateEnd, includeLabel: 0)}
|
||||
</span>
|
||||
<f:if condition="{item.dates -> f:count()} > 0">
|
||||
<f:if condition="{item.daytrip}">
|
||||
<f:else>
|
||||
<f:link.action action="detail" controller="Product" extensionName="epproducts" pluginName="product_detail"
|
||||
<f:link.action action="detail" controller="Product"
|
||||
extensionName="epproducts" pluginName="product_detail"
|
||||
arguments="{product: item.product.uid, hotel: item.hotel.uid}"
|
||||
additionalParams="{ref: referrer}"
|
||||
pageUid="{item.product.detailPage}" absolute="1"
|
||||
rel="nofollow" title="Details & Buchen">
|
||||
<span class="searchresult-item__daterange">
|
||||
<i class="fa fa-arrow-circle-right"></i> {item.dates -> f:count()} {f:if(condition: '{item.dates -> f:count()} > 1', then: 'Termine', else: 'Termin')}
|
||||
</span>
|
||||
<span class="searchresult-item__daterange">
|
||||
<i class="fa fa-arrow-circle-right"></i> {item.dates -> f:count()} {f:if(condition: '{item.dates -> f:count()} > 1', then: 'Termine', else: 'Termin')}
|
||||
</span>
|
||||
</f:link.action>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:if>
|
||||
<hr>
|
||||
<span class="searchresult-item__label">
|
||||
{f:if(condition: item.daytrip, then: 'Tageweise buchbar', else: '{ep:nightsOptions(options: item.nights, daytrip: item.daytrip)}')}
|
||||
</span>
|
||||
{f:if(condition: item.daytrip, then: 'Tageweise buchbar', else: '{ep:nightsOptions(options: item.nights, daytrip: item.daytrip)}')}
|
||||
</span>
|
||||
</div>
|
||||
<div class="teaserbox__aside__bottom">
|
||||
<span class="searchresult-item__price">ab {item.minPrice} €</span>
|
||||
<f:link.action action="detail" controller="Product" extensionName="epproducts" pluginName="product_detail"
|
||||
<f:link.action action="detail" controller="Product" extensionName="epproducts"
|
||||
pluginName="product_detail"
|
||||
arguments="{product: item.product.uid, hotel: item.hotel.uid}"
|
||||
additionalParams="{ref: referrer}"
|
||||
pageUid="{item.product.detailPage}" absolute="1"
|
||||
@@ -131,13 +137,19 @@
|
||||
</f:link.action>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="teaserbox-watchlist" x-data="watchlistToggle('{item.key}')" x-bind="trigger">
|
||||
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
|
||||
<svg class="w-5 h-5">
|
||||
<use x-bind:href="onList() ? '#icon-minus-circle' : '#icon-plus-circle'"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
<button class="teaserbox-watchlist"
|
||||
data-controller="watchlist-toggle"
|
||||
data-watchlist-toggle-key-value="{item.key}"
|
||||
data-action="watchlist-toggle#toggle">
|
||||
<span data-microtip-position="top"
|
||||
role="tooltip"
|
||||
aria-label="auf die Merkliste setzen"
|
||||
data-watchlist-toggle-target="label">
|
||||
<svg class="w-5 h-5 text-ep-secondary">
|
||||
<use href="#icon-plus-circle" data-watchlist-toggle-target="icon"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</f:for>
|
||||
</div>
|
||||
|
||||
@@ -5,15 +5,19 @@
|
||||
<f:layout name="Default"/>
|
||||
|
||||
<f:section name="Main">
|
||||
<div class="searchbox hidden-lg" x-show="$store.show.searchBox" x-transition x-data='searchBar({
|
||||
<f:format.raw>
|
||||
uri: "{ep:uri.ajax(controller: "AjaxSearchbar", action: "update", pageUid: settings.defaultAjaxUid)}",
|
||||
initialSearchParams: {searchParams -> f:format.json()},
|
||||
initialFilterOptions: {filterOptions -> f:format.json()},
|
||||
datePresets: {settings.datePickerPresets -> f:format.json()}
|
||||
</f:format.raw>
|
||||
})'>
|
||||
<div class="container">
|
||||
<div class="fixed lg:hidden top-0 left-0 inset-0 w-screen h-screen z-100 overflow-y-scroll bg-cover bg-gray-700 transform hidden-lg hidden"
|
||||
data-controller="overlay-toggle"
|
||||
data-overlay-toggle-id-value="search"
|
||||
data-overlay-toggle-target="overlay"
|
||||
data-action="overlay-toggle:toggle@window->overlay-toggle#toggle">
|
||||
<div class="relative bg-ep-primary">
|
||||
<div class="fixed top-0 left-0 inset-x-0 px-8 py-2 flex justify-end bg-ep-primary">
|
||||
<button class="focus:outline-none" data-action="overlay-toggle#invokeToggle">
|
||||
<svg class="w-10 h-10 text-white">
|
||||
<use href="#icon-close"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
|
||||
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
|
||||
pageUid="{settings.defaultSearchPageUid}" class="form-horizontal">
|
||||
|
||||
Reference in New Issue
Block a user