adds summit log table

This commit is contained in:
Christian Beutel
2024-11-02 01:09:02 +01:00
parent 9c07aae339
commit 63da3d4144
5 changed files with 253 additions and 9 deletions

View File

@@ -2,10 +2,11 @@
export let id: string;
export let title: string;
export let size: string = "max-w-2xl";
export function openModal() {
document.body.style.top = `-${window.scrollY}px`;
document.body.style.position = "fixed";
document.body.style.width = "100%";
document.body.style.top = `-${window.scrollY}px`;
(document.getElementById(id) as HTMLDialogElement).showModal();
}
@@ -25,10 +26,10 @@
{id}
tabindex="-1"
aria-hidden="true"
class="relative w-full {size} max-h-full rounded-xl text-content"
class="w-full {size} max-h-full rounded-xl text-content"
>
<!-- Modal content -->
<div class="relative bg-background shadow rounded-xl">
<div class="bg-background shadow rounded-xl">
<!-- Modal header -->
<div
class="flex items-center justify-between p-4 md:p-5 border-b border-separator rounded-t"

View File

@@ -26,7 +26,7 @@
initialValues: $summitLog,
validationSchema: summitLogSchema,
onSubmit: async (submittedValues) => {
if(!$form._gpx) {
if(!$form.expand.gpx_data) {
$form.gpx = "";
}
dispatch("save", submittedValues);

View File

@@ -0,0 +1,125 @@
<script lang="ts">
import type { SummitLog } from "$lib/models/summit_log";
import "leaflet/dist/leaflet.css";
import { onMount, tick } from "svelte";
import Modal from "../base/modal.svelte";
import SummitLogTableRow from "./summit_log_table_row.svelte";
import type { Map } from "leaflet";
import { gpx } from "$lib/vendor/toGeoJSON/toGeoJSON";
import { endIcon, startIcon } from "$lib/util/leaflet_util";
export let summitLogs: SummitLog[];
let openModal: () => void;
let closeModal: () => void;
let map: Map;
let L: any;
let layerGroup: any;
onMount(async () => {
L = (await import("leaflet")).default;
map = L.map("summit-log-table-map");
map.attributionControl.setPrefix(false);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap contributors",
}).addTo(map);
layerGroup = L.layerGroup();
layerGroup.addTo(map);
});
async function openMap(log: SummitLog) {
if (!log.expand.gpx_data) {
return;
}
layerGroup.clearLayers();
const geoJson = gpx(
new DOMParser().parseFromString(log.expand.gpx_data, "text/xml"),
);
const layer = L.geoJson(geoJson, {
onEachFeature: (feature: any, layer: any) => {
let startCoords, endCoords;
if (geoJson.features && geoJson.features.length > 0) {
const geometry = geoJson.features[0].geometry;
if (geometry.type === "LineString") {
startCoords = geometry.coordinates[0];
endCoords =
geometry.coordinates[
geometry.coordinates.length - 1
];
} else if (geometry.type === "MultiLineString") {
startCoords = (geometry as any).coordinates[0][0];
endCoords = (geometry as any).coordinates[
geometry.coordinates.length - 1
][
(geometry as any).coordinates[
geometry.coordinates.length - 1
].length - 1
];
}
}
if (startCoords && endCoords) {
const startMarker = L.marker(
[startCoords[1], startCoords[0]],
{
icon: startIcon(),
},
);
const endMarker = L.marker([endCoords[1], endCoords[0]], {
icon: endIcon(),
});
layerGroup.addLayer(startMarker);
layerGroup.addLayer(endMarker)
}
},
filter: (feature: any, layer: any) => {
return feature.geometry.type !== "Point";
},
}).addTo(map);
layerGroup.addLayer(layer);
openModal();
await tick();
map.fitBounds(layer.getBounds());
map.invalidateSize();
}
</script>
<table class="w-full">
<thead>
<tr class="text-sm">
<th class="w-24"></th>
<th>Date</th>
<th>Distance</th>
<th class="whitespace-nowrap">Elevation gain</th>
<th>Duration</th>
<th></th>
</tr>
</thead>
<tbody>
{#each summitLogs as log, i}
<SummitLogTableRow index={i} {log} on:open={() => openMap(log)}
></SummitLogTableRow>
{/each}
</tbody>
</table>
<Modal id="summit-log-table-modal" title="" bind:openModal bind:closeModal>
<div slot="content" id="summit-log-table-map" class="h-96"></div>
</Modal>
<style>
th {
padding: 0rem 0.75rem;
}
</style>

View File

@@ -0,0 +1,118 @@
<script lang="ts">
import type { SummitLog } from "$lib/models/summit_log";
import "leaflet/dist/leaflet.css";
import GPX from "$lib/models/gpx/gpx";
import {
formatDistance,
formatElevation,
formatTimeHHMM,
} from "$lib/util/format_util";
import { gpx } from "$lib/vendor/toGeoJSON/toGeoJSON";
import type { Map } from "leaflet";
import { createEventDispatcher, onMount } from "svelte";
export let index: number;
export let log: SummitLog;
let totals: {
distance: number;
elevationGain: number;
duration: number;
} | null = null;
let map: Map;
let showText: boolean = false;
const dispatch = createEventDispatcher();
onMount(async () => {
if (log.expand.gpx_data) {
await initMap();
}
});
async function initMap() {
const L = (await import("leaflet")).default;
map = L.map("mini-map-" + index, {
zoomControl: false,
scrollWheelZoom: false,
dragging: false,
});
map.attributionControl.setPrefix(false);
if (!log.expand.gpx_data || !map) {
return;
}
const gpxObject = await GPX.parse(log.expand.gpx_data);
if (gpxObject instanceof Error) {
throw gpxObject;
}
totals = gpxObject.getTotals();
const geoJson = gpx(
new DOMParser().parseFromString(log.expand.gpx_data, "text/xml"),
);
const layer = (L as any)
.geoJson(geoJson, {
filter: (feature: any, layer: any) => {
return feature.geometry.type !== "Point";
},
})
.addTo(map);
map.fitBounds(layer.getBounds());
map.invalidateSize();
}
function openMap() {
dispatch("open", log);
}
</script>
<tr class="h-24 text-center">
<td>
<button
type="button"
on:click={openMap}
class="h-24 aspect-square shrink-0 rounded-xl !bg-background"
class:hidden={!log.expand.gpx_data}
id="mini-map-{index}"
></button>
</td>
<td
>{new Date(log.date).toLocaleDateString(undefined, {
month: "2-digit",
day: "2-digit",
year: "numeric",
timeZone: "UTC",
})}</td
>
<td>
{formatDistance(totals?.distance)}
</td>
<td>
{formatElevation(totals?.elevationGain)}
</td><td>
{formatTimeHHMM(totals?.duration)}
</td>
<td>
{#if log.text}
<button on:click={() => (showText = !showText)} class="btn-icon"
><i class="fa{showText ? '' : '-regular'} fa-message"
></i></button
>
{/if}
</td>
</tr>
{#if showText}
<tr
><td class="text-left text-sm whitespace-pre-wrap" colspan="6"
>{log.text}</td
></tr
>
{/if}

View File

@@ -38,6 +38,7 @@
import CommentCard from "../comment/comment_card.svelte";
import PhotoGallery from "../photo_gallery.svelte";
import ShareInfo from "../share_info.svelte";
import SummitLogTable from "../summit_log/summit_log_table.svelte";
export let trail: Trail;
export let mode: "overview" | "map" | "list" = "map";
@@ -335,11 +336,10 @@
</div>
{/if}
{#if activeTab == 3}
<ul>
{#each trail.expand.summit_logs ?? [] as log, i}
<li><SummitLogCard {log} index={i}></SummitLogCard></li>
{/each}
</ul>
<div class="overflow-x-scroll">
<SummitLogTable summitLogs={trail.expand.summit_logs}
></SummitLogTable>
</div>
{/if}
{#if activeTab == 4}
<div>