mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-08-31 15:43:25 +00:00
file tabs
This commit is contained in:
@@ -2,18 +2,16 @@
|
||||
import { files, selectedFiles, addSelectFile, selectFile } from '$lib/stores';
|
||||
|
||||
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>
|
||||
|
||||
<div class="flex flex-col h-full w-full">
|
||||
<Label class="w-full">Files</Label>
|
||||
<ScrollArea class="w-full h-full">
|
||||
<div class="flex flex-col">
|
||||
<div class="absolute h-10 -translate-y-10 w-fit max-w-full bg-secondary rounded-t px-1">
|
||||
<ScrollArea orientation="horizontal" class="w-full h-full" scrollbarXClasses="h-2">
|
||||
<div class="flex flex-row gap-1">
|
||||
{#each $files as file}
|
||||
<Button
|
||||
variant={$selectedFiles.has(file) ? 'outline' : 'secondary'}
|
||||
class="w-full {$selectedFiles.has(file) ? 'hover:bg-background' : 'hover:bg-secondary'}"
|
||||
<button
|
||||
class="my-1 px-1.5 py-1 rounded {$selectedFiles.has(file)
|
||||
? 'bg-background shadow'
|
||||
: 'bg-secondary'}"
|
||||
on:click={(e) => {
|
||||
if (e.shiftKey) {
|
||||
addSelectFile(file);
|
||||
@@ -23,7 +21,7 @@
|
||||
}}
|
||||
>
|
||||
{file.metadata.name}
|
||||
</Button>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</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 overflow-hidden;
|
||||
}
|
||||
|
||||
div :global(.mapboxgl-ctrl-bottom-left) {
|
||||
@apply bottom-10;
|
||||
}
|
||||
|
||||
div :global(.mapboxgl-ctrl-bottom-right) {
|
||||
@apply bottom-10;
|
||||
}
|
||||
</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">
|
||||
import Data from '$lib/components/Data.svelte';
|
||||
import FileList from '$lib/components/FileList.svelte';
|
||||
import GPXData from '$lib/components/GPXData.svelte';
|
||||
import Map from '$lib/components/Map.svelte';
|
||||
import Menu from '$lib/components/Menu.svelte';
|
||||
import Toolbar from '$lib/components/Toolbar.svelte';
|
||||
@@ -17,5 +18,6 @@
|
||||
</div>
|
||||
<div class="h-60 flex flex-row">
|
||||
<FileList />
|
||||
<GPXData />
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user