diff --git a/app/app.vue b/app/app.vue index f8eacfa..4c26c91 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,5 +1,5 @@ diff --git a/app/assets/css/main.css b/app/assets/css/main.css index 66f7832..56a23fa 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -26,6 +26,8 @@ .drawer-backdrop-enter-active, .drawer-backdrop-leave-active { transition: opacity 0.2s ease; } .modal-enter-from, .modal-leave-to, .drawer-backdrop-enter-from, .drawer-backdrop-leave-to { opacity: 0; } +.dropdown-enter-active, .dropdown-leave-active { transition: opacity 0.15s ease, transform 0.15s ease; } +.dropdown-enter-from, .dropdown-leave-to { opacity: 0; transform: translateY(-4px); } .modal-enter-active .relative, .modal-leave-active .relative { transition: transform 0.2s ease; } .modal-enter-from .relative, .modal-leave-to .relative { transform: scale(0.96); } @@ -36,6 +38,10 @@ .kestrel-map-container { background: #000 !important; } +.kestrel-map-container .leaflet-container { + border: none !important; + outline: none !important; +} .kestrel-map-container .leaflet-tile-pane, .kestrel-map-container .leaflet-map-pane, .kestrel-map-container .leaflet-tile-container { diff --git a/app/components/AddUserModal.vue b/app/components/AddUserModal.vue new file mode 100644 index 0000000..4b0bf73 --- /dev/null +++ b/app/components/AddUserModal.vue @@ -0,0 +1,115 @@ + + + diff --git a/app/components/AppDropdown.vue b/app/components/AppDropdown.vue new file mode 100644 index 0000000..39c07ca --- /dev/null +++ b/app/components/AppDropdown.vue @@ -0,0 +1,95 @@ + + + diff --git a/app/components/AppShell.vue b/app/components/AppShell.vue new file mode 100644 index 0000000..0c7be21 --- /dev/null +++ b/app/components/AppShell.vue @@ -0,0 +1,89 @@ + + + diff --git a/app/components/BaseModal.vue b/app/components/BaseModal.vue new file mode 100644 index 0000000..23a4b32 --- /dev/null +++ b/app/components/BaseModal.vue @@ -0,0 +1,36 @@ + + + diff --git a/app/components/DeleteUserConfirmModal.vue b/app/components/DeleteUserConfirmModal.vue new file mode 100644 index 0000000..a10ca4c --- /dev/null +++ b/app/components/DeleteUserConfirmModal.vue @@ -0,0 +1,46 @@ + + + diff --git a/app/components/EditUserModal.vue b/app/components/EditUserModal.vue new file mode 100644 index 0000000..149a3d2 --- /dev/null +++ b/app/components/EditUserModal.vue @@ -0,0 +1,95 @@ + + + diff --git a/app/components/KestrelMap.vue b/app/components/KestrelMap.vue index 306b934..a88d352 100644 --- a/app/components/KestrelMap.vue +++ b/app/components/KestrelMap.vue @@ -201,6 +201,7 @@ function createMap(initialCenter) { updateMarkers() updatePoiMarkers() updateLiveMarkers() + nextTick(() => map.invalidateSize()) } function updateMarkers() { @@ -403,6 +404,8 @@ function initMapWithLocation() { ) } +let resizeObserver = null + onMounted(async () => { if (!import.meta.client || typeof document === 'undefined') return const [leaflet, offline] = await Promise.all([ @@ -422,6 +425,15 @@ onMounted(async () => { leafletRef.value = { L, offlineApi: offline } initMapWithLocation() document.addEventListener('click', onDocumentClick) + + nextTick(() => { + if (mapRef.value) { + resizeObserver = new ResizeObserver(() => { + mapContext.value?.map?.invalidateSize() + }) + resizeObserver.observe(mapRef.value) + } + }) }) function onDocumentClick(e) { @@ -430,6 +442,10 @@ function onDocumentClick(e) { onBeforeUnmount(() => { document.removeEventListener('click', onDocumentClick) + if (resizeObserver && mapRef.value) { + resizeObserver.disconnect() + resizeObserver = null + } destroyMap() }) diff --git a/app/components/MembersTable.vue b/app/components/MembersTable.vue new file mode 100644 index 0000000..60756a8 --- /dev/null +++ b/app/components/MembersTable.vue @@ -0,0 +1,133 @@ + + + diff --git a/app/components/NavDrawer.vue b/app/components/NavDrawer.vue index 6e710e8..fbaf8f4 100644 --- a/app/components/NavDrawer.vue +++ b/app/components/NavDrawer.vue @@ -1,8 +1,8 @@ diff --git a/app/composables/useMediaQuery.js b/app/composables/useMediaQuery.js new file mode 100644 index 0000000..e04dad5 --- /dev/null +++ b/app/composables/useMediaQuery.js @@ -0,0 +1,21 @@ +/** + * Reactive viewport media query. SSR-safe: defaults to true (mobile) so sidebar closed on first paint. + * @param {string} query - CSS media query, e.g. '(max-width: 767px)' + * @returns {import('vue').Ref} Ref that is true when the media query matches. + */ +export function useMediaQuery(query) { + const matches = ref(true) + let mql = null + const handler = (e) => { + matches.value = e.matches + } + onMounted(() => { + mql = window.matchMedia(query) + matches.value = mql.matches + mql.addEventListener('change', handler) + }) + onBeforeUnmount(() => { + if (mql) mql.removeEventListener('change', handler) + }) + return matches +} diff --git a/app/layouts/default.vue b/app/layouts/default.vue index 46abfea..24cb403 100644 --- a/app/layouts/default.vue +++ b/app/layouts/default.vue @@ -1,71 +1,7 @@ - - diff --git a/app/pages/account.vue b/app/pages/account.vue index fa755fa..29e49eb 100644 --- a/app/pages/account.vue +++ b/app/pages/account.vue @@ -4,6 +4,51 @@ Account +
+ +
+
+ + + {{ accountInitials }} + +
+
+ + +
+
+
+