mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-01 08:12:32 +00:00
wrap file collection in single object
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import GPX from './GPX.svelte';
|
||||
|
||||
import { files } from '$lib/stores';
|
||||
import { fileCollection } from '$lib/stores';
|
||||
</script>
|
||||
|
||||
{#each $files as file}
|
||||
{#each $fileCollection.files as file}
|
||||
<GPX {file} />
|
||||
{/each}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
import Chart from 'chart.js/auto';
|
||||
import mapboxgl from 'mapbox-gl';
|
||||
|
||||
import { map, files, fileOrder, selectedFiles } from '$lib/stores';
|
||||
import { map, fileCollection, fileOrder, selectedFiles } from '$lib/stores';
|
||||
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import {
|
||||
@@ -75,10 +75,10 @@
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
title: function (context) {
|
||||
title: function () {
|
||||
return '';
|
||||
},
|
||||
label: function (context) {
|
||||
label: function (context: Chart.TooltipContext) {
|
||||
let point = context.raw;
|
||||
if (context.datasetIndex === 0) {
|
||||
let elevation = point.y.toFixed(0);
|
||||
@@ -104,7 +104,7 @@
|
||||
return `Power: ${power} W`;
|
||||
}
|
||||
},
|
||||
afterBody: function (contexts) {
|
||||
afterBody: function (contexts: Chart.TooltipContext[]) {
|
||||
let context = contexts.filter((context) => context.datasetIndex === 0);
|
||||
if (context.length === 0) return;
|
||||
let point = context[0].raw;
|
||||
@@ -124,7 +124,13 @@
|
||||
stacked: false
|
||||
};
|
||||
|
||||
let datasets = {
|
||||
let datasets: {
|
||||
[key: string]: {
|
||||
id: string;
|
||||
label: string;
|
||||
units: string;
|
||||
};
|
||||
} = {
|
||||
speed: {
|
||||
id: 'speed',
|
||||
label: 'Speed',
|
||||
@@ -179,7 +185,7 @@
|
||||
{
|
||||
id: 'toggleMarker',
|
||||
events: ['mouseout'],
|
||||
afterEvent: function (chart, args) {
|
||||
afterEvent: function (chart: Chart, args: { event: Chart.ChartEvent }) {
|
||||
if (args.event.type === 'mouseout') {
|
||||
if ($map && marker) {
|
||||
marker.remove();
|
||||
@@ -194,7 +200,7 @@
|
||||
|
||||
$: if (chart) {
|
||||
let gpxFiles = new GPXFiles(Array.from($selectedFiles));
|
||||
let order = $fileOrder.length == 0 ? $files : $fileOrder;
|
||||
let order = $fileOrder.length == 0 ? $fileCollection.files : $fileOrder;
|
||||
gpxFiles.files.sort(function (a, b) {
|
||||
return order.indexOf(a) - order.indexOf(b);
|
||||
});
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { fileOrder, files, selectedFiles, selectFiles } from '$lib/stores';
|
||||
import { fileOrder, fileCollection, selectedFiles, selectFiles } from '$lib/stores';
|
||||
|
||||
import { ScrollArea } from '$lib/components/ui/scroll-area/index';
|
||||
import Sortable from 'sortablejs/Sortable';
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
function selectAllFiles() {
|
||||
selectedFiles.update((selectedFiles) => {
|
||||
get(files).forEach((file) => {
|
||||
get(fileCollection).files.forEach((file) => {
|
||||
selectedFiles.add(file);
|
||||
});
|
||||
return selectedFiles;
|
||||
@@ -53,12 +53,12 @@
|
||||
avoidImplicitDeselect: true,
|
||||
onSelect: (e) => {
|
||||
const index = parseInt(e.item.getAttribute('data-id'));
|
||||
addSelectFile($files[index]);
|
||||
addSelectFile($fileCollection.files[index]);
|
||||
if (!e.originalEvent.shiftKey && $selectedFiles.size > 1) {
|
||||
$selectedFiles.forEach((file) => {
|
||||
if (file !== $files[index]) {
|
||||
if (file !== $fileCollection.files[index]) {
|
||||
deselectFile(file);
|
||||
const index = $files.indexOf(file);
|
||||
const index = $fileCollection.files.indexOf(file);
|
||||
Sortable.utils.deselect(buttons[index]);
|
||||
}
|
||||
});
|
||||
@@ -66,10 +66,12 @@
|
||||
},
|
||||
onDeselect: (e) => {
|
||||
const index = parseInt(e.item.getAttribute('data-id'));
|
||||
deselectFile($files[index]);
|
||||
deselectFile($fileCollection.files[index]);
|
||||
},
|
||||
onSort: () => {
|
||||
$fileOrder = sortable.toArray().map((index) => $files[parseInt(index)]);
|
||||
$fileOrder = sortable
|
||||
.toArray()
|
||||
.map((index: string) => $fileCollection.files[parseInt(index)]);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -82,23 +84,23 @@
|
||||
Sortable.utils.deselect(button);
|
||||
}
|
||||
});
|
||||
const index = $files.indexOf(file);
|
||||
const index = $fileCollection.files.indexOf(file);
|
||||
Sortable.utils.select(buttons[index]);
|
||||
selectFile(file);
|
||||
},
|
||||
addSelect: (file: GPXFile) => {
|
||||
const index = $files.indexOf(file);
|
||||
const index = $fileCollection.files.indexOf(file);
|
||||
Sortable.utils.select(buttons[index]);
|
||||
addSelectFile(file);
|
||||
},
|
||||
selectAllFiles: () => {
|
||||
$files.forEach((file, index) => {
|
||||
$fileCollection.files.forEach((file, index) => {
|
||||
Sortable.utils.select(buttons[index]);
|
||||
});
|
||||
selectAllFiles();
|
||||
},
|
||||
removeSelect: (file: GPXFile) => {
|
||||
const index = $files.indexOf(file);
|
||||
const index = $fileCollection.files.indexOf(file);
|
||||
Sortable.utils.deselect(buttons[index]);
|
||||
deselectFile(file);
|
||||
}
|
||||
@@ -109,7 +111,7 @@
|
||||
<div class="absolute h-10 -translate-y-10 w-fit max-w-full bg-secondary rounded-t">
|
||||
<ScrollArea orientation="horizontal" class="w-full h-full" scrollbarXClasses="h-2">
|
||||
<div bind:this={tabs} class="flex flex-row gap-1">
|
||||
{#each $files as file, index}
|
||||
{#each $fileCollection.files as file, index}
|
||||
<button
|
||||
bind:this={buttons[index]}
|
||||
data-id={index}
|
||||
|
@@ -41,7 +41,7 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { GPXFile } from 'gpx';
|
||||
import { map, selectedFiles, selectFiles, files } from '$lib/stores';
|
||||
import { map, selectedFiles, selectFiles, fileCollection } from '$lib/stores';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export let file: GPXFile;
|
||||
@@ -127,7 +127,7 @@
|
||||
onMount(() => {
|
||||
addGPXLayer();
|
||||
if ($map) {
|
||||
if ($files.length == 1) {
|
||||
if ($fileCollection.files.length == 1) {
|
||||
$map.fitBounds([file.statistics.bounds.southWest, file.statistics.bounds.northEast], {
|
||||
padding: 60,
|
||||
linear: true,
|
||||
|
@@ -15,7 +15,7 @@
|
||||
} from 'lucide-svelte';
|
||||
|
||||
import {
|
||||
files,
|
||||
fileCollection,
|
||||
selectedFiles,
|
||||
duplicateSelectedFiles,
|
||||
exportAllFiles,
|
||||
@@ -62,7 +62,7 @@
|
||||
<Menubar.Item on:click={exportSelectedFiles} disabled={$selectedFiles.size == 0}>
|
||||
<Download size="16" class="mr-1" /> Export... <Menubar.Shortcut>⌘S</Menubar.Shortcut>
|
||||
</Menubar.Item>
|
||||
<Menubar.Item on:click={exportAllFiles} disabled={$files.length == 0}>
|
||||
<Menubar.Item on:click={exportAllFiles} disabled={$fileCollection.files.length == 0}>
|
||||
<Download size="16" class="mr-1" /> Export all... <Menubar.Shortcut
|
||||
>⇧⌘S</Menubar.Shortcut
|
||||
>
|
||||
@@ -86,7 +86,7 @@
|
||||
<Menubar.Item
|
||||
class="text-destructive data-[highlighted]:text-destructive"
|
||||
on:click={removeAllFiles}
|
||||
disabled={$files.length == 0}
|
||||
disabled={$fileCollection.files.length == 0}
|
||||
>
|
||||
<Trash2 size="16" class="mr-1" /> Delete all<Menubar.Shortcut>⇧⌘⌫</Menubar.Shortcut
|
||||
></Menubar.Item
|
||||
|
@@ -1,14 +1,21 @@
|
||||
import { writable, get } from 'svelte/store';
|
||||
|
||||
import mapboxgl from 'mapbox-gl';
|
||||
import { GPXFile, buildGPX, parseGPX } from 'gpx';
|
||||
import { GPXFile, GPXFiles, buildGPX, parseGPX } from 'gpx';
|
||||
|
||||
export const map = writable<mapboxgl.Map | null>(null);
|
||||
export const files = writable<GPXFile[]>([]);
|
||||
export const fileCollection = writable<GPXFiles>(new GPXFiles([]));
|
||||
export const fileOrder = writable<GPXFile[]>([]);
|
||||
export const selectedFiles = writable<Set<GPXFile>>(new Set());
|
||||
export const selectFiles = writable<{ [key: string]: (file?: GPXFile) => void }>({});
|
||||
|
||||
export function addFile(file: GPXFile) {
|
||||
fileCollection.update($files => {
|
||||
$files.files.push(file);
|
||||
return $files;
|
||||
});
|
||||
}
|
||||
|
||||
export function triggerFileInput() {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
@@ -25,15 +32,15 @@ export function triggerFileInput() {
|
||||
|
||||
export async function loadFiles(list: FileList) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
await loadFile(list[i]);
|
||||
if (i == 0) {
|
||||
get(selectFiles).select(get(files)[get(files).length - 1]);
|
||||
let file = await loadFile(list[i]);
|
||||
if (i == 0 && file) {
|
||||
get(selectFiles).select(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadFile(file: File) {
|
||||
let result = await new Promise<void>((resolve) => {
|
||||
let result = await new Promise<GPXFile | null>((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
let data = reader.result?.toString() ?? null;
|
||||
@@ -42,9 +49,11 @@ export async function loadFile(file: File) {
|
||||
if (gpx.metadata.name === undefined) {
|
||||
gpx.metadata['name'] = file.name.split('.').slice(0, -1).join('.');
|
||||
}
|
||||
files.update($files => [...$files, gpx]);
|
||||
addFile(gpx);
|
||||
resolve(gpx);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
@@ -59,15 +68,15 @@ export function duplicateSelectedFiles() {
|
||||
|
||||
export function duplicateFile(file: GPXFile) {
|
||||
let clone = file.clone();
|
||||
files.update($files => [...$files, clone]);
|
||||
addFile(clone);
|
||||
}
|
||||
|
||||
export function removeSelectedFiles() {
|
||||
let index = 0;
|
||||
while (index < get(files).length) {
|
||||
if (get(selectedFiles).has(get(files)[index])) {
|
||||
files.update($files => {
|
||||
$files.splice(index, 1);
|
||||
while (index < get(fileCollection).files.length) {
|
||||
if (get(selectedFiles).has(get(fileCollection).files[index])) {
|
||||
fileCollection.update($files => {
|
||||
$files.files.splice(index, 1);
|
||||
return $files;
|
||||
});
|
||||
} else {
|
||||
@@ -78,8 +87,8 @@ export function removeSelectedFiles() {
|
||||
}
|
||||
|
||||
export function removeAllFiles() {
|
||||
files.update($files => {
|
||||
$files.splice(0, $files.length);
|
||||
fileCollection.update($files => {
|
||||
$files.files.splice(0, $files.files.length);
|
||||
return $files;
|
||||
});
|
||||
get(selectedFiles).clear();
|
||||
@@ -90,8 +99,8 @@ export function exportSelectedFiles() {
|
||||
}
|
||||
|
||||
export async function exportAllFiles() {
|
||||
for (let i = 0; i < get(files).length; i++) {
|
||||
exportFile(get(files)[i]);
|
||||
for (let file of get(fileCollection).files) {
|
||||
exportFile(file);
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user