wrap file collection in single object

This commit is contained in:
vcoppe
2024-04-22 17:22:21 +02:00
parent c61b559343
commit 8f6c1fc88d
6 changed files with 60 additions and 43 deletions

View File

@@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import GPX from './GPX.svelte'; import GPX from './GPX.svelte';
import { files } from '$lib/stores'; import { fileCollection } from '$lib/stores';
</script> </script>
{#each $files as file} {#each $fileCollection.files as file}
<GPX {file} /> <GPX {file} />
{/each} {/each}

View File

@@ -6,7 +6,7 @@
import Chart from 'chart.js/auto'; import Chart from 'chart.js/auto';
import mapboxgl from 'mapbox-gl'; 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 { onDestroy, onMount } from 'svelte';
import { import {
@@ -75,10 +75,10 @@
}, },
tooltip: { tooltip: {
callbacks: { callbacks: {
title: function (context) { title: function () {
return ''; return '';
}, },
label: function (context) { label: function (context: Chart.TooltipContext) {
let point = context.raw; let point = context.raw;
if (context.datasetIndex === 0) { if (context.datasetIndex === 0) {
let elevation = point.y.toFixed(0); let elevation = point.y.toFixed(0);
@@ -104,7 +104,7 @@
return `Power: ${power} W`; return `Power: ${power} W`;
} }
}, },
afterBody: function (contexts) { afterBody: function (contexts: Chart.TooltipContext[]) {
let context = contexts.filter((context) => context.datasetIndex === 0); let context = contexts.filter((context) => context.datasetIndex === 0);
if (context.length === 0) return; if (context.length === 0) return;
let point = context[0].raw; let point = context[0].raw;
@@ -124,7 +124,13 @@
stacked: false stacked: false
}; };
let datasets = { let datasets: {
[key: string]: {
id: string;
label: string;
units: string;
};
} = {
speed: { speed: {
id: 'speed', id: 'speed',
label: 'Speed', label: 'Speed',
@@ -179,7 +185,7 @@
{ {
id: 'toggleMarker', id: 'toggleMarker',
events: ['mouseout'], events: ['mouseout'],
afterEvent: function (chart, args) { afterEvent: function (chart: Chart, args: { event: Chart.ChartEvent }) {
if (args.event.type === 'mouseout') { if (args.event.type === 'mouseout') {
if ($map && marker) { if ($map && marker) {
marker.remove(); marker.remove();
@@ -194,7 +200,7 @@
$: if (chart) { $: if (chart) {
let gpxFiles = new GPXFiles(Array.from($selectedFiles)); 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) { gpxFiles.files.sort(function (a, b) {
return order.indexOf(a) - order.indexOf(b); return order.indexOf(a) - order.indexOf(b);
}); });

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <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 { ScrollArea } from '$lib/components/ui/scroll-area/index';
import Sortable from 'sortablejs/Sortable'; import Sortable from 'sortablejs/Sortable';
@@ -30,7 +30,7 @@
function selectAllFiles() { function selectAllFiles() {
selectedFiles.update((selectedFiles) => { selectedFiles.update((selectedFiles) => {
get(files).forEach((file) => { get(fileCollection).files.forEach((file) => {
selectedFiles.add(file); selectedFiles.add(file);
}); });
return selectedFiles; return selectedFiles;
@@ -53,12 +53,12 @@
avoidImplicitDeselect: true, avoidImplicitDeselect: true,
onSelect: (e) => { onSelect: (e) => {
const index = parseInt(e.item.getAttribute('data-id')); const index = parseInt(e.item.getAttribute('data-id'));
addSelectFile($files[index]); addSelectFile($fileCollection.files[index]);
if (!e.originalEvent.shiftKey && $selectedFiles.size > 1) { if (!e.originalEvent.shiftKey && $selectedFiles.size > 1) {
$selectedFiles.forEach((file) => { $selectedFiles.forEach((file) => {
if (file !== $files[index]) { if (file !== $fileCollection.files[index]) {
deselectFile(file); deselectFile(file);
const index = $files.indexOf(file); const index = $fileCollection.files.indexOf(file);
Sortable.utils.deselect(buttons[index]); Sortable.utils.deselect(buttons[index]);
} }
}); });
@@ -66,10 +66,12 @@
}, },
onDeselect: (e) => { onDeselect: (e) => {
const index = parseInt(e.item.getAttribute('data-id')); const index = parseInt(e.item.getAttribute('data-id'));
deselectFile($files[index]); deselectFile($fileCollection.files[index]);
}, },
onSort: () => { 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); Sortable.utils.deselect(button);
} }
}); });
const index = $files.indexOf(file); const index = $fileCollection.files.indexOf(file);
Sortable.utils.select(buttons[index]); Sortable.utils.select(buttons[index]);
selectFile(file); selectFile(file);
}, },
addSelect: (file: GPXFile) => { addSelect: (file: GPXFile) => {
const index = $files.indexOf(file); const index = $fileCollection.files.indexOf(file);
Sortable.utils.select(buttons[index]); Sortable.utils.select(buttons[index]);
addSelectFile(file); addSelectFile(file);
}, },
selectAllFiles: () => { selectAllFiles: () => {
$files.forEach((file, index) => { $fileCollection.files.forEach((file, index) => {
Sortable.utils.select(buttons[index]); Sortable.utils.select(buttons[index]);
}); });
selectAllFiles(); selectAllFiles();
}, },
removeSelect: (file: GPXFile) => { removeSelect: (file: GPXFile) => {
const index = $files.indexOf(file); const index = $fileCollection.files.indexOf(file);
Sortable.utils.deselect(buttons[index]); Sortable.utils.deselect(buttons[index]);
deselectFile(file); deselectFile(file);
} }
@@ -109,7 +111,7 @@
<div class="absolute h-10 -translate-y-10 w-fit max-w-full bg-secondary rounded-t"> <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"> <ScrollArea orientation="horizontal" class="w-full h-full" scrollbarXClasses="h-2">
<div bind:this={tabs} class="flex flex-row gap-1"> <div bind:this={tabs} class="flex flex-row gap-1">
{#each $files as file, index} {#each $fileCollection.files as file, index}
<button <button
bind:this={buttons[index]} bind:this={buttons[index]}
data-id={index} data-id={index}

View File

@@ -41,7 +41,7 @@
<script lang="ts"> <script lang="ts">
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { GPXFile } from 'gpx'; 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'; import { get } from 'svelte/store';
export let file: GPXFile; export let file: GPXFile;
@@ -127,7 +127,7 @@
onMount(() => { onMount(() => {
addGPXLayer(); addGPXLayer();
if ($map) { if ($map) {
if ($files.length == 1) { if ($fileCollection.files.length == 1) {
$map.fitBounds([file.statistics.bounds.southWest, file.statistics.bounds.northEast], { $map.fitBounds([file.statistics.bounds.southWest, file.statistics.bounds.northEast], {
padding: 60, padding: 60,
linear: true, linear: true,

View File

@@ -15,7 +15,7 @@
} from 'lucide-svelte'; } from 'lucide-svelte';
import { import {
files, fileCollection,
selectedFiles, selectedFiles,
duplicateSelectedFiles, duplicateSelectedFiles,
exportAllFiles, exportAllFiles,
@@ -62,7 +62,7 @@
<Menubar.Item on:click={exportSelectedFiles} disabled={$selectedFiles.size == 0}> <Menubar.Item on:click={exportSelectedFiles} disabled={$selectedFiles.size == 0}>
<Download size="16" class="mr-1" /> Export... <Menubar.Shortcut>⌘S</Menubar.Shortcut> <Download size="16" class="mr-1" /> Export... <Menubar.Shortcut>⌘S</Menubar.Shortcut>
</Menubar.Item> </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 <Download size="16" class="mr-1" /> Export all... <Menubar.Shortcut
>⇧⌘S</Menubar.Shortcut >⇧⌘S</Menubar.Shortcut
> >
@@ -86,7 +86,7 @@
<Menubar.Item <Menubar.Item
class="text-destructive data-[highlighted]:text-destructive" class="text-destructive data-[highlighted]:text-destructive"
on:click={removeAllFiles} on:click={removeAllFiles}
disabled={$files.length == 0} disabled={$fileCollection.files.length == 0}
> >
<Trash2 size="16" class="mr-1" /> Delete all<Menubar.Shortcut>⇧⌘⌫</Menubar.Shortcut <Trash2 size="16" class="mr-1" /> Delete all<Menubar.Shortcut>⇧⌘⌫</Menubar.Shortcut
></Menubar.Item ></Menubar.Item

View File

@@ -1,14 +1,21 @@
import { writable, get } from 'svelte/store'; import { writable, get } from 'svelte/store';
import mapboxgl from 'mapbox-gl'; 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 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 fileOrder = writable<GPXFile[]>([]);
export const selectedFiles = writable<Set<GPXFile>>(new Set()); export const selectedFiles = writable<Set<GPXFile>>(new Set());
export const selectFiles = writable<{ [key: string]: (file?: GPXFile) => void }>({}); 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() { export function triggerFileInput() {
const input = document.createElement('input'); const input = document.createElement('input');
input.type = 'file'; input.type = 'file';
@@ -25,15 +32,15 @@ export function triggerFileInput() {
export async function loadFiles(list: FileList) { export async function loadFiles(list: FileList) {
for (let i = 0; i < list.length; i++) { for (let i = 0; i < list.length; i++) {
await loadFile(list[i]); let file = await loadFile(list[i]);
if (i == 0) { if (i == 0 && file) {
get(selectFiles).select(get(files)[get(files).length - 1]); get(selectFiles).select(file);
} }
} }
} }
export async function loadFile(file: 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(); const reader = new FileReader();
reader.onload = () => { reader.onload = () => {
let data = reader.result?.toString() ?? null; let data = reader.result?.toString() ?? null;
@@ -42,9 +49,11 @@ export async function loadFile(file: File) {
if (gpx.metadata.name === undefined) { if (gpx.metadata.name === undefined) {
gpx.metadata['name'] = file.name.split('.').slice(0, -1).join('.'); 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); reader.readAsText(file);
}); });
@@ -59,15 +68,15 @@ export function duplicateSelectedFiles() {
export function duplicateFile(file: GPXFile) { export function duplicateFile(file: GPXFile) {
let clone = file.clone(); let clone = file.clone();
files.update($files => [...$files, clone]); addFile(clone);
} }
export function removeSelectedFiles() { export function removeSelectedFiles() {
let index = 0; let index = 0;
while (index < get(files).length) { while (index < get(fileCollection).files.length) {
if (get(selectedFiles).has(get(files)[index])) { if (get(selectedFiles).has(get(fileCollection).files[index])) {
files.update($files => { fileCollection.update($files => {
$files.splice(index, 1); $files.files.splice(index, 1);
return $files; return $files;
}); });
} else { } else {
@@ -78,8 +87,8 @@ export function removeSelectedFiles() {
} }
export function removeAllFiles() { export function removeAllFiles() {
files.update($files => { fileCollection.update($files => {
$files.splice(0, $files.length); $files.files.splice(0, $files.files.length);
return $files; return $files;
}); });
get(selectedFiles).clear(); get(selectedFiles).clear();
@@ -90,8 +99,8 @@ export function exportSelectedFiles() {
} }
export async function exportAllFiles() { export async function exportAllFiles() {
for (let i = 0; i < get(files).length; i++) { for (let file of get(fileCollection).files) {
exportFile(get(files)[i]); exportFile(file);
await new Promise(resolve => setTimeout(resolve, 200)); await new Promise(resolve => setTimeout(resolve, 200));
} }
} }