mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-08-31 23:53:25 +00:00
scrollable poi content, closes #124
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
import sanitizeHtml from 'sanitize-html';
|
import sanitizeHtml from 'sanitize-html';
|
||||||
import type { Waypoint } from 'gpx';
|
import type { Waypoint } from 'gpx';
|
||||||
import type { PopupItem } from '$lib/components/MapPopup';
|
import type { PopupItem } from '$lib/components/MapPopup';
|
||||||
|
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
|
||||||
|
|
||||||
export let waypoint: PopupItem<Waypoint>;
|
export let waypoint: PopupItem<Waypoint>;
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Card.Title>
|
</Card.Title>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content class="flex flex-col p-0 text-sm">
|
<Card.Content class="flex flex-col text-sm p-0">
|
||||||
<div class="flex flex-row items-center text-muted-foreground text-xs whitespace-nowrap">
|
<div class="flex flex-row items-center text-muted-foreground text-xs whitespace-nowrap">
|
||||||
{#if symbolKey}
|
{#if symbolKey}
|
||||||
<span>
|
<span>
|
||||||
@@ -66,12 +67,14 @@
|
|||||||
<WithUnits value={waypoint.item.ele} type="elevation" />
|
<WithUnits value={waypoint.item.ele} type="elevation" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if waypoint.item.desc}
|
<ScrollArea class="flex flex-col" viewportClasses="max-h-[30dvh]">
|
||||||
<span class="whitespace-pre-wrap">{@html sanitize(waypoint.item.desc)}</span>
|
{#if waypoint.item.desc}
|
||||||
{/if}
|
<span class="whitespace-pre-wrap">{@html sanitize(waypoint.item.desc)}</span>
|
||||||
{#if waypoint.item.cmt && waypoint.item.cmt !== waypoint.item.desc}
|
{/if}
|
||||||
<span class="whitespace-pre-wrap">{@html sanitize(waypoint.item.cmt)}</span>
|
{#if waypoint.item.cmt && waypoint.item.cmt !== waypoint.item.desc}
|
||||||
{/if}
|
<span class="whitespace-pre-wrap">{@html sanitize(waypoint.item.cmt)}</span>
|
||||||
|
{/if}
|
||||||
|
</ScrollArea>
|
||||||
{#if $currentTool === Tool.WAYPOINT}
|
{#if $currentTool === Tool.WAYPOINT}
|
||||||
<Button
|
<Button
|
||||||
class="mt-2 w-full px-2 py-1 h-8 justify-start"
|
class="mt-2 w-full px-2 py-1 h-8 justify-start"
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import { dbUtils } from '$lib/db';
|
import { dbUtils } from '$lib/db';
|
||||||
import type { PopupItem } from '$lib/components/MapPopup';
|
import type { PopupItem } from '$lib/components/MapPopup';
|
||||||
|
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
|
||||||
|
|
||||||
export let poi: PopupItem<any>;
|
export let poi: PopupItem<any>;
|
||||||
|
|
||||||
@@ -42,29 +43,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</Card.Title>
|
</Card.Title>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
{#if tags.image || tags['image:0']}
|
|
||||||
<div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto">
|
|
||||||
<!-- svelte-ignore a11y-missing-attribute -->
|
|
||||||
<img src={tags.image ?? tags['image:0']} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<Card.Content class="flex flex-col p-0 text-sm mt-1 whitespace-normal break-all">
|
<Card.Content class="flex flex-col p-0 text-sm mt-1 whitespace-normal break-all">
|
||||||
<div class="grid grid-cols-[auto_auto] gap-x-3">
|
<ScrollArea class="flex flex-col" viewportClasses="max-h-[30dvh]">
|
||||||
{#each Object.entries(tags) as [key, value]}
|
{#if tags.image || tags['image:0']}
|
||||||
{#if key !== 'name' && !key.includes('image')}
|
<div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto">
|
||||||
<span class="font-mono">{key}</span>
|
<!-- svelte-ignore a11y-missing-attribute -->
|
||||||
{#if key === 'website' || key === 'contact:website' || key === 'contact:facebook' || key === 'contact:instagram' || key === 'contact:twitter'}
|
<img src={tags.image ?? tags['image:0']} />
|
||||||
<a href={value} target="_blank" class="text-link underline">{value}</a>
|
</div>
|
||||||
{:else if key === 'phone' || key === 'contact:phone'}
|
{/if}
|
||||||
<a href={'tel:' + value} class="text-link underline">{value}</a>
|
<div class="grid grid-cols-[auto_auto] gap-x-3">
|
||||||
{:else if key === 'email' || key === 'contact:email'}
|
{#each Object.entries(tags) as [key, value]}
|
||||||
<a href={'mailto:' + value} class="text-link underline">{value}</a>
|
{#if key !== 'name' && !key.includes('image')}
|
||||||
{:else}
|
<span class="font-mono">{key}</span>
|
||||||
<span>{value}</span>
|
{#if key === 'website' || key.startsWith('website:') || key === 'contact:website' || key === 'contact:facebook' || key === 'contact:instagram' || key === 'contact:twitter'}
|
||||||
|
<a href={value} target="_blank" class="text-link underline">{value}</a>
|
||||||
|
{:else if key === 'phone' || key === 'contact:phone'}
|
||||||
|
<a href={'tel:' + value} class="text-link underline">{value}</a>
|
||||||
|
{:else if key === 'email' || key === 'contact:email'}
|
||||||
|
<a href={'mailto:' + value} class="text-link underline">{value}</a>
|
||||||
|
{:else}
|
||||||
|
<span>{value}</span>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/each}
|
||||||
{/each}
|
</div>
|
||||||
</div>
|
</ScrollArea>
|
||||||
<Button
|
<Button
|
||||||
class="mt-2"
|
class="mt-2"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
@@ -1,31 +1,33 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ScrollArea as ScrollAreaPrimitive } from "bits-ui";
|
import { ScrollArea as ScrollAreaPrimitive } from 'bits-ui';
|
||||||
import { Scrollbar } from "./index.js";
|
import { Scrollbar } from './index.js';
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from '$lib/utils.js';
|
||||||
|
|
||||||
type $$Props = ScrollAreaPrimitive.Props & {
|
type $$Props = ScrollAreaPrimitive.Props & {
|
||||||
orientation?: "vertical" | "horizontal" | "both";
|
orientation?: 'vertical' | 'horizontal' | 'both';
|
||||||
scrollbarXClasses?: string;
|
scrollbarXClasses?: string;
|
||||||
scrollbarYClasses?: string;
|
scrollbarYClasses?: string;
|
||||||
|
viewportClasses?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
let className: $$Props["class"] = undefined;
|
let className: $$Props['class'] = undefined;
|
||||||
export { className as class };
|
export { className as class };
|
||||||
export let orientation = "vertical";
|
export let orientation = 'vertical';
|
||||||
export let scrollbarXClasses: string = "";
|
export let scrollbarXClasses: string = '';
|
||||||
export let scrollbarYClasses: string = "";
|
export let scrollbarYClasses: string = '';
|
||||||
|
export let viewportClasses: string = '';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ScrollAreaPrimitive.Root {...$$restProps} class={cn("relative overflow-hidden", className)}>
|
<ScrollAreaPrimitive.Root {...$$restProps} class={cn('relative overflow-hidden', className)}>
|
||||||
<ScrollAreaPrimitive.Viewport class="h-full w-full rounded-[inherit]">
|
<ScrollAreaPrimitive.Viewport class={cn('h-full w-full rounded-[inherit]', viewportClasses)}>
|
||||||
<ScrollAreaPrimitive.Content>
|
<ScrollAreaPrimitive.Content>
|
||||||
<slot />
|
<slot />
|
||||||
</ScrollAreaPrimitive.Content>
|
</ScrollAreaPrimitive.Content>
|
||||||
</ScrollAreaPrimitive.Viewport>
|
</ScrollAreaPrimitive.Viewport>
|
||||||
{#if orientation === "vertical" || orientation === "both"}
|
{#if orientation === 'vertical' || orientation === 'both'}
|
||||||
<Scrollbar orientation="vertical" class={scrollbarYClasses} />
|
<Scrollbar orientation="vertical" class={scrollbarYClasses} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if orientation === "horizontal" || orientation === "both"}
|
{#if orientation === 'horizontal' || orientation === 'both'}
|
||||||
<Scrollbar orientation="horizontal" class={scrollbarXClasses} />
|
<Scrollbar orientation="horizontal" class={scrollbarXClasses} />
|
||||||
{/if}
|
{/if}
|
||||||
<ScrollAreaPrimitive.Corner />
|
<ScrollAreaPrimitive.Corner />
|
||||||
|
Reference in New Issue
Block a user