adds i18n
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
export let id: string;
|
||||
export let title: string;
|
||||
export let size: string = "2xl";
|
||||
export let size: string = "max-w-2xl";
|
||||
export function openModal() {
|
||||
document.body.style.position = "fixed";
|
||||
document.body.style.width = "100%";
|
||||
@@ -25,7 +25,7 @@
|
||||
{id}
|
||||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
class="relative w-full max-w-{size} max-h-full rounded-xl text-content"
|
||||
class="relative w-full {size} max-h-full rounded-xl text-content"
|
||||
>
|
||||
<!-- Modal content -->
|
||||
<div class="relative bg-background shadow">
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<script lang="ts">
|
||||
import Modal from "$lib/components/base/modal.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
|
||||
export let title: string = "Confirm Deletion";
|
||||
export let text: string;
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function confirm() {
|
||||
dispatch("confirm");
|
||||
@@ -19,11 +20,11 @@
|
||||
<Modal id="confirm-modal" {title} bind:openModal bind:closeModal>
|
||||
<p slot="content">{text}</p>
|
||||
<div slot="footer" class="flex items-center gap-4">
|
||||
<button class="btn-secondary" on:click={closeModal}>Cancel</button>
|
||||
<button
|
||||
class="btn-danger"
|
||||
type="button"
|
||||
on:click={confirm}>Delete</button
|
||||
<button class="btn-secondary" on:click={closeModal}
|
||||
>{$_("cancel")}</button
|
||||
>
|
||||
<button class="btn-danger" type="button" on:click={confirm}
|
||||
>{$_("delete")}</button
|
||||
>
|
||||
</div></Modal
|
||||
>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { _ } from "svelte-i18n";
|
||||
export let width: number = 256;
|
||||
</script>
|
||||
|
||||
@@ -10,5 +11,5 @@
|
||||
alt="Empty State showing a wanderer going into the distance"
|
||||
/>
|
||||
|
||||
<h3 class="text-2xl font-semibold text-center">No results found</h3>
|
||||
<h3 class="text-2xl font-semibold text-center">{$_("no-results")}</h3>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script>
|
||||
import LogoTextLight from "./logo/logo_text_light.svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
</script>
|
||||
|
||||
<footer class="bg-footer-background text-white w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-y-8 p-12 mt-12 rounded-t-3xl">
|
||||
@@ -11,18 +13,18 @@
|
||||
<div>
|
||||
<h5 class="font-semibold">wanderer</h5>
|
||||
<ul class="mt-4 text-sm">
|
||||
<li>About</li>
|
||||
<li>Features</li>
|
||||
<li>Changelog</li>
|
||||
<li>License</li>
|
||||
<li>{$_('about')}</li>
|
||||
<li>{$_('features')}</li>
|
||||
<li>{$_('changelog')}</li>
|
||||
<li><a href="https://github.com/Flomp/wanderer/blob/main/LICENSE">{$_('license')}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="font-semibold">Community</h5>
|
||||
<ul class="mt-4 text-sm">
|
||||
<li>GitHub</li>
|
||||
<li>Issues</li>
|
||||
<li>Contribute</li>
|
||||
<li><a href="https://github.com/Flomp/wanderer">GitHub</a></li>
|
||||
<li><a href="https://github.com/Flomp/wanderer/issues">Issues</a></li>
|
||||
<li><a href="https://github.com/Flomp/wanderer/pulls">{$_('contribute')}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { List } from "$lib/models/list";
|
||||
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
export let list: List;
|
||||
export let active: boolean = false;
|
||||
|
||||
const dropdownItems: DropdownItem[] = [
|
||||
{ text: "Edit", value: "edit" },
|
||||
{ text: "Delete", value: "delete" },
|
||||
{ text: $_("edit"), value: "edit" },
|
||||
{ text: $_("delete"), value: "delete" },
|
||||
];
|
||||
</script>
|
||||
|
||||
@@ -21,7 +22,9 @@
|
||||
alt="avatar"
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex w-16 md:w-24 aspect-square shrink-0 items-center justify-center">
|
||||
<div
|
||||
class="flex w-16 md:w-24 aspect-square shrink-0 items-center justify-center"
|
||||
>
|
||||
<i class="fa fa-table-list text-5xl"></i>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
import { list } from "$lib/stores/list_store";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib/index";
|
||||
import { util } from "$lib/vendor/svelte-form-lib/util";
|
||||
import { _ } from "svelte-i18n";
|
||||
import Modal from "../base/modal.svelte";
|
||||
import TextField from "../base/text_field.svelte";
|
||||
import Textarea from "../base/textarea.svelte";
|
||||
|
||||
export let openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
|
||||
@@ -37,9 +37,8 @@
|
||||
}
|
||||
|
||||
function handleAvatarSelection() {
|
||||
const files = (
|
||||
document.getElementById("avatar") as HTMLInputElement
|
||||
).files;
|
||||
const files = (document.getElementById("avatar") as HTMLInputElement)
|
||||
.files;
|
||||
|
||||
if (!files) {
|
||||
return;
|
||||
@@ -55,7 +54,7 @@
|
||||
|
||||
<Modal
|
||||
id="list-modal"
|
||||
title="New List"
|
||||
title={$form.id ? $_("edit-list") : $_("new-list")}
|
||||
let:openModal
|
||||
bind:openModal
|
||||
bind:closeModal
|
||||
@@ -67,7 +66,7 @@
|
||||
class="modal-content space-y-4"
|
||||
on:submit={handleSubmit}
|
||||
>
|
||||
<label for="avatar" class="text-sm font-medium block"> Avatar </label>
|
||||
<label for="avatar" class="text-sm font-medium block"> {$_('avatar')} </label>
|
||||
<input
|
||||
name="avatar"
|
||||
type="file"
|
||||
@@ -87,13 +86,13 @@
|
||||
<button
|
||||
class="btn-secondary"
|
||||
type="button"
|
||||
on:click={openAvatarBrowser}>Change...</button
|
||||
on:click={openAvatarBrowser}>{$_('change')}...</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<TextField
|
||||
name="name"
|
||||
label="Name"
|
||||
label={$_("name")}
|
||||
bind:value={$form.name}
|
||||
error={$errors.name}
|
||||
on:change={handleChange}
|
||||
@@ -101,14 +100,18 @@
|
||||
|
||||
<Textarea
|
||||
name="description"
|
||||
label="Description"
|
||||
label={$_("description")}
|
||||
bind:value={$form.description}
|
||||
error={$errors.description}
|
||||
on:change={handleChange}
|
||||
></Textarea>
|
||||
</form>
|
||||
<div slot="footer" class="flex items-center gap-4">
|
||||
<button class="btn-secondary" on:click={closeModal}>Cancel</button>
|
||||
<button class="btn-primary" type="submit" form="list-form">Save</button>
|
||||
<button class="btn-secondary" on:click={closeModal}
|
||||
>{$_("cancel")}</button
|
||||
>
|
||||
<button class="btn-primary" type="submit" form="list-form"
|
||||
>{$_("save")}</button
|
||||
>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import { lists, lists_index } from "$lib/stores/list_store";
|
||||
import Modal from "../base/modal.svelte";
|
||||
import type { List } from "$lib/models/list";
|
||||
import { lists } from "$lib/stores/list_store";
|
||||
import { trail } from "$lib/stores/trail_store";
|
||||
import { _ } from "svelte-i18n";
|
||||
import Modal from "../base/modal.svelte";
|
||||
|
||||
export let openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
@@ -23,8 +24,8 @@
|
||||
|
||||
<Modal
|
||||
id="list-modal"
|
||||
title="Select List"
|
||||
size="md"
|
||||
title={$_("select-list")}
|
||||
size="max-w-sm"
|
||||
let:openModal
|
||||
bind:openModal
|
||||
bind:closeModal
|
||||
|
||||
@@ -3,22 +3,23 @@
|
||||
import LogoText from "$lib/components/logo/logo_text.svelte";
|
||||
import { theme, toggleTheme } from "$lib/stores/theme_store";
|
||||
import { currentUser, logout } from "$lib/stores/user_store";
|
||||
import { getFileURL } from "$lib/util/file_util";
|
||||
import { backInOut, cubicOut } from "svelte/easing";
|
||||
import { tweened } from "svelte/motion";
|
||||
import Drawer from "./base/drawer.svelte";
|
||||
import Dropdown from "./base/dropdown.svelte";
|
||||
import LogoTextLight from "./logo/logo_text_light.svelte";
|
||||
import Drawer from "./base/drawer.svelte";
|
||||
import { _, format } from "svelte-i18n";
|
||||
|
||||
const navBarItems = [
|
||||
let navBarItems = [
|
||||
{ text: "Home", value: "/" },
|
||||
{ text: "Trails", value: "/trails" },
|
||||
{ text: "Map", value: "/map" },
|
||||
{ text: "Lists", value: "/lists" },
|
||||
{ text: $_("trail", { values: { n: 2 } }), value: "/trails" },
|
||||
{ text: $_("map"), value: "/map" },
|
||||
];
|
||||
|
||||
const dropdownItems = [
|
||||
{ text: "Profile", value: "profile", icon: "user" },
|
||||
{ text: "Logout", value: "logout", icon: "right-from-bracket" },
|
||||
{ text: $_("profile"), value: "profile", icon: "user" },
|
||||
{ text: $_("logout"), value: "logout", icon: "right-from-bracket" },
|
||||
];
|
||||
|
||||
const indicatorPosition = tweened(0, {
|
||||
@@ -104,11 +105,19 @@
|
||||
data-sveltekit-preload-data="off">{item.text}</a
|
||||
>
|
||||
{/each}
|
||||
{#if $currentUser}
|
||||
<a
|
||||
class="font-semibold text-xl"
|
||||
href="/lists"
|
||||
data-sveltekit-preload-data="off"
|
||||
>{$_("list", { values: { n: 2 } })}</a
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<hr class="my-6 border-input-border" />
|
||||
<div class="flex flex-col basis-full">
|
||||
<a class="btn-primary btn-large text-center mx-4" href="/trail/edit/new"
|
||||
><i class="fa fa-plus mr-2"></i>New Trail</a
|
||||
><i class="fa fa-plus mr-2"></i>{$_('new-trail')}</a
|
||||
>
|
||||
{#if $currentUser}
|
||||
<div class="basis-full"></div>
|
||||
@@ -116,9 +125,9 @@
|
||||
<div class="flex gap-4 items-center m-4">
|
||||
<img
|
||||
class="rounded-full w-8 aspect-square"
|
||||
src={$currentUser.avatar ||
|
||||
src={getFileURL($currentUser, $currentUser.avatar) ||
|
||||
`https://api.dicebear.com/7.x/initials/svg?seed=${$currentUser.username}&backgroundType=gradientLinear`}
|
||||
alt=""
|
||||
alt="avatar"
|
||||
/>
|
||||
<div>
|
||||
<p class="text-sm">{$currentUser.username}</p>
|
||||
@@ -153,6 +162,14 @@
|
||||
data-sveltekit-preload-data="off">{item.text}</a
|
||||
>
|
||||
{/each}
|
||||
{#if $currentUser}
|
||||
<a
|
||||
class="font-semibold z-10"
|
||||
href="/lists"
|
||||
data-sveltekit-preload-data="off"
|
||||
>{$_("list", { values: { n: 2 } })}</a
|
||||
>
|
||||
{/if}
|
||||
</menu>
|
||||
{#if $currentUser}
|
||||
<div class="hidden md:flex gap-6 items-center">
|
||||
@@ -163,7 +180,7 @@
|
||||
on:click={() => toggleTheme()}
|
||||
></button>
|
||||
<a class="btn-primary btn-large" href="/trail/edit/new"
|
||||
><i class="fa fa-plus mr-2"></i>New Trail</a
|
||||
><i class="fa fa-plus mr-2"></i>{$_('new-trail')}</a
|
||||
>
|
||||
<Dropdown
|
||||
items={dropdownItems}
|
||||
@@ -176,8 +193,9 @@
|
||||
>
|
||||
<img
|
||||
class="rounded-full"
|
||||
src="https://api.dicebear.com/7.x/initials/svg?seed={$currentUser.username}&backgroundType=gradientLinear"
|
||||
alt=""
|
||||
src={getFileURL($currentUser, $currentUser.avatar) ||
|
||||
`https://api.dicebear.com/7.x/initials/svg?seed=${$currentUser.username}&backgroundType=gradientLinear`}
|
||||
alt="avatar"
|
||||
/>
|
||||
</button>
|
||||
</Dropdown>
|
||||
@@ -190,7 +208,7 @@
|
||||
: 'moon'}"
|
||||
on:click={() => toggleTheme()}
|
||||
></button>
|
||||
<a class="btn-primary btn-large" href="/login">Login</a>
|
||||
<a class="btn-primary btn-large" href="/login">{$_('login')}</a>
|
||||
</div>
|
||||
{/if}
|
||||
<button
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let src: string;
|
||||
export let isThumbnail: boolean = false;
|
||||
@@ -30,13 +31,13 @@
|
||||
<button
|
||||
type="button"
|
||||
class="tooltip"
|
||||
data-title="Make thumbnail"
|
||||
data-title={$_("make-thumbnail")}
|
||||
on:click={handleThumbnailClick}
|
||||
><i class="fa fa-file-image text-primary"></i></button
|
||||
>
|
||||
<button
|
||||
class="tooltip"
|
||||
data-title="Delete"
|
||||
data-title={$_("delete")}
|
||||
on:click={handleDeleteClick}
|
||||
type="button"><i class="fa fa-trash text-red-500"></i></button
|
||||
>
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
<script lang="ts">
|
||||
import type { SummitLog } from "$lib/models/summit_log";
|
||||
import { format, parse } from "date-fns";
|
||||
import Dropdown from "../base/dropdown.svelte";
|
||||
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let log: SummitLog;
|
||||
export let mode: "show" | "edit" = "show";
|
||||
|
||||
const dropdownItems = [
|
||||
{ text: "Edit", value: "edit" },
|
||||
{ text: "Delete", value: "delete" },
|
||||
const dropdownItems: DropdownItem[] = [
|
||||
{ text: $_("edit"), value: "edit" },
|
||||
{ text: $_("delete"), value: "delete" },
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="p-4 my-2 border border-input-border rounded-xl">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<h5 class="font-medium mr-2">{format(log.date, 'dd.MM.yyyy')}</h5>
|
||||
<h5 class="font-medium mr-2">{format(log.date, "dd.MM.yyyy")}</h5>
|
||||
|
||||
{#if mode == "edit"}
|
||||
<Dropdown items={dropdownItems} on:change></Dropdown>
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
import { summitLogSchema, type SummitLog } from "$lib/models/summit_log";
|
||||
import { summitLog } from "$lib/stores/summit_log_store";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib/index";
|
||||
import { util } from "$lib/vendor/svelte-form-lib/util";
|
||||
import { _ } from "svelte-i18n";
|
||||
import Datepicker from "../base/datepicker.svelte";
|
||||
import Modal from "../base/modal.svelte";
|
||||
import TextField from "../base/text_field.svelte";
|
||||
import { util } from "$lib/vendor/svelte-form-lib/util";
|
||||
|
||||
export let openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
const { form, errors, handleChange, handleSubmit } = createForm<SummitLog>({
|
||||
initialValues: $summitLog,
|
||||
validationSchema: summitLogSchema,
|
||||
onSubmit: async (submittedValues) => {
|
||||
onSubmit: async (submittedValues) => {
|
||||
dispatch("save", submittedValues);
|
||||
closeModal!();
|
||||
},
|
||||
});
|
||||
$: form.set(util.cloneDeep($summitLog));
|
||||
$: form.set(util.cloneDeep($summitLog));
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
id="summit-log-modal"
|
||||
title="Add Entry"
|
||||
title={$form.id ? $_("edit-entry") : $_("add-entry")}
|
||||
let:openModal
|
||||
bind:openModal
|
||||
bind:closeModal
|
||||
@@ -42,7 +42,7 @@
|
||||
<div class="flex gap-4">
|
||||
<Datepicker
|
||||
name="date"
|
||||
label="Date"
|
||||
label={$_("date")}
|
||||
bind:value={$form.date}
|
||||
error={$errors.date}
|
||||
on:change={handleChange}
|
||||
@@ -50,7 +50,7 @@
|
||||
<div class="basis-full">
|
||||
<TextField
|
||||
name="text"
|
||||
label="Text"
|
||||
label={$_("text")}
|
||||
bind:value={$form.text}
|
||||
error={$errors.text}
|
||||
on:change={handleChange}
|
||||
@@ -59,9 +59,11 @@
|
||||
</div>
|
||||
</form>
|
||||
<div slot="footer" class="flex items-center gap-4">
|
||||
<button class="btn-secondary" on:click={closeModal}>Cancel</button>
|
||||
<button class="btn-secondary" on:click={closeModal}
|
||||
>{$_("cancel")}</button
|
||||
>
|
||||
<button class="btn-primary" type="submit" form="summit-log-form"
|
||||
>Save</button
|
||||
>{$_("save")}</button
|
||||
>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
111
web/src/lib/components/trail/map_with_elevation.svelte
Normal file
111
web/src/lib/components/trail/map_with_elevation.svelte
Normal file
@@ -0,0 +1,111 @@
|
||||
<script lang="ts">
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import { currentUser } from "$lib/stores/user_store";
|
||||
import { getFileURL } from "$lib/util/file_util";
|
||||
import { createMarkerFromWaypoint } from "$lib/util/leaflet_util";
|
||||
import type { Map, Marker } from "leaflet";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
export let trail: Trail;
|
||||
export let markers: Marker[] = [];
|
||||
|
||||
let L: any;
|
||||
let map: Map;
|
||||
let controlElevation: any;
|
||||
|
||||
$: if (trail.expand.gpx_data && controlElevation) {
|
||||
controlElevation.load(trail.expand.gpx_data);
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
L = (await import("leaflet")).default;
|
||||
await import("leaflet-gpx");
|
||||
await import("leaflet.awesome-markers");
|
||||
//@ts-ignore
|
||||
await import("$lib/vendor/leaflet-elevation/src/index.js");
|
||||
|
||||
map = L.map("map").setView([trail.lat ?? 0, trail.lon ?? 0], 14);
|
||||
|
||||
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
}).addTo(map);
|
||||
|
||||
const elevation_options = {
|
||||
height: 200,
|
||||
|
||||
theme: "lightblue-theme",
|
||||
detached: true,
|
||||
elevationDiv: "#elevation",
|
||||
closeBtn: false,
|
||||
followMarker: true,
|
||||
autofitBounds: true,
|
||||
imperial: $currentUser?.unit == "imperial" ?? false,
|
||||
reverseCoords: false,
|
||||
acceleration: false,
|
||||
slope: true,
|
||||
speed: false,
|
||||
altitude: true,
|
||||
time: true,
|
||||
distance: true,
|
||||
// Summary track info style: "inline" || "multiline" || false
|
||||
summary: false,
|
||||
downloadLink: false,
|
||||
ruler: false,
|
||||
legend: true,
|
||||
// Toggle "leaflet-almostover" integration
|
||||
almostOver: true,
|
||||
// Toggle "leaflet-distance-markers" integration
|
||||
distanceMarkers: false,
|
||||
// Toggle "leaflet-edgescale" integration
|
||||
edgeScale: false,
|
||||
// Toggle "leaflet-hotline" integration
|
||||
hotline: true,
|
||||
// Display track datetimes: true || false
|
||||
timestamps: false,
|
||||
waypoints: false,
|
||||
wptIcons: false,
|
||||
wptLabels: false,
|
||||
preferCanvas: true,
|
||||
trkStart: {
|
||||
icon: L.AwesomeMarkers.icon({
|
||||
icon: "circle-half-stroke",
|
||||
prefix: "fa",
|
||||
markerColor: "cadetblue",
|
||||
iconColor: "white",
|
||||
}),
|
||||
},
|
||||
trkEnd: {
|
||||
icon: L.AwesomeMarkers.icon({
|
||||
icon: "flag-checkered",
|
||||
prefix: "fa",
|
||||
markerColor: "cadetblue",
|
||||
iconColor: "white",
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
controlElevation = L.control.elevation(elevation_options).addTo(map);
|
||||
|
||||
for (const waypoint of trail.expand.waypoints) {
|
||||
const marker = createMarkerFromWaypoint(L, waypoint);
|
||||
marker.addTo(map);
|
||||
markers.push(marker);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="map-container" class="flex flex-col">
|
||||
<div id="map" class="rounded-xl z-0 basis-full"></div>
|
||||
<div id="elevation"></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#map-container {
|
||||
height: calc(100vh - 180px);
|
||||
}
|
||||
@media only screen and (min-width: 768px) {
|
||||
#map-container {
|
||||
height: calc(100vh - 124px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import { getFileURL } from "$lib/util/file_util";
|
||||
import { formatMeters, formatTimeHHMM } from "$lib/util/format_util";
|
||||
import { formatDistance, formatElevation, formatTimeHHMM } from "$lib/util/format_util";
|
||||
|
||||
export let trail: Trail;
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
</div>
|
||||
<div class="flex mt-2 gap-4 text-sm text-gray-500 whitespace-nowrap">
|
||||
<span
|
||||
><i class="fa fa-left-right mr-2"></i>{formatMeters(
|
||||
><i class="fa fa-left-right mr-2"></i>{formatDistance(
|
||||
trail.distance,
|
||||
)}</span
|
||||
>
|
||||
<span
|
||||
><i class="fa fa-up-down mr-2"></i>{formatMeters(
|
||||
><i class="fa fa-up-down mr-2"></i>{formatElevation(
|
||||
trail.elevation_gain,
|
||||
)}</span
|
||||
>
|
||||
|
||||
136
web/src/lib/components/trail/trail_dropdown.svelte
Normal file
136
web/src/lib/components/trail/trail_dropdown.svelte
Normal file
@@ -0,0 +1,136 @@
|
||||
<script lang="ts">
|
||||
import { goto } from "$app/navigation";
|
||||
import type { List } from "$lib/models/list";
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import {
|
||||
lists_add_trail,
|
||||
lists_index,
|
||||
lists_remove_trail,
|
||||
} from "$lib/stores/list_store";
|
||||
import { show_toast } from "$lib/stores/toast_store";
|
||||
import { trails_delete } from "$lib/stores/trail_store";
|
||||
import { getFileURL } from "$lib/util/file_util";
|
||||
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
|
||||
import ConfirmModal from "../confirm_modal.svelte";
|
||||
import ListSelectModal from "../list/list_select_modal.svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let trail: Trail;
|
||||
export let mode: "overview" | "map";
|
||||
|
||||
let openConfirmModal: () => void;
|
||||
let openListSelectModal: () => void;
|
||||
|
||||
const dropdownItems: DropdownItem[] = [
|
||||
mode == "overview"
|
||||
? { text: $_("show-on-map"), value: "show", icon: "map" }
|
||||
: {
|
||||
text: $_("show-in-overview"),
|
||||
value: "show",
|
||||
icon: "table-columns",
|
||||
},
|
||||
|
||||
{ text: $_("directions"), value: "direction", icon: "car" },
|
||||
...(trail.gpx
|
||||
? [
|
||||
{
|
||||
text: $_("download-gpx"),
|
||||
value: "download",
|
||||
icon: "download",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ text: $_("add-to-list"), value: "list", icon: "bookmark" },
|
||||
{ text: $_("edit"), value: "edit", icon: "pen" },
|
||||
{ text: $_("delete"), value: "delete", icon: "trash" },
|
||||
];
|
||||
|
||||
async function handleDropdownClick(item: { text: string; value: any }) {
|
||||
if (item.value == "show") {
|
||||
goto(
|
||||
mode == "overview"
|
||||
? `/map/trail/${trail.id!}`
|
||||
: `/trail/view/${trail.id!}`,
|
||||
);
|
||||
} else if (item.value == "list") {
|
||||
openListSelectModal();
|
||||
} else if (item.value == "direction") {
|
||||
window
|
||||
.open(
|
||||
`https://www.google.com/maps/dir/Current+Location/${trail.lat},${trail.lon}`,
|
||||
"_blank",
|
||||
)
|
||||
?.focus();
|
||||
} else if (item.value == "download") {
|
||||
downloadURI(getFileURL(trail, trail.gpx), trail.gpx!);
|
||||
} else if (item.value == "edit") {
|
||||
goto(`/trail/edit/${trail.id}`);
|
||||
} else if (item.value == "delete") {
|
||||
openConfirmModal();
|
||||
}
|
||||
}
|
||||
|
||||
function downloadURI(uri: string, name: string) {
|
||||
var link = document.createElement("a");
|
||||
link.setAttribute("download", name);
|
||||
link.href = uri;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
}
|
||||
|
||||
async function deleteTrail() {
|
||||
trails_delete(trail).then(() => history.back());
|
||||
}
|
||||
|
||||
async function handleListSelection(list: List) {
|
||||
try {
|
||||
if (list.trails?.includes(trail.id!)) {
|
||||
await lists_remove_trail(list, trail);
|
||||
show_toast({
|
||||
type: "success",
|
||||
icon: "check",
|
||||
text: `Removed trail from "${list.name}"`,
|
||||
});
|
||||
} else {
|
||||
await lists_add_trail(list, trail);
|
||||
show_toast({
|
||||
type: "success",
|
||||
icon: "check",
|
||||
text: `Added trail to "${list.name}"`,
|
||||
});
|
||||
}
|
||||
await lists_index();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
show_toast({
|
||||
type: "error",
|
||||
icon: "close",
|
||||
text: "Error adding trail to list.",
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dropdown
|
||||
items={dropdownItems}
|
||||
on:change={(e) => handleDropdownClick(e.detail)}
|
||||
let:toggleMenu={openDropdown}
|
||||
><button
|
||||
class="rounded-full bg-white text-black hover:bg-gray-200 focus:ring-4 ring-gray-100/50 transition-colors h-12 w-12"
|
||||
on:click={openDropdown}
|
||||
>
|
||||
<i class="fa fa-ellipsis-vertical"></i>
|
||||
</button></Dropdown
|
||||
>
|
||||
|
||||
<ConfirmModal
|
||||
text={$_("delete-trail-confirm")}
|
||||
bind:openModal={openConfirmModal}
|
||||
on:confirm={deleteTrail}
|
||||
></ConfirmModal>
|
||||
<ListSelectModal
|
||||
bind:openModal={openListSelectModal}
|
||||
on:change={(e) => handleListSelection(e.detail)}
|
||||
></ListSelectModal>
|
||||
@@ -3,7 +3,7 @@
|
||||
import type { Category } from "$lib/models/category";
|
||||
import type { TrailFilter } from "$lib/models/trail";
|
||||
import { country_codes } from "$lib/util/country_code_util";
|
||||
import { formatMeters } from "$lib/util/format_util";
|
||||
import { formatDistance, formatElevation } from "$lib/util/format_util";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import DoubleSlider from "../base/double_slider.svelte";
|
||||
import type { RadioItem } from "../base/radio_group.svelte";
|
||||
@@ -11,6 +11,7 @@
|
||||
import Search, { type SearchItem } from "../base/search.svelte";
|
||||
import Slider from "../base/slider.svelte";
|
||||
import { slide } from "svelte/transition";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let categories: Category[];
|
||||
export let filterExpanded: boolean = true;
|
||||
@@ -33,9 +34,9 @@
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const radioGroupItems: RadioItem[] = [
|
||||
{ text: "Completed", value: "completed" },
|
||||
{ text: "Not completed", value: "not_completed" },
|
||||
{ text: "No preference", value: "no_preference" },
|
||||
{ text: $_("completed"), value: "completed" },
|
||||
{ text: $_("not-completed"), value: "not_completed" },
|
||||
{ text: $_("no-preference"), value: "no_preference" },
|
||||
];
|
||||
|
||||
let searchDropdownItems: SearchItem[] = [];
|
||||
@@ -112,7 +113,7 @@
|
||||
<Search
|
||||
bind:value={filter.q}
|
||||
on:update={update}
|
||||
placeholder="Search trails..."
|
||||
placeholder="{$_('search-trails')}..."
|
||||
></Search>
|
||||
</div>
|
||||
<button
|
||||
@@ -128,7 +129,7 @@
|
||||
{#if showTrailSearch}
|
||||
<hr class="my-4 border-separator" />
|
||||
{/if}
|
||||
<p class="text-sm font-medium pb-4">Category</p>
|
||||
<p class="text-sm font-medium pb-4">{$_("category")}</p>
|
||||
{#each categories as category, i}
|
||||
<div class="flex items-center mb-4">
|
||||
<input
|
||||
@@ -146,11 +147,11 @@
|
||||
{/each}
|
||||
<hr class="my-4 border-separator" />
|
||||
{#if showCitySearch}
|
||||
<p class="text-sm font-medium pb-4">Near</p>
|
||||
<p class="text-sm font-medium pb-4">{$_("near")}</p>
|
||||
<div class="mb-8">
|
||||
<Search
|
||||
items={searchDropdownItems}
|
||||
placeholder="Search cities..."
|
||||
placeholder="{$_('search-cities')}..."
|
||||
bind:value={citySearchQuery}
|
||||
on:update={(e) => searchCities(e.detail)}
|
||||
on:click={(e) => handleSearchClick(e.detail)}
|
||||
@@ -162,12 +163,12 @@
|
||||
on:set={() => update()}
|
||||
></Slider>
|
||||
<p>
|
||||
<span class="text-gray-500 text-sm">Radius:</span>
|
||||
{formatMeters(filter.near.radius)}
|
||||
<span class="text-gray-500 text-sm">{$_("radius")}:</span>
|
||||
{formatDistance(filter.near.radius)}
|
||||
</p>
|
||||
<hr class="my-4 border-separator" />
|
||||
{/if}
|
||||
<p class="text-sm font-medium pb-4">Distance</p>
|
||||
<p class="text-sm font-medium pb-4">{$_("distance")}</p>
|
||||
<DoubleSlider
|
||||
minValue={filter.distanceMin}
|
||||
maxValue={filter.distanceMax}
|
||||
@@ -176,11 +177,11 @@
|
||||
on:set={() => update()}
|
||||
></DoubleSlider>
|
||||
<div class="flex justify-between">
|
||||
<span>{formatMeters(filter.distanceMin)}</span>
|
||||
<span>{formatMeters(filter.distanceMax)}</span>
|
||||
<span>{formatDistance(filter.distanceMin)}</span>
|
||||
<span>{formatDistance(filter.distanceMax)}</span>
|
||||
</div>
|
||||
<hr class="my-4 border-separator" />
|
||||
<p class="text-sm font-medium pb-4">Elevation Gain</p>
|
||||
<p class="text-sm font-medium pb-4">{$_("elevation-gain")}</p>
|
||||
<DoubleSlider
|
||||
minValue={filter.elevationGainMin}
|
||||
maxValue={filter.elevationGainMax}
|
||||
@@ -189,11 +190,11 @@
|
||||
on:set={() => update()}
|
||||
></DoubleSlider>
|
||||
<div class="flex justify-between">
|
||||
<span>{formatMeters(filter.elevationGainMin)}</span>
|
||||
<span>{formatMeters(filter.elevationGainMax)}</span>
|
||||
<span>{formatElevation(filter.elevationGainMin)}</span>
|
||||
<span>{formatElevation(filter.elevationGainMax)}</span>
|
||||
</div>
|
||||
<hr class="my-4 border-separator" />
|
||||
<p class="text-sm font-medium pb-4">Completed</p>
|
||||
<p class="text-sm font-medium pb-4">{$_("completed")}</p>
|
||||
<RadioGroup
|
||||
name="completed"
|
||||
items={radioGroupItems}
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
import EmptyStateSearch from "../empty_states/empty_state_search.svelte";
|
||||
import TrailCard from "./trail_card.svelte";
|
||||
import TrailListItem from "./trail_list_item.svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let filter: TrailFilter;
|
||||
export let trails: Trail[];
|
||||
|
||||
const displayOptions: SelectItem[] = [
|
||||
{ text: "Cards", value: "cards" },
|
||||
{ text: "List", value: "list" },
|
||||
{ text: $_('card', {values: {n: 2}}), value: "cards" },
|
||||
{ text: $_('list', {values: {n: 1}}), value: "list" },
|
||||
];
|
||||
|
||||
let selectedDisplayOption = displayOptions[0].value;
|
||||
@@ -19,10 +20,10 @@
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
const sortOptions: SelectItem[] = [
|
||||
{ text: "Alphabetical", value: "name" },
|
||||
{ text: "Creation date", value: "created" },
|
||||
{ text: "Distance", value: "distance" },
|
||||
{ text: "Elevation gain", value: "elevation_gain" },
|
||||
{ text: $_('alphabetical'), value: "name" },
|
||||
{ text: $_('creation-date'), value: "created" },
|
||||
{ text: $_('distance'), value: "distance" },
|
||||
{ text: $_('elevation-gain'), value: "elevation_gain" },
|
||||
];
|
||||
|
||||
onMount(() => {
|
||||
@@ -53,7 +54,7 @@
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-start gap-8 justify-end mx-4">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 pb-2">Sort</p>
|
||||
<p class="text-sm text-gray-500 pb-2">{$_('sort')}</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<Select
|
||||
bind:value={filter.sort}
|
||||
@@ -70,7 +71,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 pb-2">Display as</p>
|
||||
<p class="text-sm text-gray-500 pb-2">{$_('display-as')}</p>
|
||||
|
||||
<Select
|
||||
bind:value={selectedDisplayOption}
|
||||
@@ -91,6 +92,7 @@
|
||||
class="max-w-full"
|
||||
class:basis-full={selectedDisplayOption === "list"}
|
||||
href="/trail/view/{trail.id}"
|
||||
data-sveltekit-preload-data="off"
|
||||
>
|
||||
{#if selectedDisplayOption === "cards"}
|
||||
<TrailCard {trail}></TrailCard>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import { getFileURL } from "$lib/util/file_util";
|
||||
import { formatMeters, formatTimeHHMM } from "$lib/util/format_util";
|
||||
import { formatDistance, formatElevation, formatTimeHHMM } from "$lib/util/format_util";
|
||||
|
||||
export let trail: Trail;
|
||||
</script>
|
||||
@@ -21,12 +21,12 @@
|
||||
<h5><i class="fa fa-location-dot mr-3"></i>{trail.location}</h5>
|
||||
<div class="flex mt-2 gap-4 text-sm text-gray-500">
|
||||
<span
|
||||
><i class="fa fa-left-right mr-2"></i>{formatMeters(
|
||||
><i class="fa fa-left-right mr-2"></i>{formatDistance(
|
||||
trail.distance,
|
||||
)}</span
|
||||
>
|
||||
<span
|
||||
><i class="fa fa-up-down mr-2"></i>{formatMeters(
|
||||
><i class="fa fa-up-down mr-2"></i>{formatElevation(
|
||||
trail.elevation_gain,
|
||||
)}</span
|
||||
>
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { Waypoint } from "$lib/models/waypoint";
|
||||
import { _ } from "svelte-i18n";
|
||||
import Dropdown from "../base/dropdown.svelte";
|
||||
|
||||
|
||||
export let waypoint: Waypoint;
|
||||
export let mode: "show" | "edit" = "show";
|
||||
|
||||
const dropdownItems = [
|
||||
{ text: "Edit", value: "edit" },
|
||||
{ text: "Delete", value: "delete" },
|
||||
{ text: $_("edit"), value: "edit" },
|
||||
{ text: $_("delete"), value: "delete" },
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="p-4 border border-input-border rounded-md my-2 hover:bg-menu-item-background-hover">
|
||||
<div
|
||||
class="p-4 border border-input-border rounded-md my-2 hover:bg-menu-item-background-hover"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<h5>
|
||||
<i class="fa fa-{waypoint.icon} mr-2"></i>{waypoint.name}
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
import { waypoint } from "$lib/stores/waypoint_store";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib/index";
|
||||
import { util } from "$lib/vendor/svelte-form-lib/util";
|
||||
import { _ } from "svelte-i18n";
|
||||
import Modal from "../base/modal.svelte";
|
||||
import TextField from "../base/text_field.svelte";
|
||||
import Textarea from "../base/textarea.svelte";
|
||||
|
||||
export let openModal: (() => void) | undefined = undefined;
|
||||
export let closeModal: (() => void) | undefined = undefined;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<Modal
|
||||
id="waypoint-modal"
|
||||
title="Add Waypoint"
|
||||
title={$form.id ? $_("edit-waypoint") : $_("add-waypoint")}
|
||||
let:openModal
|
||||
bind:openModal
|
||||
bind:closeModal
|
||||
@@ -43,7 +43,7 @@
|
||||
<div class="basis-full">
|
||||
<TextField
|
||||
name="name"
|
||||
label="Name"
|
||||
label={$_("name")}
|
||||
bind:value={$form.name}
|
||||
error={$errors.name}
|
||||
on:change={handleChange}
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<TextField
|
||||
name="icon"
|
||||
label="Icon"
|
||||
label={$_("icon")}
|
||||
bind:value={$form.icon}
|
||||
icon={$form.icon}
|
||||
error={$errors.icon}
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
<Textarea
|
||||
name="description"
|
||||
label="Description"
|
||||
label={$_("description")}
|
||||
bind:value={$form.description}
|
||||
error={$errors.description}
|
||||
on:change={handleChange}
|
||||
@@ -70,14 +70,14 @@
|
||||
<div class="flex gap-4">
|
||||
<TextField
|
||||
name="lat"
|
||||
label="Latitude"
|
||||
label={$_("latitude")}
|
||||
bind:value={$form.lat}
|
||||
error={$errors.lat}
|
||||
on:change={handleChange}
|
||||
></TextField>
|
||||
<TextField
|
||||
name="lon"
|
||||
label="Longitude"
|
||||
label={$_("longitude")}
|
||||
bind:value={$form.lon}
|
||||
error={$errors.lat}
|
||||
on:change={handleChange}
|
||||
@@ -85,9 +85,11 @@
|
||||
</div>
|
||||
</form>
|
||||
<div slot="footer" class="flex items-center gap-4">
|
||||
<button class="btn-secondary" on:click={closeModal}>Cancel</button>
|
||||
<button class="btn-secondary" on:click={closeModal}
|
||||
>{$_("cancel")}</button
|
||||
>
|
||||
<button class="btn-primary" type="submit" form="waypoint-form"
|
||||
>Save</button
|
||||
>{$_("save")}</button
|
||||
>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
14
web/src/lib/i18n/index.ts
Normal file
14
web/src/lib/i18n/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { browser } from '$app/environment'
|
||||
import { currentUser } from '$lib/stores/user_store'
|
||||
import { init, register } from 'svelte-i18n'
|
||||
import { get } from 'svelte/store'
|
||||
|
||||
const defaultLocale = 'en'
|
||||
|
||||
register('en', () => import('./locales/en.json'))
|
||||
register('de', () => import('./locales/de.json'))
|
||||
|
||||
init({
|
||||
fallbackLocale: defaultLocale,
|
||||
initialLocale: browser ? get(currentUser)?.language ?? window.navigator.language : defaultLocale,
|
||||
})
|
||||
96
web/src/lib/i18n/locales/de.json
Normal file
96
web/src/lib/i18n/locales/de.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"about": "Über",
|
||||
"account-delete-confirm": "Du bist dabei, dein Konto zu löschen. Alle deine Routen werden ebenfalls gelöscht. Möchtest du fortfahren?",
|
||||
"add-entry": "Eintrag hinzufügen",
|
||||
"add-to-list": "Zu Liste hinzufügen",
|
||||
"add-waypoint": "Wegpunkt hinzufügen",
|
||||
"alphabetical": "Alphabetisch",
|
||||
"already-account": "Du hast bereits ein Konto?",
|
||||
"avatar": "Avatar",
|
||||
"basic-info": "Basisinformation",
|
||||
"cancel": "Abbrechen",
|
||||
"card": "{n, plural, =1 {Karte} other {Karten}}",
|
||||
"categories": "Kategorien",
|
||||
"category": "Kategorie",
|
||||
"change": "Ändern",
|
||||
"changelog": "Changelog",
|
||||
"completed": "Abgeschlossen",
|
||||
"contribute": "Mitwirken",
|
||||
"create-new-list": "Neue Liste erstellen",
|
||||
"creation-date": "Erstellungsdatum",
|
||||
"danger-zone": "Gefahrenzone",
|
||||
"date": "Datum",
|
||||
"default-location": "Standort",
|
||||
"delete": "Löschen",
|
||||
"delete-account": "Konto löschen",
|
||||
"delete-list-confirm": "Möchten Sie diese Liste wirklich löschen? Die Routen in der Liste sind danach weiterhin verfügbar.",
|
||||
"delete-trail-confirm": "Möchtest du diese Route wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"describe-your-trail": "Beschreibe deine Route",
|
||||
"description": "Beschreibung",
|
||||
"directions": "Wegbeschreibung",
|
||||
"display-as": "Anzeigen als",
|
||||
"distance": "Distanz",
|
||||
"download-gpx": "GPX herunterladen",
|
||||
"edit": "Bearbeiten",
|
||||
"edit-entry": "Eintrag bearbeiten",
|
||||
"edit-list": "Liste bearbeiten",
|
||||
"edit-waypoint": "Wegpunkt bearbeiten",
|
||||
"elevation-gain": "Höhenunterschied",
|
||||
"email": "Email",
|
||||
"english": "Englisch",
|
||||
"est-duration": "Gesch. Dauer",
|
||||
"explore": "Erkunden",
|
||||
"features": "Feature",
|
||||
"german": "Deutsch",
|
||||
"hero_section_0_text": "Entdecke spannende Routen, speichere deine Favoriten und erlebe die Schönheit der Natur. Finde dein nächstes Abenteuer!",
|
||||
"hero_section_1_text": "Hier sind einige Routen, die dir gefallen könnten. Oder du wirfst einen Blick auf die vollständige Liste.",
|
||||
"hero_section_2_text": "Wusstest due schon? Du kannst nicht nur deine Wanderwege speichern. Es gibt viele Kategorien für alle deine Abenteuer.",
|
||||
"icon": "Icon",
|
||||
"imperial": "Amerikanisch",
|
||||
"language": "Sprache",
|
||||
"latitude": "Breitengrad",
|
||||
"license": "Lizenz",
|
||||
"list": "{n, plural, =1 {Liste} other {Listen}}",
|
||||
"location": "Standort",
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"longitude": "Längengrad",
|
||||
"make-one": "Neues erstellen!",
|
||||
"make-thumbnail": "Thumbnail festlegen",
|
||||
"map": "Karte",
|
||||
"metric": "Metrisch",
|
||||
"name": "Name",
|
||||
"near": "Nahe",
|
||||
"new-list": "Neue Liste",
|
||||
"new-trail": "Neue Route",
|
||||
"no-account": "Du hast noch kein Konto?",
|
||||
"no-preference": "Keine Präferenz",
|
||||
"no-results": "Keine Ergebnisse gefunden",
|
||||
"not-completed": "Nicht abgeschlossen",
|
||||
"password": "Passwort",
|
||||
"photos": "Fotos",
|
||||
"pick-a-trail": "Route auswählen",
|
||||
"profile": "Profil",
|
||||
"public": "Öffentlich",
|
||||
"radius": "Radius",
|
||||
"register": "Registrieren",
|
||||
"save": "Speichern",
|
||||
"save-trail": "Route speichern",
|
||||
"search-cities": "Städte suchen",
|
||||
"search-for-trails-places": "Suche nach Wegen, Orten",
|
||||
"search-trails": "Route suchen",
|
||||
"select-list": "Liste auswählen",
|
||||
"settings": "Einstellungen",
|
||||
"show-in-overview": "In der Übersicht anzeigen",
|
||||
"show-on-map": "Auf der Karte anzeigen",
|
||||
"slogan": "Speichere deine Abenteuer!",
|
||||
"sort": "Sortieren",
|
||||
"summit-book": "Gipfelbuch",
|
||||
"text": "Text",
|
||||
"trail": "{n, plural, =1 {Route} other {Routen}}",
|
||||
"units": "Einheiten",
|
||||
"upload-gpx": "GPX hochladen",
|
||||
"username": "Nutzername",
|
||||
"waypoints": "Wegpunkte",
|
||||
"welcome_to": "Willkommen bei"
|
||||
}
|
||||
96
web/src/lib/i18n/locales/en.json
Normal file
96
web/src/lib/i18n/locales/en.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"about": "About",
|
||||
"account-delete-confirm": "You are about to delete your account. All your trails will also be deleted. Do you want to proceed?",
|
||||
"add-entry": "Add Entry",
|
||||
"add-to-list": "Add to list",
|
||||
"add-waypoint": "Add Waypoint",
|
||||
"alphabetical": "Alphabetical",
|
||||
"already-account": "Already have an account?",
|
||||
"avatar": "Avatar",
|
||||
"basic-info": "Basic Info",
|
||||
"cancel": "Cancel",
|
||||
"card": "{n, plural, =1 {Card} other {Cards}}",
|
||||
"categories": "Categories",
|
||||
"category": "Category",
|
||||
"change": "Change",
|
||||
"changelog": "Changelog",
|
||||
"completed": "Completed",
|
||||
"contribute": "Contribute",
|
||||
"create-new-list": "Create new list",
|
||||
"creation-date": "Creation date",
|
||||
"danger-zone": "Danger zone",
|
||||
"date": "Date",
|
||||
"default-location": "Default Location",
|
||||
"delete": "Delete",
|
||||
"delete-account": "Delete Account",
|
||||
"delete-list-confirm": "Do you really want to delete this list? The trails in the list will still be available.",
|
||||
"delete-trail-confirm": "Do you really want to delete this trail? This action cannot be undone.",
|
||||
"describe-your-trail": "Describe your trail",
|
||||
"description": "Description",
|
||||
"directions": "Directions",
|
||||
"display-as": "Display as",
|
||||
"distance": "Distance",
|
||||
"download-gpx": "Download GPX",
|
||||
"edit": "Edit",
|
||||
"edit-entry": "Edit Entry",
|
||||
"edit-list": "Edit List",
|
||||
"edit-waypoint": "Edit Waypoint",
|
||||
"elevation-gain": "Elevation Gain",
|
||||
"email": "Email",
|
||||
"english": "English",
|
||||
"est-duration": "Est. duration",
|
||||
"explore": "Explore",
|
||||
"features": "Features",
|
||||
"german": "German",
|
||||
"hero_section_0_text": "Explore exciting trails, save your favorites, and experience the beauty of nature. Find your next adventure!",
|
||||
"hero_section_1_text": "Here are some trails you might like. Or you can just go to the full list right now.",
|
||||
"hero_section_2_text": "Did you know? You cannot only save you hiking trails. There are many categories for all your adventures.",
|
||||
"icon": "Icon",
|
||||
"imperial": "Imperial",
|
||||
"language": "Language",
|
||||
"latitude": "Latitude",
|
||||
"license": "License",
|
||||
"list": "{n, plural, =1 {List} other {Lists}}",
|
||||
"location": "Location",
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"longitude": "Longitude",
|
||||
"make-one": "Make one!",
|
||||
"make-thumbnail": "Make thumbnail",
|
||||
"map": "Map",
|
||||
"metric": "Metric",
|
||||
"name": "Name",
|
||||
"near": "Near",
|
||||
"new-list": "New List",
|
||||
"new-trail": "New Trail",
|
||||
"no-account": "Don't have an account?",
|
||||
"no-preference": "No preference",
|
||||
"no-results": "No results found",
|
||||
"not-completed": "Not completed",
|
||||
"password": "Password",
|
||||
"photos": "Photos",
|
||||
"pick-a-trail": "Pick a trail",
|
||||
"profile": "Profile",
|
||||
"public": "Public",
|
||||
"radius": "Radius",
|
||||
"register": "Register",
|
||||
"save": "Save",
|
||||
"save-trail": "Save Trail",
|
||||
"search-cities": "Search cities",
|
||||
"search-for-trails-places": "Search for trails, places",
|
||||
"search-trails": "Search trails",
|
||||
"select-list": "Select List",
|
||||
"settings": "Settings",
|
||||
"show-in-overview": "Show in overview",
|
||||
"show-on-map": "Show on map",
|
||||
"slogan": "Save your adventures!",
|
||||
"sort": "Sort",
|
||||
"summit-book": "Summit Book",
|
||||
"text": "Text",
|
||||
"trail": "{n, plural, =1 {Trail} other {Trails}}",
|
||||
"units": "Units",
|
||||
"upload-gpx": "Upload GPX",
|
||||
"username": "Username",
|
||||
"waypoints": "Waypoints",
|
||||
"welcome_to": "Welcome to"
|
||||
}
|
||||
@@ -1,15 +1,19 @@
|
||||
import { regenerateInstance } from "$lib/meilisearch";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { type AuthModel } from 'pocketbase';
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
|
||||
export type User = {
|
||||
id: string,
|
||||
username?: string,
|
||||
email?: string,
|
||||
password: string,
|
||||
avatar?: string;
|
||||
unit?: "metric" | "imperial";
|
||||
language?: "en" | "de";
|
||||
location?: {name: string, lat: number, lon: number}
|
||||
}
|
||||
|
||||
export const currentUser: Writable<AuthModel | null> = writable<AuthModel | null>()
|
||||
export const currentUser: Writable<User | null> = writable<User | null>()
|
||||
|
||||
export async function users_create(user: User) {
|
||||
const model = await pb.collection('users').create({ ...user, passwordConfirm: user.password });
|
||||
@@ -25,4 +29,18 @@ export async function login(user: User) {
|
||||
export async function logout() {
|
||||
pb.authStore.clear();
|
||||
regenerateInstance()
|
||||
}
|
||||
|
||||
export async function users_update(id: string, user: User | { [K in keyof User]?: User[K] } | FormData) {
|
||||
let model = await pb
|
||||
.collection("users")
|
||||
.update<User>(id, user);
|
||||
|
||||
currentUser.set(model);
|
||||
}
|
||||
|
||||
export async function users_delete(user: User) {
|
||||
let success = await pb
|
||||
.collection("users")
|
||||
.delete(user.id);
|
||||
}
|
||||
10
web/src/lib/util/authorization_util.ts
Normal file
10
web/src/lib/util/authorization_util.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
const privateRoutes = [
|
||||
"/profile",
|
||||
"/lists"
|
||||
]
|
||||
|
||||
export function isRouteProtected(path: string) {
|
||||
return privateRoutes.some(allowedPath =>
|
||||
path === allowedPath || path.startsWith(allowedPath + '/')
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { currentUser } from "$lib/stores/user_store";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
export function formatTimeHHMM(minutes?: number) {
|
||||
if (!minutes) {
|
||||
@@ -10,14 +12,39 @@ export function formatTimeHHMM(minutes?: number) {
|
||||
return (h < 10 ? "0" : "") + h.toString() + "h " + (m < 10 ? "0" : "") + Math.round(m).toString() + "m";
|
||||
}
|
||||
|
||||
export function formatMeters(meters?: number) {
|
||||
export function formatDistance(meters?: number) {
|
||||
if (meters === undefined) {
|
||||
return "-";
|
||||
}
|
||||
if (meters % 1 === 0) {
|
||||
return meters >= 1000 ? `${(meters / 1000)} km` : `${meters} m`;
|
||||
|
||||
const unit = get(currentUser)?.unit ?? "metric";
|
||||
|
||||
if (unit == "metric") {
|
||||
if (meters % 1 === 0) {
|
||||
return meters >= 1000 ? `${(meters / 1000)} km` : `${meters} m`;
|
||||
} else {
|
||||
return meters >= 1000 ? `${(meters / 1000).toFixed(2)} km` : `${Math.round(meters)} m`
|
||||
}
|
||||
} else {
|
||||
return meters >= 1000 ? `${(meters / 1000).toFixed(2)} km` : `${Math.round(meters)} m`
|
||||
const miles = meters * 0.000621371;
|
||||
const roundedMiles = miles.toFixed(2);
|
||||
|
||||
return `${roundedMiles} mi`;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatElevation(meters?: number) {
|
||||
if (meters === undefined) {
|
||||
return "-";
|
||||
}
|
||||
|
||||
const unit = get(currentUser)?.unit ?? "metric";
|
||||
|
||||
if (unit == "metric") {
|
||||
return `${Math.round(meters)} m`
|
||||
} else {
|
||||
const feet = meters * 3.28084;
|
||||
|
||||
return `${Math.round(feet)} ft`;
|
||||
}
|
||||
}
|
||||
|
||||
361
web/src/lib/vendor/leaflet-elevation/src/control.js
vendored
361
web/src/lib/vendor/leaflet-elevation/src/control.js
vendored
@@ -1,4 +1,4 @@
|
||||
import * as _ from './utils';
|
||||
import * as _ from './utils';
|
||||
import { Options } from './options';
|
||||
|
||||
// "leaflet-i18n" fallback
|
||||
@@ -11,20 +11,20 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
includes: L.Evented ? L.Evented.prototype : L.Mixin.Events,
|
||||
|
||||
options: Options,
|
||||
__mileFactor: 0.621371, // 1 km = (0.621371 mi)
|
||||
__footFactor: 3.28084, // 1 m = (3.28084 ft)
|
||||
__D3: 'https://unpkg.com/d3@7.8.4/dist/d3.min.js',
|
||||
__TOGEOJSON: 'https://unpkg.com/@tmcw/togeojson@5.6.2/dist/togeojson.umd.js',
|
||||
__LGEOMUTIL: 'https://unpkg.com/leaflet-geometryutil@0.10.1/src/leaflet.geometryutil.js',
|
||||
__LALMOSTOVER: 'https://unpkg.com/leaflet-almostover@1.0.1/src/leaflet.almostover.js',
|
||||
__LHOTLINE: '../libs/leaflet-hotline.min.js',
|
||||
__LDISTANCEM: '../libs/leaflet-distance-marker.min.js',
|
||||
__LEDGESCALE: '../libs/leaflet-edgescale.min.js',
|
||||
__LCHART: '../src/components/chart.js',
|
||||
__LMARKER: '../src/components/marker.js',
|
||||
__LSUMMARY: '../src/components/summary.js',
|
||||
__mileFactor: 0.621371, // 1 km = (0.621371 mi)
|
||||
__footFactor: 3.28084, // 1 m = (3.28084 ft)
|
||||
__D3: 'https://unpkg.com/d3@7.8.4/dist/d3.min.js',
|
||||
__TOGEOJSON: 'https://unpkg.com/@tmcw/togeojson@5.6.2/dist/togeojson.umd.js',
|
||||
__LGEOMUTIL: 'https://unpkg.com/leaflet-geometryutil@0.10.1/src/leaflet.geometryutil.js',
|
||||
__LALMOSTOVER: 'https://unpkg.com/leaflet-almostover@1.0.1/src/leaflet.almostover.js',
|
||||
__LHOTLINE: '../libs/leaflet-hotline.min.js',
|
||||
__LDISTANCEM: '../libs/leaflet-distance-marker.min.js',
|
||||
__LEDGESCALE: '../libs/leaflet-edgescale.min.js',
|
||||
__LCHART: '../src/components/chart.js',
|
||||
__LMARKER: '../src/components/marker.js',
|
||||
__LSUMMARY: '../src/components/summary.js',
|
||||
__modulesFolder: '../src/handlers/',
|
||||
__btnIcon: '../images/elevation.svg',
|
||||
__btnIcon: '../images/elevation.svg',
|
||||
|
||||
/*
|
||||
* Add data to the diagram either from GPX or GeoJSON and update the axis domain and data
|
||||
@@ -38,7 +38,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
this._addLayer(layer);
|
||||
this._fireEvt("eledata_added", { data: d, layer: layer, track_info: this.track_info });
|
||||
} else {
|
||||
this.once('modules_loaded', () => this.addData(d,layer));
|
||||
this.once('modules_loaded', () => this.addData(d, layer));
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -61,15 +61,15 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
* Reset data and display
|
||||
*/
|
||||
clear() {
|
||||
if (this._marker) this._marker.remove();
|
||||
if (this._chart) this._clearChart();
|
||||
if (this._layers) this._clearLayers(this._layers);
|
||||
if (this._markers) this._clearLayers(this._markers);
|
||||
if (this._marker) this._marker.remove();
|
||||
if (this._chart) this._clearChart();
|
||||
if (this._layers) this._clearLayers(this._layers);
|
||||
if (this._markers) this._clearLayers(this._markers);
|
||||
if (this._circleMarkers) this._circleMarkers.remove();
|
||||
if (this._hotline) this._hotline.eachLayer(l => l.options.renderer.remove()); // hotfix for: https://github.com/Raruto/leaflet-elevation/issues/233
|
||||
if (this._hotline) this._clearLayers(this._hotline);
|
||||
if (this._hotline) this._hotline.eachLayer(l => l.options.renderer.remove()); // hotfix for: https://github.com/Raruto/leaflet-elevation/issues/233
|
||||
if (this._hotline) this._clearLayers(this._hotline);
|
||||
|
||||
this._data = [];
|
||||
this._data = [];
|
||||
this.track_info = {};
|
||||
|
||||
this._fireEvt("eledata_clear");
|
||||
@@ -79,7 +79,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
|
||||
_clearChart() {
|
||||
if (this._events && this._events.elechart_updated) {
|
||||
this._events.elechart_updated.forEach(({fn, ctx}) => this.off('elechart_updated', fn, ctx));
|
||||
this._events.elechart_updated.forEach(({ fn, ctx }) => this.off('elechart_updated', fn, ctx));
|
||||
}
|
||||
if (this._chart && this._chart._container) {
|
||||
this._chart._container.selectAll('g.point .point').remove();
|
||||
@@ -177,26 +177,26 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
// Fixes: https://github.com/Raruto/leaflet-elevation/pull/240
|
||||
opts = L.setOptions(this, L.extend({}, _.cloneDeep(Options), opts)); // "deep copy" nested objects (multiple charts)
|
||||
|
||||
this._data = [];
|
||||
this._layers = L.featureGroup();
|
||||
this._markers = L.featureGroup();
|
||||
this._hotline = L.featureGroup();
|
||||
this._circleMarkers = L.featureGroup();
|
||||
this._data = [];
|
||||
this._layers = L.featureGroup();
|
||||
this._markers = L.featureGroup();
|
||||
this._hotline = L.featureGroup();
|
||||
this._circleMarkers = L.featureGroup();
|
||||
this._markedSegments = L.polyline([]);
|
||||
this._start = L.circleMarker([0,0], (opts.trkStart || Options.trkStart));
|
||||
this._end = L.circleMarker([0,0], (opts.trkEnd || Options.trkEnd));
|
||||
this._chartEnabled = true;
|
||||
this._yCoordMax = -Infinity;
|
||||
this.track_info = {};
|
||||
this._start = L.marker([0, 0], (opts.trkStart || Options.trkStart));
|
||||
this._end = L.marker([0, 0], (opts.trkEnd || Options.trkEnd));
|
||||
this._chartEnabled = true;
|
||||
this._yCoordMax = -Infinity;
|
||||
this.track_info = {};
|
||||
// this.handlers = [];
|
||||
|
||||
if (opts.followMarker) this._setMapView = _.throttle(this._setMapView, 300, this);
|
||||
if (opts.legend) opts.margins.bottom += 30;
|
||||
if (opts.theme) opts.polylineSegments.className += ' ' + opts.theme;
|
||||
if (opts.wptIcons === true) opts.wptIcons = Options.wptIcons;
|
||||
if (opts.followMarker) this._setMapView = _.throttle(this._setMapView, 300, this);
|
||||
if (opts.legend) opts.margins.bottom += 30;
|
||||
if (opts.theme) opts.polylineSegments.className += ' ' + opts.theme;
|
||||
if (opts.wptIcons === true) opts.wptIcons = Options.wptIcons;
|
||||
if (opts.distanceMarkers === true) opts.distanceMarkers = Options.distanceMarkers;
|
||||
if (opts.trkStart) this._start.addTo(this._circleMarkers);
|
||||
if (opts.trkEnd) this._end.addTo(this._circleMarkers);
|
||||
if (opts.trkStart) this._start.addTo(this._circleMarkers);
|
||||
if (opts.trkEnd) this._end.addTo(this._circleMarkers);
|
||||
|
||||
|
||||
this._markedSegments.setStyle(opts.polylineSegments);
|
||||
@@ -217,14 +217,14 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
if (Array.isArray(src)) {
|
||||
return Promise.all(src.map(m => this.import(m)));
|
||||
}
|
||||
switch(src) {
|
||||
case this.__D3: condition = typeof d3 !== 'object'; break;
|
||||
case this.__TOGEOJSON: condition = typeof toGeoJSON !== 'object'; break;
|
||||
case this.__LGEOMUTIL: condition = typeof L.GeometryUtil !== 'object'; break;
|
||||
case this.__LALMOSTOVER: condition = typeof L.Handler.AlmostOver !== 'function'; break;
|
||||
case this.__LDISTANCEM: condition = typeof L.DistanceMarkers !== 'function'; break;
|
||||
case this.__LEDGESCALE: condition = typeof L.Control.EdgeScale !== 'function'; break;
|
||||
case this.__LHOTLINE: condition = typeof L.Hotline !== 'function'; break;
|
||||
switch (src) {
|
||||
case this.__D3: condition = typeof d3 !== 'object'; break;
|
||||
case this.__TOGEOJSON: condition = typeof toGeoJSON !== 'object'; break;
|
||||
case this.__LGEOMUTIL: condition = typeof L.GeometryUtil !== 'object'; break;
|
||||
case this.__LALMOSTOVER: condition = typeof L.Handler.AlmostOver !== 'function'; break;
|
||||
case this.__LDISTANCEM: condition = typeof L.DistanceMarkers !== 'function'; break;
|
||||
case this.__LEDGESCALE: condition = typeof L.Control.EdgeScale !== 'function'; break;
|
||||
case this.__LHOTLINE: condition = typeof L.Hotline !== 'function'; break;
|
||||
}
|
||||
return condition !== false ? import(_.resolveURL(src, this.options.srcFolder)) : Promise.resolve();
|
||||
},
|
||||
@@ -233,7 +233,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
* Load elevation data (GPX, GeoJSON, KML or TCX).
|
||||
*/
|
||||
load(data) {
|
||||
this._parseFromString(data).then( geojson => geojson ? this._loadLayer(geojson) : this._loadFile(data));
|
||||
this._parseFromString(data).then(geojson => geojson ? this._loadLayer(geojson) : this._loadFile(data));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -270,17 +270,17 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
this._container = null;
|
||||
|
||||
map
|
||||
.off('zoom viewreset zoomanim', this._hideMarker, this)
|
||||
.off('resize', this._resetView, this)
|
||||
.off('resize', this._resizeChart, this)
|
||||
.off('mousedown', this._resetDrag, this);
|
||||
.off('zoom viewreset zoomanim', this._hideMarker, this)
|
||||
.off('resize', this._resetView, this)
|
||||
.off('resize', this._resizeChart, this)
|
||||
.off('mousedown', this._resetDrag, this);
|
||||
|
||||
_.off(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
||||
_.off(map.getContainer(), 'touchstart', this._resetDrag, this);
|
||||
_.off(document, 'keydown', this._onKeyDown, this);
|
||||
_.off(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
||||
_.off(map.getContainer(), 'touchstart', this._resetDrag, this);
|
||||
_.off(document, 'keydown', this._onKeyDown, this);
|
||||
|
||||
this
|
||||
.off('eledata_added eledata_loaded', this._updateChart, this)
|
||||
.off('eledata_added eledata_loaded', this._updateChart, this)
|
||||
.off('eledata_added eledata_loaded', this._updateSummary, this);
|
||||
|
||||
this.fire('remove');
|
||||
@@ -316,16 +316,16 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
}
|
||||
|
||||
// Standard GeoJSON
|
||||
if (d.type === "FeatureCollection" ) {
|
||||
if (d.type === "FeatureCollection") {
|
||||
return _.each(d.features, feature => this._addData(feature));
|
||||
} else if (d.type === "Feature") {
|
||||
let geom = d.geometry;
|
||||
if (geom) {
|
||||
switch (geom.type) {
|
||||
case 'LineString': return this._addGeoJSONData(geom.coordinates, d.properties);
|
||||
case 'MultiLineString': return _.each(geom.coordinates, (coords, i) => this._addGeoJSONData(coords, d.properties, i));
|
||||
case 'LineString': return this._addGeoJSONData(geom.coordinates, d.properties);
|
||||
case 'MultiLineString': return _.each(geom.coordinates, (coords, i) => this._addGeoJSONData(coords, d.properties, i));
|
||||
case 'Point':
|
||||
default: return console.warn('Unsopperted GeoJSON feature geometry type:' + geom.type);
|
||||
default: return console.warn('Unsopperted GeoJSON feature geometry type:' + geom.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,19 +343,19 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
_addGeoJSONData(coords, properties, nestingLevel) {
|
||||
|
||||
// "coordinateProperties" property is generated inside "@tmcw/toGeoJSON"
|
||||
let props = (properties && properties.coordinateProperties) || properties;
|
||||
let props = (properties && properties.coordinateProperties) || properties;
|
||||
|
||||
coords.forEach((point, i) => {
|
||||
|
||||
// GARMIN_EXTENSIONS = ["hr", "cad", "atemp", "wtemp", "depth", "course", "bearing"];
|
||||
point.meta = point.meta ?? { time: null, ele: null };
|
||||
|
||||
|
||||
point.prev = (attr) => (attr ? this._data[i > 0 ? i - 1 : 0][attr] : this._data[i > 0 ? i - 1 : 0]);
|
||||
|
||||
this.fire("elepoint_init", { point: point, props: props, id: i, isMulti: nestingLevel });
|
||||
|
||||
this._addPoint(
|
||||
point.lat ?? point[1],
|
||||
point.lat ?? point[1],
|
||||
point.lng ?? point[0],
|
||||
point.alt ?? point.meta.ele ?? point[2]
|
||||
);
|
||||
@@ -390,7 +390,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
if (layer) this._layers.addLayer(layer)
|
||||
// Postpone adding the distance markers (lazy: true)
|
||||
if (layer && this.options.distanceMarkers && this.options.distanceMarkers.lazy) {
|
||||
layer.on('add remove', ({target, type}) => L.DistanceMarkers && target instanceof L.Polyline && target[type + 'DistanceMarkers']());
|
||||
layer.on('add remove', ({ target, type }) => L.DistanceMarkers && target instanceof L.Polyline && target[type + 'DistanceMarkers']());
|
||||
}
|
||||
return layer;
|
||||
},
|
||||
@@ -415,8 +415,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
this.once('eledata_clear', () => {
|
||||
map.almostOver.removeLayer(layer);
|
||||
map
|
||||
.off('almost:move', this._onMouseMoveLayer, this)
|
||||
.off('almost:out', this._onMouseOut, this);
|
||||
.off('almost:move', this._onMouseMoveLayer, this)
|
||||
.off('almost:out', this._onMouseOut, this);
|
||||
})
|
||||
}
|
||||
}) : Promise.resolve();
|
||||
@@ -445,7 +445,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
.then(() => {
|
||||
layer.eachLayer((trkseg) => {
|
||||
if (trkseg.feature.geometry.type != "Point") {
|
||||
let geo = L.geoJson(trkseg.toGeoJSON(), { coordsToLatLng: (coords) => L.latLng(coords[0], coords[1], coords[2] * (this.options.altitudeFactor || 1))});
|
||||
let geo = L.geoJson(trkseg.toGeoJSON(), { coordsToLatLng: (coords) => L.latLng(coords[0], coords[1], coords[2] * (this.options.altitudeFactor || 1)) });
|
||||
let line = L.hotline(geo.toGeoJSON().features[0].geometry.coordinates, {
|
||||
renderer: L.Hotline.renderer(),
|
||||
min: isFinite(this.track_info[prop + '_min']) ? this.track_info[prop + '_min'] : 0,
|
||||
@@ -460,8 +460,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
outlineWidth: 1
|
||||
}).addTo(this._hotline);
|
||||
let alpha = trkseg.options.style && trkseg.options.style.opacity || 1;
|
||||
trkseg.on('add remove', ({type}) => {
|
||||
trkseg.setStyle({opacity: (type == 'add' ? 0 : alpha)});
|
||||
trkseg.on('add remove', ({ type }) => {
|
||||
trkseg.setStyle({ opacity: (type == 'add' ? 0 : alpha) });
|
||||
line[(type == 'add' ? 'addTo' : 'removeFrom')](trkseg._map);
|
||||
if (line._renderer) line._renderer._container.parentElement.insertBefore(line._renderer._container, line._renderer._container.parentElement.firstChild);
|
||||
});
|
||||
@@ -478,7 +478,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
if (map) {
|
||||
if (this._data.length) {
|
||||
this._start.setLatLng(this._data[0].latlng);
|
||||
this._end.setLatLng(this._data[this._data.length -1].latlng);
|
||||
this._end.setLatLng(this._data[this._data.length - 1].latlng);
|
||||
}
|
||||
Promise.all([
|
||||
this._initHotLine(layer),
|
||||
@@ -521,27 +521,27 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
*/
|
||||
_fixCanvasPaths() {
|
||||
let oldProto = L.Canvas.prototype._fillStroke;
|
||||
let control = this;
|
||||
let control = this;
|
||||
|
||||
let theme = this.options.theme.split(' ')[0].replace('-theme', '');
|
||||
let color = _.Colors[theme] || {};
|
||||
let theme = this.options.theme.split(' ')[0].replace('-theme', '');
|
||||
let color = _.Colors[theme] || {};
|
||||
|
||||
L.Canvas.include({
|
||||
_fillStroke(ctx, layer) {
|
||||
if (control._layers.hasLayer(layer)) {
|
||||
|
||||
let options = layer.options;
|
||||
let options = layer.options;
|
||||
|
||||
options.color = color.line || color.area || theme;
|
||||
options.color = color.line || color.area || theme;
|
||||
options.stroke = !!options.color;
|
||||
|
||||
oldProto.call(this, ctx, layer);
|
||||
|
||||
if (options.stroke && options.weight !== 0) {
|
||||
let oldVal = ctx.globalCompositeOperation || 'source-over';
|
||||
let oldVal = ctx.globalCompositeOperation || 'source-over';
|
||||
ctx.globalCompositeOperation = 'destination-over'
|
||||
ctx.strokeStyle = color.outline || '#FFF';
|
||||
ctx.lineWidth = options.weight * 1.75;
|
||||
ctx.strokeStyle = color.outline || '#FFF';
|
||||
ctx.lineWidth = options.weight * 1.75;
|
||||
ctx.stroke();
|
||||
ctx.globalCompositeOperation = oldVal;
|
||||
}
|
||||
@@ -560,7 +560,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
*/
|
||||
_fixTooltipSize() {
|
||||
this.on('elechart_init', () =>
|
||||
this.once('elechart_change elechart_hover', ({data, xCoord}) => {
|
||||
this.once('elechart_change elechart_hover', ({ data, xCoord }) => {
|
||||
if (this._chartEnabled) {
|
||||
this._chart._showDiagramIndicator(data, xCoord);
|
||||
this._chart._showDiagramIndicator(data, xCoord);
|
||||
@@ -606,16 +606,16 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
*/
|
||||
_initChart(container) {
|
||||
let opts = this.options;
|
||||
let map = this._map;
|
||||
let map = this._map;
|
||||
|
||||
if (opts.detached) {
|
||||
let { offsetWidth, offsetHeight} = this.eleDiv;
|
||||
if (offsetWidth > 0) opts.width = offsetWidth;
|
||||
if (offsetHeight > 20) opts.height = offsetHeight - 20; // 20 = horizontal scrollbar size.
|
||||
let { offsetWidth, offsetHeight } = this.eleDiv;
|
||||
if (offsetWidth > 0) opts.width = offsetWidth;
|
||||
if (offsetHeight > 20) opts.height = offsetHeight - 20; // 20 = horizontal scrollbar size.
|
||||
} else {
|
||||
let { clientWidth } = map.getContainer();
|
||||
opts._maxWidth = opts._maxWidth > opts.width ? opts._maxWidth : opts.width;
|
||||
this._container.style.maxWidth = opts._maxWidth + 'px';
|
||||
let { clientWidth } = map.getContainer();
|
||||
opts._maxWidth = opts._maxWidth > opts.width ? opts._maxWidth : opts.width;
|
||||
this._container.style.maxWidth = opts._maxWidth + 'px';
|
||||
if (opts._maxWidth > clientWidth) opts.width = clientWidth - 30;
|
||||
}
|
||||
|
||||
@@ -623,75 +623,75 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
.import([this.__D3, this.__LCHART])
|
||||
.then((m) => {
|
||||
|
||||
let chart = this._chart = new (m[1] || Elevation).Chart(opts, this);
|
||||
|
||||
this._x = this._chart._x;
|
||||
this._y = this._chart._y;
|
||||
|
||||
d3
|
||||
.select(container)
|
||||
.call(chart.render())
|
||||
|
||||
chart
|
||||
.on('reset_drag', this._hideMarker, this)
|
||||
.on('mouse_enter', this._onMouseEnter, this)
|
||||
.on('dragged', this._onDragEnd, this)
|
||||
.on('mouse_move', this._onMouseMove, this)
|
||||
.on('mouse_out', this._onMouseOut, this)
|
||||
.on('ruler_filter', this._onRulerFilter, this)
|
||||
.on('zoom', this._updateChart, this)
|
||||
.on('elepath_toggle', this._onToggleChart, this)
|
||||
.on('margins_updated', this._resizeChart, this);
|
||||
|
||||
|
||||
this.fire("elechart_init");
|
||||
let chart = this._chart = new (m[1] || Elevation).Chart(opts, this);
|
||||
|
||||
map
|
||||
.on('zoom viewreset zoomanim', this._hideMarker, this)
|
||||
.on('resize', this._resetView, this)
|
||||
.on('resize', this._resizeChart, this)
|
||||
.on('rotate', this._rotateMarker, this)
|
||||
.on('mousedown', this._resetDrag, this);
|
||||
this._x = this._chart._x;
|
||||
this._y = this._chart._y;
|
||||
|
||||
_.on(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
||||
_.on(map.getContainer(), 'touchstart', this._resetDrag, this);
|
||||
_.on(document, 'keydown', this._onKeyDown, this);
|
||||
d3
|
||||
.select(container)
|
||||
.call(chart.render())
|
||||
|
||||
this
|
||||
.on('eledata_added eledata_loaded', this._updateChart, this)
|
||||
.on('eledata_added eledata_loaded', this._updateSummary, this);
|
||||
chart
|
||||
.on('reset_drag', this._hideMarker, this)
|
||||
.on('mouse_enter', this._onMouseEnter, this)
|
||||
.on('dragged', this._onDragEnd, this)
|
||||
.on('mouse_move', this._onMouseMove, this)
|
||||
.on('mouse_out', this._onMouseOut, this)
|
||||
.on('ruler_filter', this._onRulerFilter, this)
|
||||
.on('zoom', this._updateChart, this)
|
||||
.on('elepath_toggle', this._onToggleChart, this)
|
||||
.on('margins_updated', this._resizeChart, this);
|
||||
|
||||
this._updateChart();
|
||||
this._updateSummary();
|
||||
});
|
||||
|
||||
this.fire("elechart_init");
|
||||
|
||||
map
|
||||
.on('zoom viewreset zoomanim', this._hideMarker, this)
|
||||
.on('resize', this._resetView, this)
|
||||
.on('resize', this._resizeChart, this)
|
||||
.on('rotate', this._rotateMarker, this)
|
||||
.on('mousedown', this._resetDrag, this);
|
||||
|
||||
_.on(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
||||
_.on(map.getContainer(), 'touchstart', this._resetDrag, this);
|
||||
_.on(document, 'keydown', this._onKeyDown, this);
|
||||
|
||||
this
|
||||
.on('eledata_added eledata_loaded', this._updateChart, this)
|
||||
.on('eledata_added eledata_loaded', this._updateSummary, this);
|
||||
|
||||
this._updateChart();
|
||||
this._updateSummary();
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
|
||||
_initLayer() {
|
||||
this._layers
|
||||
.on('layeradd layerremove', ({layer, type}) => {
|
||||
let node = layer.getElement && layer.getElement();
|
||||
_.toggleClass(node, this.options.polyline.className + ' ' + this.options.theme, type == 'layeradd');
|
||||
_.toggleEvent(layer, "mousemove", this._onMouseMoveLayer.bind(this), type == 'layeradd')
|
||||
_.toggleEvent(layer, "mouseout", this._onMouseOut.bind(this), type == 'layeradd');
|
||||
.on('layeradd layerremove', ({ layer, type }) => {
|
||||
let node = layer.getElement && layer.getElement();
|
||||
_.toggleClass(node, this.options.polyline.className + ' ' + this.options.theme, type == 'layeradd');
|
||||
_.toggleEvent(layer, "mousemove", this._onMouseMoveLayer.bind(this), type == 'layeradd')
|
||||
_.toggleEvent(layer, "mouseout", this._onMouseOut.bind(this), type == 'layeradd');
|
||||
});
|
||||
},
|
||||
|
||||
_initMarker(map) {
|
||||
let pane = map.getPane('elevationPane');
|
||||
let pane = map.getPane('elevationPane');
|
||||
if (!pane) {
|
||||
pane = this._pane = map.createPane('elevationPane', map.getPane('norotatePane') || map.getPane('mapPane'));
|
||||
pane.style.zIndex = 625; // This pane is above markers but below popups.
|
||||
pane = this._pane = map.createPane('elevationPane', map.getPane('norotatePane') || map.getPane('mapPane'));
|
||||
pane.style.zIndex = 625; // This pane is above markers but below popups.
|
||||
pane.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
if (this._renderer) this._renderer.remove()
|
||||
this._renderer = L.svg({ pane: "elevationPane" }).addTo(this._map); // default leaflet svg renderer
|
||||
this._renderer = L.svg({ pane: "elevationPane" }).addTo(this._map); // default leaflet svg renderer
|
||||
|
||||
this.import([this.__D3, this.__LMARKER])
|
||||
.then((m) => {
|
||||
this._marker = new (m[1] || Elevation).Marker(this.options, this);
|
||||
this._marker = new (m[1] || Elevation).Marker(this.options, this);
|
||||
this.fire("elechart_marker");
|
||||
});
|
||||
},
|
||||
@@ -707,8 +707,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
this.options.collapsed ? this._collapse() : this._expand();
|
||||
|
||||
if (this.options.autohide) {
|
||||
_.on(container, 'mouseover', this._expand, this);
|
||||
_.on(container, 'mouseout', this._collapse, this);
|
||||
_.on(container, 'mouseover', this._expand, this);
|
||||
_.on(container, 'mouseout', this._collapse, this);
|
||||
this._map.on('click', this._collapse, this);
|
||||
}
|
||||
|
||||
@@ -722,7 +722,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
},
|
||||
|
||||
_initSummary(container) {
|
||||
this.import(this.__LSUMMARY).then((m)=>{
|
||||
this.import(this.__LSUMMARY).then((m) => {
|
||||
this._summary = new (m || Elevation).Summary({ summary: this.options.summary }, this);
|
||||
|
||||
this.on('elechart_init', () => {
|
||||
@@ -734,14 +734,9 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
/**
|
||||
* Retrieve data from a remote url (HTTP).
|
||||
*/
|
||||
_loadFile(url) {
|
||||
fetch(url)
|
||||
.then((response) => response.text())
|
||||
.then((data) => {
|
||||
this._downloadURL = url; // TODO: handle multiple urls?
|
||||
this._parseFromString(data)
|
||||
.then( geojson => geojson && this._loadLayer(geojson));
|
||||
}).catch((err) => console.warn(err));
|
||||
_loadFile(data) {
|
||||
this._parseFromString(data)
|
||||
.then(geojson => geojson && this._loadLayer(geojson));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -790,14 +785,14 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
if ([true, 'dots'].includes(waypoints)) {
|
||||
this._registerCheckPoint({
|
||||
latlng: latlng,
|
||||
label : ([true, 'dots'].includes(wptLabels) ? name : '')
|
||||
label: ([true, 'dots'].includes(wptLabels) ? name : '')
|
||||
});
|
||||
}
|
||||
// Handle map waypoints (markers)
|
||||
if ([true, 'markers'].includes(waypoints) && wptIcons != false) {
|
||||
return this._registerMarker({
|
||||
latlng : latlng,
|
||||
sym : (sym ?? name).replace(' ', '-').replace('"', '').replace("'", '').toLowerCase(),
|
||||
latlng: latlng,
|
||||
sym: (sym ?? name).replace(' ', '-').replace('"', '').replace("'", '').toLowerCase(),
|
||||
content: [true, 'markers'].includes(wptLabels) && (name || desc) && decodeURI("<b>" + name + "</b>" + (desc.length > 0 ? '<br>' + desc : ''))
|
||||
});
|
||||
}
|
||||
@@ -819,15 +814,15 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
return layer;
|
||||
},
|
||||
|
||||
_onDragEnd({ dragstart, dragend}) {
|
||||
_onDragEnd({ dragstart, dragend }) {
|
||||
this._hideMarker();
|
||||
this.fitBounds(L.latLngBounds([dragstart.latlng, dragend.latlng]));
|
||||
|
||||
this.fire("elechart_dragged");
|
||||
},
|
||||
|
||||
_onKeyDown({key}) {
|
||||
if (!this.options.detached && key === "Escape"){
|
||||
_onKeyDown({ key }) {
|
||||
if (!this.options.detached && key === "Escape") {
|
||||
this._collapse()
|
||||
};
|
||||
},
|
||||
@@ -842,7 +837,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
/*
|
||||
* Handles the moueseover the chart and displays distance and altitude level.
|
||||
*/
|
||||
_onMouseMove({xCoord}) {
|
||||
_onMouseMove({ xCoord }) {
|
||||
if (this._chartEnabled && this._data.length) {
|
||||
let item = this._findItemForX(xCoord);
|
||||
if (item) {
|
||||
@@ -856,7 +851,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
}
|
||||
|
||||
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
||||
this.fire("elechart_hover", { data: item, xCoord: xCoord });
|
||||
this.fire("elechart_hover", { data: item, xCoord: xCoord });
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -864,16 +859,16 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
/*
|
||||
* Handles mouseover events of the data layers on the map.
|
||||
*/
|
||||
_onMouseMoveLayer({latlng}) {
|
||||
_onMouseMoveLayer({ latlng }) {
|
||||
if (this._data.length) {
|
||||
let item = this._findItemForLatLng(latlng);
|
||||
if (item) {
|
||||
let xCoord = item.xDiagCoord;
|
||||
|
||||
|
||||
if (this._chartEnabled) this._chart._showDiagramIndicator(item, xCoord);
|
||||
|
||||
|
||||
this._updateMarker(item);
|
||||
|
||||
|
||||
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
||||
}
|
||||
}
|
||||
@@ -898,7 +893,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
/**
|
||||
* Handles the drag event over the ruler filter.
|
||||
*/
|
||||
_onRulerFilter({coords}) {
|
||||
_onRulerFilter({ coords }) {
|
||||
this._updateMapSegments(coords);
|
||||
},
|
||||
|
||||
@@ -958,7 +953,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
if (data.indexOf("<") != 0) {
|
||||
throw 'Invalid XML';
|
||||
}
|
||||
let xml = (new DOMParser()).parseFromString(data, "text/xml");
|
||||
let xml = (new DOMParser()).parseFromString(data, "text/xml");
|
||||
let type = xml.documentElement.tagName.toLowerCase(); // "kml" or "gpx"
|
||||
let name = xml.getElementsByTagName('name');
|
||||
if (xml.getElementsByTagName('parsererror').length) {
|
||||
@@ -967,7 +962,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
if (!(type in toGeoJSON)) {
|
||||
type = xml.documentElement.tagName == "TrainingCenterDatabase" ? 'tcx' : 'gpx';
|
||||
}
|
||||
let geojson = toGeoJSON[type](xml);
|
||||
let geojson = toGeoJSON[type](xml);
|
||||
geojson.name = name.length > 0 ? (Array.from(name).find(tag => tag.parentElement.tagName == "trk") ?? name[0]).textContent : '';
|
||||
return geojson;
|
||||
},
|
||||
@@ -1006,7 +1001,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
/**
|
||||
* Base handler for iterative track statistics (dist, time, z, slope, speed, acceleration, ...)
|
||||
*/
|
||||
_registerDataAttribute(props) {
|
||||
_registerDataAttribute(props) {
|
||||
|
||||
// parse of "coordinateProperties" for later usage
|
||||
if (props.coordPropsToMeta) {
|
||||
@@ -1020,7 +1015,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
let lastValid = {};
|
||||
|
||||
// iteration
|
||||
this.on("elepoint_added", ({index, point}) => {
|
||||
this.on("elepoint_added", ({ index, point }) => {
|
||||
i = index;
|
||||
|
||||
prev = curr ?? this._data[i]; // same as: this._data[i > 0 ? i - 1 : i]
|
||||
@@ -1032,11 +1027,11 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
// check and fix missing data on last added point
|
||||
if (i > 0 && isNaN(prev[attr])) {
|
||||
if (!isNaN(lastValid[attr]) && !isNaN(curr[attr])) {
|
||||
prev[attr] = (lastValid[attr] + curr[attr]) / 2;
|
||||
prev[attr] = (lastValid[attr] + curr[attr]) / 2;
|
||||
} else if (!isNaN(lastValid[attr])) {
|
||||
prev[attr] = lastValid[attr];
|
||||
prev[attr] = lastValid[attr];
|
||||
} else if (!isNaN(curr[attr])) {
|
||||
prev[attr] = curr[attr];
|
||||
prev[attr] = curr[attr];
|
||||
}
|
||||
// update "yAttr" and "xAttr"
|
||||
if (props.meta) {
|
||||
@@ -1054,9 +1049,9 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
|
||||
// Limit "crazy" delta values.
|
||||
if (props.deltaMax) {
|
||||
curr[attr] =_.wrapDelta(curr[attr], prev[attr], props.deltaMax);
|
||||
curr[attr] = _.wrapDelta(curr[attr], prev[attr], props.deltaMax);
|
||||
}
|
||||
|
||||
|
||||
// Range of acceptable values.
|
||||
if (props.clampRange) {
|
||||
curr[attr] = _.clamp(curr[attr], props.clampRange);
|
||||
@@ -1135,7 +1130,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
|
||||
if (this.options[name] !== "summary") {
|
||||
if (scale) this._registerAxisScale(L.extend({ name, label: unit }, scale));
|
||||
if (path) this._registerAreaPath(L.extend({ name }, path));
|
||||
if (path) this._registerAreaPath(L.extend({ name }, path));
|
||||
}
|
||||
|
||||
if (tooltip || props.tooltips) {
|
||||
@@ -1149,11 +1144,11 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
}
|
||||
},
|
||||
|
||||
_registerMarker({latlng, sym, content}) {
|
||||
_registerMarker({ latlng, sym, content }) {
|
||||
let { wptIcons } = this.options;
|
||||
// generate and cache appropriate icon symbol
|
||||
if (!wptIcons.hasOwnProperty(sym)) {
|
||||
wptIcons[sym] = L.divIcon(L.extend({}, wptIcons[""].options, { html: '<i class="elevation-waypoint-icon ' + sym + '"></i>' } ));
|
||||
wptIcons[sym] = L.divIcon(L.extend({}, wptIcons[""].options, { html: '<i class="elevation-waypoint-icon ' + sym + '"></i>' }));
|
||||
}
|
||||
let marker = L.marker(latlng, { icon: wptIcons[sym] });
|
||||
if (content) {
|
||||
@@ -1162,12 +1157,12 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
}
|
||||
return this._addMarker(marker)
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add chart or marker tooltip info
|
||||
*/
|
||||
_registerTooltip(props) {
|
||||
props.chart && this.on("elechart_init", () => this._chart._registerTooltip(L.extend({}, props, { value: props.chart })));
|
||||
props.chart && this.on("elechart_init", () => this._chart._registerTooltip(L.extend({}, props, { value: props.chart })));
|
||||
props.marker && this.on("elechart_marker", () => this._marker._registerTooltip(L.extend({}, props, { value: props.marker })));
|
||||
},
|
||||
|
||||
@@ -1175,7 +1170,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
* Add summary info to diagram
|
||||
*/
|
||||
_registerSummary(props) {
|
||||
this.on('elechart_summary', () => this._summary._registerSummary(props));
|
||||
this.on('elechart_summary', () => this._summary._registerSummary(props));
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -1229,7 +1224,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
_setMapView(item) {
|
||||
if (this._map && this.options.followMarker) {
|
||||
let zoom = this._map.getZoom();
|
||||
let z = this.options.zFollow;
|
||||
let z = this.options.zFollow;
|
||||
if (typeof z === "number") {
|
||||
this._map.setView(item.latlng, (zoom < z ? z : zoom), { animate: true, duration: 0.25 });
|
||||
} else if (!this._map.getBounds().contains(item.latlng)) {
|
||||
@@ -1244,13 +1239,13 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
_updateChart() {
|
||||
if (this._chart && this._container) {
|
||||
this.fire("elechart_axis");
|
||||
|
||||
|
||||
this._chart.update({ data: this._data, options: this.options });
|
||||
|
||||
this._x = this._chart._x;
|
||||
this._y = this._chart._y;
|
||||
|
||||
this.fire('elechart_updated');
|
||||
|
||||
this._x = this._chart._x;
|
||||
this._y = this._chart._y;
|
||||
|
||||
this.fire('elechart_updated');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1260,10 +1255,10 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
||||
_updateMarker(item) {
|
||||
if (this._marker) {
|
||||
this._marker.update({
|
||||
map : this._map,
|
||||
item : item,
|
||||
yCoordMax : this._yCoordMax || 0,
|
||||
options : this.options
|
||||
map: this._map,
|
||||
item: item,
|
||||
yCoordMax: this._yCoordMax || 0,
|
||||
options: this.options
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
}
|
||||
|
||||
.elevation-control .grid,
|
||||
.elevation-control .area > foreignObject,
|
||||
.elevation-control .area>foreignObject,
|
||||
.elevation-control .axis,
|
||||
.elevation-control .tooltip,
|
||||
.height-focus.line {
|
||||
@@ -49,13 +49,17 @@
|
||||
.elevation-control .axis text,
|
||||
.elevation-control .legend text,
|
||||
.elevation-control .point text {
|
||||
fill: #000;
|
||||
fill: #fff;
|
||||
font-weight: 700;
|
||||
paint-order: stroke fill;
|
||||
stroke: #fff;
|
||||
stroke: #000;
|
||||
stroke-width: 2px
|
||||
}
|
||||
|
||||
.elevation-control .legend .legend-switcher-symbol {
|
||||
fill: rgb(135, 135, 135);
|
||||
}
|
||||
|
||||
.elevation-control .y.axis text {
|
||||
text-anchor: end;
|
||||
}
|
||||
@@ -229,11 +233,11 @@
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.elevation-control.elevation-collapsed > * {
|
||||
.elevation-control.elevation-collapsed>* {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.elevation-control.elevation-collapsed > .elevation-toggle-icon {
|
||||
.elevation-control.elevation-collapsed>.elevation-toggle-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -312,6 +316,7 @@
|
||||
--ele-stroke: #4682B4;
|
||||
--ele-circle: #fff;
|
||||
--ele-line: #000;
|
||||
--ele-grid: rgba(var(--input-border));
|
||||
}
|
||||
|
||||
.elevation-detached.lightblue-theme .area {
|
||||
@@ -326,4 +331,4 @@
|
||||
text-align: center;
|
||||
color: #000;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user