WIP Migrate to stimulus.js

This commit is contained in:
Björn Fromme
2021-08-16 13:07:33 +02:00
parent 01a9d6123f
commit 6db1cb23b3
14 changed files with 206 additions and 128 deletions
@@ -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)
@@ -1,8 +0,0 @@
export default () => ({
show() {
return this.$store.watchlist.length > 0
},
label() {
return `Merkliste (${this.$store.watchlist.length})`
}
})
@@ -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'
},
})
@@ -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)
}
}
}
@@ -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
@@ -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})`
}
}
}
@@ -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 &amp; 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 &amp; 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">