Enhance frontend components and introduce new features

- Added a custom light theme in app.css to match the dark theme's palette.
- Introduced AdminBreadcrumbs component for improved navigation in admin pages.
- Implemented Skeleton component for loading states in various views.
- Added ToastContainer for displaying notifications and alerts.
- Enhanced MapView with loading indicators and improved marker handling.
- Updated MapCoordsDisplay to allow copying of shareable links.
- Refactored MapControls and MapContextMenu for better usability.
- Improved user experience in profile and admin pages with loading states and search functionality.
This commit is contained in:
2026-03-01 15:19:55 +03:00
parent 6529d7370e
commit 2bd2c8dbca
15 changed files with 817 additions and 212 deletions

View File

@@ -1,5 +1,19 @@
<template>
<div class="relative h-full w-full" @click="(e: MouseEvent) => e.button === 0 && mapLogic.closeContextMenus()">
<div
v-if="!mapReady"
class="absolute inset-0 z-[400] flex flex-col items-center justify-center gap-6 bg-base-200/95 p-8"
aria-busy="true"
aria-label="Loading map"
>
<Skeleton class="h-12 w-48" />
<div class="flex gap-3">
<Skeleton class="h-10 w-24" />
<Skeleton class="h-10 w-32" />
<Skeleton class="h-10 w-28" />
</div>
<Skeleton class="h-64 w-72 max-w-full" />
</div>
<div
v-if="mapsLoaded && maps.length === 0"
class="absolute inset-0 z-[500] flex flex-col items-center justify-center gap-4 bg-base-200/90 p-6"
@@ -14,9 +28,9 @@
</div>
</div>
<div ref="mapRef" class="map h-full w-full" />
<MapMapCoordsDisplay
:mapid="mapLogic.state.mapid"
:display-coords="mapLogic.state.displayCoords"
<MapCoordsDisplay
:mapid="mapLogic.state.mapid.value"
:display-coords="mapLogic.state.displayCoords.value"
/>
<MapControls
:show-grid-coordinates="mapLogic.state.showGridCoordinates.value"
@@ -38,16 +52,16 @@
@zoom-out="mapLogic.zoomOutControl(leafletMap)"
@reset-view="mapLogic.resetView(leafletMap)"
/>
<MapMapContextMenu
<MapContextMenu
:context-menu="mapLogic.contextMenu"
@wipe-tile="onWipeTile"
@rewrite-coords="onRewriteCoords"
@hide-marker="onHideMarker"
/>
<MapMapCoordSetModal
:coord-set-from="mapLogic.coordSetFrom"
:coord-set="mapLogic.coordSet"
:open="mapLogic.coordSetModalOpen"
<MapCoordSetModal
:coord-set-from="mapLogic.coordSetFrom.value"
:coord-set="mapLogic.coordSet.value"
:open="mapLogic.coordSetModalOpen.value"
@close="mapLogic.closeCoordSetModal()"
@submit="onSubmitCoordSet"
/>
@@ -56,6 +70,9 @@
<script setup lang="ts">
import MapControls from '~/components/map/MapControls.vue'
import MapCoordsDisplay from '~/components/map/MapCoordsDisplay.vue'
import MapContextMenu from '~/components/map/MapContextMenu.vue'
import MapCoordSetModal from '~/components/map/MapCoordSetModal.vue'
import { HnHDefaultZoom, HnHMaxZoom, HnHMinZoom, TileSize } from '~/lib/LeafletCustomTypes'
import { initLeafletMap, type MapInitResult } from '~/composables/useMapInit'
import { startMapUpdates, type UseMapUpdatesReturn } from '~/composables/useMapUpdates'
@@ -77,7 +94,16 @@ const props = withDefaults(
const mapRef = ref<HTMLElement | null>(null)
const api = useMapApi()
const mapLogic = useMapLogic()
const { resolvePath } = useAppPaths()
/** Fallback marker icon (simple pin) when the real icon image fails to load. */
const FALLBACK_MARKER_ICON =
'data:image/svg+xml,' +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32"><path fill="%236366f1" stroke="%234f46e5" stroke-width="1.5" d="M16 4c-4 0-7 3-7 7 0 5 7 13 7 13s7-8 7-13c0-4-3-7-7-7z"/><circle cx="16" cy="11" r="3" fill="white"/></svg>'
)
const mapReady = ref(false)
const maps = ref<MapInfo[]>([])
const mapsLoaded = ref(false)
const questGivers = ref<Array<{ id: number; name: string }>>([])
@@ -203,6 +229,8 @@ onMounted(async () => {
getTrackingCharacterId: () => mapLogic.state.trackingCharacterId.value,
setTrackingCharacterId: (id: number) => { mapLogic.state.trackingCharacterId.value = id },
onMarkerContextMenu: mapLogic.openMarkerContextMenu,
resolveIconUrl: (path) => resolvePath(path),
fallbackIconUrl: FALLBACK_MARKER_ICON,
})
updatesHandle = startMapUpdates({
@@ -245,6 +273,7 @@ onMounted(async () => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (leafletMap) leafletMap.invalidateSize()
mapReady.value = true
})
})
})