mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-01-15 14:18:41 +00:00
Compare commits
5 Commits
graphhoppe
...
e7a1d0488b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7a1d0488b | ||
|
|
22b8e0edb4 | ||
|
|
d062a38e8f | ||
|
|
affa59130f | ||
|
|
3c816567bc |
@@ -1,6 +0,0 @@
|
|||||||
# Ignore files for PNPM, NPM and YARN
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
src/lib/components/ui
|
|
||||||
*.mdx
|
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"postinstall": "npm run build",
|
"postinstall": "npm run build",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "prettier --check . --config ../.prettierrc && eslint .",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write . --config ../.prettierrc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
website/.prettierignore
Normal file
3
website/.prettierignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
src/lib/components/ui
|
||||||
|
src/lib/docs/**/*.mdx
|
||||||
|
**/*.webmanifest
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||||
"style": "default",
|
"style": "default",
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"css": "src/app.css",
|
"css": "src/app.css",
|
||||||
"baseColor": "slate"
|
"baseColor": "slate"
|
||||||
},
|
},
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"components": "$lib/components",
|
"components": "$lib/components",
|
||||||
"utils": "$lib/utils",
|
"utils": "$lib/utils",
|
||||||
"ui": "$lib/components/ui",
|
"ui": "$lib/components/ui",
|
||||||
"hooks": "$lib/hooks",
|
"hooks": "$lib/hooks",
|
||||||
"lib": "$lib"
|
"lib": "$lib"
|
||||||
},
|
},
|
||||||
"typescript": true,
|
"typescript": true,
|
||||||
"registry": "https://shadcn-svelte.com/registry"
|
"registry": "https://shadcn-svelte.com/registry"
|
||||||
}
|
}
|
||||||
|
|||||||
27
website/package-lock.json
generated
27
website/package-lock.json
generated
@@ -14,7 +14,7 @@
|
|||||||
"@mapbox/sphericalmercator": "^2.0.1",
|
"@mapbox/sphericalmercator": "^2.0.1",
|
||||||
"@mapbox/tilebelt": "^2.0.2",
|
"@mapbox/tilebelt": "^2.0.2",
|
||||||
"@types/mapbox__sphericalmercator": "^1.2.3",
|
"@types/mapbox__sphericalmercator": "^1.2.3",
|
||||||
"chart.js": "^4.4.9",
|
"chart.js": "^4.5.1",
|
||||||
"chartjs-plugin-zoom": "^2.2.0",
|
"chartjs-plugin-zoom": "^2.2.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dexie": "^4.0.11",
|
"dexie": "^4.0.11",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"gpx": "file:../gpx",
|
"gpx": "file:../gpx",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"mapbox-gl": "^3.16.0",
|
"mapbox-gl": "^3.17.0",
|
||||||
"mapillary-js": "^4.1.2",
|
"mapillary-js": "^4.1.2",
|
||||||
"png.js": "^0.2.1",
|
"png.js": "^0.2.1",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.0",
|
||||||
@@ -3664,9 +3664,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/chart.js": {
|
"node_modules/chart.js": {
|
||||||
"version": "4.4.9",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||||
"integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==",
|
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kurkle/color": "^0.3.0"
|
"@kurkle/color": "^0.3.0"
|
||||||
@@ -6069,12 +6069,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mapbox-gl": {
|
"node_modules/mapbox-gl": {
|
||||||
"version": "3.16.0",
|
"version": "3.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.17.0.tgz",
|
||||||
"integrity": "sha512-rluV1Zp/0oHf1Y9BV+nePRNnKyTdljko3E19CzO5rBqtQaNUYS0ePCMPRtxOuWRwSdKp3f9NWJkOCjemM8nmjw==",
|
"integrity": "sha512-nCrDKRlr5di6xUksUDslNWwxroJ5yv1hT8pyVFtcpWJOOKsYQxF/wOFTMie8oxMnXeFkrz1Tl1TwA1XN1yX0KA==",
|
||||||
"license": "SEE LICENSE IN LICENSE.txt",
|
"license": "SEE LICENSE IN LICENSE.txt",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"src/style-spec",
|
"src/style-spec",
|
||||||
|
"test/build/vite",
|
||||||
|
"test/build/webpack",
|
||||||
"test/build/typings"
|
"test/build/typings"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6102,7 +6104,6 @@
|
|||||||
"pbf": "^4.0.1",
|
"pbf": "^4.0.1",
|
||||||
"potpack": "^2.0.0",
|
"potpack": "^2.0.0",
|
||||||
"quickselect": "^3.0.0",
|
"quickselect": "^3.0.0",
|
||||||
"serialize-to-js": "^3.1.2",
|
|
||||||
"supercluster": "^8.0.1",
|
"supercluster": "^8.0.1",
|
||||||
"tinyqueue": "^3.0.0"
|
"tinyqueue": "^3.0.0"
|
||||||
}
|
}
|
||||||
@@ -7634,14 +7635,6 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/serialize-to-js": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/set-cookie-parser": {
|
"node_modules/set-cookie-parser": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz",
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "prettier --check . --config ../.prettierrc && eslint .",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write . --config ../.prettierrc"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lucide/svelte": "^0.544.0",
|
"@lucide/svelte": "^0.544.0",
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
"@mapbox/sphericalmercator": "^2.0.1",
|
"@mapbox/sphericalmercator": "^2.0.1",
|
||||||
"@mapbox/tilebelt": "^2.0.2",
|
"@mapbox/tilebelt": "^2.0.2",
|
||||||
"@types/mapbox__sphericalmercator": "^1.2.3",
|
"@types/mapbox__sphericalmercator": "^1.2.3",
|
||||||
"chart.js": "^4.4.9",
|
"chart.js": "^4.5.1",
|
||||||
"chartjs-plugin-zoom": "^2.2.0",
|
"chartjs-plugin-zoom": "^2.2.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dexie": "^4.0.11",
|
"dexie": "^4.0.11",
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
"gpx": "file:../gpx",
|
"gpx": "file:../gpx",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"mapbox-gl": "^3.16.0",
|
"mapbox-gl": "^3.17.0",
|
||||||
"mapillary-js": "^4.1.2",
|
"mapillary-js": "^4.1.2",
|
||||||
"png.js": "^0.2.1",
|
"png.js": "^0.2.1",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.0",
|
||||||
|
|||||||
@@ -1,126 +1,126 @@
|
|||||||
@import "tailwindcss";
|
@import 'tailwindcss';
|
||||||
@import "tw-animate-css";
|
@import 'tw-animate-css';
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: hsl(0 0% 100%) /* <- Wrap in HSL */;
|
--background: hsl(0 0% 100%) /* <- Wrap in HSL */;
|
||||||
--foreground: hsl(240 10% 3.9%);
|
--foreground: hsl(240 10% 3.9%);
|
||||||
--muted: hsl(240 4.8% 95.9%);
|
--muted: hsl(240 4.8% 95.9%);
|
||||||
--muted-foreground: hsl(240 3.8% 46.1%);
|
--muted-foreground: hsl(240 3.8% 46.1%);
|
||||||
--popover: hsl(0 0% 100%);
|
--popover: hsl(0 0% 100%);
|
||||||
--popover-foreground: hsl(240 10% 3.9%);
|
--popover-foreground: hsl(240 10% 3.9%);
|
||||||
--card: hsl(0 0% 100%);
|
--card: hsl(0 0% 100%);
|
||||||
--card-foreground: hsl(240 10% 3.9%);
|
--card-foreground: hsl(240 10% 3.9%);
|
||||||
--border: hsl(240 5.9% 90%);
|
--border: hsl(240 5.9% 90%);
|
||||||
--input: hsl(240 5.9% 90%);
|
--input: hsl(240 5.9% 90%);
|
||||||
--primary: hsl(240 5.9% 10%);
|
--primary: hsl(240 5.9% 10%);
|
||||||
--primary-foreground: hsl(0 0% 98%);
|
--primary-foreground: hsl(0 0% 98%);
|
||||||
--secondary: hsl(240 4.8% 95.9%);
|
--secondary: hsl(240 4.8% 95.9%);
|
||||||
--secondary-foreground: hsl(240 5.9% 10%);
|
--secondary-foreground: hsl(240 5.9% 10%);
|
||||||
--accent: hsl(240 4.8% 95.9%);
|
--accent: hsl(240 4.8% 95.9%);
|
||||||
--accent-foreground: hsl(240 5.9% 10%);
|
--accent-foreground: hsl(240 5.9% 10%);
|
||||||
--destructive: hsl(0 72.2% 50.6%);
|
--destructive: hsl(0 72.2% 50.6%);
|
||||||
--destructive-foreground: hsl(0 0% 98%);
|
--destructive-foreground: hsl(0 0% 98%);
|
||||||
--ring: hsl(240 10% 3.9%);
|
--ring: hsl(240 10% 3.9%);
|
||||||
--sidebar: hsl(0 0% 98%);
|
--sidebar: hsl(0 0% 98%);
|
||||||
--sidebar-foreground: hsl(240 5.3% 26.1%);
|
--sidebar-foreground: hsl(240 5.3% 26.1%);
|
||||||
--sidebar-primary: hsl(240 5.9% 10%);
|
--sidebar-primary: hsl(240 5.9% 10%);
|
||||||
--sidebar-primary-foreground: hsl(0 0% 98%);
|
--sidebar-primary-foreground: hsl(0 0% 98%);
|
||||||
--sidebar-accent: hsl(240 4.8% 95.9%);
|
--sidebar-accent: hsl(240 4.8% 95.9%);
|
||||||
--sidebar-accent-foreground: hsl(240 5.9% 10%);
|
--sidebar-accent-foreground: hsl(240 5.9% 10%);
|
||||||
--sidebar-border: hsl(220 13% 91%);
|
--sidebar-border: hsl(220 13% 91%);
|
||||||
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
||||||
|
|
||||||
--support: rgb(220 15 130);
|
--support: rgb(220 15 130);
|
||||||
--link: rgb(0 110 180);
|
--link: rgb(0 110 180);
|
||||||
--selection: hsl(240 4.8% 93%);
|
--selection: hsl(240 4.8% 93%);
|
||||||
|
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: hsl(240 10% 3.9%);
|
--background: hsl(240 10% 3.9%);
|
||||||
--foreground: hsl(0 0% 98%);
|
--foreground: hsl(0 0% 98%);
|
||||||
--muted: hsl(240 3.7% 15.9%);
|
--muted: hsl(240 3.7% 15.9%);
|
||||||
--muted-foreground: hsl(240 5% 64.9%);
|
--muted-foreground: hsl(240 5% 64.9%);
|
||||||
--popover: hsl(240 10% 3.9%);
|
--popover: hsl(240 10% 3.9%);
|
||||||
--popover-foreground: hsl(0 0% 98%);
|
--popover-foreground: hsl(0 0% 98%);
|
||||||
--card: hsl(240 10% 3.9%);
|
--card: hsl(240 10% 3.9%);
|
||||||
--card-foreground: hsl(0 0% 98%);
|
--card-foreground: hsl(0 0% 98%);
|
||||||
--border: hsl(240 3.7% 15.9%);
|
--border: hsl(240 3.7% 15.9%);
|
||||||
--input: hsl(240 3.7% 15.9%);
|
--input: hsl(240 3.7% 15.9%);
|
||||||
--primary: hsl(0 0% 98%);
|
--primary: hsl(0 0% 98%);
|
||||||
--primary-foreground: hsl(240 5.9% 10%);
|
--primary-foreground: hsl(240 5.9% 10%);
|
||||||
--secondary: hsl(240 3.7% 15.9%);
|
--secondary: hsl(240 3.7% 15.9%);
|
||||||
--secondary-foreground: hsl(0 0% 98%);
|
--secondary-foreground: hsl(0 0% 98%);
|
||||||
--accent: hsl(240 3.7% 15.9%);
|
--accent: hsl(240 3.7% 15.9%);
|
||||||
--accent-foreground: hsl(0 0% 98%);
|
--accent-foreground: hsl(0 0% 98%);
|
||||||
--destructive: hsl(0 62.8% 30.6%);
|
--destructive: hsl(0 62.8% 30.6%);
|
||||||
--destructive-foreground: hsl(0 0% 98%);
|
--destructive-foreground: hsl(0 0% 98%);
|
||||||
--ring: hsl(240 4.9% 83.9%);
|
--ring: hsl(240 4.9% 83.9%);
|
||||||
--sidebar: hsl(240 5.9% 10%);
|
--sidebar: hsl(240 5.9% 10%);
|
||||||
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
||||||
--sidebar-primary: hsl(224.3 76.3% 48%);
|
--sidebar-primary: hsl(224.3 76.3% 48%);
|
||||||
--sidebar-primary-foreground: hsl(0 0% 100%);
|
--sidebar-primary-foreground: hsl(0 0% 100%);
|
||||||
--sidebar-accent: hsl(240 3.7% 15.9%);
|
--sidebar-accent: hsl(240 3.7% 15.9%);
|
||||||
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
||||||
--sidebar-border: hsl(240 3.7% 15.9%);
|
--sidebar-border: hsl(240 3.7% 15.9%);
|
||||||
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
||||||
|
|
||||||
--support: rgb(255 110 190);
|
--support: rgb(255 110 190);
|
||||||
--link: rgb(80 190 255);
|
--link: rgb(80 190 255);
|
||||||
--selection: hsl(240 3.7% 22%);
|
--selection: hsl(240 3.7% 22%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
/* Radius (for rounded-*) */
|
/* Radius (for rounded-*) */
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
|
||||||
/* Colors */
|
|
||||||
--color-background: var(--background);
|
|
||||||
--color-foreground: var(--foreground);
|
|
||||||
--color-muted: var(--muted);
|
|
||||||
--color-muted-foreground: var(--muted-foreground);
|
|
||||||
--color-popover: var(--popover);
|
|
||||||
--color-popover-foreground: var(--popover-foreground);
|
|
||||||
--color-card: var(--card);
|
|
||||||
--color-card-foreground: var(--card-foreground);
|
|
||||||
--color-border: var(--border);
|
|
||||||
--color-input: var(--input);
|
|
||||||
--color-primary: var(--primary);
|
|
||||||
--color-primary-foreground: var(--primary-foreground);
|
|
||||||
--color-secondary: var(--secondary);
|
|
||||||
--color-secondary-foreground: var(--secondary-foreground);
|
|
||||||
--color-accent: var(--accent);
|
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
|
||||||
--color-destructive: var(--destructive);
|
|
||||||
--color-destructive-foreground: var(--destructive-foreground);
|
|
||||||
--color-ring: var(--ring);
|
|
||||||
--color-radius: var(--radius);
|
|
||||||
--color-sidebar: var(--sidebar);
|
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
|
||||||
--color-support: var(--support);
|
|
||||||
--color-link: var(--link);
|
|
||||||
|
|
||||||
--breakpoint-xs: 540px;
|
/* Colors */
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-destructive-foreground: var(--destructive-foreground);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-radius: var(--radius);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
--color-support: var(--support);
|
||||||
|
--color-link: var(--link);
|
||||||
|
|
||||||
|
--breakpoint-xs: 540px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -538,6 +538,7 @@
|
|||||||
let targetInput =
|
let targetInput =
|
||||||
e &&
|
e &&
|
||||||
e.target &&
|
e.target &&
|
||||||
|
e.target instanceof HTMLElement &&
|
||||||
(e.target.tagName === 'INPUT' ||
|
(e.target.tagName === 'INPUT' ||
|
||||||
e.target.tagName === 'TEXTAREA' ||
|
e.target.tagName === 'TEXTAREA' ||
|
||||||
e.target.tagName === 'SELECT' ||
|
e.target.tagName === 'SELECT' ||
|
||||||
|
|||||||
@@ -14,7 +14,12 @@ import {
|
|||||||
getTemperatureWithUnits,
|
getTemperatureWithUnits,
|
||||||
getVelocityWithUnits,
|
getVelocityWithUnits,
|
||||||
} from '$lib/units';
|
} from '$lib/units';
|
||||||
import Chart from 'chart.js/auto';
|
import Chart, {
|
||||||
|
type ChartEvent,
|
||||||
|
type ChartOptions,
|
||||||
|
type ScriptableLineSegmentContext,
|
||||||
|
type TooltipItem,
|
||||||
|
} from 'chart.js/auto';
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl from 'mapbox-gl';
|
||||||
import { get, type Readable, type Writable } from 'svelte/store';
|
import { get, type Readable, type Writable } from 'svelte/store';
|
||||||
import { map } from '$lib/components/map/map';
|
import { map } from '$lib/components/map/map';
|
||||||
@@ -27,6 +32,20 @@ const { distanceUnits, velocityUnits, temperatureUnits } = settings;
|
|||||||
Chart.defaults.font.family =
|
Chart.defaults.font.family =
|
||||||
'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'; // Tailwind CSS font
|
'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'; // Tailwind CSS font
|
||||||
|
|
||||||
|
interface ElevationProfilePoint {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
time?: Date;
|
||||||
|
slope: {
|
||||||
|
at: number;
|
||||||
|
segment: number;
|
||||||
|
length: number;
|
||||||
|
};
|
||||||
|
extensions: Record<string, any>;
|
||||||
|
coordinates: [number, number];
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class ElevationProfile {
|
export class ElevationProfile {
|
||||||
private _chart: Chart | null = null;
|
private _chart: Chart | null = null;
|
||||||
private _canvas: HTMLCanvasElement;
|
private _canvas: HTMLCanvasElement;
|
||||||
@@ -90,7 +109,7 @@ export class ElevationProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
let options = {
|
let options: ChartOptions<'line'> = {
|
||||||
animation: false,
|
animation: false,
|
||||||
parsing: false,
|
parsing: false,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
@@ -98,8 +117,8 @@ export class ElevationProfile {
|
|||||||
x: {
|
x: {
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function (value: number) {
|
callback: function (value: number | string) {
|
||||||
return `${value.toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
|
return `${(value as number).toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
|
||||||
},
|
},
|
||||||
align: 'inner',
|
align: 'inner',
|
||||||
maxRotation: 0,
|
maxRotation: 0,
|
||||||
@@ -108,8 +127,8 @@ export class ElevationProfile {
|
|||||||
y: {
|
y: {
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function (value: number) {
|
callback: function (value: number | string) {
|
||||||
return getElevationWithUnits(value, false);
|
return getElevationWithUnits(value as number, false);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -140,8 +159,8 @@ export class ElevationProfile {
|
|||||||
title: () => {
|
title: () => {
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
label: (context: Chart.TooltipContext) => {
|
label: (context: TooltipItem<'line'>) => {
|
||||||
let point = context.raw;
|
let point = context.raw as ElevationProfilePoint;
|
||||||
if (context.datasetIndex === 0) {
|
if (context.datasetIndex === 0) {
|
||||||
const map_ = get(map);
|
const map_ = get(map);
|
||||||
if (map_ && this._marker) {
|
if (map_ && this._marker) {
|
||||||
@@ -165,10 +184,10 @@ export class ElevationProfile {
|
|||||||
return `${i18n._('quantities.power')}: ${getPowerWithUnits(point.y)}`;
|
return `${i18n._('quantities.power')}: ${getPowerWithUnits(point.y)}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
afterBody: (contexts: Chart.TooltipContext[]) => {
|
afterBody: (contexts: TooltipItem<'line'>[]) => {
|
||||||
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 as ElevationProfilePoint;
|
||||||
let slope = {
|
let slope = {
|
||||||
at: point.slope.at.toFixed(1),
|
at: point.slope.at.toFixed(1),
|
||||||
segment: point.slope.segment.toFixed(1),
|
segment: point.slope.segment.toFixed(1),
|
||||||
@@ -227,6 +246,7 @@ export class ElevationProfile {
|
|||||||
onPanStart: () => {
|
onPanStart: () => {
|
||||||
this._panning = true;
|
this._panning = true;
|
||||||
this._slicedGPXStatistics.set(undefined);
|
this._slicedGPXStatistics.set(undefined);
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
onPanComplete: () => {
|
onPanComplete: () => {
|
||||||
this._panning = false;
|
this._panning = false;
|
||||||
@@ -238,13 +258,13 @@ export class ElevationProfile {
|
|||||||
},
|
},
|
||||||
mode: 'x',
|
mode: 'x',
|
||||||
onZoomStart: ({ chart, event }: { chart: Chart; event: any }) => {
|
onZoomStart: ({ chart, event }: { chart: Chart; event: any }) => {
|
||||||
|
if (!this._chart) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const maxZoom = this._chart.getInitialScaleBounds()?.x?.max ?? 0;
|
||||||
if (
|
if (
|
||||||
event.deltaY < 0 &&
|
event.deltaY < 0 &&
|
||||||
Math.abs(
|
Math.abs(maxZoom / this._chart.getZoomLevel()) < 0.01
|
||||||
this._chart.getInitialScaleBounds().x.max /
|
|
||||||
this._chart.options.plugins.zoom.limits.x.minRange -
|
|
||||||
this._chart.getZoomLevel()
|
|
||||||
) < 0.01
|
|
||||||
) {
|
) {
|
||||||
// Disable wheel pan if zoomed in to the max, and zooming in
|
// Disable wheel pan if zoomed in to the max, and zooming in
|
||||||
return false;
|
return false;
|
||||||
@@ -262,7 +282,6 @@ export class ElevationProfile {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
stacked: false,
|
|
||||||
onResize: () => {
|
onResize: () => {
|
||||||
this.updateOverlay();
|
this.updateOverlay();
|
||||||
},
|
},
|
||||||
@@ -270,7 +289,7 @@ export class ElevationProfile {
|
|||||||
|
|
||||||
let datasets: string[] = ['speed', 'hr', 'cad', 'atemp', 'power'];
|
let datasets: string[] = ['speed', 'hr', 'cad', 'atemp', 'power'];
|
||||||
datasets.forEach((id) => {
|
datasets.forEach((id) => {
|
||||||
options.scales[`y${id}`] = {
|
options.scales![`y${id}`] = {
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
grid: {
|
grid: {
|
||||||
@@ -291,7 +310,7 @@ export class ElevationProfile {
|
|||||||
{
|
{
|
||||||
id: 'toggleMarker',
|
id: 'toggleMarker',
|
||||||
events: ['mouseout'],
|
events: ['mouseout'],
|
||||||
afterEvent: (chart: Chart, args: { event: Chart.ChartEvent }) => {
|
afterEvent: (chart: Chart, args: { event: ChartEvent }) => {
|
||||||
if (args.event.type === 'mouseout') {
|
if (args.event.type === 'mouseout') {
|
||||||
const map_ = get(map);
|
const map_ = get(map);
|
||||||
if (map_ && this._marker) {
|
if (map_ && this._marker) {
|
||||||
@@ -305,7 +324,7 @@ export class ElevationProfile {
|
|||||||
|
|
||||||
let startIndex = 0;
|
let startIndex = 0;
|
||||||
let endIndex = 0;
|
let endIndex = 0;
|
||||||
const getIndex = (evt) => {
|
const getIndex = (evt: PointerEvent) => {
|
||||||
if (!this._chart) {
|
if (!this._chart) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -329,16 +348,16 @@ export class ElevationProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let point = points.find((point) => point.element.raw);
|
const point = points.find((point) => (point.element as any).raw);
|
||||||
if (point) {
|
if (point) {
|
||||||
return point.element.raw.index;
|
return (point.element as any).raw.index;
|
||||||
} else {
|
} else {
|
||||||
return points[0].index;
|
return points[0].index;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let dragStarted = false;
|
let dragStarted = false;
|
||||||
const onMouseDown = (evt) => {
|
const onMouseDown = (evt: PointerEvent) => {
|
||||||
if (evt.shiftKey) {
|
if (evt.shiftKey) {
|
||||||
// Panning interaction
|
// Panning interaction
|
||||||
return;
|
return;
|
||||||
@@ -347,7 +366,7 @@ export class ElevationProfile {
|
|||||||
this._canvas.style.cursor = 'col-resize';
|
this._canvas.style.cursor = 'col-resize';
|
||||||
startIndex = getIndex(evt);
|
startIndex = getIndex(evt);
|
||||||
};
|
};
|
||||||
const onMouseMove = (evt) => {
|
const onMouseMove = (evt: PointerEvent) => {
|
||||||
if (dragStarted) {
|
if (dragStarted) {
|
||||||
this._dragging = true;
|
this._dragging = true;
|
||||||
endIndex = getIndex(evt);
|
endIndex = getIndex(evt);
|
||||||
@@ -367,7 +386,7 @@ export class ElevationProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onMouseUp = (evt) => {
|
const onMouseUp = (evt: PointerEvent) => {
|
||||||
dragStarted = false;
|
dragStarted = false;
|
||||||
this._dragging = false;
|
this._dragging = false;
|
||||||
this._canvas.style.cursor = '';
|
this._canvas.style.cursor = '';
|
||||||
@@ -463,8 +482,8 @@ export class ElevationProfile {
|
|||||||
normalized: true,
|
normalized: true,
|
||||||
yAxisID: 'ypower',
|
yAxisID: 'ypower',
|
||||||
};
|
};
|
||||||
this._chart.options.scales.x['min'] = 0;
|
this._chart.options.scales!.x!['min'] = 0;
|
||||||
this._chart.options.scales.x['max'] = getConvertedDistance(data.global.distance.total);
|
this._chart.options.scales!.x!['max'] = getConvertedDistance(data.global.distance.total);
|
||||||
|
|
||||||
this.setVisibility();
|
this.setVisibility();
|
||||||
this.setFill();
|
this.setFill();
|
||||||
@@ -513,21 +532,24 @@ export class ElevationProfile {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const elevationFill = get(this._elevationFill);
|
const elevationFill = get(this._elevationFill);
|
||||||
|
const dataset = this._chart.data.datasets[0];
|
||||||
|
let segment: any = {};
|
||||||
if (elevationFill === 'slope') {
|
if (elevationFill === 'slope') {
|
||||||
this._chart.data.datasets[0]['segment'] = {
|
segment = {
|
||||||
backgroundColor: this.slopeFillCallback,
|
backgroundColor: this.slopeFillCallback,
|
||||||
};
|
};
|
||||||
} else if (elevationFill === 'surface') {
|
} else if (elevationFill === 'surface') {
|
||||||
this._chart.data.datasets[0]['segment'] = {
|
segment = {
|
||||||
backgroundColor: this.surfaceFillCallback,
|
backgroundColor: this.surfaceFillCallback,
|
||||||
};
|
};
|
||||||
} else if (elevationFill === 'highway') {
|
} else if (elevationFill === 'highway') {
|
||||||
this._chart.data.datasets[0]['segment'] = {
|
segment = {
|
||||||
backgroundColor: this.highwayFillCallback,
|
backgroundColor: this.highwayFillCallback,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
this._chart.data.datasets[0]['segment'] = {};
|
segment = {};
|
||||||
}
|
}
|
||||||
|
Object.assign(dataset, { segment });
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOverlay() {
|
updateOverlay() {
|
||||||
@@ -575,19 +597,22 @@ export class ElevationProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slopeFillCallback(context) {
|
slopeFillCallback(context: ScriptableLineSegmentContext & { p0: { raw: any } }) {
|
||||||
return getSlopeColor(context.p0.raw.slope.segment);
|
const point = context.p0.raw as ElevationProfilePoint;
|
||||||
|
return getSlopeColor(point.slope.segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
surfaceFillCallback(context) {
|
surfaceFillCallback(context: ScriptableLineSegmentContext & { p0: { raw: any } }) {
|
||||||
return getSurfaceColor(context.p0.raw.extensions.surface);
|
const point = context.p0.raw as ElevationProfilePoint;
|
||||||
|
return getSurfaceColor(point.extensions.surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
highwayFillCallback(context) {
|
highwayFillCallback(context: ScriptableLineSegmentContext & { p0: { raw: any } }) {
|
||||||
|
const point = context.p0.raw as ElevationProfilePoint;
|
||||||
return getHighwayColor(
|
return getHighwayColor(
|
||||||
context.p0.raw.extensions.highway,
|
point.extensions.highway,
|
||||||
context.p0.raw.extensions.sac_scale,
|
point.extensions.sac_scale,
|
||||||
context.p0.raw.extensions.mtb_scale
|
point.extensions.mtb_scale
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { get, type Readable } from 'svelte/store';
|
import { get, type Readable } from 'svelte/store';
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl, { type FilterSpecification } from 'mapbox-gl';
|
||||||
import { map } from '$lib/components/map/map';
|
import { map } from '$lib/components/map/map';
|
||||||
import { waypointPopup, trackpointPopup } from './gpx-layer-popup';
|
import { waypointPopup, trackpointPopup } from './gpx-layer-popup';
|
||||||
import {
|
import {
|
||||||
@@ -281,25 +281,23 @@ export class GPXLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let visibleSegments: [number, number][] = [];
|
let visibleTrackSegmentIds: string[] = [];
|
||||||
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
||||||
if (!segment._data.hidden) {
|
if (!segment._data.hidden) {
|
||||||
visibleSegments.push([trackIndex, segmentIndex]);
|
visibleTrackSegmentIds.push(`${trackIndex}-${segmentIndex}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const segmentFilter: FilterSpecification = [
|
||||||
|
'in',
|
||||||
|
['get', 'trackSegmentId'],
|
||||||
|
['literal', visibleTrackSegmentIds],
|
||||||
|
];
|
||||||
|
|
||||||
_map.setFilter(
|
_map.setFilter(this.fileId, segmentFilter, { validate: false });
|
||||||
this.fileId,
|
|
||||||
[
|
if (_map.getLayer(this.fileId + '-direction')) {
|
||||||
'any',
|
_map.setFilter(this.fileId + '-direction', segmentFilter, { validate: false });
|
||||||
...visibleSegments.map(([trackIndex, segmentIndex]) => [
|
}
|
||||||
'all',
|
|
||||||
['==', 'trackIndex', trackIndex],
|
|
||||||
['==', 'segmentIndex', segmentIndex],
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
{ validate: false }
|
|
||||||
);
|
|
||||||
|
|
||||||
let visibleWaypoints: number[] = [];
|
let visibleWaypoints: number[] = [];
|
||||||
file.wpt.forEach((waypoint, waypointIndex) => {
|
file.wpt.forEach((waypoint, waypointIndex) => {
|
||||||
@@ -313,21 +311,6 @@ export class GPXLayer {
|
|||||||
['in', ['get', 'waypointIndex'], ['literal', visibleWaypoints]],
|
['in', ['get', 'waypointIndex'], ['literal', visibleWaypoints]],
|
||||||
{ validate: false }
|
{ validate: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_map.getLayer(this.fileId + '-direction')) {
|
|
||||||
_map.setFilter(
|
|
||||||
this.fileId + '-direction',
|
|
||||||
[
|
|
||||||
'any',
|
|
||||||
...visibleSegments.map(([trackIndex, segmentIndex]) => [
|
|
||||||
'all',
|
|
||||||
['==', 'trackIndex', trackIndex],
|
|
||||||
['==', 'segmentIndex', segmentIndex],
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
{ validate: false }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// No reliable way to check if the map is ready to add sources and layers
|
// No reliable way to check if the map is ready to add sources and layers
|
||||||
return;
|
return;
|
||||||
@@ -686,6 +669,7 @@ export class GPXLayer {
|
|||||||
}
|
}
|
||||||
feature.properties.trackIndex = trackIndex;
|
feature.properties.trackIndex = trackIndex;
|
||||||
feature.properties.segmentIndex = segmentIndex;
|
feature.properties.segmentIndex = segmentIndex;
|
||||||
|
feature.properties.trackSegmentId = `${trackIndex}-${segmentIndex}`;
|
||||||
|
|
||||||
segmentIndex++;
|
segmentIndex++;
|
||||||
if (segmentIndex >= file.trk[trackIndex].trkseg.length) {
|
if (segmentIndex >= file.trk[trackIndex].trkseg.length) {
|
||||||
|
|||||||
Reference in New Issue
Block a user