mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 00:32:33 +00:00
export dialog progress
This commit is contained in:
8
website/package-lock.json
generated
8
website/package-lock.json
generated
@@ -10,7 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@internationalized/date": "^3.5.4",
|
"@internationalized/date": "^3.5.4",
|
||||||
"@mapbox/mapbox-gl-geocoder": "^5.0.2",
|
"@mapbox/mapbox-gl-geocoder": "^5.0.2",
|
||||||
"bits-ui": "^0.21.10",
|
"bits-ui": "^0.21.11",
|
||||||
"chart.js": "^4.4.3",
|
"chart.js": "^4.4.3",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dexie": "^4.0.7",
|
"dexie": "^4.0.7",
|
||||||
@@ -1911,9 +1911,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bits-ui": {
|
"node_modules/bits-ui": {
|
||||||
"version": "0.21.10",
|
"version": "0.21.11",
|
||||||
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.10.tgz",
|
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.11.tgz",
|
||||||
"integrity": "sha512-KuweEOKO0Rr8XX87dQh46G9mG0bZSmTqNxj5qBazz4OTQC+oPKui04/wP/ISsCOSGFomaRydTULqh4p+nsyc2g==",
|
"integrity": "sha512-pFS/9z1qLaPZwb+9Tm0YS4iBp+ClsJBARMZWFOjv0lGCYpzAN7lx4eNk3SbSB5QMBUKwoVjr9Rai71ROq3RD1Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@internationalized/date": "^3.5.1",
|
"@internationalized/date": "^3.5.1",
|
||||||
"@melt-ui/svelte": "0.76.2",
|
"@melt-ui/svelte": "0.76.2",
|
||||||
|
@@ -45,7 +45,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@internationalized/date": "^3.5.4",
|
"@internationalized/date": "^3.5.4",
|
||||||
"@mapbox/mapbox-gl-geocoder": "^5.0.2",
|
"@mapbox/mapbox-gl-geocoder": "^5.0.2",
|
||||||
"bits-ui": "^0.21.10",
|
"bits-ui": "^0.21.11",
|
||||||
"chart.js": "^4.4.3",
|
"chart.js": "^4.4.3",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dexie": "^4.0.7",
|
"dexie": "^4.0.7",
|
||||||
|
77
website/src/lib/components/Export.svelte
Normal file
77
website/src/lib/components/Export.svelte
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
import { Dialog } from 'bits-ui';
|
||||||
|
import {
|
||||||
|
currentTool,
|
||||||
|
exportAllFiles,
|
||||||
|
exportSelectedFiles,
|
||||||
|
ExportState,
|
||||||
|
exportState
|
||||||
|
} from '$lib/stores';
|
||||||
|
import { fileObservers } from '$lib/db';
|
||||||
|
import { Cloud, Download } from 'lucide-svelte';
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
import { selection } from './file-list/Selection';
|
||||||
|
|
||||||
|
let open = false;
|
||||||
|
|
||||||
|
$: if ($exportState !== ExportState.NONE) {
|
||||||
|
open = true;
|
||||||
|
$currentTool = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog.Root
|
||||||
|
bind:open
|
||||||
|
onOpenChange={(isOpen) => {
|
||||||
|
if (!isOpen) {
|
||||||
|
$exportState = ExportState.NONE;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Dialog.Trigger class="hidden" />
|
||||||
|
<Dialog.Portal>
|
||||||
|
<Dialog.Content
|
||||||
|
class="fixed left-[50%] top-[50%] z-50 w-fit max-w-full translate-x-[-50%] translate-y-[-50%] flex flex-col items-center gap-2 border bg-background p-3 shadow-lg rounded-md"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center gap-2 rounded border p-2">
|
||||||
|
<div class="flex flex-row items-center gap-2 text-sm">
|
||||||
|
<span>⚠️</span>
|
||||||
|
<span class="text-center max-w-96">
|
||||||
|
{$_('menu.support_message')}
|
||||||
|
</span>
|
||||||
|
<span>⚠️</span>
|
||||||
|
</div>
|
||||||
|
<Button class="bg-support w-fit" href="https://ko-fi.com/gpxstudio">
|
||||||
|
{$_('menu.support_button')}
|
||||||
|
<span class="ml-2">🙏</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2 justify-center">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
on:click={() => {
|
||||||
|
if ($exportState === ExportState.SELECTION) {
|
||||||
|
exportSelectedFiles();
|
||||||
|
} else if ($exportState === ExportState.ALL) {
|
||||||
|
exportAllFiles();
|
||||||
|
}
|
||||||
|
open = false;
|
||||||
|
$exportState = ExportState.NONE;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Download size="16" class="mr-1" />
|
||||||
|
{#if $fileObservers.size === 1 || ($exportState === ExportState.SELECTION && $selection.size === 1)}
|
||||||
|
{$_('menu.download_file')}
|
||||||
|
{:else}
|
||||||
|
{$_('menu.download_files')}
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline">
|
||||||
|
<Cloud size="16" class="mr-1" />
|
||||||
|
{$_('menu.save_drive')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Portal>
|
||||||
|
</Dialog.Root>
|
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as Menubar from '$lib/components/ui/menubar/index.js';
|
import * as Menubar from '$lib/components/ui/menubar/index.js';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import Logo from './Logo.svelte';
|
import Logo from '$lib/components/Logo.svelte';
|
||||||
import Shortcut from './Shortcut.svelte';
|
import Shortcut from '$lib/components/Shortcut.svelte';
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
Copy,
|
Copy,
|
||||||
@@ -44,8 +44,6 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
map,
|
map,
|
||||||
exportAllFiles,
|
|
||||||
exportSelectedFiles,
|
|
||||||
triggerFileInput,
|
triggerFileInput,
|
||||||
createFile,
|
createFile,
|
||||||
loadFiles,
|
loadFiles,
|
||||||
@@ -55,7 +53,9 @@
|
|||||||
hideSelection,
|
hideSelection,
|
||||||
anyHidden,
|
anyHidden,
|
||||||
editMetadata,
|
editMetadata,
|
||||||
editStyle
|
editStyle,
|
||||||
|
exportState,
|
||||||
|
ExportState
|
||||||
} from '$lib/stores';
|
} from '$lib/stores';
|
||||||
import {
|
import {
|
||||||
copied,
|
copied,
|
||||||
@@ -70,6 +70,8 @@
|
|||||||
import { anySelectedLayer } from '$lib/components/layer-control/utils';
|
import { anySelectedLayer } from '$lib/components/layer-control/utils';
|
||||||
import { defaultOverlays } from '$lib/assets/layers';
|
import { defaultOverlays } from '$lib/assets/layers';
|
||||||
import LayerControlSettings from '$lib/components/layer-control/LayerControlSettings.svelte';
|
import LayerControlSettings from '$lib/components/layer-control/LayerControlSettings.svelte';
|
||||||
|
import { allowedPastes, ListFileItem, ListTrackItem } from '$lib/components/file-list/FileList';
|
||||||
|
import Export from '$lib/components/Export.svelte';
|
||||||
|
|
||||||
import { resetMode, setMode, systemPrefersMode } from 'mode-watcher';
|
import { resetMode, setMode, systemPrefersMode } from 'mode-watcher';
|
||||||
|
|
||||||
@@ -77,7 +79,6 @@
|
|||||||
import { languages } from '$lib/languages';
|
import { languages } from '$lib/languages';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { base } from '$app/paths';
|
import { base } from '$app/paths';
|
||||||
import { allowedPastes, ListFileItem, ListTrackItem } from './file-list/FileList';
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
distanceUnits,
|
distanceUnits,
|
||||||
@@ -164,12 +165,18 @@
|
|||||||
<Shortcut key="D" ctrl={true} />
|
<Shortcut key="D" ctrl={true} />
|
||||||
</Menubar.Item>
|
</Menubar.Item>
|
||||||
<Menubar.Separator />
|
<Menubar.Separator />
|
||||||
<Menubar.Item on:click={exportSelectedFiles} disabled={$selection.size == 0}>
|
<Menubar.Item
|
||||||
|
on:click={() => ($exportState = ExportState.SELECTION)}
|
||||||
|
disabled={$selection.size == 0}
|
||||||
|
>
|
||||||
<Download size="16" class="mr-1" />
|
<Download size="16" class="mr-1" />
|
||||||
{$_('menu.export')}
|
{$_('menu.export')}
|
||||||
<Shortcut key="S" ctrl={true} />
|
<Shortcut key="S" ctrl={true} />
|
||||||
</Menubar.Item>
|
</Menubar.Item>
|
||||||
<Menubar.Item on:click={exportAllFiles} disabled={$fileObservers.size == 0}>
|
<Menubar.Item
|
||||||
|
on:click={() => ($exportState = ExportState.ALL)}
|
||||||
|
disabled={$fileObservers.size == 0}
|
||||||
|
>
|
||||||
<Download size="16" class="mr-1" />
|
<Download size="16" class="mr-1" />
|
||||||
{$_('menu.export_all')}
|
{$_('menu.export_all')}
|
||||||
<Shortcut key="S" ctrl={true} shift={true} />
|
<Shortcut key="S" ctrl={true} shift={true} />
|
||||||
@@ -452,6 +459,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Export />
|
||||||
<LayerControlSettings bind:open={layerSettingsOpen} />
|
<LayerControlSettings bind:open={layerSettingsOpen} />
|
||||||
|
|
||||||
<svelte:window
|
<svelte:window
|
||||||
@@ -478,9 +486,11 @@
|
|||||||
}
|
}
|
||||||
} else if ((e.key === 's' || e.key == 'S') && (e.metaKey || e.ctrlKey)) {
|
} else if ((e.key === 's' || e.key == 'S') && (e.metaKey || e.ctrlKey)) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
exportAllFiles();
|
if ($fileObservers.size > 0) {
|
||||||
} else {
|
$exportState = ExportState.ALL;
|
||||||
exportSelectedFiles();
|
}
|
||||||
|
} else if ($selection.size > 0) {
|
||||||
|
$exportState = ExportState.SELECTION;
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
} else if ((e.key === 'z' || e.key == 'Z') && (e.metaKey || e.ctrlKey)) {
|
} else if ((e.key === 'z' || e.key == 'Z') && (e.metaKey || e.ctrlKey)) {
|
||||||
|
@@ -312,26 +312,26 @@ export function updateSelectionFromKey(down: boolean, shift: boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportSelectedFiles() {
|
async function exportFiles(fileIds: string[]) {
|
||||||
get(selection).forEach(async (item) => {
|
for (let fileId of fileIds) {
|
||||||
if (item instanceof ListFileItem) {
|
let file = getFile(fileId);
|
||||||
let file = getFile(item.getFileId());
|
|
||||||
if (file) {
|
if (file) {
|
||||||
exportFile(file);
|
exportFile(file);
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
await new Promise(resolve => setTimeout(resolve, 200));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportSelectedFiles() {
|
||||||
|
let fileIds: string[] = [];
|
||||||
|
applyToOrderedSelectedItemsFromFile(async (fileId, level, items) => {
|
||||||
|
fileIds.push(fileId);
|
||||||
});
|
});
|
||||||
|
exportFiles(fileIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportAllFiles() {
|
export function exportAllFiles() {
|
||||||
get(fileObservers).forEach(async (file) => {
|
exportFiles(get(fileOrder));
|
||||||
let f = get(file);
|
|
||||||
if (f) {
|
|
||||||
exportFile(f.file);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportFile(file: GPXFile) {
|
export function exportFile(file: GPXFile) {
|
||||||
@@ -398,6 +398,13 @@ export function showSelection() {
|
|||||||
export const editMetadata = writable(false);
|
export const editMetadata = writable(false);
|
||||||
export const editStyle = writable(false);
|
export const editStyle = writable(false);
|
||||||
|
|
||||||
|
export enum ExportState {
|
||||||
|
NONE,
|
||||||
|
SELECTION,
|
||||||
|
ALL
|
||||||
|
}
|
||||||
|
export const exportState = writable<ExportState>(ExportState.NONE);
|
||||||
|
|
||||||
let stravaCookies: any = null;
|
let stravaCookies: any = null;
|
||||||
function refreshStravaCookies() {
|
function refreshStravaCookies() {
|
||||||
/*
|
/*
|
||||||
|
@@ -17,6 +17,11 @@
|
|||||||
"cut": "Cut",
|
"cut": "Cut",
|
||||||
"export": "Export...",
|
"export": "Export...",
|
||||||
"export_all": "Export all...",
|
"export_all": "Export all...",
|
||||||
|
"support_message": "The tool is free to use, but not free to run. Please consider supporting the website if you use it frequently.",
|
||||||
|
"support_button": "Help keep the website free",
|
||||||
|
"download_file": "Download file",
|
||||||
|
"download_files": "Download files",
|
||||||
|
"save_drive": "Save to Google Drive",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"undo": "Undo",
|
"undo": "Undo",
|
||||||
"redo": "Redo",
|
"redo": "Redo",
|
||||||
|
Reference in New Issue
Block a user