mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 16:52:31 +00:00
file tabs
This commit is contained in:
@@ -2,18 +2,16 @@
|
|||||||
import { files, selectedFiles, addSelectFile, selectFile } from '$lib/stores';
|
import { files, selectedFiles, addSelectFile, selectFile } from '$lib/stores';
|
||||||
|
|
||||||
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
|
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
|
||||||
import { Button } from '$lib/components/ui/button';
|
|
||||||
import { Label } from '$lib/components/ui/label';
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col h-full w-full">
|
<div class="absolute h-10 -translate-y-10 w-fit max-w-full bg-secondary rounded-t px-1">
|
||||||
<Label class="w-full">Files</Label>
|
<ScrollArea orientation="horizontal" class="w-full h-full" scrollbarXClasses="h-2">
|
||||||
<ScrollArea class="w-full h-full">
|
<div class="flex flex-row gap-1">
|
||||||
<div class="flex flex-col">
|
|
||||||
{#each $files as file}
|
{#each $files as file}
|
||||||
<Button
|
<button
|
||||||
variant={$selectedFiles.has(file) ? 'outline' : 'secondary'}
|
class="my-1 px-1.5 py-1 rounded {$selectedFiles.has(file)
|
||||||
class="w-full {$selectedFiles.has(file) ? 'hover:bg-background' : 'hover:bg-secondary'}"
|
? 'bg-background shadow'
|
||||||
|
: 'bg-secondary'}"
|
||||||
on:click={(e) => {
|
on:click={(e) => {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
addSelectFile(file);
|
addSelectFile(file);
|
||||||
@@ -23,7 +21,7 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{file.metadata.name}
|
{file.metadata.name}
|
||||||
</Button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
64
website/src/lib/components/GPXData.svelte
Normal file
64
website/src/lib/components/GPXData.svelte
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as Card from '$lib/components/ui/card';
|
||||||
|
import GPXDataItem from '$lib/components/GPXDataItem.svelte';
|
||||||
|
|
||||||
|
import { GPXStatistics } from 'gpx';
|
||||||
|
|
||||||
|
import { selectedFiles } from '$lib/stores';
|
||||||
|
import { MoveDownRight, MoveUpRight, Ruler, Timer, Zap } from 'lucide-svelte';
|
||||||
|
|
||||||
|
let gpxData: GPXStatistics = new GPXStatistics();
|
||||||
|
|
||||||
|
$: {
|
||||||
|
gpxData = new GPXStatistics();
|
||||||
|
$selectedFiles.forEach((file) => {
|
||||||
|
gpxData.mergeWith(file.statistics);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toHHMMSS(seconds: number) {
|
||||||
|
var hours = Math.floor(seconds / 3600);
|
||||||
|
var minutes = Math.floor(seconds / 60) % 60;
|
||||||
|
var seconds = Math.round(seconds % 60);
|
||||||
|
|
||||||
|
return [hours, minutes, seconds]
|
||||||
|
.map((v) => (v < 10 ? '0' + v : v))
|
||||||
|
.filter((v, i) => v !== '00' || i > 0)
|
||||||
|
.join(':');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Card.Root class="h-full overflow-hidden border-none">
|
||||||
|
<Card.Content class="flex flex-col flex-wrap gap-4 p-2">
|
||||||
|
<GPXDataItem>
|
||||||
|
<span slot="data" class="flex flex-row items-center">
|
||||||
|
<Ruler size="18" class="mr-1" />
|
||||||
|
{gpxData.distance.total.toFixed(2)} km
|
||||||
|
</span>
|
||||||
|
<span slot="tooltip">Distance</span>
|
||||||
|
</GPXDataItem>
|
||||||
|
<GPXDataItem>
|
||||||
|
<span slot="data" class="flex flex-row items-center">
|
||||||
|
<MoveUpRight size="18" class="mr-1" />
|
||||||
|
{gpxData.elevation.gain.toFixed(0)} m
|
||||||
|
<MoveDownRight size="18" class="mx-1" />
|
||||||
|
{gpxData.elevation.loss.toFixed(0)} m
|
||||||
|
</span>
|
||||||
|
<span slot="tooltip">Elevation</span>
|
||||||
|
</GPXDataItem>
|
||||||
|
<GPXDataItem>
|
||||||
|
<span slot="data" class="flex flex-row items-center">
|
||||||
|
<Zap size="18" class="mr-1" />
|
||||||
|
{gpxData.speed.moving.toFixed(2)} km/h
|
||||||
|
</span>
|
||||||
|
<span slot="tooltip">Time</span>
|
||||||
|
</GPXDataItem>
|
||||||
|
<GPXDataItem>
|
||||||
|
<span slot="data" class="flex flex-row items-center">
|
||||||
|
<Timer size="18" class="mr-1" />
|
||||||
|
{toHHMMSS(gpxData.time.moving)} / {toHHMMSS(gpxData.time.total)}
|
||||||
|
</span>
|
||||||
|
<span slot="tooltip">Moving time / Total time</span>
|
||||||
|
</GPXDataItem>
|
||||||
|
</Card.Content>
|
||||||
|
</Card.Root>
|
12
website/src/lib/components/GPXDataItem.svelte
Normal file
12
website/src/lib/components/GPXDataItem.svelte
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger>
|
||||||
|
<slot name="data" />
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Content side="top">
|
||||||
|
<slot name="tooltip" />
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Root>
|
@@ -113,4 +113,12 @@
|
|||||||
@apply h-full;
|
@apply h-full;
|
||||||
@apply overflow-hidden;
|
@apply overflow-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div :global(.mapboxgl-ctrl-bottom-left) {
|
||||||
|
@apply bottom-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
div :global(.mapboxgl-ctrl-bottom-right) {
|
||||||
|
@apply bottom-10;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
13
website/src/lib/components/ui/card/card-content.svelte
Normal file
13
website/src/lib/components/ui/card/card-content.svelte
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("p-6 pt-0", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
website/src/lib/components/ui/card/card-description.svelte
Normal file
13
website/src/lib/components/ui/card/card-description.svelte
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLParagraphElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p class={cn("text-sm text-muted-foreground", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</p>
|
13
website/src/lib/components/ui/card/card-footer.svelte
Normal file
13
website/src/lib/components/ui/card/card-footer.svelte
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("flex items-center p-6 pt-0", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
website/src/lib/components/ui/card/card-header.svelte
Normal file
13
website/src/lib/components/ui/card/card-header.svelte
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("flex flex-col space-y-1.5 p-6", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
21
website/src/lib/components/ui/card/card-title.svelte
Normal file
21
website/src/lib/components/ui/card/card-title.svelte
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import type { HeadingLevel } from "./index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
|
||||||
|
tag?: HeadingLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let tag: $$Props["tag"] = "h3";
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:element
|
||||||
|
this={tag}
|
||||||
|
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</svelte:element>
|
16
website/src/lib/components/ui/card/card.svelte
Normal file
16
website/src/lib/components/ui/card/card.svelte
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
24
website/src/lib/components/ui/card/index.ts
Normal file
24
website/src/lib/components/ui/card/index.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import Root from "./card.svelte";
|
||||||
|
import Content from "./card-content.svelte";
|
||||||
|
import Description from "./card-description.svelte";
|
||||||
|
import Footer from "./card-footer.svelte";
|
||||||
|
import Header from "./card-header.svelte";
|
||||||
|
import Title from "./card-title.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Title,
|
||||||
|
//
|
||||||
|
Root as Card,
|
||||||
|
Content as CardContent,
|
||||||
|
Description as CardDescription,
|
||||||
|
Footer as CardFooter,
|
||||||
|
Header as CardHeader,
|
||||||
|
Title as CardTitle,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Data from '$lib/components/Data.svelte';
|
import Data from '$lib/components/Data.svelte';
|
||||||
import FileList from '$lib/components/FileList.svelte';
|
import FileList from '$lib/components/FileList.svelte';
|
||||||
|
import GPXData from '$lib/components/GPXData.svelte';
|
||||||
import Map from '$lib/components/Map.svelte';
|
import Map from '$lib/components/Map.svelte';
|
||||||
import Menu from '$lib/components/Menu.svelte';
|
import Menu from '$lib/components/Menu.svelte';
|
||||||
import Toolbar from '$lib/components/Toolbar.svelte';
|
import Toolbar from '$lib/components/Toolbar.svelte';
|
||||||
@@ -17,5 +18,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="h-60 flex flex-row">
|
<div class="h-60 flex flex-row">
|
||||||
<FileList />
|
<FileList />
|
||||||
|
<GPXData />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user