mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-08-31 23:53:25 +00:00
compact elevation profile dataset control
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button } from '$lib/components/ui/button/index.js';
|
import { Button } from '$lib/components/ui/button/index.js';
|
||||||
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
|
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
|
||||||
|
import type { Builder } from 'bits-ui';
|
||||||
|
|
||||||
export let variant:
|
export let variant:
|
||||||
| 'default'
|
| 'default'
|
||||||
@@ -12,11 +13,12 @@
|
|||||||
| undefined = 'default';
|
| undefined = 'default';
|
||||||
export let label: string;
|
export let label: string;
|
||||||
export let side: 'top' | 'right' | 'bottom' | 'left' = 'top';
|
export let side: 'top' | 'right' | 'bottom' | 'left' = 'top';
|
||||||
|
export let builders: Builder[] = [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Tooltip.Root>
|
<Tooltip.Root>
|
||||||
<Tooltip.Trigger asChild let:builder>
|
<Tooltip.Trigger asChild let:builder>
|
||||||
<Button builders={[builder]} {variant} {...$$restProps} on:click>
|
<Button builders={[...builders, builder]} {variant} {...$$restProps} on:click>
|
||||||
<slot />
|
<slot />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip.Trigger>
|
</Tooltip.Trigger>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte';
|
||||||
|
import * as Popover from '$lib/components/ui/popover';
|
||||||
import * as ToggleGroup from '$lib/components/ui/toggle-group';
|
import * as ToggleGroup from '$lib/components/ui/toggle-group';
|
||||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
|
||||||
import Chart from 'chart.js/auto';
|
import Chart from 'chart.js/auto';
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl from 'mapbox-gl';
|
||||||
import { map } from '$lib/stores';
|
import { map } from '$lib/stores';
|
||||||
@@ -12,7 +13,10 @@
|
|||||||
Orbit,
|
Orbit,
|
||||||
SquareActivity,
|
SquareActivity,
|
||||||
Thermometer,
|
Thermometer,
|
||||||
Zap
|
Zap,
|
||||||
|
Circle,
|
||||||
|
Check,
|
||||||
|
ChartNoAxesColumn
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
import { surfaceColors } from '$lib/assets/surfaces';
|
import { surfaceColors } from '$lib/assets/surfaces';
|
||||||
import { _, locale } from 'svelte-i18n';
|
import { _, locale } from 'svelte-i18n';
|
||||||
@@ -28,8 +32,7 @@
|
|||||||
getHeartRateWithUnits,
|
getHeartRateWithUnits,
|
||||||
getPowerWithUnits,
|
getPowerWithUnits,
|
||||||
getTemperatureWithUnits,
|
getTemperatureWithUnits,
|
||||||
getVelocityWithUnits,
|
getVelocityWithUnits
|
||||||
secondsToHHMMSS
|
|
||||||
} from '$lib/units';
|
} from '$lib/units';
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from 'svelte/store';
|
||||||
import { DateFormatter } from '@internationalized/date';
|
import { DateFormatter } from '@internationalized/date';
|
||||||
@@ -39,7 +42,6 @@
|
|||||||
|
|
||||||
export let gpxStatistics: Writable<GPXStatistics>;
|
export let gpxStatistics: Writable<GPXStatistics>;
|
||||||
export let slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>;
|
export let slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>;
|
||||||
export let panelSize: number;
|
|
||||||
export let additionalDatasets: string[];
|
export let additionalDatasets: string[];
|
||||||
export let elevationFill: 'slope' | 'surface' | undefined;
|
export let elevationFill: 'slope' | 'surface' | undefined;
|
||||||
export let showControls: boolean = true;
|
export let showControls: boolean = true;
|
||||||
@@ -74,12 +76,10 @@
|
|||||||
x: {
|
x: {
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function (value: number, index: number, ticks: { value: number }[]) {
|
callback: function (value: number) {
|
||||||
if (index === ticks.length - 1) {
|
|
||||||
return `${value.toFixed(1).replace(/\.0+$/, '')}`;
|
|
||||||
}
|
|
||||||
return `${value.toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
|
return `${value.toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
|
||||||
}
|
},
|
||||||
|
align: 'inner'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
@@ -532,75 +532,122 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="h-full grow min-w-0 flex flex-row gap-4 items-center {$$props.class ?? ''}">
|
<div class="h-full grow min-w-0 relative {$$props.class ?? ''}">
|
||||||
<div class="grow h-full min-w-0 relative">
|
<canvas bind:this={overlay} class="w-full h-full absolute pointer-events-none"></canvas>
|
||||||
<canvas bind:this={overlay} class=" w-full h-full absolute pointer-events-none"></canvas>
|
<canvas bind:this={canvas} class="w-full h-full"></canvas>
|
||||||
<canvas bind:this={canvas} class="w-full h-full"></canvas>
|
|
||||||
</div>
|
|
||||||
{#if showControls}
|
{#if showControls}
|
||||||
<div class="h-full flex flex-col justify-center" style="width: {panelSize > 158 ? 22 : 42}px">
|
<div class="absolute bottom-10 right-1.5">
|
||||||
<ToggleGroup.Root
|
<Popover.Root>
|
||||||
class="{panelSize > 158
|
<Popover.Trigger asChild let:builder>
|
||||||
? 'flex-col'
|
<ButtonWithTooltip
|
||||||
: 'flex-row'} flex-wrap gap-0 min-h-0 content-center border rounded-t-md"
|
label={$_('chart.settings')}
|
||||||
type="single"
|
builders={[builder]}
|
||||||
bind:value={elevationFill}
|
variant="outline"
|
||||||
>
|
class="p-1 h-7 opacity-70 hover:opacity-100 transition-opacity duration-300 hover:bg-background"
|
||||||
<ToggleGroup.Item class="p-0 w-5 h-5" value="slope" aria-label={$_('chart.show_slope')}>
|
|
||||||
<Tooltip side="left" label={$_('chart.show_slope')}>
|
|
||||||
<TriangleRight size="15" />
|
|
||||||
</Tooltip>
|
|
||||||
</ToggleGroup.Item>
|
|
||||||
<ToggleGroup.Item class="p-0 w-5 h-5" value="surface" aria-label={$_('chart.show_surface')}>
|
|
||||||
<Tooltip side="left" label={$_('chart.show_surface')}>
|
|
||||||
<BrickWall size="15" />
|
|
||||||
</Tooltip>
|
|
||||||
</ToggleGroup.Item>
|
|
||||||
</ToggleGroup.Root>
|
|
||||||
<ToggleGroup.Root
|
|
||||||
class="{panelSize > 158
|
|
||||||
? 'flex-col'
|
|
||||||
: 'flex-row'} flex-wrap gap-0 min-h-0 content-center border rounded-b-md -mt-[1px]"
|
|
||||||
type="multiple"
|
|
||||||
bind:value={additionalDatasets}
|
|
||||||
>
|
|
||||||
<ToggleGroup.Item
|
|
||||||
class="p-0 w-5 h-5"
|
|
||||||
value="speed"
|
|
||||||
aria-label={$velocityUnits === 'speed' ? $_('chart.show_speed') : $_('chart.show_pace')}
|
|
||||||
>
|
|
||||||
<Tooltip
|
|
||||||
side="left"
|
|
||||||
label={$velocityUnits === 'speed' ? $_('chart.show_speed') : $_('chart.show_pace')}
|
|
||||||
>
|
>
|
||||||
<Zap size="15" />
|
<ChartNoAxesColumn size="16" />
|
||||||
</Tooltip>
|
</ButtonWithTooltip>
|
||||||
</ToggleGroup.Item>
|
</Popover.Trigger>
|
||||||
<ToggleGroup.Item class="p-0 w-5 h-5" value="hr" aria-label={$_('chart.show_heartrate')}>
|
<Popover.Content class="w-fit p-0 flex flex-col divide-y" side="left" sideOffset={-32}>
|
||||||
<Tooltip side="left" label={$_('chart.show_heartrate')}>
|
<ToggleGroup.Root
|
||||||
<HeartPulse size="15" />
|
class="flex flex-col items-start gap-0 p-1"
|
||||||
</Tooltip>
|
type="single"
|
||||||
</ToggleGroup.Item>
|
bind:value={elevationFill}
|
||||||
<ToggleGroup.Item class="p-0 w-5 h-5" value="cad" aria-label={$_('chart.show_cadence')}>
|
>
|
||||||
<Tooltip side="left" label={$_('chart.show_cadence')}>
|
<ToggleGroup.Item
|
||||||
<Orbit size="15" />
|
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
</Tooltip>
|
value="slope"
|
||||||
</ToggleGroup.Item>
|
>
|
||||||
<ToggleGroup.Item
|
<div class="w-6 flex justify-center items-center">
|
||||||
class="p-0 w-5 h-5"
|
{#if elevationFill === 'slope'}
|
||||||
value="atemp"
|
<Circle class="h-1.5 w-1.5 fill-current text-current" />
|
||||||
aria-label={$_('chart.show_temperature')}
|
{/if}
|
||||||
>
|
</div>
|
||||||
<Tooltip side="left" label={$_('chart.show_temperature')}>
|
<TriangleRight size="15" class="mr-1" />
|
||||||
<Thermometer size="15" />
|
{$_('quantities.slope')}
|
||||||
</Tooltip>
|
</ToggleGroup.Item>
|
||||||
</ToggleGroup.Item>
|
<ToggleGroup.Item
|
||||||
<ToggleGroup.Item class="p-0 w-5 h-5" value="power" aria-label={$_('chart.show_power')}>
|
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
<Tooltip side="left" label={$_('chart.show_power')}>
|
value="surface"
|
||||||
<SquareActivity size="15" />
|
variant="outline"
|
||||||
</Tooltip>
|
>
|
||||||
</ToggleGroup.Item>
|
<div class="w-6 flex justify-center items-center">
|
||||||
</ToggleGroup.Root>
|
{#if elevationFill === 'surface'}
|
||||||
|
<Circle class="h-1.5 w-1.5 fill-current text-current" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<BrickWall size="15" class="mr-1" />
|
||||||
|
{$_('quantities.surface')}
|
||||||
|
</ToggleGroup.Item>
|
||||||
|
</ToggleGroup.Root>
|
||||||
|
<ToggleGroup.Root
|
||||||
|
class="flex flex-col items-start gap-0 p-1"
|
||||||
|
type="multiple"
|
||||||
|
bind:value={additionalDatasets}
|
||||||
|
>
|
||||||
|
<ToggleGroup.Item
|
||||||
|
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
|
value="speed"
|
||||||
|
>
|
||||||
|
<div class="w-6 flex justify-center items-center">
|
||||||
|
{#if additionalDatasets.includes('speed')}
|
||||||
|
<Check size="14" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<Zap size="15" class="mr-1" />
|
||||||
|
{$velocityUnits === 'speed' ? $_('quantities.speed') : $_('quantities.pace')}
|
||||||
|
</ToggleGroup.Item>
|
||||||
|
<ToggleGroup.Item
|
||||||
|
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
|
value="hr"
|
||||||
|
>
|
||||||
|
<div class="w-6 flex justify-center items-center">
|
||||||
|
{#if additionalDatasets.includes('hr')}
|
||||||
|
<Check size="14" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<HeartPulse size="15" class="mr-1" />
|
||||||
|
{$_('quantities.heartrate')}
|
||||||
|
</ToggleGroup.Item>
|
||||||
|
<ToggleGroup.Item
|
||||||
|
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
|
value="cad"
|
||||||
|
>
|
||||||
|
<div class="w-6 flex justify-center items-center">
|
||||||
|
{#if additionalDatasets.includes('cad')}
|
||||||
|
<Check size="14" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<Orbit size="15" class="mr-1" />
|
||||||
|
{$_('quantities.cadence')}
|
||||||
|
</ToggleGroup.Item>
|
||||||
|
<ToggleGroup.Item
|
||||||
|
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
|
value="atemp"
|
||||||
|
>
|
||||||
|
<div class="w-6 flex justify-center items-center">
|
||||||
|
{#if additionalDatasets.includes('atemp')}
|
||||||
|
<Check size="14" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<Thermometer size="15" class="mr-1" />
|
||||||
|
{$_('quantities.temperature')}
|
||||||
|
</ToggleGroup.Item>
|
||||||
|
<ToggleGroup.Item
|
||||||
|
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
|
||||||
|
value="power"
|
||||||
|
>
|
||||||
|
<div class="w-6 flex justify-center items-center">
|
||||||
|
{#if additionalDatasets.includes('power')}
|
||||||
|
<Check size="14" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<SquareActivity size="15" class="mr-1" />
|
||||||
|
{$_('quantities.power')}
|
||||||
|
</ToggleGroup.Item>
|
||||||
|
</ToggleGroup.Root>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -341,14 +341,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chart": {
|
"chart": {
|
||||||
"show_slope": "Show slope data",
|
"settings": "Elevation profile settings"
|
||||||
"show_surface": "Show surface data",
|
|
||||||
"show_speed": "Show speed data",
|
|
||||||
"show_pace": "Show pace data",
|
|
||||||
"show_heartrate": "Show heart rate data",
|
|
||||||
"show_cadence": "Show cadence data",
|
|
||||||
"show_temperature": "Show temperature data",
|
|
||||||
"show_power": "Show power data"
|
|
||||||
},
|
},
|
||||||
"quantities": {
|
"quantities": {
|
||||||
"distance": "Distance",
|
"distance": "Distance",
|
||||||
|
Reference in New Issue
Block a user