2024-04-08 17:12:39 +02:00
|
|
|
<script lang="ts">
|
2025-02-02 11:17:22 +01:00
|
|
|
import * as Menubar from '$lib/components/ui/menubar/index.js';
|
|
|
|
|
import { Button } from '$lib/components/ui/button';
|
|
|
|
|
import Logo from '$lib/components/Logo.svelte';
|
|
|
|
|
import Shortcut from '$lib/components/Shortcut.svelte';
|
|
|
|
|
import {
|
|
|
|
|
Plus,
|
|
|
|
|
Copy,
|
|
|
|
|
Download,
|
|
|
|
|
Undo2,
|
|
|
|
|
Redo2,
|
|
|
|
|
Trash2,
|
|
|
|
|
Heart,
|
|
|
|
|
Map,
|
|
|
|
|
Layers2,
|
|
|
|
|
Box,
|
|
|
|
|
Milestone,
|
|
|
|
|
Coins,
|
|
|
|
|
Ruler,
|
|
|
|
|
Zap,
|
|
|
|
|
Thermometer,
|
|
|
|
|
Sun,
|
|
|
|
|
Moon,
|
|
|
|
|
Layers,
|
|
|
|
|
ListTree,
|
|
|
|
|
Languages,
|
|
|
|
|
Settings,
|
|
|
|
|
Info,
|
|
|
|
|
File,
|
|
|
|
|
View,
|
|
|
|
|
FilePen,
|
|
|
|
|
HeartHandshake,
|
|
|
|
|
PersonStanding,
|
|
|
|
|
Eye,
|
|
|
|
|
EyeOff,
|
|
|
|
|
ClipboardCopy,
|
|
|
|
|
Scissors,
|
|
|
|
|
ClipboardPaste,
|
|
|
|
|
PaintBucket,
|
|
|
|
|
FolderOpen,
|
|
|
|
|
FileStack,
|
|
|
|
|
FileX,
|
|
|
|
|
BookOpenText,
|
|
|
|
|
ChartArea,
|
|
|
|
|
Maximize,
|
2025-06-21 21:07:36 +02:00
|
|
|
} from '@lucide/svelte';
|
|
|
|
|
import { map } from '$lib/components/map/utils.svelte';
|
|
|
|
|
import { editMetadata } from '$lib/components/file-list/metadata/utils.svelte';
|
|
|
|
|
import { editStyle } from '$lib/components/file-list/style/utils.svelte';
|
|
|
|
|
import { exportState, ExportState } from '$lib/components/export/utils.svelte';
|
|
|
|
|
// import {
|
|
|
|
|
// triggerFileInput,
|
|
|
|
|
// createFile,
|
|
|
|
|
// loadFiles,
|
|
|
|
|
// updateSelectionFromKey,
|
|
|
|
|
// allHidden,
|
|
|
|
|
// } from '$lib/stores';
|
2025-10-05 19:34:05 +02:00
|
|
|
// import { canUndo, canRedo, fileActions, fileObservers, settings } from '$lib/db';
|
2025-06-21 21:07:36 +02:00
|
|
|
import { anySelectedLayer } from '$lib/components/map/layer-control/utils.svelte';
|
2025-02-02 11:17:22 +01:00
|
|
|
import { defaultOverlays } from '$lib/assets/layers';
|
2025-06-21 21:07:36 +02:00
|
|
|
// import LayerControlSettings from '$lib/components/map/layer-control/LayerControlSettings.svelte';
|
2025-10-05 19:34:05 +02:00
|
|
|
import {
|
|
|
|
|
allowedPastes,
|
|
|
|
|
ListFileItem,
|
|
|
|
|
ListTrackItem,
|
|
|
|
|
} from '$lib/components/file-list/file-list';
|
2025-06-21 21:07:36 +02:00
|
|
|
import Export from '$lib/components/export/Export.svelte';
|
2025-06-08 16:32:41 +02:00
|
|
|
import { mode, setMode } from 'mode-watcher';
|
2025-06-21 21:07:36 +02:00
|
|
|
import { i18n } from '$lib/i18n.svelte';
|
2025-02-02 11:17:22 +01:00
|
|
|
import { languages } from '$lib/languages';
|
|
|
|
|
import { getURLForLanguage } from '$lib/utils';
|
2025-06-21 21:07:36 +02:00
|
|
|
import { settings } from '$lib/logic/settings.svelte';
|
2025-10-05 19:34:05 +02:00
|
|
|
import {
|
|
|
|
|
createFile,
|
|
|
|
|
fileActions,
|
|
|
|
|
loadFiles,
|
|
|
|
|
pasteSelection,
|
|
|
|
|
triggerFileInput,
|
|
|
|
|
} from '$lib/logic/file-actions.svelte';
|
|
|
|
|
import { fileStateCollection } from '$lib/logic/file-state.svelte';
|
|
|
|
|
import { fileActionManager } from '$lib/logic/file-action-manager.svelte';
|
|
|
|
|
import { selection } from '$lib/logic/selection.svelte';
|
2024-04-24 16:12:50 +02:00
|
|
|
|
2025-02-02 11:17:22 +01:00
|
|
|
const {
|
|
|
|
|
distanceUnits,
|
|
|
|
|
velocityUnits,
|
|
|
|
|
temperatureUnits,
|
|
|
|
|
elevationProfile,
|
|
|
|
|
treeFileView,
|
|
|
|
|
currentBasemap,
|
|
|
|
|
previousBasemap,
|
|
|
|
|
currentOverlays,
|
|
|
|
|
previousOverlays,
|
|
|
|
|
distanceMarkers,
|
|
|
|
|
directionMarkers,
|
|
|
|
|
streetViewSource,
|
|
|
|
|
routing,
|
|
|
|
|
} = settings;
|
2024-05-08 12:35:31 +02:00
|
|
|
|
2025-02-02 11:17:22 +01:00
|
|
|
function switchBasemaps() {
|
2025-06-21 21:07:36 +02:00
|
|
|
[currentBasemap.value, previousBasemap.value] = [
|
|
|
|
|
previousBasemap.value,
|
|
|
|
|
currentBasemap.value,
|
|
|
|
|
];
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|
2024-05-06 15:52:11 +02:00
|
|
|
|
2025-02-02 11:17:22 +01:00
|
|
|
function toggleOverlays() {
|
2025-06-21 21:07:36 +02:00
|
|
|
if (currentOverlays.value && anySelectedLayer(currentOverlays.value)) {
|
2025-10-05 19:34:05 +02:00
|
|
|
previousOverlays.value = JSON.parse(JSON.stringify(currentOverlays.value));
|
|
|
|
|
currentOverlays.value = defaultOverlays;
|
2025-02-02 11:17:22 +01:00
|
|
|
} else {
|
2025-10-05 19:34:05 +02:00
|
|
|
currentOverlays.value = JSON.parse(JSON.stringify(previousOverlays.value));
|
|
|
|
|
previousOverlays.value = defaultOverlays;
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-05-23 11:21:57 +02:00
|
|
|
|
2025-06-21 21:07:36 +02:00
|
|
|
let layerSettingsOpen = $state(false);
|
2024-04-08 17:12:39 +02:00
|
|
|
</script>
|
|
|
|
|
|
2024-06-20 11:59:21 +02:00
|
|
|
<div class="absolute md:top-2 left-0 right-0 z-20 flex flex-row justify-center pointer-events-none">
|
2025-02-02 11:17:22 +01:00
|
|
|
<div
|
|
|
|
|
class="w-fit flex flex-row items-center justify-center p-1 bg-background rounded-b-md md:rounded-md pointer-events-auto shadow-md"
|
|
|
|
|
>
|
2025-06-21 21:07:36 +02:00
|
|
|
<a href={getURLForLanguage(i18n.lang, '/')} target="_blank" class="shrink-0">
|
2025-02-02 11:17:22 +01:00
|
|
|
<Logo class="h-5 mt-0.5 mx-2 md:hidden" iconOnly={true} width="16" />
|
|
|
|
|
<Logo class="h-5 mt-0.5 mx-2 hidden md:block" width="96" />
|
|
|
|
|
</a>
|
2025-10-05 19:34:05 +02:00
|
|
|
<Menubar.Root class="border-none shadow-none h-fit p-0">
|
2025-02-02 11:17:22 +01:00
|
|
|
<Menubar.Menu>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Trigger aria-label={i18n._('gpx.file')}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<File size="18" class="md:hidden" />
|
2025-06-21 21:07:36 +02:00
|
|
|
<span class="hidden md:block">{i18n._('gpx.file')}</span>
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.Trigger>
|
|
|
|
|
<Menubar.Content class="border-none">
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Item onclick={createFile}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Plus size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.new')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="+" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Separator />
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Item onclick={triggerFileInput}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<FolderOpen size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.open')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="O" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Separator />
|
|
|
|
|
<Menubar.Item
|
2025-10-05 19:34:05 +02:00
|
|
|
onclick={fileActions.duplicateSelection}
|
|
|
|
|
disabled={selection.value.size == 0}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Copy size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.duplicate')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="D" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Separator />
|
|
|
|
|
<Menubar.Item
|
2025-10-05 19:34:05 +02:00
|
|
|
onclick={fileActions.deleteSelectedFiles}
|
|
|
|
|
disabled={selection.value.size == 0}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<FileX size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.close')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="⌫" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Item
|
2025-10-05 19:34:05 +02:00
|
|
|
onclick={fileActions.deleteAllFiles}
|
|
|
|
|
disabled={fileStateCollection.size == 0}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<FileX size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.close_all')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="⌫" ctrl={true} shift={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Separator />
|
|
|
|
|
<Menubar.Item
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={() => (exportState.current = ExportState.SELECTION)}
|
2025-10-05 19:34:05 +02:00
|
|
|
disabled={selection.value.size == 0}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Download size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.export')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="S" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Item
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={() => (exportState.current = ExportState.ALL)}
|
2025-10-05 19:34:05 +02:00
|
|
|
disabled={fileStateCollection.size == 0}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Download size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.export_all')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="S" ctrl={true} shift={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
</Menubar.Content>
|
|
|
|
|
</Menubar.Menu>
|
|
|
|
|
<Menubar.Menu>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Trigger aria-label={i18n._('menu.edit')}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<FilePen size="18" class="md:hidden" />
|
2025-06-21 21:07:36 +02:00
|
|
|
<span class="hidden md:block">{i18n._('menu.edit')}</span>
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.Trigger>
|
|
|
|
|
<Menubar.Content class="border-none">
|
2025-10-05 19:34:05 +02:00
|
|
|
<Menubar.Item
|
|
|
|
|
onclick={fileActionManager.undo}
|
|
|
|
|
disabled={!fileActionManager.canUndo}
|
|
|
|
|
>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Undo2 size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.undo')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="Z" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
2025-10-05 19:34:05 +02:00
|
|
|
<Menubar.Item
|
|
|
|
|
onclick={fileActionManager.redo}
|
|
|
|
|
disabled={!fileActionManager.canRedo}
|
|
|
|
|
>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Redo2 size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.redo')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="Z" ctrl={true} shift={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Separator />
|
|
|
|
|
<Menubar.Item
|
2025-10-05 19:34:05 +02:00
|
|
|
disabled={selection.value.size !== 1 ||
|
|
|
|
|
!selection.value
|
2025-02-02 11:17:22 +01:00
|
|
|
.getSelected()
|
|
|
|
|
.every(
|
|
|
|
|
(item) =>
|
|
|
|
|
item instanceof ListFileItem ||
|
|
|
|
|
item instanceof ListTrackItem
|
|
|
|
|
)}
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={() => (editMetadata.current = true)}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Info size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.metadata.button')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="I" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Item
|
2025-10-05 19:34:05 +02:00
|
|
|
disabled={selection.value.size === 0 ||
|
|
|
|
|
!selection.value
|
2025-02-02 11:17:22 +01:00
|
|
|
.getSelected()
|
|
|
|
|
.every(
|
|
|
|
|
(item) =>
|
|
|
|
|
item instanceof ListFileItem ||
|
|
|
|
|
item instanceof ListTrackItem
|
|
|
|
|
)}
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={() => (editStyle.current = true)}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<PaintBucket size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.style.button')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Item
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={() => {
|
2025-10-05 19:34:05 +02:00
|
|
|
// if ($allHidden) {
|
|
|
|
|
// fileActions.setHiddenToSelection(false);
|
|
|
|
|
// } else {
|
|
|
|
|
// fileActions.setHiddenToSelection(true);
|
|
|
|
|
// }
|
2025-02-02 11:17:22 +01:00
|
|
|
}}
|
2025-10-05 19:34:05 +02:00
|
|
|
disabled={selection.value.size == 0}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
2025-10-05 19:34:05 +02:00
|
|
|
<!-- {#if $allHidden}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Eye size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.unhide')}
|
2025-02-02 11:17:22 +01:00
|
|
|
{:else}
|
|
|
|
|
<EyeOff size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.hide')}
|
2025-10-05 19:34:05 +02:00
|
|
|
{/if} -->
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="H" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
2025-06-21 21:07:36 +02:00
|
|
|
{#if treeFileView.value}
|
2025-10-05 19:34:05 +02:00
|
|
|
{#if selection.value
|
|
|
|
|
.getSelected()
|
|
|
|
|
.some((item) => item instanceof ListFileItem)}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Menubar.Separator />
|
|
|
|
|
<Menubar.Item
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={() =>
|
2025-10-05 19:34:05 +02:00
|
|
|
fileActions.addNewTrack(
|
|
|
|
|
selection.value.getSelected()[0].getFileId()
|
|
|
|
|
)}
|
|
|
|
|
disabled={selection.value.size !== 1}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Plus size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.new_track')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.Item>
|
2025-10-05 19:34:05 +02:00
|
|
|
{:else if selection.value
|
2025-02-02 11:17:22 +01:00
|
|
|
.getSelected()
|
|
|
|
|
.some((item) => item instanceof ListTrackItem)}
|
|
|
|
|
<Menubar.Separator />
|
|
|
|
|
<Menubar.Item
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={() => {
|
2025-10-05 19:34:05 +02:00
|
|
|
let item = selection.value.getSelected()[0];
|
|
|
|
|
fileActions.addNewSegment(
|
|
|
|
|
item.getFileId(),
|
|
|
|
|
item.getTrackIndex()
|
|
|
|
|
);
|
2025-02-02 11:17:22 +01:00
|
|
|
}}
|
2025-10-05 19:34:05 +02:00
|
|
|
disabled={selection.value.size !== 1}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Plus size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.new_segment')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.Item>
|
|
|
|
|
{/if}
|
|
|
|
|
{/if}
|
|
|
|
|
<Menubar.Separator />
|
2025-10-05 19:34:05 +02:00
|
|
|
<Menubar.Item
|
|
|
|
|
onclick={selection.selectAll}
|
|
|
|
|
disabled={fileStateCollection.size == 0}
|
|
|
|
|
>
|
2025-02-02 11:17:22 +01:00
|
|
|
<FileStack size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.select_all')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="A" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Item
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={() => {
|
2025-10-05 19:34:05 +02:00
|
|
|
if (selection.value.size > 0) {
|
|
|
|
|
// centerMapOnSelection();
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Maximize size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.center')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="⏎" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
2025-06-21 21:07:36 +02:00
|
|
|
{#if treeFileView.value}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Menubar.Separator />
|
2025-10-05 19:34:05 +02:00
|
|
|
<Menubar.Item
|
|
|
|
|
onclick={selection.copySelection}
|
|
|
|
|
disabled={selection.value.size === 0}
|
|
|
|
|
>
|
2025-02-02 11:17:22 +01:00
|
|
|
<ClipboardCopy size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.copy')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="C" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
2025-10-05 19:34:05 +02:00
|
|
|
<Menubar.Item
|
|
|
|
|
onclick={selection.cutSelection}
|
|
|
|
|
disabled={selection.value.size === 0}
|
|
|
|
|
>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Scissors size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.cut')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="X" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Item
|
2025-10-05 19:34:05 +02:00
|
|
|
disabled={selection.copied === undefined ||
|
|
|
|
|
selection.copied.length === 0 ||
|
|
|
|
|
(selection.value.size > 0 &&
|
|
|
|
|
!allowedPastes[selection.copied[0].level].includes(
|
|
|
|
|
selection.value.getSelected().pop()?.level
|
2025-02-02 11:17:22 +01:00
|
|
|
))}
|
2025-06-21 21:07:36 +02:00
|
|
|
onclick={pasteSelection}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<ClipboardPaste size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.paste')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="V" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
{/if}
|
|
|
|
|
<Menubar.Separator />
|
2025-10-05 19:34:05 +02:00
|
|
|
<Menubar.Item
|
|
|
|
|
onclick={fileActions.deleteSelection}
|
|
|
|
|
disabled={selection.value.size == 0}
|
|
|
|
|
>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Trash2 size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.delete')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="⌫" ctrl={true} />
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
</Menubar.Content>
|
|
|
|
|
</Menubar.Menu>
|
|
|
|
|
<Menubar.Menu>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Trigger aria-label={i18n._('menu.view')}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<View size="18" class="md:hidden" />
|
2025-06-21 21:07:36 +02:00
|
|
|
<span class="hidden md:block">{i18n._('menu.view')}</span>
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.Trigger>
|
|
|
|
|
<Menubar.Content class="border-none">
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.CheckboxItem bind:checked={elevationProfile.value}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<ChartArea size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.elevation_profile')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="P" ctrl={true} />
|
|
|
|
|
</Menubar.CheckboxItem>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.CheckboxItem bind:checked={treeFileView.value}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<ListTree size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.tree_file_view')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Shortcut key="L" ctrl={true} />
|
|
|
|
|
</Menubar.CheckboxItem>
|
|
|
|
|
<Menubar.Separator />
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Item inset onclick={switchBasemaps}>
|
|
|
|
|
<Map size="16" class="mr-1" />{i18n._('menu.switch_basemap')}<Shortcut
|
2025-02-02 11:17:22 +01:00
|
|
|
key="F1"
|
|
|
|
|
/>
|
|
|
|
|
</Menubar.Item>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Item inset onclick={toggleOverlays}>
|
|
|
|
|
<Layers2 size="16" class="mr-1" />{i18n._('menu.toggle_overlays')}<Shortcut
|
2025-02-02 11:17:22 +01:00
|
|
|
key="F2"
|
|
|
|
|
/>
|
|
|
|
|
</Menubar.Item>
|
|
|
|
|
<Menubar.Separator />
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.CheckboxItem bind:checked={distanceMarkers.value}>
|
|
|
|
|
<Coins size="16" class="mr-1" />{i18n._('menu.distance_markers')}<Shortcut
|
2025-02-02 11:17:22 +01:00
|
|
|
key="F3"
|
|
|
|
|
/>
|
|
|
|
|
</Menubar.CheckboxItem>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.CheckboxItem bind:checked={directionMarkers.value}>
|
|
|
|
|
<Milestone size="16" class="mr-1" />{i18n._(
|
|
|
|
|
'menu.direction_markers'
|
|
|
|
|
)}<Shortcut key="F4" />
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.CheckboxItem>
|
|
|
|
|
<Menubar.Separator />
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Item inset onclick={map.toggle3D}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Box size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.toggle_3d')}
|
|
|
|
|
<Shortcut key="{i18n._('menu.ctrl')}+{i18n._('menu.drag')}" />
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.Item>
|
|
|
|
|
</Menubar.Content>
|
|
|
|
|
</Menubar.Menu>
|
|
|
|
|
<Menubar.Menu>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Trigger aria-label={i18n._('menu.settings')}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Settings size="18" class="md:hidden" />
|
|
|
|
|
<span class="hidden md:block">
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.settings')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</span>
|
|
|
|
|
</Menubar.Trigger>
|
|
|
|
|
<Menubar.Content class="border-none">
|
|
|
|
|
<Menubar.Sub>
|
|
|
|
|
<Menubar.SubTrigger>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Ruler size="16" class="mr-1" />{i18n._('menu.distance_units')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.SubTrigger>
|
|
|
|
|
<Menubar.SubContent>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.RadioGroup bind:value={distanceUnits.value}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Menubar.RadioItem value="metric"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('menu.metric')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Menubar.RadioItem value="imperial"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('menu.imperial')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Menubar.RadioItem value="nautical"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('menu.nautical')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
</Menubar.RadioGroup>
|
|
|
|
|
</Menubar.SubContent>
|
|
|
|
|
</Menubar.Sub>
|
|
|
|
|
<Menubar.Sub>
|
|
|
|
|
<Menubar.SubTrigger>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Zap size="16" class="mr-1" />{i18n._('menu.velocity_units')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.SubTrigger>
|
|
|
|
|
<Menubar.SubContent>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.RadioGroup bind:value={velocityUnits.value}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Menubar.RadioItem value="speed"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('quantities.speed')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Menubar.RadioItem value="pace"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('quantities.pace')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
</Menubar.RadioGroup>
|
|
|
|
|
</Menubar.SubContent>
|
|
|
|
|
</Menubar.Sub>
|
|
|
|
|
<Menubar.Sub>
|
|
|
|
|
<Menubar.SubTrigger>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Thermometer size="16" class="mr-1" />{i18n._('menu.temperature_units')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.SubTrigger>
|
|
|
|
|
<Menubar.SubContent>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.RadioGroup bind:value={temperatureUnits.value}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Menubar.RadioItem value="celsius"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('menu.celsius')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Menubar.RadioItem value="fahrenheit"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('menu.fahrenheit')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
</Menubar.RadioGroup>
|
|
|
|
|
</Menubar.SubContent>
|
|
|
|
|
</Menubar.Sub>
|
|
|
|
|
<Menubar.Separator />
|
|
|
|
|
<Menubar.Sub>
|
|
|
|
|
<Menubar.SubTrigger>
|
|
|
|
|
<Languages size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.language')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.SubTrigger>
|
|
|
|
|
<Menubar.SubContent>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.RadioGroup value={i18n.lang}>
|
2025-02-02 11:17:22 +01:00
|
|
|
{#each Object.entries(languages) as [lang, label]}
|
|
|
|
|
<a href={getURLForLanguage(lang, '/app')}>
|
|
|
|
|
<Menubar.RadioItem value={lang}>{label}</Menubar.RadioItem>
|
|
|
|
|
</a>
|
|
|
|
|
{/each}
|
|
|
|
|
</Menubar.RadioGroup>
|
|
|
|
|
</Menubar.SubContent>
|
|
|
|
|
</Menubar.Sub>
|
|
|
|
|
<Menubar.Sub>
|
|
|
|
|
<Menubar.SubTrigger>
|
2025-06-08 16:32:41 +02:00
|
|
|
{#if mode.current === 'light' || !mode.current}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Sun size="16" class="mr-1" />
|
|
|
|
|
{:else}
|
|
|
|
|
<Moon size="16" class="mr-1" />
|
|
|
|
|
{/if}
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.mode')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.SubTrigger>
|
|
|
|
|
<Menubar.SubContent>
|
|
|
|
|
<Menubar.RadioGroup
|
2025-06-08 16:32:41 +02:00
|
|
|
value={mode.current ?? 'light'}
|
2025-02-02 11:17:22 +01:00
|
|
|
onValueChange={(value) => {
|
2025-06-21 21:07:36 +02:00
|
|
|
setMode(value as 'light' | 'dark');
|
2025-02-02 11:17:22 +01:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Menubar.RadioItem value="light"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('menu.light')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.RadioItem value="dark"
|
|
|
|
|
>{i18n._('menu.dark')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
</Menubar.RadioGroup>
|
|
|
|
|
</Menubar.SubContent>
|
|
|
|
|
</Menubar.Sub>
|
|
|
|
|
<Menubar.Separator />
|
|
|
|
|
<Menubar.Sub>
|
|
|
|
|
<Menubar.SubTrigger>
|
|
|
|
|
<PersonStanding size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.street_view_source')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.SubTrigger>
|
|
|
|
|
<Menubar.SubContent>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.RadioGroup bind:value={streetViewSource.value}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Menubar.RadioItem value="mapillary"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('menu.mapillary')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<Menubar.RadioItem value="google"
|
2025-06-21 21:07:36 +02:00
|
|
|
>{i18n._('menu.google')}</Menubar.RadioItem
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
</Menubar.RadioGroup>
|
|
|
|
|
</Menubar.SubContent>
|
|
|
|
|
</Menubar.Sub>
|
2025-06-21 21:07:36 +02:00
|
|
|
<Menubar.Item onclick={() => (layerSettingsOpen = true)}>
|
2025-02-02 11:17:22 +01:00
|
|
|
<Layers size="16" class="mr-1" />
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.layers')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</Menubar.Item>
|
|
|
|
|
</Menubar.Content>
|
|
|
|
|
</Menubar.Menu>
|
|
|
|
|
</Menubar.Root>
|
|
|
|
|
<div class="h-fit flex flex-row items-center ml-1 gap-1">
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
href="./help"
|
|
|
|
|
target="_blank"
|
|
|
|
|
class="cursor-default h-fit rounded-sm px-3 py-0.5"
|
2025-06-21 21:07:36 +02:00
|
|
|
aria-label={i18n._('menu.help')}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<BookOpenText size="18" class="md:hidden" />
|
|
|
|
|
<span class="hidden md:block">
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.help')}
|
2025-02-02 11:17:22 +01:00
|
|
|
</span>
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
href="https://ko-fi.com/gpxstudio"
|
|
|
|
|
target="_blank"
|
|
|
|
|
class="cursor-default h-fit rounded-sm font-bold text-support hover:text-support px-3 py-0.5"
|
2025-06-21 21:07:36 +02:00
|
|
|
aria-label={i18n._('menu.donate')}
|
2025-02-02 11:17:22 +01:00
|
|
|
>
|
|
|
|
|
<HeartHandshake size="18" class="md:hidden" />
|
|
|
|
|
<span class="hidden md:flex flex-row items-center">
|
2025-06-21 21:07:36 +02:00
|
|
|
{i18n._('menu.donate')}
|
2025-02-02 11:17:22 +01:00
|
|
|
<Heart size="16" class="ml-1" fill="rgb(var(--support))" />
|
|
|
|
|
</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-04-08 17:12:39 +02:00
|
|
|
</div>
|
2024-04-09 18:46:01 +02:00
|
|
|
|
2024-06-28 15:43:57 +02:00
|
|
|
<Export />
|
2025-06-21 21:07:36 +02:00
|
|
|
<!-- <LayerControlSettings bind:open={layerSettingsOpen} /> -->
|
2024-05-23 11:21:57 +02:00
|
|
|
|
2024-04-18 15:30:19 +02:00
|
|
|
<svelte:window
|
2025-02-02 11:17:22 +01:00
|
|
|
on:keydown={(e) => {
|
|
|
|
|
let targetInput =
|
|
|
|
|
e.target.tagName === 'INPUT' ||
|
|
|
|
|
e.target.tagName === 'TEXTAREA' ||
|
|
|
|
|
e.target.tagName === 'SELECT' ||
|
|
|
|
|
e.target.role === 'combobox' ||
|
|
|
|
|
e.target.role === 'radio' ||
|
|
|
|
|
e.target.role === 'menu' ||
|
|
|
|
|
e.target.role === 'menuitem' ||
|
|
|
|
|
e.target.role === 'menuitemradio' ||
|
|
|
|
|
e.target.role === 'menuitemcheckbox';
|
2024-06-29 11:40:27 +02:00
|
|
|
|
2025-02-02 11:17:22 +01:00
|
|
|
if (e.key === '+' && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
createFile();
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'o' && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
triggerFileInput();
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'd' && (e.metaKey || e.ctrlKey)) {
|
2025-10-05 19:34:05 +02:00
|
|
|
fileActions.duplicateSelection();
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'c' && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
if (!targetInput) {
|
2025-10-05 19:34:05 +02:00
|
|
|
selection.copySelection();
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
} else if (e.key === 'x' && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
if (!targetInput) {
|
2025-10-05 19:34:05 +02:00
|
|
|
selection.cutSelection();
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
} else if (e.key === 'v' && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
if (!targetInput) {
|
|
|
|
|
pasteSelection();
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
} else if ((e.key === 's' || e.key == 'S') && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
if (e.shiftKey) {
|
2025-10-05 19:34:05 +02:00
|
|
|
if (fileStateCollection.size > 0) {
|
2025-06-21 21:07:36 +02:00
|
|
|
exportState.current = ExportState.ALL;
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|
2025-10-05 19:34:05 +02:00
|
|
|
} else if (selection.value.size > 0) {
|
2025-06-21 21:07:36 +02:00
|
|
|
exportState.current = ExportState.SELECTION;
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
} else if ((e.key === 'z' || e.key == 'Z') && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
if (e.shiftKey) {
|
2025-10-05 19:34:05 +02:00
|
|
|
fileActionManager.redo();
|
2025-02-02 11:17:22 +01:00
|
|
|
} else {
|
2025-10-05 19:34:05 +02:00
|
|
|
fileActionManager.undo();
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
} else if ((e.key === 'Backspace' || e.key === 'Delete') && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
if (!targetInput) {
|
|
|
|
|
if (e.shiftKey) {
|
2025-10-05 19:34:05 +02:00
|
|
|
fileActions.deleteAllFiles();
|
2025-02-02 11:17:22 +01:00
|
|
|
} else {
|
2025-10-05 19:34:05 +02:00
|
|
|
fileActions.deleteSelection();
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
} else if (e.key === 'a' && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
if (!targetInput) {
|
2025-10-05 19:34:05 +02:00
|
|
|
selection.selectAll();
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
} else if (e.key === 'i' && (e.metaKey || e.ctrlKey)) {
|
|
|
|
|
if (
|
2025-10-05 19:34:05 +02:00
|
|
|
selection.value.size === 1 &&
|
|
|
|
|
selection.value
|
2025-02-02 11:17:22 +01:00
|
|
|
.getSelected()
|
|
|
|
|
.every((item) => item instanceof ListFileItem || item instanceof ListTrackItem)
|
|
|
|
|
) {
|
2025-06-21 21:07:36 +02:00
|
|
|
editMetadata.current = true;
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'p' && (e.metaKey || e.ctrlKey)) {
|
2025-10-05 19:34:05 +02:00
|
|
|
elevationProfile.value = !elevationProfile.value;
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'l' && (e.metaKey || e.ctrlKey)) {
|
2025-10-05 19:34:05 +02:00
|
|
|
treeFileView.value = !treeFileView.value;
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'h' && (e.metaKey || e.ctrlKey)) {
|
2025-10-05 19:34:05 +02:00
|
|
|
// if ($allHidden) {
|
|
|
|
|
// fileActions.setHiddenToSelection(false);
|
|
|
|
|
// } else {
|
|
|
|
|
// fileActions.setHiddenToSelection(true);
|
|
|
|
|
// }
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
|
2025-10-05 19:34:05 +02:00
|
|
|
// if ($selection.size > 0) {
|
|
|
|
|
// centerMapOnSelection();
|
|
|
|
|
// }
|
2025-02-02 11:17:22 +01:00
|
|
|
} else if (e.key === 'F1') {
|
|
|
|
|
switchBasemaps();
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'F2') {
|
|
|
|
|
toggleOverlays();
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'F3') {
|
2025-10-05 19:34:05 +02:00
|
|
|
distanceMarkers.value = !distanceMarkers.value;
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'F4') {
|
2025-10-05 19:34:05 +02:00
|
|
|
directionMarkers.value = !directionMarkers.value;
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (e.key === 'F5') {
|
2025-10-05 19:34:05 +02:00
|
|
|
routing.value = !routing.value;
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
} else if (
|
|
|
|
|
e.key === 'ArrowRight' ||
|
|
|
|
|
e.key === 'ArrowDown' ||
|
|
|
|
|
e.key === 'ArrowLeft' ||
|
|
|
|
|
e.key === 'ArrowUp'
|
|
|
|
|
) {
|
|
|
|
|
if (!targetInput) {
|
2025-10-05 19:34:05 +02:00
|
|
|
// updateSelectionFromKey(e.key === 'ArrowRight' || e.key === 'ArrowDown', e.shiftKey);
|
2025-02-02 11:17:22 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
on:dragover={(e) => e.preventDefault()}
|
|
|
|
|
on:drop={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
if (e.dataTransfer.files.length > 0) {
|
|
|
|
|
loadFiles(e.dataTransfer.files);
|
|
|
|
|
}
|
|
|
|
|
}}
|
2024-04-18 15:30:19 +02:00
|
|
|
/>
|
|
|
|
|
|
2024-04-09 18:46:01 +02:00
|
|
|
<style lang="postcss">
|
2025-06-21 21:07:36 +02:00
|
|
|
@reference "../../app.css";
|
|
|
|
|
|
2025-02-02 11:17:22 +01:00
|
|
|
div :global(button) {
|
|
|
|
|
@apply hover:bg-accent;
|
|
|
|
|
@apply px-3;
|
|
|
|
|
@apply py-0.5;
|
|
|
|
|
}
|
2024-04-09 18:46:01 +02:00
|
|
|
</style>
|