610 lines
17 KiB
JavaScript
610 lines
17 KiB
JavaScript
;(function () {
|
||
console.log('Salony widget loaded ....')
|
||
|
||
const API_URL = 'https://serwer1852487.home.pl/Geo/api/salony/'
|
||
const root = document.getElementById('salony-widget')
|
||
const initialCity = root.dataset.city || null
|
||
|
||
if (!root) return
|
||
|
||
root.innerHTML = `
|
||
<div class="mapFull">
|
||
<div id="map"></div>
|
||
</div>
|
||
|
||
<div class="mapResults">
|
||
<h1>ZNAJDŹ SALON</h1>
|
||
|
||
<div class="searchCont">
|
||
<input id="tinp" type="text" placeholder="ZNAJDŹ SKLEP">
|
||
<ul class="suggestList">
|
||
<li class="firstSuggest"></li>
|
||
</ul>
|
||
<label for="tinp">Wpisz Miasto bądź województwo</label>
|
||
<button id="search">
|
||
<i class="fas fa-search" style="min-height: 14px;"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<p class="resultQ">
|
||
WYNIKI: <span class="dynamic">0</span>
|
||
</p>
|
||
|
||
<ul id="shopList">
|
||
<li class="forCloning place">
|
||
<input type="radio" name="shopPlace">
|
||
<div class="shopHiddenInfo">
|
||
<p class="lat"></p>
|
||
<p class="lng"></p>
|
||
</div>
|
||
<div class="content">
|
||
<h2 class="shopAddress"></h2>
|
||
<p>GODZINY OTWARCIA:</p>
|
||
<p class="shopHours"></p>
|
||
<a class="link googleLink" target="_blank" rel="noopener noreferrer">
|
||
<i class="fas fa-location-arrow"></i>
|
||
Wskazówki dojazdu
|
||
</a>
|
||
<a class="link moodoLink" target="_blank" rel="noopener noreferrer">
|
||
<i class="fas fa-store"></i>
|
||
Strona salonu
|
||
</a>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
`
|
||
|
||
// Add plugins
|
||
root.innerHTML += `
|
||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
|
||
`
|
||
|
||
injectStyles()
|
||
|
||
let map
|
||
let markers = []
|
||
let places = []
|
||
var iconB = 'https://moodo.pl/data/include/cms/salony-1/mark2.png'
|
||
var iconW = 'https://moodo.pl/data/include/cms/salony-1/markw2.png'
|
||
|
||
fetch(API_URL)
|
||
.then((r) => r.json())
|
||
.then((data) => {
|
||
places = data
|
||
|
||
// setTimeout(() => {
|
||
// initMap()
|
||
// buildSuggestList()
|
||
// // renderList(data)
|
||
// renderMarkers(data)
|
||
// }, 200)
|
||
|
||
setTimeout(() => {
|
||
initMap()
|
||
buildSuggestList()
|
||
renderMarkers(data)
|
||
|
||
console.log('Initial city:', initialCity)
|
||
|
||
if (initialCity) {
|
||
const type = detectFilterType(initialCity)
|
||
|
||
document.getElementById('tinp').value = initialCity
|
||
filterList(initialCity, type)
|
||
activateFirstResult()
|
||
}
|
||
}, 200)
|
||
})
|
||
|
||
function prepareToCompare(str) {
|
||
return convertSpecialsCharacters(str.toString().toLowerCase().trim())
|
||
}
|
||
function convertSpecialsCharacters(str) {
|
||
const conversions = {}
|
||
conversions.a = 'ą'
|
||
conversions.e = 'ę'
|
||
conversions.z = 'ż|ź'
|
||
conversions.s = 'ś'
|
||
conversions.c = 'ć'
|
||
conversions.l = 'ł'
|
||
conversions.o = 'ó|ò'
|
||
conversions.A = 'Ą'
|
||
conversions.E = 'Ę'
|
||
conversions.Z = 'Ż|Ź'
|
||
conversions.S = 'Ś'
|
||
conversions.C = 'Ć'
|
||
conversions.L = 'Ł'
|
||
conversions.O = 'Ó|Ò'
|
||
|
||
for (const i in conversions) {
|
||
const re = new RegExp(conversions[i], 'g')
|
||
str = str.replace(re, i)
|
||
}
|
||
|
||
return str
|
||
}
|
||
|
||
// ---------------- MAP ----------------
|
||
function initMap() {
|
||
map = new google.maps.Map(document.getElementById('map'), {
|
||
zoom: 6,
|
||
center: { lat: 52.1, lng: 19.4 },
|
||
mapTypeId: 'roadmap',
|
||
})
|
||
}
|
||
|
||
function renderMarkers(data) {
|
||
data.forEach((place) => {
|
||
place.shops.forEach((shop) => {
|
||
if (!shop.lat || !shop.lng) return
|
||
|
||
const marker = new google.maps.Marker({
|
||
map,
|
||
icon: iconB,
|
||
position: { lat: +shop.lat, lng: +shop.lng },
|
||
})
|
||
|
||
marker.shopLat = +shop.lat
|
||
marker.shopLng = +shop.lng
|
||
|
||
marker.addListener('click', () => {
|
||
map.setZoom(16)
|
||
map.panTo(marker.getPosition())
|
||
filterList(place.name)
|
||
highlightMarker(marker)
|
||
})
|
||
|
||
markers.push(marker)
|
||
})
|
||
})
|
||
}
|
||
function highlightMarker(selectedMarker) {
|
||
markers.forEach((marker) => {
|
||
marker.setIcon(marker === selectedMarker ? iconW : iconB)
|
||
})
|
||
}
|
||
|
||
// ---------------- LIST ----------------
|
||
function renderList(data) {
|
||
const list = document.getElementById('shopList')
|
||
list
|
||
.querySelectorAll('.place:not(.forCloning)')
|
||
.forEach((el) => el.remove())
|
||
|
||
let count = 0
|
||
|
||
data.forEach((place) => {
|
||
place.shops.forEach((shop) => {
|
||
const clone = list.querySelector('.forCloning').cloneNode(true)
|
||
clone.classList.remove('forCloning')
|
||
|
||
clone.querySelector('.lat').textContent = shop.lat
|
||
clone.querySelector('.lng').textContent = shop.lng
|
||
clone.querySelector('.shopAddress').textContent = `${place.name} - ${
|
||
shop.address || ''
|
||
}`
|
||
clone.querySelector('.shopHours').textContent = shop.openHours || '—'
|
||
|
||
// GOOGLE LINK
|
||
const googleLink = clone.querySelector('.googleLink')
|
||
if (shop.urlAddress) {
|
||
googleLink.href = shop.urlAddress
|
||
} else {
|
||
googleLink.remove()
|
||
}
|
||
|
||
// MOODO LINK
|
||
const moodoLink = clone.querySelector('.moodoLink')
|
||
if (shop.shopUrl) {
|
||
moodoLink.href = shop.shopUrl
|
||
} else {
|
||
moodoLink.remove()
|
||
}
|
||
|
||
clone.addEventListener('click', (e) => {
|
||
map.setZoom(16)
|
||
map.panTo({ lat: +shop.lat, lng: +shop.lng })
|
||
|
||
document
|
||
.querySelectorAll('#shopList .place.active')
|
||
.forEach((el) => el.classList.remove('active'))
|
||
|
||
const ancestor = e.target.closest('.place')
|
||
ancestor.classList.add('active')
|
||
ancestor.getElementsByTagName('INPUT')[0].checked = true
|
||
|
||
const markerToHighlight = markers.find(
|
||
(m) => m.shopLat === +shop.lat && m.shopLng === +shop.lng
|
||
)
|
||
if (markerToHighlight) highlightMarker(markerToHighlight)
|
||
})
|
||
|
||
list.appendChild(clone)
|
||
count++
|
||
})
|
||
})
|
||
|
||
document.querySelector('.dynamic').textContent = count
|
||
document.querySelector('.resultQ').classList.add('active')
|
||
}
|
||
|
||
// ---------------- SEARCH ----------------
|
||
// function buildSuggestList() {
|
||
// const ul = document.querySelector('.suggestList')
|
||
|
||
// places.forEach((p) => {
|
||
// const li = document.createElement('li')
|
||
// li.textContent = p.name
|
||
// li.onclick = () => {
|
||
// document.getElementById('tinp').value = p.name
|
||
// filterList(p.name)
|
||
// }
|
||
// ul.appendChild(li)
|
||
// })
|
||
// }
|
||
function buildSuggestList() {
|
||
const ul = document.querySelector('.suggestList')
|
||
ul.innerHTML = '<li class="firstSuggest"></li>'
|
||
|
||
// 1️⃣ WOJEWÓDZTWA
|
||
getUniqueWojewodztwa().forEach((woj) => {
|
||
const li = document.createElement('li')
|
||
li.textContent = woj
|
||
li.dataset.type = 'woj'
|
||
li.onclick = () => {
|
||
document.getElementById('tinp').value = woj
|
||
filterList(woj, 'woj')
|
||
}
|
||
ul.appendChild(li)
|
||
})
|
||
|
||
// 2️⃣ MIASTA
|
||
places.forEach((p) => {
|
||
const li = document.createElement('li')
|
||
li.textContent = p.name
|
||
li.dataset.type = 'city'
|
||
li.onclick = () => {
|
||
document.getElementById('tinp').value = p.name
|
||
filterList(p.name, 'city')
|
||
}
|
||
ul.appendChild(li)
|
||
})
|
||
}
|
||
|
||
function filterList(val, type = 'city') {
|
||
const query = prepareToCompare(val)
|
||
let result = []
|
||
|
||
if (type === 'woj') {
|
||
result = places.filter((p) => prepareToCompare(p.woj) === query)
|
||
} else {
|
||
// exact city
|
||
result = places.filter((p) => prepareToCompare(p.name) === query)
|
||
|
||
// fallback (np. wpisywanie)
|
||
if (!result.length) {
|
||
result = places.filter((p) => prepareToCompare(p.name).includes(query))
|
||
}
|
||
}
|
||
|
||
renderList(result)
|
||
|
||
// 🗺️ CENTRUJ MAPĘ (WOJ + MIASTO)
|
||
fitMapToPlaces(result)
|
||
}
|
||
|
||
function filterAvailablePlaces(actualVal) {
|
||
let listElements = document.querySelectorAll('.suggestList li')
|
||
for (const element of listElements) {
|
||
if (
|
||
!prepareToCompare(element.innerText).includes(
|
||
prepareToCompare(actualVal)
|
||
)
|
||
) {
|
||
element.style.display = 'none'
|
||
} else {
|
||
element.style.display = 'block'
|
||
}
|
||
}
|
||
}
|
||
|
||
const searchInp = document.getElementById('tinp')
|
||
searchInp.onfocus = function (e) {
|
||
e.target.closest('.searchCont').classList.add('active')
|
||
}
|
||
searchInp.addEventListener('focusout', function (e) {
|
||
let targ = e.target
|
||
setTimeout(function () {
|
||
targ.closest('.searchCont').classList.remove('active')
|
||
}, 200)
|
||
})
|
||
searchInp.onkeyup = function (e) {
|
||
if (e.keyCode == 13) {
|
||
document.getElementById('search').click()
|
||
} else {
|
||
filterAvailablePlaces(searchInp.value)
|
||
}
|
||
}
|
||
|
||
document.getElementById('search').onclick = () => {
|
||
filterList(document.getElementById('tinp').value)
|
||
}
|
||
|
||
// ---------------- HELPERS ----------------
|
||
function activateFirstResult() {
|
||
const first = document.querySelector('#shopList .place:not(.forCloning)')
|
||
if (!first) return
|
||
|
||
first.classList.add('active')
|
||
first.querySelector('input').checked = true
|
||
|
||
const lat = +first.querySelector('.lat').textContent
|
||
const lng = +first.querySelector('.lng').textContent
|
||
|
||
map.setZoom(16)
|
||
map.panTo({ lat, lng })
|
||
|
||
const marker = markers.find((m) => m.shopLat === lat && m.shopLng === lng)
|
||
if (marker) highlightMarker(marker)
|
||
}
|
||
|
||
function getUniqueWojewodztwa() {
|
||
const set = new Set()
|
||
places.forEach((p) => {
|
||
if (p.woj) set.add(p.woj)
|
||
})
|
||
return Array.from(set).sort()
|
||
}
|
||
|
||
function detectFilterType(val) {
|
||
const q = prepareToCompare(val)
|
||
const isWoj = places.some((p) => prepareToCompare(p.woj) === q)
|
||
return isWoj ? 'woj' : 'city'
|
||
}
|
||
|
||
function fitMapToPlaces(placesArr) {
|
||
if (!placesArr || !placesArr.length) return
|
||
|
||
const bounds = new google.maps.LatLngBounds()
|
||
let hasPoints = false
|
||
|
||
placesArr.forEach((place) => {
|
||
place.shops.forEach((shop) => {
|
||
if (shop.lat && shop.lng) {
|
||
bounds.extend({
|
||
lat: +shop.lat,
|
||
lng: +shop.lng,
|
||
})
|
||
hasPoints = true
|
||
}
|
||
})
|
||
})
|
||
|
||
if (hasPoints) {
|
||
map.fitBounds(bounds)
|
||
|
||
setTimeout(() => {
|
||
if (map.getZoom() > 12) map.setZoom(12)
|
||
}, 0)
|
||
}
|
||
}
|
||
|
||
// ---------------- STYLES ----------------
|
||
|
||
function injectStyles() {
|
||
const style = document.createElement('style')
|
||
style.innerHTML = `
|
||
#salony-widget *{
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
#salony-widget{
|
||
min-height: 40vw;
|
||
display: flex;
|
||
flex-direction: row;
|
||
}
|
||
#salony-widget .mapFull {
|
||
flex-basis:65%;
|
||
min-height:40vw;
|
||
}
|
||
#salony-widget .mapFull #map{
|
||
filter: grayscale(100%);
|
||
height: 100%;
|
||
width: 100%;
|
||
}
|
||
|
||
|
||
#salony-widget .mapResults {
|
||
padding: 0 16px 0 48px;
|
||
flex-grow: 0;
|
||
flex-shrink: 0;
|
||
flex-basis: 35%;
|
||
}
|
||
#salony-widget .mapResults h1 {
|
||
color: #333;
|
||
font-family: 'Raleway', sans-serif;
|
||
font-size: 29px;
|
||
font-weight: 800;
|
||
line-height: 1.3;
|
||
margin: 0;
|
||
}
|
||
|
||
#salony-widget .mapResults .searchCont {
|
||
margin: 18px 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
position: relative;
|
||
max-width: 340px;
|
||
border: 2px solid rgba(0, 0, 0, .7);
|
||
}
|
||
#salony-widget .mapResults .searchCont input{
|
||
font-family: 'Raleway', sans-serif;
|
||
border: none;
|
||
font-size: 14px;
|
||
outline: none;
|
||
padding: 10px;
|
||
color: #333;
|
||
width: calc(100% - 40px);
|
||
height: 40px;
|
||
box-shadow: none;
|
||
border-radius: 0;
|
||
}
|
||
#salony-widget .mapResults .searchCont label{
|
||
font-family: 'Raleway', sans-serif;
|
||
font-size: 12px;
|
||
font-weight: 800;
|
||
position: absolute;
|
||
color: rgba(0, 0, 0, .6);
|
||
top: -10px;
|
||
padding: 2px;
|
||
background: #fff;
|
||
left: 4px;
|
||
transform: scale(0);
|
||
opacity: 0;
|
||
transition: 0.5s;
|
||
}
|
||
#salony-widget .mapResults .searchCont button{
|
||
cursor: pointer;
|
||
border: none;
|
||
width: 40px;
|
||
height: 40px;
|
||
outline: none;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #fff;
|
||
}
|
||
#salony-widget .mapResults .searchCont .suggestList {
|
||
position: absolute;
|
||
box-shadow: 2px 3px 10px rgba(0, 0, 0, .3);
|
||
top: 20px;
|
||
border-radius: 3px;
|
||
padding: 0 !important;
|
||
max-height: 100px;
|
||
z-index: 10;
|
||
overflow: auto;
|
||
width: 100%;
|
||
background: #fff;
|
||
display: none;
|
||
|
||
margin: 20px 0;
|
||
list-style-type: none;
|
||
}
|
||
#salony-widget .mapResults .searchCont.active .suggestList {
|
||
display: block;
|
||
}
|
||
#salony-widget .mapResults .searchCont .suggestList li {
|
||
font-family: 'Raleway', sans-serif;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
padding: 5px;
|
||
transition: 0.3s;
|
||
align-items: flex-start;
|
||
}
|
||
#salony-widget .mapResults .searchCont .suggestList li:hover {
|
||
background: #f2f2f2;
|
||
}
|
||
#salony-widget .mapResults .searchCont .suggestList li.firstSuggest {
|
||
padding: 0px;
|
||
}
|
||
#salony-widget .mapResults .searchCont label {
|
||
font-family: 'Raleway', sans-serif;
|
||
font-size: 12px;
|
||
font-weight: 800;
|
||
position: absolute;
|
||
color: rgba(0, 0, 0, .6);
|
||
top: -10px;
|
||
padding: 2px;
|
||
background: #fff;
|
||
left: 4px;
|
||
transform: scale(0);
|
||
opacity: 0;
|
||
transition: 0.5s;
|
||
}
|
||
#salony-widget .mapResults .searchCont.active label {
|
||
transform: scale(1);
|
||
opacity: 1;
|
||
}
|
||
|
||
#salony-widget .mapResults .resultQ {
|
||
visibility: hidden;
|
||
color: #333;
|
||
font-family: 'Raleway', sans-serif;
|
||
font-weight: 300;
|
||
font-size: 14px;
|
||
}
|
||
#salony-widget .mapResults .resultQ.active {
|
||
visibility: visible;
|
||
}
|
||
#salony-widget .mapResults #shopList {
|
||
padding: 0 5px;
|
||
margin: 20px 0;
|
||
list-style-type: none;
|
||
max-height: 360px;
|
||
overflow: auto;
|
||
}
|
||
#salony-widget .mapResults #shopList li.place.forCloning {
|
||
display: none;
|
||
}
|
||
#salony-widget .mapResults #shopList li.place {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
padding: 10px 0;
|
||
cursor: pointer;
|
||
}
|
||
#salony-widget .mapResults #shopList li.place a {
|
||
font-family: 'Raleway', sans-serif;
|
||
margin-top: 8px;
|
||
display: none;
|
||
font-size: 13px;
|
||
color: #000;
|
||
text-decoration: none;
|
||
}
|
||
#salony-widget .mapResults #shopList li.place.active a {
|
||
display: block;
|
||
}
|
||
#salony-widget .mapResults #shopList li.place input{
|
||
margin-right: 10px;
|
||
position: relative;
|
||
top: 6px;
|
||
color: #000;
|
||
}
|
||
#salony-widget .mapResults #shopList li.place .shopHiddenInfo{
|
||
display: none;
|
||
}
|
||
#salony-widget .mapResults #shopList li.place .content h2{
|
||
color: #333;
|
||
line-height: 1.3;
|
||
font-family: 'Raleway', sans-serif;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
}
|
||
#salony-widget .mapResults #shopList li.place .content p{
|
||
color: #333;
|
||
font-family: 'Raleway', sans-serif;
|
||
line-height: 1.3;
|
||
font-weight: 300;
|
||
font-size: 14px;
|
||
}
|
||
|
||
@media(max-width: 978px) {
|
||
#salony-widget{
|
||
flex-direction: column;
|
||
row-gap: 20px;
|
||
}
|
||
#salony-widget .mapResults{
|
||
padding-left: 16px;
|
||
}
|
||
#salony-widget .mapFull #map{
|
||
min-height: 40vw;
|
||
}
|
||
}
|
||
`
|
||
document.head.appendChild(style)
|
||
}
|
||
})()
|