mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 00:32:33 +00:00
resize panels
This commit is contained in:
@@ -8,11 +8,12 @@
|
|||||||
import Toolbar from '$lib/components/toolbar/Toolbar.svelte';
|
import Toolbar from '$lib/components/toolbar/Toolbar.svelte';
|
||||||
import StreetViewControl from '$lib/components/street-view-control/StreetViewControl.svelte';
|
import StreetViewControl from '$lib/components/street-view-control/StreetViewControl.svelte';
|
||||||
import LayerControl from '$lib/components/layer-control/LayerControl.svelte';
|
import LayerControl from '$lib/components/layer-control/LayerControl.svelte';
|
||||||
|
import Resizer from '$lib/components/Resizer.svelte';
|
||||||
import { Toaster } from '$lib/components/ui/sonner';
|
import { Toaster } from '$lib/components/ui/sonner';
|
||||||
|
|
||||||
import { settings } from '$lib/db';
|
import { settings } from '$lib/db';
|
||||||
|
|
||||||
const { verticalFileView, elevationProfile } = settings;
|
const { verticalFileView, elevationProfile, bottomPanelSize, rightPanelSize } = settings;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="fixed flex flex-row w-screen h-screen">
|
<div class="fixed flex flex-row w-screen h-screen">
|
||||||
@@ -31,7 +32,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="{$elevationProfile ? 'h-48' : 'h-10'} flex flex-row gap-2 overflow-hidden">
|
{#if $elevationProfile}
|
||||||
|
<Resizer orientation="row" bind:after={$bottomPanelSize} minAfter={100} maxAfter={300} />
|
||||||
|
{/if}
|
||||||
|
<div
|
||||||
|
class="{$elevationProfile ? '' : 'h-10'} flex flex-row gap-2"
|
||||||
|
style={$elevationProfile ? `height: ${$bottomPanelSize}px` : ''}
|
||||||
|
>
|
||||||
<GPXStatistics />
|
<GPXStatistics />
|
||||||
{#if $elevationProfile}
|
{#if $elevationProfile}
|
||||||
<ElevationProfile />
|
<ElevationProfile />
|
||||||
@@ -39,7 +46,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if $verticalFileView}
|
{#if $verticalFileView}
|
||||||
<FileList orientation="vertical" recursive={true} class="w-60" />
|
<Resizer orientation="col" bind:after={$rightPanelSize} minAfter={100} maxAfter={400} />
|
||||||
|
<FileList orientation="vertical" recursive={true} style="width: {$rightPanelSize}px" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -57,7 +57,7 @@
|
|||||||
let marker: mapboxgl.Marker | null = null;
|
let marker: mapboxgl.Marker | null = null;
|
||||||
let dragging = false;
|
let dragging = false;
|
||||||
|
|
||||||
let { distanceUnits, velocityUnits, temperatureUnits } = settings;
|
let { distanceUnits, velocityUnits, temperatureUnits, bottomPanelSize } = settings;
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
animation: false,
|
animation: false,
|
||||||
@@ -116,8 +116,8 @@
|
|||||||
if (dragging) {
|
if (dragging) {
|
||||||
marker.remove();
|
marker.remove();
|
||||||
} else {
|
} else {
|
||||||
marker.addTo($map);
|
|
||||||
marker.setLngLat(point.coordinates);
|
marker.setLngLat(point.coordinates);
|
||||||
|
marker.addTo($map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return `${$_('quantities.elevation')}: ${getElevationWithUnits(point.y, false)}`;
|
return `${$_('quantities.elevation')}: ${getElevationWithUnits(point.y, false)}`;
|
||||||
@@ -520,12 +520,16 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="h-full grow min-w-0 flex flex-row gap-4 items-center py-2 pr-4">
|
<div class="h-full grow min-w-0 flex flex-row gap-4 items-center py-2 pr-4">
|
||||||
<div class="h-full grow min-w-0">
|
<div class="grow h-full min-w-0">
|
||||||
<canvas bind:this={overlay} class="absolute pointer-events-none"></canvas>
|
<canvas bind:this={overlay} class="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>
|
</div>
|
||||||
<div class="w-fit flex flex-col border rounded">
|
<div class="h-full flex flex-col justify-center">
|
||||||
<ToggleGroup.Root class="flex-col gap-0" type="single" bind:value={elevationFill}>
|
<ToggleGroup.Root
|
||||||
|
class="flex-col flex-wrap gap-0 min-h-0 content-center border rounded-t"
|
||||||
|
type="single"
|
||||||
|
bind:value={elevationFill}
|
||||||
|
>
|
||||||
<ToggleGroup.Item class="p-0 w-6 h-6" value="slope">
|
<ToggleGroup.Item class="p-0 w-6 h-6" value="slope">
|
||||||
<Tooltip side="left">
|
<Tooltip side="left">
|
||||||
<TriangleRight slot="data" size="16" />
|
<TriangleRight slot="data" size="16" />
|
||||||
@@ -539,8 +543,11 @@
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ToggleGroup.Item>
|
</ToggleGroup.Item>
|
||||||
</ToggleGroup.Root>
|
</ToggleGroup.Root>
|
||||||
<Separator />
|
<ToggleGroup.Root
|
||||||
<ToggleGroup.Root class="flex-col gap-0" type="multiple" bind:value={additionalDatasets}>
|
class="flex-col flex-wrap gap-0 min-h-0 content-center border rounded-b -mt-[1px]"
|
||||||
|
type="multiple"
|
||||||
|
bind:value={additionalDatasets}
|
||||||
|
>
|
||||||
<ToggleGroup.Item class="p-0 w-6 h-6" value="speed">
|
<ToggleGroup.Item class="p-0 w-6 h-6" value="speed">
|
||||||
<Tooltip side="left">
|
<Tooltip side="left">
|
||||||
<Zap slot="data" size="16" />
|
<Zap slot="data" size="16" />
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import type { GPXStatistics } from 'gpx';
|
import type { GPXStatistics } from 'gpx';
|
||||||
|
|
||||||
const { velocityUnits, elevationProfile } = settings;
|
const { velocityUnits, elevationProfile, bottomPanelSize } = settings;
|
||||||
|
|
||||||
let statistics: GPXStatistics;
|
let statistics: GPXStatistics;
|
||||||
|
|
||||||
@@ -48,29 +48,33 @@
|
|||||||
</span>
|
</span>
|
||||||
<span slot="tooltip">{$_('quantities.elevation')}</span>
|
<span slot="tooltip">{$_('quantities.elevation')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip>
|
{#if $bottomPanelSize > 120 || !$elevationProfile}
|
||||||
<span slot="data" class="flex flex-row items-center">
|
<Tooltip>
|
||||||
<Zap size="18" class="mr-1" />
|
<span slot="data" class="flex flex-row items-center">
|
||||||
<WithUnits value={statistics.global.speed.moving} type="speed" showUnits={false} />
|
<Zap size="18" class="mr-1" />
|
||||||
<span class="mx-1">/</span>
|
<WithUnits value={statistics.global.speed.moving} type="speed" showUnits={false} />
|
||||||
<WithUnits value={statistics.global.speed.total} type="speed" />
|
<span class="mx-1">/</span>
|
||||||
</span>
|
<WithUnits value={statistics.global.speed.total} type="speed" />
|
||||||
<span slot="tooltip"
|
</span>
|
||||||
>{$velocityUnits === 'speed' ? $_('quantities.speed') : $_('quantities.pace')} ({$_(
|
<span slot="tooltip"
|
||||||
'quantities.moving'
|
>{$velocityUnits === 'speed' ? $_('quantities.speed') : $_('quantities.pace')} ({$_(
|
||||||
)} / {$_('quantities.total')})</span
|
'quantities.moving'
|
||||||
>
|
)} / {$_('quantities.total')})</span
|
||||||
</Tooltip>
|
>
|
||||||
<Tooltip>
|
</Tooltip>
|
||||||
<span slot="data" class="flex flex-row items-center">
|
{/if}
|
||||||
<Timer size="18" class="mr-1" />
|
{#if $bottomPanelSize > 160 || !$elevationProfile}
|
||||||
<WithUnits value={statistics.global.time.moving} type="time" />
|
<Tooltip>
|
||||||
<span class="mx-1">/</span>
|
<span slot="data" class="flex flex-row items-center">
|
||||||
<WithUnits value={statistics.global.time.total} type="time" />
|
<Timer size="18" class="mr-1" />
|
||||||
</span>
|
<WithUnits value={statistics.global.time.moving} type="time" />
|
||||||
<span slot="tooltip"
|
<span class="mx-1">/</span>
|
||||||
>{$_('quantities.time')} ({$_('quantities.moving')} / {$_('quantities.total')})</span
|
<WithUnits value={statistics.global.time.total} type="time" />
|
||||||
>
|
</span>
|
||||||
</Tooltip>
|
<span slot="tooltip"
|
||||||
|
>{$_('quantities.time')} ({$_('quantities.moving')} / {$_('quantities.total')})</span
|
||||||
|
>
|
||||||
|
</Tooltip>
|
||||||
|
{/if}
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
|
@@ -21,7 +21,8 @@
|
|||||||
easing: () => 1
|
easing: () => 1
|
||||||
};
|
};
|
||||||
|
|
||||||
const { distanceUnits, elevationProfile, verticalFileView } = settings;
|
const { distanceUnits, elevationProfile, verticalFileView, bottomPanelSize, rightPanelSize } =
|
||||||
|
settings;
|
||||||
let scaleControl = new mapboxgl.ScaleControl({
|
let scaleControl = new mapboxgl.ScaleControl({
|
||||||
unit: $distanceUnits
|
unit: $distanceUnits
|
||||||
});
|
});
|
||||||
@@ -117,7 +118,10 @@
|
|||||||
scaleControl.setUnit($distanceUnits);
|
scaleControl.setUnit($distanceUnits);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if ($map && (!$verticalFileView || !$elevationProfile)) {
|
$: if (
|
||||||
|
$map &&
|
||||||
|
(!$verticalFileView || !$elevationProfile || $bottomPanelSize || $rightPanelSize)
|
||||||
|
) {
|
||||||
$map.resize();
|
$map.resize();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
40
website/src/lib/components/Resizer.svelte
Normal file
40
website/src/lib/components/Resizer.svelte
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let orientation: 'col' | 'row' = 'col';
|
||||||
|
|
||||||
|
export let after: number;
|
||||||
|
export let minAfter: number = 0;
|
||||||
|
export let maxAfter: number = Number.MAX_SAFE_INTEGER;
|
||||||
|
|
||||||
|
function handleMouseDown(event: MouseEvent) {
|
||||||
|
const startX = event.clientX;
|
||||||
|
const startY = event.clientY;
|
||||||
|
const startAfter = after;
|
||||||
|
|
||||||
|
const handleMouseMove = (event: MouseEvent) => {
|
||||||
|
const newAfter =
|
||||||
|
startAfter + (orientation === 'col' ? startX - event.clientX : startY - event.clientY);
|
||||||
|
if (newAfter >= minAfter && newAfter <= maxAfter) {
|
||||||
|
after = newAfter;
|
||||||
|
} else if (newAfter < minAfter && after !== minAfter) {
|
||||||
|
after = minAfter;
|
||||||
|
} else if (newAfter > maxAfter && after !== maxAfter) {
|
||||||
|
after = maxAfter;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
window.removeEventListener('mousemove', handleMouseMove);
|
||||||
|
window.removeEventListener('mouseup', handleMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', handleMouseMove);
|
||||||
|
window.addEventListener('mouseup', handleMouseUp);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="{orientation === 'col'
|
||||||
|
? 'w-1 h-full cursor-col-resize'
|
||||||
|
: 'w-full h-1 cursor-row-resize'} {orientation}"
|
||||||
|
on:mousedown={handleMouseDown}
|
||||||
|
/>
|
@@ -53,6 +53,7 @@
|
|||||||
class="flex {orientation === 'vertical'
|
class="flex {orientation === 'vertical'
|
||||||
? 'flex-col py-1 pl-1 min-h-screen'
|
? 'flex-col py-1 pl-1 min-h-screen'
|
||||||
: 'flex-row'} {$$props.class ?? ''}"
|
: 'flex-row'} {$$props.class ?? ''}"
|
||||||
|
{...$$restProps}
|
||||||
>
|
>
|
||||||
<FileListNode bind:node={$fileObservers} item={new ListRootItem()} />
|
<FileListNode bind:node={$fileObservers} item={new ListRootItem()} />
|
||||||
{#if orientation === 'vertical'}
|
{#if orientation === 'vertical'}
|
||||||
|
@@ -47,10 +47,16 @@ function dexieSettingStore<T>(setting: string, initial: T): Writable<T> {
|
|||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
subscribe: store.subscribe,
|
subscribe: store.subscribe,
|
||||||
set: (value: any) => db.settings.put(value, setting),
|
set: (value: any) => {
|
||||||
|
if (value !== get(store)) {
|
||||||
|
db.settings.put(value, setting);
|
||||||
|
}
|
||||||
|
},
|
||||||
update: (callback: (value: any) => any) => {
|
update: (callback: (value: any) => any) => {
|
||||||
let newValue = callback(get(store));
|
let newValue = callback(get(store));
|
||||||
db.settings.put(newValue, setting);
|
if (newValue !== get(store)) {
|
||||||
|
db.settings.put(newValue, setting);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -105,6 +111,8 @@ export const settings = {
|
|||||||
fileOrder: dexieSettingStore<string[]>('fileOrder', []),
|
fileOrder: dexieSettingStore<string[]>('fileOrder', []),
|
||||||
defaultOpacity: dexieSettingStore('defaultOpacity', 0.7),
|
defaultOpacity: dexieSettingStore('defaultOpacity', 0.7),
|
||||||
defaultWeight: dexieSettingStore('defaultWeight', 5),
|
defaultWeight: dexieSettingStore('defaultWeight', 5),
|
||||||
|
bottomPanelSize: dexieSettingStore('bottomPanelSize', 192),
|
||||||
|
rightPanelSize: dexieSettingStore('rightPanelSize', 240),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wrap Dexie live queries in a Svelte store to avoid triggering the query for every subscriber
|
// Wrap Dexie live queries in a Svelte store to avoid triggering the query for every subscriber
|
||||||
|
Reference in New Issue
Block a user