Files
wanderer/web/src/lib/components/trail/trail_table.svelte
tofublock 85729e9728 Feature/trail table view (#138)
* add "Table"

* add trails table view

* style table header

* add author image, adjust table layout

* tooltip for author image

* i18n for author tooltip

* trail sorting by duration and author

* remove sort by elevation loss, add sort by duration instead

* improve table layout

* fix meilisearch migration

* revert removing elevation_loss from sortOptions

* add badge for public trails

* hide public badge on small screens

* adds share info button to trail_table
2024-12-13 20:39:42 +01:00

182 lines
7.5 KiB
Svelte

<script lang="ts">
import type { Trail, TrailFilter } from "$lib/models/trail";
import {
formatDistance,
formatElevation,
formatTimeHHMM,
} from "$lib/util/format_util";
import { _ } from "svelte-i18n";
import type { SelectItem } from "../base/select.svelte";
import { goto } from "$app/navigation";
import { createEventDispatcher } from "svelte";
import { getFileURL } from "$lib/util/file_util";
import ShareInfo from "../share_info.svelte";
export let tableHeader: SelectItem[];
export let trails: Trail[] | null = null;
export let filter: TrailFilter | null = null;
let dispatch = createEventDispatcher();
function getColumnWidth(columnValue: string): string {
switch (columnValue) {
case "name":
return "w-[25%]";
case "distance":
return "w-[15%]";
case "duration":
case "difficulty":
return "w-[11%]";
case "elevation_gain":
case "created":
case "date":
return "w-[5%]";
default:
return "";
}
}
</script>
<div
class="table-container w-full border border-input-border rounded-xl overflow-x-scroll"
>
<table class="w-full">
<thead>
<tr class="bg-secondary-hover">
{#each tableHeader as column}
<th
class="p-4 text-left text-sm font-medium {getColumnWidth(
column.value,
)}"
on:click={() => dispatch("sort", column.value)}
>
<div class="cursor-pointer">
{column.text}
{#if filter && filter.sort === column.value}
<i
id="sort-order-btn"
class="fa fa-arrow-up"
class:rotated={filter.sortOrder == "-"}
></i>
{/if}
</div>
</th>
{/each}
</tr>
</thead>
<tbody>
{#if trails}
{#each trails as trail}
<tr
class="border-t border-input-border cursor-pointer hover:bg-secondary-hover transition-colors"
on:click={() => goto(`/trail/view/${trail.id}`)}
>
<td
class="flex justify-between items-center text-sm relative"
>
<div class="p-4 w-[75%]" title={trail.name}>
{trail.name}
</div>
<div class="flex flex-col items-center">
{#if trail.expand && trail.expand.author}
<div class="author-icon">
<img
title={`${trail.public ? $_("public") + " " : ""}${$_("by")} ${trail.expand.author.username}`}
class="rounded-full w-5 aspect-square mx-1 inline"
src={getFileURL(
trail.expand.author,
trail.expand.author.avatar,
) ||
`https://api.dicebear.com/7.x/initials/svg?seed=${trail.expand.author.username}&backgroundType=gradientLinear`}
alt="avatar"
/>
</div>
{/if}
<div class="flex gap-x-1">
{#if trail.public}
<div
class="public-icon tooltip"
data-title={$_("public")}
>
<i class="fa fa-globe"></i>
</div>
{/if}
{#if trail.expand?.trail_share_via_trail?.length}
<ShareInfo type="trail" subject={trail}
></ShareInfo>
{/if}
</div>
</div>
</td>
<td class="p-4 text-sm">
{formatDistance(trail.distance)}
</td>
<td class="p-4 text-sm">
{formatTimeHHMM(trail.duration)}
</td>
<td class="p-4 text-sm">
{trail.difficulty}
</td>
<td class="p-4 text-sm">
{formatElevation(trail.elevation_gain)}
</td>
<td class="p-4 text-sm">
{#if trail.created}
{new Date(trail.created).toLocaleDateString(
undefined,
{
month: "2-digit",
day: "2-digit",
year: "numeric",
timeZone: "UTC",
},
)}
{/if}
</td>
<td class="p-4 text-sm">
{#if trail.date}
{new Date(trail.date).toLocaleDateString(
undefined,
{
month: "2-digit",
day: "2-digit",
year: "numeric",
timeZone: "UTC",
},
)}
{/if}
</td>
</tr>
{/each}
{:else}
{#each { length: 12 } as _, index}
<tr
class="border-t border-input-border cursor-pointer bg-secondary-hover transition-colors animate-pulse"
>
{#each { length: tableHeader.length } as _, index}
<td class="p-4 text-sm"
><div
class="h-4 bg-menu-item-background-focus rounded w-3/4"
></div></td
>
{/each}
</tr>
{/each}
{/if}
</tbody>
</table>
</div>
<style>
.table-container {
container-type: inline-size;
}
@container (max-width: 660px) {
th:nth-last-child(-n + 2),
td:nth-last-child(-n + 2) {
display: none;
}
}
</style>