adds summit log table
This commit is contained in:
@@ -2,10 +2,11 @@
|
|||||||
export let id: string;
|
export let id: string;
|
||||||
export let title: string;
|
export let title: string;
|
||||||
export let size: string = "max-w-2xl";
|
export let size: string = "max-w-2xl";
|
||||||
|
|
||||||
export function openModal() {
|
export function openModal() {
|
||||||
|
document.body.style.top = `-${window.scrollY}px`;
|
||||||
document.body.style.position = "fixed";
|
document.body.style.position = "fixed";
|
||||||
document.body.style.width = "100%";
|
document.body.style.width = "100%";
|
||||||
document.body.style.top = `-${window.scrollY}px`;
|
|
||||||
|
|
||||||
(document.getElementById(id) as HTMLDialogElement).showModal();
|
(document.getElementById(id) as HTMLDialogElement).showModal();
|
||||||
}
|
}
|
||||||
@@ -25,10 +26,10 @@
|
|||||||
{id}
|
{id}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
aria-hidden="true"
|
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 -->
|
<!-- Modal content -->
|
||||||
<div class="relative bg-background shadow rounded-xl">
|
<div class="bg-background shadow rounded-xl">
|
||||||
<!-- Modal header -->
|
<!-- Modal header -->
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between p-4 md:p-5 border-b border-separator rounded-t"
|
class="flex items-center justify-between p-4 md:p-5 border-b border-separator rounded-t"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
initialValues: $summitLog,
|
initialValues: $summitLog,
|
||||||
validationSchema: summitLogSchema,
|
validationSchema: summitLogSchema,
|
||||||
onSubmit: async (submittedValues) => {
|
onSubmit: async (submittedValues) => {
|
||||||
if(!$form._gpx) {
|
if(!$form.expand.gpx_data) {
|
||||||
$form.gpx = "";
|
$form.gpx = "";
|
||||||
}
|
}
|
||||||
dispatch("save", submittedValues);
|
dispatch("save", submittedValues);
|
||||||
|
|||||||
125
web/src/lib/components/summit_log/summit_log_table.svelte
Normal file
125
web/src/lib/components/summit_log/summit_log_table.svelte
Normal 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>
|
||||||
118
web/src/lib/components/summit_log/summit_log_table_row.svelte
Normal file
118
web/src/lib/components/summit_log/summit_log_table_row.svelte
Normal 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}
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
import CommentCard from "../comment/comment_card.svelte";
|
import CommentCard from "../comment/comment_card.svelte";
|
||||||
import PhotoGallery from "../photo_gallery.svelte";
|
import PhotoGallery from "../photo_gallery.svelte";
|
||||||
import ShareInfo from "../share_info.svelte";
|
import ShareInfo from "../share_info.svelte";
|
||||||
|
import SummitLogTable from "../summit_log/summit_log_table.svelte";
|
||||||
|
|
||||||
export let trail: Trail;
|
export let trail: Trail;
|
||||||
export let mode: "overview" | "map" | "list" = "map";
|
export let mode: "overview" | "map" | "list" = "map";
|
||||||
@@ -335,11 +336,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if activeTab == 3}
|
{#if activeTab == 3}
|
||||||
<ul>
|
<div class="overflow-x-scroll">
|
||||||
{#each trail.expand.summit_logs ?? [] as log, i}
|
<SummitLogTable summitLogs={trail.expand.summit_logs}
|
||||||
<li><SummitLogCard {log} index={i}></SummitLogCard></li>
|
></SummitLogTable>
|
||||||
{/each}
|
</div>
|
||||||
</ul>
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if activeTab == 4}
|
{#if activeTab == 4}
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user