adds docs

This commit is contained in:
Christian Beutel
2024-09-14 18:06:07 +02:00
parent ca34e6db95
commit 414fa9b752
29 changed files with 902 additions and 314 deletions

View File

@@ -4,19 +4,18 @@
import type { DropdownItem } from "$lib/components/base/dropdown.svelte";
import ConfirmModal from "$lib/components/confirm_modal.svelte";
import ListCard from "$lib/components/list/list_card.svelte";
import ListModal from "$lib/components/list/list_modal.svelte";
import ListPanel from "$lib/components/list/list_panel.svelte";
import ListShareModal from "$lib/components/list/list_share_modal.svelte";
import MapWithElevationMultiple from "$lib/components/trail/map_with_elevation_multiple.svelte";
import TrailInfoPanel from "$lib/components/trail/trail_info_panel.svelte";
import TrailList from "$lib/components/trail/trail_list.svelte";
import { List } from "$lib/models/list";
import type { TrailFilter } from "$lib/models/trail";
import type { Trail } from "$lib/models/trail";
import {
list,
lists,
lists_create,
lists_delete,
lists_index,
lists_update,
} from "$lib/stores/list_store";
import { fetchGPX } from "$lib/stores/trail_store";
import "$lib/vendor/leaflet-elevation/src/index.css";
@@ -27,136 +26,171 @@
import { onMount, tick } from "svelte";
import { _ } from "svelte-i18n";
let openListModal: () => void;
let openConfirmModal: () => void;
let openShareModal: () => void;
let listToBeDeleted: List | null = null;
let map: Map;
let mapWithElevationMultiple: MapWithElevationMultiple;
let markers: any[];
let showMap: boolean = true;
let selectedList: List | null = null;
let selectedTrail: Trail | null = null;
onMount(() => {
if ($page.url.searchParams.get("list")) {
const listToFocus = $lists.find((l) => l.id == $page.url.searchParams.get("list"));
if(listToFocus) {
const listToFocus = $lists.find(
(l) => l.id == $page.url.searchParams.get("list"),
);
if (listToFocus) {
setCurrentList(listToFocus);
}
}
});
async function toggleMap() {
showMap = !showMap;
if (showMap) {
setCurrentList($list);
await tick();
map.invalidateSize();
}
}
async function saveList(e: CustomEvent<{ list: List; avatar?: File }>) {
const result = e.detail;
if (result.list.id) {
await lists_update(result.list, result.avatar);
} else {
await lists_create(result.list, result.avatar);
}
await lists_index();
}
async function handleDropdownClick(
e: CustomEvent<DropdownItem>,
currentList: List,
) {
async function handleDropdownClick(e: CustomEvent<DropdownItem>) {
const item = e.detail;
if (!selectedList) {
return;
}
if (item.value == "share") {
list.set(currentList);
await tick();
openShareModal();
} else if (item.value == "edit") {
goto("/lists/edit/" + currentList.id);
goto("/lists/edit/" + selectedList.id);
} else if (item.value == "delete") {
openConfirmModal();
listToBeDeleted = currentList;
}
}
async function deleteList() {
if (!listToBeDeleted) {
if (!selectedList) {
return;
}
await lists_delete(listToBeDeleted);
await lists_delete(selectedList);
await lists_index();
listToBeDeleted = null;
selectedList = null;
}
async function setCurrentList(item: List) {
if (item.expand && item.expand.trails.length > 0) {
if (
item.expand &&
item.expand.trails &&
item.expand.trails.length > 0
) {
for (const trail of item.expand.trails) {
const gpxData: string = await fetchGPX(trail);
trail.expand.gpx_data = gpxData;
}
}
list.set(item);
selectedList = item;
}
function back() {
if (selectedTrail) {
selectedTrail = null;
mapWithElevationMultiple.resetSelection();
} else if (selectedList) {
selectedList = null;
map.flyTo([0, 0], 4, {
duration: 0.25,
easeLinearity: 0.25,
noMoveStart: true,
});
}
}
function selectTrail(trail: Trail) {
selectedTrail = trail;
mapWithElevationMultiple.selectTrail(trail.id!);
window.scrollTo({ top: 0 });
}
function highlightTrail(trail: Trail) {
mapWithElevationMultiple.highlightTrail(trail.id!);
}
function unHighlightTrail(trail: Trail) {
mapWithElevationMultiple.unHighlightTrail(trail.id!);
}
</script>
<svelte:head>
<title>{$_("list", { values: { n: 2 } })} | wanderer</title>
</svelte:head>
<main class="grid grid-cols-1 md:grid-cols-[430px_1fr] gap-4 lg:gap-4 mx-4">
<ul
class="list-list md:mx-auto rounded-xl border border-input-border max-h-full"
<main class="grid grid-cols-1 md:grid-cols-[430px_1fr] gap-4 lg:gap-4 md:mx-4">
<div
class="list-list md:mx-auto rounded-xl border border-input-border max-h-full w-full order-1 md:order-none"
>
<div
class="flex gap-x-4 items-center justify-between p-4 bg-background z-50 rounded-xl"
>
<button
class="btn-icon"
class:btn-disabled={!selectedList}
disabled={!selectedList}
on:click={back}><i class="fa fa-arrow-left"></i></button
>
<a class="btn-primary btn-large text-center" href="/lists/edit/new"
><i class="fa fa-plus mr-2"></i>{$_("new-list")}</a
>
<button type="button" class="btn-icon" on:click={toggleMap}
<!-- <button type="button" class="btn-icon" on:click={toggleMap}
><i class="fa-regular fa-{showMap ? 'rectangle-list' : 'map'}"
></i></button
>
> -->
</div>
<hr class="border-separator mb-2" />
<hr class="border-separator" />
<div class="px-4">
{#each $lists as item, i}
<li
class="list-list-item my-1"
on:click={() => setCurrentList(item)}
role="presentation"
>
<ListCard
list={item}
on:change={(e) => handleDropdownClick(e, item)}
active={item.id === $list.id}
></ListCard>
</li>
{/each}
<div>
{#if !selectedList}
<div class="px-4 mt-2">
{#each $lists as item, i}
<div
class="list-list-item my-1"
on:click={() => setCurrentList(item)}
role="presentation"
>
<ListCard list={item}></ListCard>
</div>
{/each}
</div>
{:else if selectedList && !selectedTrail}
<ListPanel
list={selectedList}
on:mouseenter={(e) => highlightTrail(e.detail.trail)}
on:mouseleave={(e) => unHighlightTrail(e.detail.trail)}
on:change={(e) => handleDropdownClick(e)}
on:click={(e) => selectTrail(e.detail.trail)}
></ListPanel>
{:else if selectedList && selectedTrail}
<TrailInfoPanel trail={selectedTrail} mode="list" {markers}
></TrailInfoPanel>
{/if}
</div>
</ul>
<div id="trail-map" class="sticky top-[62px]" class:hidden={!showMap}>
</div>
<div id="trail-map" class="md:sticky md:top-[62px]" class:hidden={!showMap}>
<MapWithElevationMultiple
trails={$list.expand?.trails ?? []}
trails={selectedList?.expand?.trails ?? []}
bind:map
bind:this={mapWithElevationMultiple}
bind:markers
options={{ itinerary: true, flyToBounds: true }}
></MapWithElevationMultiple>
</div>
<div class="min-w-0" class:hidden={showMap}>
<TrailList trails={$list.expand?.trails ?? []}></TrailList>
<TrailList trails={selectedList?.expand?.trails ?? []}></TrailList>
</div>
<ListModal bind:openModal={openListModal} on:save={saveList}></ListModal>
<ConfirmModal
text={$_("delete-list-confirm")}
bind:openModal={openConfirmModal}
on:confirm={deleteList}
></ConfirmModal>
<ListShareModal list={$list} bind:openShareModal></ListShareModal>
{#if selectedList}
<ListShareModal list={selectedList} bind:openShareModal
></ListShareModal>
{/if}
</main>
<style>

View File

@@ -126,14 +126,17 @@
async function handleSearchClick(item: SearchItem) {
const trail = await trails_show(item.value, true);
$form.trails?.push(trail.id!);
$form.expand!.trails = [...$form.expand!.trails, trail];
$form.expand = {
trails: [...($form.expand?.trails ?? []), trail],
list_share_via_list: $form.expand?.list_share_via_list,
};
}
function deleteTrail(trail: Trail) {
$form.trails = $form.trails?.filter((id) => id !== trail.id);
$form.expand!.trails = $form.expand!.trails.filter(
$form.expand!.trails = $form.expand!.trails!.filter(
(t) => t.id !== trail.id,
);
);
}
async function findNewTrailShares() {
@@ -174,6 +177,26 @@
text: $_("list-saved-successfully"),
});
}
function moveTrail(trail: Trail, index: number, direction: 1 | -1) {
(document?.activeElement as HTMLElement).blur()
if (!$form.trails || !$form.expand || !$form.expand.trails) {
return;
}
const previousTrail = $form.expand?.trails?.at(index + direction);
const previousTrailId = $form.trails?.at(index + direction);
if (!previousTrail || !previousTrailId) {
return;
}
$form.expand!.trails![index] = previousTrail;
$form.expand!.trails![index + direction] = trail;
$form.trails[index] = previousTrailId;
$form.trails[index + direction] = trail.id!;
}
</script>
<svelte:head>
@@ -181,6 +204,7 @@
>{$form.id ? `${$form.name} | ${$_("edit")}` : $_("new-list")} | wanderer</title
>
</svelte:head>
<main class="grid grid-cols-1 md:grid-cols-[440px_1fr]">
<form
id="list-form"
@@ -246,14 +270,15 @@
placeholder="{$_('search-trails')}..."
items={searchDropdownItems}
></Search>
{#if $form.expand?.trails.length}
<!-- svelte-ignore a11y-no-static-element-interactions -->
{#if $form.expand?.trails?.length}
{#each $form.expand?.trails ?? [] as trail, i}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="flex gap-4 p-4 rounded-xl border border-input-border cursor-pointer hover:bg-secondary-hover transition-colors items-center"
class:border-primary={i == activeTrailIndex}
on:click={() => map.selectTrail(i)}
class:bg-secondary-hover={i == activeTrailIndex}
role="presentation"
on:mouseenter={() => map.highlightTrail(trail.id ?? "")}
on:mouseleave={() => map.unHighlightTrail(trail.id ?? "")}
on:click={() => map.selectTrail(trail.id ?? "")}
>
<div class="shrink-0">
<img
@@ -268,17 +293,14 @@
/>
</div>
<div class="basis-full">
<div class="flex items-center justify-between">
<h4 class="font-semibold text-lg">
{trail.name}
</h4>
<span class="text-sm"
><i class="fa fa-gauge mr-2"></i>{$_(
trail.difficulty ?? "?",
)}</span
>
</div>
<h4 class="font-semibold text-lg">
{trail.name}
</h4>
<span class="text-sm"
><i class="fa fa-gauge mr-2"></i>{$_(
trail.difficulty ?? "?",
)}</span
>
<div class="flex mt-1 gap-4 text-sm text-gray-500">
<span
><i class="fa fa-left-right mr-2"
@@ -296,12 +318,34 @@
>
</div>
</div>
<button
type="button"
class="btn-icon text-red-500"
on:click={() => deleteTrail(trail)}
><i class="fa fa-trash"></i></button
>
<div class="basis-0">
{#if i > 0}
<button
on:click|stopPropagation={() =>
moveTrail(trail, i, -1)}
type="button"
class="btn-icon"
>
<i class="fa fa-chevron-up"></i>
</button>
{/if}
{#if i < $form.expand?.trails?.length - 1}
<button
on:click|stopPropagation={() =>
moveTrail(trail, i, 1)}
type="button"
class="btn-icon"
>
<i class="fa fa-chevron-down"></i>
</button>
{/if}
<button
type="button"
class="btn-icon text-red-500"
on:click={() => deleteTrail(trail)}
><i class="fa fa-trash"></i></button
>
</div>
</div>
{/each}
{:else}
@@ -328,7 +372,7 @@
</main>
<ConfirmModal
text={$_('list-share-warning-update')}
text={$_("list-share-warning-update")}
title={$_("confirm-share")}
action="confirm"
bind:openModal={openConfirmModal}
@@ -337,7 +381,8 @@
<style>
@media only screen and (min-width: 768px) {
#trail-map, #list-form {
#trail-map,
#list-form {
height: calc(100vh - 124px);
}
}

View File

@@ -20,7 +20,7 @@ export const load: Load = async ({ params, fetch, data }) => {
return { list: list, previewUrl: previewURL }
} catch (e) {
} catch (e) {
if (e instanceof ClientResponseError) {
return error(e.status as any, e.message)
}

View File

@@ -104,12 +104,12 @@
);
}
function handleTrailCardMouseEnter(index: number) {
mapWithElevation.openPopup(index)
function handleTrailCardMouseEnter(trail: Trail) {
mapWithElevation.openPopup(trail.id!)
}
function handleTrailCardMouseLeave(index: number) {
mapWithElevation.closePopup(index)
function handleTrailCardMouseLeave(trail: Trail) {
mapWithElevation.closePopup(trail.id!)
}
async function handleFilterUpdate(filter: TrailFilter) {
@@ -210,8 +210,8 @@
<a href="map/trail/{trail.id}">
<TrailCard
{trail}
on:mouseenter={() => handleTrailCardMouseEnter(i)}
on:mouseleave={() => handleTrailCardMouseLeave(i)}
on:mouseenter={() => handleTrailCardMouseEnter(trail)}
on:mouseleave={() => handleTrailCardMouseLeave(trail)}
></TrailCard>
</a>
{/each}