tethys.backend/resources/js/Components/Map/zoom.component.vue
Arno Kaimbacher 4229001572
Some checks failed
build.yaml / Enhance Map Zoom Control and Improve Map Page Layout (push) Failing after 0s
Enhance Map Zoom Control and Improve Map Page Layout
- Refactored zoom control component for better accessibility and styling.
- Added hover effects and improved button states for zoom in/out buttons.
- Updated map page layout with enhanced dataset card design and responsive styles.
- Introduced empty state for no datasets found and improved results header.
- Added icons for dataset cards and improved author display.
2025-11-05 13:15:23 +01:00

348 lines
7.5 KiB
Vue

<template>
<div class="zoom-control-container">
<button
ref="inputPlus"
class="zoom-button zoom-button-plus"
type="button"
@click.stop.prevent="zoomIn"
:disabled="isZoomInDisabled"
aria-label="Zoom in"
>
<BaseIcon v-if="mdiPlus" :path="mdiPlus" :size="20" />
</button>
<div class="zoom-separator"></div>
<button
ref="inputMinus"
class="zoom-button zoom-button-minus"
type="button"
@click.stop.prevent="zoomOut"
:disabled="isZoomOutDisabled"
aria-label="Zoom out"
>
<BaseIcon v-if="mdiMinus" :path="mdiMinus" :size="20" />
</button>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Ref } from 'vue-facing-decorator';
import { MapService } from '@/Stores/map.service';
import BaseIcon from '@/Components/BaseIcon.vue';
import { mdiPlus, mdiMinus } from '@mdi/js';
import { Map } from 'leaflet';
@Component({
name: 'zoom-control',
components: {
BaseIcon,
},
})
export class ZoomControlComponent extends Vue {
mdiPlus = mdiPlus;
mdiMinus = mdiMinus;
/**
* Connect map id.
*/
@Prop() public mapId: string;
@Ref('inputPlus') inputPlus: HTMLButtonElement;
@Ref('inputMinus') inputMinus: HTMLButtonElement;
mapService = MapService();
map: Map | null = null;
isZoomInDisabled = false;
isZoomOutDisabled = false;
mounted() {
let map = (this.map = this.mapService.getMap(this.mapId));
if (map) {
map.on('zoomend zoomlevelschange', this.updateDisabled, this);
this.updateDisabled();
}
}
unmounted() {
if (this.map) {
this.map.off('zoomend zoomlevelschange', this.updateDisabled, this);
}
}
public zoomIn() {
let map = this.mapService.getMap(this.mapId);
map && map.zoomIn();
}
public zoomOut() {
let map = this.mapService.getMap(this.mapId);
map && map.zoomOut();
}
public updateDisabled() {
let map = this.mapService.getMap(this.mapId);
if (!map) return;
this.isZoomInDisabled = map.getZoom() >= map.getMaxZoom();
this.isZoomOutDisabled = map.getZoom() <= map.getMinZoom();
if (this.inputPlus) {
this.inputPlus.disabled = this.isZoomInDisabled;
this.inputPlus.setAttribute('aria-disabled', this.isZoomInDisabled.toString());
}
if (this.inputMinus) {
this.inputMinus.disabled = this.isZoomOutDisabled;
this.inputMinus.setAttribute('aria-disabled', this.isZoomOutDisabled.toString());
}
}
}
export default ZoomControlComponent;
</script>
<style scoped>
.zoom-control-container {
position: absolute;
left: 1rem;
top: 1rem;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 0;
background: white;
border-radius: 0.75rem;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
transition: box-shadow 0.2s ease;
}
.zoom-control-container:hover {
box-shadow:
0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.dark .zoom-control-container {
background: #1f2937;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
}
.dark .zoom-control-container:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
}
.zoom-button {
display: flex;
align-items: center;
justify-content: center;
width: 2.5rem;
height: 2.5rem;
padding: 0;
background: white;
border: none;
color: #374151;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
outline: none;
}
.dark .zoom-button {
background: #1f2937;
color: #d1d5db;
}
.zoom-button:hover:not(:disabled) {
background: #65dc21;
color: white;
transform: scale(1.05);
}
.dark .zoom-button:hover:not(:disabled) {
background: #65dc21;
}
.zoom-button:active:not(:disabled) {
transform: scale(0.95);
}
.zoom-button:disabled {
cursor: not-allowed;
opacity: 0.4;
background: #f3f4f6;
color: #9ca3af;
}
.dark .zoom-button:disabled {
background: #111827;
color: #4b5563;
}
.zoom-button:focus-visible {
outline: 2px solid #65dc21;
outline-offset: -2px;
}
/* Icon sizing */
.zoom-button :deep(svg) {
width: 1.25rem;
height: 1.25rem;
}
/* Separator between buttons */
.zoom-separator {
height: 1px;
background: #e5e7eb;
}
.dark .zoom-separator {
background: #374151;
}
/* Hover effect for the plus button */
.zoom-button-plus::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(101, 220, 33, 0.1) 0%, rgba(53, 124, 6, 0.1) 100%);
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
}
.zoom-button-plus:hover:not(:disabled)::after {
opacity: 1;
}
/* Hover effect for the minus button */
.zoom-button-minus::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(101, 220, 33, 0.1) 0%, rgba(53, 124, 6, 0.1) 100%);
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
}
.zoom-button-minus:hover:not(:disabled)::after {
opacity: 1;
}
/* Responsive design */
@media (max-width: 768px) {
.zoom-control-container {
left: 0.75rem;
top: 0.75rem;
}
.zoom-button {
width: 2.25rem;
height: 2.25rem;
}
.zoom-button :deep(svg) {
width: 1.125rem;
height: 1.125rem;
}
}
@media (max-width: 640px) {
.zoom-control-container {
left: 0.5rem;
top: 0.5rem;
}
.zoom-button {
width: 2rem;
height: 2rem;
}
.zoom-button :deep(svg) {
width: 1rem;
height: 1rem;
}
}
/* Animation for button press */
@keyframes buttonPress {
0% {
transform: scale(1);
}
50% {
transform: scale(0.95);
}
100% {
transform: scale(1);
}
}
.zoom-button:active:not(:disabled) {
animation: buttonPress 0.2s ease;
}
/* Tooltip-like effect on hover (optional) */
.zoom-button-plus:hover:not(:disabled)::before {
content: 'Zoom In';
position: absolute;
left: calc(100% + 0.5rem);
top: 50%;
transform: translateY(-50%);
background: #1f2937;
color: white;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
white-space: nowrap;
opacity: 0;
animation: fadeIn 0.2s ease 0.5s forwards;
pointer-events: none;
z-index: 1001;
}
.zoom-button-minus:hover:not(:disabled)::before {
content: 'Zoom Out';
position: absolute;
left: calc(100% + 0.5rem);
top: 50%;
transform: translateY(-50%);
background: #1f2937;
color: white;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
white-space: nowrap;
opacity: 0;
animation: fadeIn 0.2s ease 0.5s forwards;
pointer-events: none;
z-index: 1001;
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
/* Hide tooltips on mobile */
@media (max-width: 768px) {
.zoom-button-plus:hover:not(:disabled)::before,
.zoom-button-minus:hover:not(:disabled)::before {
display: none;
}
}
</style>