initial version
This commit is contained in:
69
pages/about.vue
Normal file
69
pages/about.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="about-container">
|
||||
<h1 class="about-title">Augsburg Mosaic</h1>
|
||||
<p class="about-description">
|
||||
Welcome to Augsburg Mosaic, a delightful endeavor crafted by the creative mind <a href="https://wieerwill.de" class="external-link">WieErWill</a>.
|
||||
This project blossomed from a genuine fondness for the city of Augsburg, aiming to unfold its hidden gems and notable locales to both visitors and residents alike.
|
||||
</p>
|
||||
<p class="about-detail">
|
||||
Augsburg Mosaic serves as a digital guide, offering a curated list of places categorized as Attractions, Food spots, Cultural hubs, Nature retreats, and Others.
|
||||
Through an interactive map and a user-friendly list, one can explore, filter, and learn more about each location.
|
||||
Utilizing technologies such as Vue.js, Nuxt.js, Leaflet.js for map rendering, and Pinia for state management, this project embodies a seamless blend of design and functionality,
|
||||
all while being a purely static site optimized for SEO. This also means, i don't use cookies or any other data, especially not yours in any way.
|
||||
</p>
|
||||
<p class="about-note">
|
||||
This endeavor is purely a hobby, unfunded yet rich in spirit. Feel free to traverse through the mosaic of Augsburg,
|
||||
all I hope is for it to bring joy and not to cause harm. The icons embellishing this site are courtesy of FontAwesome,
|
||||
while the images are captured through my lens.
|
||||
</p>
|
||||
<a href="/" class="back-link"><strong>Back to Map</strong></a>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.about-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
background-color: #f7f4f4;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 16px 16px rgba(0, 0, 0, 0.2);
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.about-title {
|
||||
font-size: 36px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.about-description,
|
||||
.about-detail,
|
||||
.about-note {
|
||||
text-align: left;
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.external-link,
|
||||
.back-link {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.external-link:hover,
|
||||
.back-link:hover {
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
margin-top: 20px;
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #3498db;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
160
pages/index.vue
Normal file
160
pages/index.vue
Normal file
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<Header />
|
||||
<div class="container">
|
||||
<div class="sidebar" :class="{ 'is-mobile': isMobile }">
|
||||
<Filter :locations="locations" @update-filter="updateFilteredLocations" />
|
||||
<div class="location-list-container">
|
||||
<LocationList :locations="filteredLocations" :selectedLocation="selectedLocation"
|
||||
@location-click="handleLocationClick" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="map-container" v-if="!isMobile">
|
||||
<Map :center="mapCenter" :zoom="mapZoom" :maxBounds="maxBounds" :locations="filteredLocations"
|
||||
@marker-click="handleMarkerClick" :selectedLocation="selectedLocation" ref="map" />
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import locationsData from '~/assets/locations.json';
|
||||
import Header from '~/components/Header.vue';
|
||||
import Footer from '~/components/Footer.vue';
|
||||
import Filter from '~/components/Filter.vue';
|
||||
import LocationList from '~/components/LocationList.vue';
|
||||
import Map from '~/components/Map.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isMobile: false,
|
||||
maxBounds: [
|
||||
[48.2, 10.6], // Southwest coordinates
|
||||
[48.6, 11.2] // Northeast coordinates
|
||||
],
|
||||
mapCenter: [48.3689, 10.8979],
|
||||
mapZoom: 12,
|
||||
selectedLocation: null,
|
||||
filteredLocations: [],
|
||||
locations: locationsData
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Header,
|
||||
Footer,
|
||||
Filter,
|
||||
LocationList,
|
||||
Map
|
||||
},
|
||||
created() {
|
||||
this.filteredLocations = [...this.locations].sort((a, b) => a.title.localeCompare(b.title))
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(this.updateHeaderFooterHeight);
|
||||
window.addEventListener('resize', this.updateHeaderFooterHeight);
|
||||
|
||||
this.$nextTick(this.updateLocationListHeight);
|
||||
window.addEventListener('resize', this.updateLocationListHeight);
|
||||
|
||||
this.handleResize();
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.updateHeaderFooterHeight);
|
||||
window.removeEventListener('resize', this.updateLocationListHeight);
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
},
|
||||
methods: {
|
||||
updateFilteredLocations(filteredLocations) {
|
||||
this.filteredLocations = filteredLocations;
|
||||
},
|
||||
handleLocationClick(location) {
|
||||
this.selectedLocation = location;
|
||||
this.mapCenter = location.coordinates;
|
||||
this.mapZoom = 16;
|
||||
},
|
||||
handleMarkerClick(location) {
|
||||
this.selectedLocation = location;
|
||||
this.mapCenter = location.coordinates;
|
||||
},
|
||||
handleResize() {
|
||||
this.isMobile = window.innerWidth <= 768;
|
||||
},
|
||||
updateHeaderFooterHeight() {
|
||||
const headerHeight = document.querySelector('header').offsetHeight;
|
||||
const footerHeight = document.querySelector('footer').offsetHeight;
|
||||
const totalHeight = headerHeight + footerHeight;
|
||||
document.documentElement.style.setProperty('--header-footer-height', `${totalHeight}px`);
|
||||
},
|
||||
updateLocationListHeight() {
|
||||
const filterHeight = document.querySelector('.filter-container').offsetHeight;
|
||||
const containerHeight = document.querySelector('.container').clientHeight;
|
||||
const newHeight = containerHeight - filterHeight - 45
|
||||
const locationListElement = document.querySelector('.location-list');
|
||||
locationListElement.style.height = `${newHeight}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
background-color: #e0f7fa;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: calc(100vh - var(--header-footer-height));
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex: 1;
|
||||
background-color: #f9f9f9;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #434343
|
||||
}
|
||||
|
||||
.location-list-container {
|
||||
overflow-y: auto;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.map-container {
|
||||
flex: 1;
|
||||
transition: flex 0.3s;
|
||||
min-width: 10rem;
|
||||
min-height: 10rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.is-mobile {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
.is-mobile .map-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
/* Mosaic style background for body */
|
||||
body {
|
||||
background-color: #ffffff;
|
||||
background-image:
|
||||
linear-gradient(45deg, #f06, #f06 25%, transparent 25%, transparent 75%, #f06 75%, #f06),
|
||||
linear-gradient(45deg, #f06, #f06 25%, transparent 25%, transparent 75%, #f06 75%, #f06);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 10px 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user